Merge pull request #605 from Kingtous/wip/flutter_desktop_ffi

WIP: flutter desktop ffi
This commit is contained in:
RustDesk 2022-05-25 16:02:44 +08:00 committed by GitHub
commit 0046407c1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 234 additions and 93 deletions

View File

@ -78,11 +78,19 @@ jobs:
shell: bash
run: |
case ${{ matrix.job.target }} in
x86_64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt install -y g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake ;;
x86_64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt install -y g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake libclang-dev;;
# arm-unknown-linux-*) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;;
# aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;;
esac
- name: Install flutter
uses: subosito/flutter-action@v2
with:
channel: 'stable'
- name: Install flutter rust bridge deps
run: |
dart pub global activate ffigen
- name: Restore from cache and install vcpkg
uses: lukka/run-vcpkg@v7
with:

View File

@ -82,11 +82,11 @@ fn main() {
hbb_common::gen_version();
install_oboe();
// there is problem with cfg(target_os) in build.rs, so use our workaround
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
if target_os == "android" || target_os == "ios" {
// let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
// if target_os == "android" || target_os == "ios" {
gen_flutter_rust_bridge();
return;
}
// return;
// }
#[cfg(all(windows, feature = "inline"))]
build_manifest();
#[cfg(windows)]

View File

@ -0,0 +1,87 @@
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/models/model.dart';
import 'package:provider/provider.dart';
class DesktopHomePage extends StatefulWidget {
DesktopHomePage({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() => _DesktopHomePageState();
}
class _DesktopHomePageState extends State<DesktopHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Row(
children: [
Flexible(
child: buildServerInfo(context),
flex: 1,
),
Flexible(
child: buildServerBoard(context),
flex: 4,
),
],
),
),
);
}
buildServerInfo(BuildContext context) {
return ChangeNotifierProvider.value(
value: FFI.serverModel,
child: Column(
children: [buildIDBoard(context)],
),
);
}
buildServerBoard(BuildContext context) {
return Center(
child: Text("waiting implementation"),
);
}
buildIDBoard(BuildContext context) {
final model = FFI.serverModel;
return Card(
elevation: 0.5,
child: Container(
margin: EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
children: [
Container(
width: 4,
height: 70,
decoration: BoxDecoration(color: MyTheme.accent),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
translate("ID"),
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.w500),
),
TextFormField(
controller: model.serverId,
),
],
),
),
),
],
),
),
);
}
}

View File

@ -1,16 +1,13 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/pages/desktop_home_page.dart';
import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:provider/provider.dart';
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:firebase_core/firebase_core.dart';
import 'common.dart';
import 'mobile/pages/home_page.dart';
import 'mobile/pages/server_page.dart';
import 'mobile/pages/settings_page.dart';
import 'models/model.dart';
import 'pages/home_page.dart';
import 'pages/server_page.dart';
import 'pages/settings_page.dart';
Future<Null> main() async {
WidgetsFlutterBinding.ensureInitialized();
@ -20,6 +17,10 @@ Future<Null> main() async {
toAndroidChannelInit();
}
refreshCurrentUser();
if (isDesktop) {
print("desktop mode: starting service");
FFI.serverModel.startService();
}
runApp(App());
}

View File

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/models/chat_model.dart';
import 'package:provider/provider.dart';
import '../models/model.dart';
import '../../models/model.dart';
import 'home_page.dart';
ChatPage chatPage = ChatPage();

View File

@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter_hbb/pages/file_manager_page.dart';
import 'package:flutter_hbb/mobile/pages/file_manager_page.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import 'dart:async';
import '../common.dart';
import '../models/model.dart';
import '../../common.dart';
import '../../models/model.dart';
import 'home_page.dart';
import 'remote_page.dart';
import 'settings_page.dart';

View File

