feat: add url scheme handler for macos
This commit is contained in:
parent
a9fc63c34f
commit
4dfae8da10
@ -11,8 +11,9 @@ const String kPeerPlatformLinux = "Linux";
|
||||
const String kPeerPlatformMacOS = "Mac OS";
|
||||
const String kPeerPlatformAndroid = "Android";
|
||||
|
||||
/// [kAppTypeMain] used by 'Desktop Main Page' , 'Mobile (Client and Server)' , 'Desktop CM Page', "Install Page"
|
||||
/// [kAppTypeMain] used by 'Desktop Main Page' , 'Mobile (Client and Server)', "Install Page"
|
||||
const String kAppTypeMain = "main";
|
||||
const String kAppTypeConnectionManager = "cm";
|
||||
const String kAppTypeDesktopRemote = "remote";
|
||||
const String kAppTypeDesktopFileTransfer = "file transfer";
|
||||
const String kAppTypeDesktopPortForward = "port forward";
|
||||
|
@ -211,7 +211,7 @@ void runMultiWindow(
|
||||
}
|
||||
|
||||
void runConnectionManagerScreen(bool hide) async {
|
||||
await initEnv(kAppTypeMain);
|
||||
await initEnv(kAppTypeConnectionManager);
|
||||
_runApp(
|
||||
'',
|
||||
const DesktopServerPage(),
|
||||
|
@ -199,6 +199,9 @@ class FfiModel with ChangeNotifier {
|
||||
final peer_id = evt['peer_id'].toString();
|
||||
await bind.sessionSwitchSides(id: peer_id);
|
||||
closeConnection(id: peer_id);
|
||||
} else if (name == "on_url_scheme_received") {
|
||||
final url = evt['url'].toString();
|
||||
parseRustdeskUri(url);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import 'package:external_path/external_path.dart';
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hbb/consts.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:win32/win32.dart' as win32;
|
||||
@ -46,6 +47,8 @@ class PlatformFFI {
|
||||
|
||||
static get localeName => Platform.localeName;
|
||||
|
||||
static get isMain => instance._appType == kAppTypeMain;
|
||||
|
||||
static Future<String> getVersion() async {
|
||||
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||
return packageInfo.version;
|
||||
@ -112,8 +115,11 @@ class PlatformFFI {
|
||||
}
|
||||
_ffiBind = RustdeskImpl(dylib);
|
||||
if (Platform.isLinux) {
|
||||
// start dbus service, no need to await
|
||||
await _ffiBind.mainStartDbusServer();
|
||||
// Start a dbus service, no need to await
|
||||
_ffiBind.mainStartDbusServer();
|
||||
} else if (Platform.isMacOS) {
|
||||
// Start an ipc server for handling url schemes.
|
||||
_ffiBind.mainStartIpcUrlServer();
|
||||
}
|
||||
_startListenEvent(_ffiBind); // global event
|
||||
try {
|
||||
|
@ -1,8 +1,4 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
ffi::{CStr, CString},
|
||||
os::raw::c_char,
|
||||
};
|
||||
use std::{collections::HashMap, ffi::{CStr, CString}, os::raw::c_char, thread};
|
||||
use std::str::FromStr;
|
||||
|
||||
use flutter_rust_bridge::{StreamSink, SyncReturn, ZeroCopyBuffer};
|
||||
@ -1261,6 +1257,23 @@ pub fn main_hide_docker() -> SyncReturn<bool> {
|
||||
SyncReturn(true)
|
||||
}
|
||||
|
||||
/// Start an ipc server for receiving the url scheme.
|
||||
///
|
||||
/// * Should only be called in the main flutter window.
|
||||
/// * macOS only
|
||||
pub fn main_start_ipc_url_server() {
|
||||
#[cfg(target_os = "macos")]
|
||||
thread::spawn(move || crate::server::start_ipc_url_server());
|
||||
}
|
||||
|
||||
/// Send a url scheme throught the ipc.
|
||||
///
|
||||
/// * macOS only
|
||||
pub fn send_url_scheme(url: String) {
|
||||
#[cfg(target_os = "macos")]
|
||||
thread::spawn(move || crate::ui::macos::handle_url_scheme(url));
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
pub mod server_side {
|
||||
use hbb_common::log;
|
||||
@ -1268,7 +1281,7 @@ pub mod server_side {
|
||||
JNIEnv,
|
||||
objects::{JClass, JString},
|
||||
sys::jstring,
|
||||
};
|
||||
};
|
||||
|
||||
use crate::start_server;
|
||||
|
||||
|
11
src/ipc.rs
11
src/ipc.rs
@ -16,10 +16,10 @@ use hbb_common::{
|
||||
config::{self, Config, Config2},
|
||||
futures::StreamExt as _,
|
||||
futures_util::sink::SinkExt,
|
||||
log, password_security as password, timeout, tokio,
|
||||
log, password_security as password, ResultType, timeout,
|
||||
tokio,
|
||||
tokio::io::{AsyncRead, AsyncWrite},
|
||||
tokio_util::codec::Framed,
|
||||
ResultType,
|
||||
};
|
||||
|
||||
use crate::rendezvous_mediator::RendezvousMediator;
|
||||
@ -210,6 +210,7 @@ pub enum Data {
|
||||
DataPortableService(DataPortableService),
|
||||
SwitchSidesRequest(String),
|
||||
SwitchSidesBack,
|
||||
UrlLink(String)
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
@ -832,3 +833,9 @@ pub async fn test_rendezvous_server() -> ResultType<()> {
|
||||
c.send(&Data::TestRendezvousServer).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
pub async fn send_url_scheme(url: String) -> ResultType<()> {
|
||||
connect(1_000, "_url").await?.send(&Data::UrlLink(url)).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,8 +1,13 @@
|
||||
use crate::ipc::Data;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
net::SocketAddr,
|
||||
sync::{Arc, Mutex, RwLock, Weak},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use bytes::Bytes;
|
||||
|
||||
pub use connection::*;
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
use hbb_common::config::Config2;
|
||||
use hbb_common::{
|
||||
allow_err,
|
||||
anyhow::{anyhow, Context},
|
||||
@ -12,19 +17,19 @@ use hbb_common::{
|
||||
message_proto::*,
|
||||
protobuf::{Enum, Message as _},
|
||||
rendezvous_proto::*,
|
||||
ResultType,
|
||||
socket_client,
|
||||
sodiumoxide::crypto::{box_, secretbox, sign},
|
||||
timeout, tokio, ResultType, Stream,
|
||||
sodiumoxide::crypto::{box_, secretbox, sign}, Stream, timeout, tokio,
|
||||
};
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
use service::ServiceTmpl;
|
||||
use hbb_common::config::Config2;
|
||||
use hbb_common::tcp::new_listener;
|
||||
use service::{GenericService, Service, Subscriber};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
net::SocketAddr,
|
||||
sync::{Arc, Mutex, RwLock, Weak},
|
||||
time::Duration,
|
||||
};
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
use service::ServiceTmpl;
|
||||
|
||||
use crate::ipc::{connect, Data};
|
||||
use crate::ui_interface::SENDER;
|
||||
|
||||
pub mod audio_service;
|
||||
cfg_if::cfg_if! {
|
||||
@ -55,8 +60,6 @@ mod service;
|
||||
mod video_qos;
|
||||
pub mod video_service;
|
||||
|
||||
use hbb_common::tcp::new_listener;
|
||||
|
||||
pub type Childs = Arc<Mutex<Vec<std::process::Child>>>;
|
||||
type ConnMap = HashMap<i32, ConnInner>;
|
||||
|
||||
@ -425,6 +428,50 @@ pub async fn start_server(is_server: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
pub async fn start_ipc_url_server() {
|
||||
log::debug!("Start an ipc server for listening to url schemes");
|
||||
match crate::ipc::new_listener("_url").await {
|
||||
Ok(mut incoming) => {
|
||||
while let Some(Ok(conn)) = incoming.next().await {
|
||||
let mut conn = crate::ipc::Connection::new(conn);
|
||||
match conn.next_timeout(1000).await {
|
||||
Ok(Some(data)) => {
|
||||
match data {
|
||||
Data::UrlLink(url) => {
|
||||
#[cfg(feature = "flutter")]
|
||||
{
|
||||
if let Some(stream) = crate::flutter::GLOBAL_EVENT_STREAM.read().unwrap().get(
|
||||
crate::flutter::APP_TYPE_MAIN
|
||||
) {
|
||||
let mut m = HashMap::new();
|
||||
m.insert("name", "on_url_scheme_received");
|
||||
m.insert("url", url.as_str());
|
||||
stream.add(serde_json::to_string(&m).unwrap());
|
||||
} else {
|
||||
log::warn!("No main window app found!");
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
log::warn!("An unexpected data was sent to the ipc url server.")
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("{}", err);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("{}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
async fn sync_and_watch_config_dir() {
|
||||
if crate::platform::is_root() {
|
||||
|
@ -17,7 +17,9 @@ use objc::runtime::Class;
|
||||
use objc_id::WeakId;
|
||||
use sciter::{Host, make_args};
|
||||
|
||||
use hbb_common::log;
|
||||
use hbb_common::{log, tokio};
|
||||
|
||||
use crate::ui_cm_interface::start_ipc;
|
||||
|
||||
static APP_HANDLER_IVAR: &str = "GoDeskAppHandler";
|
||||
|
||||
@ -181,16 +183,22 @@ extern "C" fn handle_menu_item(this: &mut Object, _: Sel, item: id) {
|
||||
}
|
||||
}
|
||||
|
||||
/// The function to handle the url scheme sent by system.
|
||||
/// The function to handle the url scheme sent by the system.
|
||||
///
|
||||
/// 1. Try to send the url scheme from ipc.
|
||||
/// 2. If failed to send the url scheme, we open a new main window to handle this url scheme.
|
||||
pub fn handle_url_scheme(url: String) {
|
||||
unimplemented!();
|
||||
if let Err(err) = crate::ipc::send_url_scheme(url.clone()) {
|
||||
log::debug!("Send the url to the existing flutter process failed, {}. Let's open a new program to handle this.", err);
|
||||
let _ = crate::run_me(vec![url]);
|
||||
}
|
||||
}
|
||||
|
||||
extern fn handle_apple_event(_this: &Object, _cmd: Sel, event: u64, _reply: u64) {
|
||||
let event = event as *mut Object;
|
||||
let url = fruitbasket::parse_url_event(event);
|
||||
log::debug!("event found {}", url);
|
||||
handle_url_scheme(url);
|
||||
log::debug!("an event was received: {}", url);
|
||||
std::thread::spawn(move || handle_url_scheme(url));
|
||||
}
|
||||
|
||||
unsafe fn make_menu_item(title: &str, key: &str, tag: u32) -> *mut Object {
|
||||
|
Loading…
x
Reference in New Issue
Block a user