Merge pull request #3076 from Kingtous/master

feat: add url scheme handler for macos
This commit is contained in:
RustDesk
2023-02-05 12:38:01 +08:00
committed by GitHub
14 changed files with 211 additions and 48 deletions

View File

@@ -1,4 +1,6 @@
use hbb_common::log;
use std::future::Future;
use hbb_common::{log, ResultType};
/// shared by flutter and sciter main function
///
@@ -346,5 +348,11 @@ fn core_main_invoke_new_connection(mut args: std::env::Args) -> Option<Vec<Strin
return if res { None } else { Some(Vec::new()) };
}
#[cfg(target_os = "macos")]
return Some(Vec::new());
{
return if let Err(_) = crate::ipc::send_url_scheme(uni_links) {
Some(Vec::new())
} else {
None
}
}
}

View File

@@ -1,30 +1,27 @@
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};
use serde_json::json;
use crate::common::is_keyboard_mode_supported;
use hbb_common::message_proto::KeyboardMode;
use hbb_common::ResultType;
use hbb_common::{
config::{self, LocalConfig, PeerConfig, ONLINE},
config::{self, LocalConfig, ONLINE, PeerConfig},
fs, log,
};
use std::str::FromStr;
use hbb_common::message_proto::KeyboardMode;
use hbb_common::ResultType;
// use crate::hbbs_http::account::AuthResult;
use crate::flutter::{self, SESSIONS};
use crate::ui_interface::{self, *};
use crate::{
client::file_trait::FileManager,
common::make_fd_to_json,
flutter::{session_add, session_start_},
};
use crate::common::is_keyboard_mode_supported;
use crate::flutter::{self, SESSIONS};
use crate::ui_interface::{self, *};
// use crate::hbbs_http::account::AuthResult;
fn initialize(app_dir: &str) {
*config::APP_DIR.write().unwrap() = app_dir.to_owned();
#[cfg(target_os = "android")]
@@ -1255,16 +1252,32 @@ 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;
use jni::{
JNIEnv,
objects::{JClass, JString},
sys::jstring,
JNIEnv,
};
use hbb_common::log;
use crate::start_server;
#[no_mangle]

View File

@@ -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(())
}

View File

@@ -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,18 @@ 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};
pub mod audio_service;
cfg_if::cfg_if! {
@@ -55,8 +59,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 +427,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() {

View File

@@ -1,3 +1,5 @@
use std::{ffi::c_void, rc::Rc};
#[cfg(target_os = "macos")]
use cocoa::{
appkit::{NSApp, NSApplication, NSApplicationActivationPolicy::*, NSMenu, NSMenuItem},
@@ -8,11 +10,16 @@ use objc::{
class,
declare::ClassDecl,
msg_send,
runtime::{Object, Sel, BOOL},
runtime::{BOOL, Object, Sel},
sel, sel_impl,
};
use sciter::{make_args, Host};
use std::{ffi::c_void, rc::Rc};
use objc::runtime::Class;
use objc_id::WeakId;
use sciter::{Host, make_args};
use hbb_common::{log, tokio};
use crate::ui_cm_interface::start_ipc;
static APP_HANDLER_IVAR: &str = "GoDeskAppHandler";
@@ -98,12 +105,21 @@ unsafe fn set_delegate(handler: Option<Box<dyn AppHandler>>) {
sel!(handleMenuItem:),
handle_menu_item as extern "C" fn(&mut Object, Sel, id),
);
decl.add_method(sel!(handleEvent:withReplyEvent:), handle_apple_event as extern fn(&Object, Sel, u64, u64));
let decl = decl.register();
let delegate: id = msg_send![decl, alloc];
let () = msg_send![delegate, init];
let state = DelegateState { handler };
let handler_ptr = Box::into_raw(Box::new(state));
(*delegate).set_ivar(APP_HANDLER_IVAR, handler_ptr as *mut c_void);
// Set the url scheme handler
let cls = Class::get("NSAppleEventManager").unwrap();
let manager: *mut Object = msg_send![cls, sharedAppleEventManager];
let _: () = msg_send![manager,
setEventHandler: delegate
andSelector: sel!(handleEvent:withReplyEvent:)
forEventClass: fruitbasket::kInternetEventClass
andEventID: fruitbasket::kAEGetURL];
let () = msg_send![NSApp(), setDelegate: delegate];
}
@@ -167,6 +183,24 @@ extern "C" fn handle_menu_item(this: &mut Object, _: Sel, item: id) {
}
}
/// 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) {
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!("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 {
let title = NSString::alloc(nil).init_str(title);
let action = sel!(handleMenuItem:);