@ -7,8 +7,8 @@ import 'package:flutter_breadcrumb/flutter_breadcrumb.dart';
import 'package:wakelock/wakelock.dart';
import 'package:toggle_switch/toggle_switch.dart';
import '../common.dart';
import '../models/model.dart';
import '../../common.dart';
import '../../models/model.dart';
import '../widgets/dialog.dart';
class FileManagerPage extends StatefulWidget {

View File

@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_hbb/pages/chat_page.dart';
import 'package:flutter_hbb/pages/server_page.dart';
import 'package:flutter_hbb/pages/settings_page.dart';
import '../common.dart';
import 'package:flutter_hbb/mobile/pages/chat_page.dart';
import 'package:flutter_hbb/mobile/pages/server_page.dart';
import 'package:flutter_hbb/mobile/pages/settings_page.dart';
import '../../common.dart';
import '../widgets/overlay.dart';
import 'connection_page.dart';

View File

@ -1,16 +1,16 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/models/chat_model.dart';
import 'package:flutter_hbb/widgets/gesture_help.dart';
import 'package:flutter_hbb/mobile/widgets/gesture_help.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:provider/provider.dart';
import 'package:flutter/services.dart';
import 'dart:ui' as ui;
import 'dart:async';
import 'package:wakelock/wakelock.dart';
import '../common.dart';
import '../../common.dart';
import '../widgets/gestures.dart';
import '../models/model.dart';
import '../../models/model.dart';
import '../widgets/dialog.dart';
import '../widgets/overlay.dart';

View File

@ -6,8 +6,8 @@ import 'package:zxing2/qrcode.dart';
import 'dart:io';
import 'dart:async';
import 'dart:convert';
import '../common.dart';
import '../models/model.dart';
import '../../common.dart';
import '../../models/model.dart';
class ScanPage extends StatefulWidget {
@override

View File

@ -1,13 +1,13 @@
import 'package:flutter/material.dart';
import 'package:flutter_hbb/models/model.dart';
import 'package:flutter_hbb/widgets/dialog.dart';
import 'package:flutter_hbb/mobile/widgets/dialog.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:provider/provider.dart';
import '../common.dart';
import '../models/server_model.dart';
import '../../common.dart';
import '../../models/server_model.dart';
import 'home_page.dart';
import '../models/model.dart';
import '../../models/model.dart';
class ServerPage extends StatelessWidget implements PageShape {
@override

View File

@ -4,9 +4,9 @@ import 'package:url_launcher/url_launcher.dart';
import 'package:provider/provider.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../common.dart';
import '../../common.dart';
import '../widgets/dialog.dart';
import '../models/model.dart';
import '../../models/model.dart';
import 'home_page.dart';
import 'scan_page.dart';

View File

@ -2,8 +2,8 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import '../common.dart';
import '../models/model.dart';
import '../../common.dart';
import '../../models/model.dart';
void clientClose() {
msgBox('', 'Close', 'Are you sure to close the connection?');

View File

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_hbb/common.dart';
import 'package:toggle_switch/toggle_switch.dart';
import '../models/model.dart';
import '../../models/model.dart';
class GestureIcons {
static const String _family = 'gestureicons';

View File

@ -2,7 +2,7 @@ import 'package:draggable_float_widget/draggable_float_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common.dart';
import '../models/model.dart';
import '../../models/model.dart';
import '../pages/chat_page.dart';
OverlayEntry? chatIconOverlayEntry;

View File

@ -3,7 +3,7 @@ import 'dart:convert';
import 'package:dash_chat/dash_chat.dart';
import 'package:flutter/material.dart';
import '../widgets/overlay.dart';
import '../../mobile/widgets/overlay.dart';
import 'model.dart';
class MessageBody {

View File

@ -1,8 +1,8 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/pages/file_manager_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/mobile/pages/file_manager_page.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:path/path.dart' as Path;

View File

@ -12,8 +12,8 @@ import 'package:flutter/material.dart';
import 'package:tuple/tuple.dart';
import 'dart:async';
import '../common.dart';
import '../widgets/dialog.dart';
import '../widgets/overlay.dart';
import '../mobile/widgets/dialog.dart';
import '../mobile/widgets/overlay.dart';
import 'native_model.dart' if (dart.library.html) 'web_model.dart';
typedef HandleMsgBox = void Function(Map<String, dynamic> evt, String id);

View File

@ -1,15 +1,17 @@
import 'dart:convert';
import 'dart:ffi';
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 'package:package_info/package_info.dart';
import 'package:external_path/external_path.dart';
import 'package:ffi/ffi.dart';
import 'package:flutter/services.dart';
import '../generated_bridge.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:path_provider/path_provider.dart';
import '../common.dart';
import '../generated_bridge.dart';
class RgbaFrame extends Struct {
@Uint32()
@ -60,12 +62,18 @@ class PlatformFFI {
isIOS = Platform.isIOS;
isAndroid = Platform.isAndroid;
isDesktop = Platform.isWindows || Platform.isMacOS || Platform.isLinux;
if (isDesktop) {
// TODO
return;
}
// if (isDesktop) {
// // TODO
// return;
// }
final dylib = Platform.isAndroid
? DynamicLibrary.open('librustdesk.so')
: Platform.isLinux
? DynamicLibrary.open("/usr/lib/rustdesk/librustdesk.so")
: Platform.isWindows
? DynamicLibrary.open("librustdesk.dll")
: Platform.isMacOS
? DynamicLibrary.open("librustdesk.dylib")
: DynamicLibrary.process();
print('initializing FFI');
try {

View File

@ -1,9 +1,12 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:wakelock/wakelock.dart';
import '../common.dart';
import '../pages/server_page.dart';
import '../mobile/pages/server_page.dart';
import 'model.dart';
const loginDialogTag = "LOGIN";
@ -203,8 +206,11 @@ class ServerModel with ChangeNotifier {
FFI.setByName("start_service");
getIDPasswd();
updateClientState();
if (!Platform.isLinux) {
// current linux is not supported
Wakelock.enable();
}
}
Future<Null> stopService() async {
_isStart = false;
@ -212,8 +218,11 @@ class ServerModel with ChangeNotifier {
await FFI.invokeMethod("stop_service");
FFI.setByName("stop_service");
notifyListeners();
if (!Platform.isLinux) {
// current linux is not supported
Wakelock.disable();
}
}
Future<Null> initInput() async {
await FFI.invokeMethod("init_input");

View File

@ -1,15 +0,0 @@
import 'package:flutter/material.dart';
class DesktopHomePage extends StatefulWidget {
DesktopHomePage({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() => _DesktopHomePageState();
}
class _DesktopHomePageState extends State<DesktopHomePage> {
@override
Widget build(BuildContext context) {
return Text("Hello Desktop");
}
}

View File

@ -7,7 +7,7 @@ import Foundation
import firebase_analytics
import firebase_core
import package_info
import package_info_plus_macos
import path_provider_macos
import shared_preferences_macos
import url_launcher_macos
@ -16,7 +16,7 @@ import wakelock_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FLTFirebaseAnalyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAnalyticsPlugin"))
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
FLTPackageInfoPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlugin"))
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))

View File

@ -262,14 +262,14 @@ packages:
name: http_parser
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0"
version: "4.0.1"
image:
dependency: "direct main"
description:
name: image
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.3"
version: "3.2.0"
image_picker:
dependency: "direct main"
description:
@ -297,7 +297,7 @@ packages:
name: image_picker_ios
url: "https://pub.dartlang.org"
source: hosted
version: "0.8.5+2"
version: "0.8.5+5"
image_picker_platform_interface:
dependency: transitive
description:
@ -347,13 +347,48 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
package_info:
package_info_plus:
dependency: "direct main"
description:
name: package_info
name: package_info_plus
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
version: "1.4.2"
package_info_plus_linux:
dependency: transitive
description:
name: package_info_plus_linux
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.5"
package_info_plus_macos:
dependency: transitive
description:
name: package_info_plus_macos
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
package_info_plus_platform_interface:
dependency: transitive
description:
name: package_info_plus_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
package_info_plus_web:
dependency: transitive
description:
name: package_info_plus_web
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.5"
package_info_plus_windows:
dependency: transitive
description:
name: package_info_plus_windows
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.5"
path:
dependency: transitive
description:
@ -423,7 +458,7 @@ packages:
name: petitparser
url: "https://pub.dartlang.org"
source: hosted
version: "4.4.0"
version: "5.0.0"
platform:
dependency: transitive
description:
@ -603,7 +638,7 @@ packages:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
version: "1.3.1"
url_launcher:
dependency: "direct main"
description:
@ -624,7 +659,7 @@ packages:
name: url_launcher_ios
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.16"
version: "6.0.17"
url_launcher_linux:
dependency: transitive
description:
@ -715,7 +750,7 @@ packages:
name: win32
url: "https://pub.dartlang.org"
source: hosted
version: "2.5.2"
version: "2.6.1"
xdg_directories:
dependency: transitive
description:
@ -729,7 +764,7 @@ packages:
name: xml
url: "https://pub.dartlang.org"
source: hosted
version: "5.3.1"
version: "6.0.1"
yaml:
dependency: transitive
description:
@ -745,5 +780,5 @@ packages:
source: hosted
version: "0.1.0"
sdks:
dart: ">=2.17.0-0 <3.0.0"
dart: ">=2.17.0 <3.0.0"
flutter: ">=2.10.0"

View File

@ -37,7 +37,7 @@ dependencies:
wakelock: ^0.5.2
device_info: ^2.0.2
firebase_analytics: ^9.1.5
package_info: ^2.0.2
package_info_plus: ^1.4.2
url_launcher: ^6.0.9
shared_preferences: ^2.0.6
toggle_switch: ^1.4.0

View File

@ -39,7 +39,7 @@ lazy_static::lazy_static! {
pub static ref PROD_RENDEZVOUS_SERVER: Arc<RwLock<String>> = Default::default();
pub static ref APP_NAME: Arc<RwLock<String>> = Arc::new(RwLock::new("RustDesk".to_owned()));
}
#[cfg(any(target_os = "android", target_os = "ios"))]
// #[cfg(any(target_os = "android", target_os = "ios"))]
lazy_static::lazy_static! {
pub static ref APP_DIR: Arc<RwLock<String>> = Default::default();
pub static ref APP_HOME_DIR: Arc<RwLock<String>> = Default::default();

View File

@ -12,7 +12,7 @@ use hbb_common::{
rendezvous_proto::*,
sleep, socket_client, tokio, ResultType,
};
#[cfg(any(target_os = "android", target_os = "ios", feature = "cli"))]
// #[cfg(any(target_os = "android", target_os = "ios", feature = "cli"))]
use hbb_common::{config::RENDEZVOUS_PORT, futures::future::join_all};
use std::sync::{Arc, Mutex};
@ -336,7 +336,7 @@ pub async fn get_nat_type(ms_timeout: u64) -> i32 {
crate::ipc::get_nat_type(ms_timeout).await
}
#[cfg(any(target_os = "android", target_os = "ios", feature = "cli"))]
// #[cfg(any(target_os = "android", target_os = "ios", feature = "cli"))]
#[tokio::main(flavor = "current_thread")]
async fn test_rendezvous_server_() {
let servers = Config::get_rendezvous_servers();
@ -363,7 +363,7 @@ async fn test_rendezvous_server_() {
join_all(futs).await;
}
#[cfg(any(target_os = "android", target_os = "ios", feature = "cli"))]
// #[cfg(any(target_os = "android", target_os = "ios", feature = "cli"))]
pub fn test_rendezvous_server() {
std::thread::spawn(test_rendezvous_server_);
}

View File

@ -21,11 +21,11 @@ pub mod ipc;
pub mod ui;
mod version;
pub use version::*;
#[cfg(any(target_os = "android", target_os = "ios"))]
// #[cfg(any(target_os = "android", target_os = "ios"))]
mod bridge_generated;
#[cfg(any(target_os = "android", target_os = "ios"))]
// #[cfg(any(target_os = "android", target_os = "ios"))]
pub mod mobile;
#[cfg(any(target_os = "android", target_os = "ios"))]
// #[cfg(any(target_os = "android", target_os = "ios"))]
pub mod mobile_ffi;
use common::*;
#[cfg(feature = "cli")]

View File

@ -1165,7 +1165,7 @@ pub fn make_fd_to_json(fd: FileDirectory) -> String {
// Server Side
// TODO connection_manager need use struct and trait,impl default method
#[cfg(target_os = "android")]
#[cfg(not(any(target_os = "ios")))]
pub mod connection_manager {
use std::{
collections::HashMap,
@ -1191,6 +1191,7 @@ pub mod connection_manager {
task::spawn_blocking,
},
};
#[cfg(any(target_os = "android"))]
use scrap::android::call_main_service_set_by_name;
use serde_derive::Serialize;
@ -1253,6 +1254,7 @@ pub mod connection_manager {
client.authorized = true;
let client_json = serde_json::to_string(&client).unwrap_or("".into());
// send to Android service,active notification no matter UI is shown or not.
#[cfg(any(target_os = "android"))]
if let Err(e) = call_main_service_set_by_name(
"on_client_authorized",
Some(&client_json),
@ -1265,6 +1267,7 @@ pub mod connection_manager {
} else {
let client_json = serde_json::to_string(&client).unwrap_or("".into());
// send to Android service,active notification no matter UI is shown or not.
#[cfg(any(target_os = "android"))]
if let Err(e) = call_main_service_set_by_name(
"try_start_without_auth",
Some(&client_json),
@ -1343,6 +1346,7 @@ pub mod connection_manager {
.next()
.is_none()
{
#[cfg(any(target_os = "android"))]
if let Err(e) = call_main_service_set_by_name("stop_capture", None, None) {
log::debug!("stop_capture err:{}", e);
}

View File

@ -29,6 +29,7 @@ fn initialize(app_dir: &str) {
use hbb_common::env_logger::*;
init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "debug"));
}
#[cfg(any(target_os = "android", target_os = "ios", feature = "cli"))]
crate::common::test_rendezvous_server();
crate::common::test_nat_type();
#[cfg(target_os = "android")]
@ -182,9 +183,11 @@ unsafe extern "C" fn set_by_name(name: *const c_char, value: *const c_char) {
"init" => {
initialize(value);
}
#[cfg(any(target_os = "android", target_os = "ios"))]
"info1" => {
*crate::common::MOBILE_INFO1.lock().unwrap() = value.to_owned();
}
#[cfg(any(target_os = "android", target_os = "ios"))]
"info2" => {
*crate::common::MOBILE_INFO2.lock().unwrap() = value.to_owned();
}
@ -293,6 +296,7 @@ unsafe extern "C" fn set_by_name(name: *const c_char, value: *const c_char) {
if name == "custom-rendezvous-server" {
#[cfg(target_os = "android")]
crate::rendezvous_mediator::RendezvousMediator::restart();
#[cfg(any(target_os = "android", target_os = "ios", feature = "cli"))]
crate::common::test_rendezvous_server();
}
}