Skip to content

Commit 2dd5d58

Browse files
author
Iam54r1n4
committed
Add mac os deep link code
1 parent 7833647 commit 2dd5d58

1 file changed

Lines changed: 186 additions & 8 deletions

File tree

src-tauri/src/deep_link/macos.rs

Lines changed: 186 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,189 @@
1-
pub fn register<F: FnMut(String) + Send + 'static>(
2-
identifier: &str,
3-
scheme: &str,
4-
handler: F,
5-
) -> Result<(), std::io::Error> {
6-
unimplemented!()
1+
use std::{
2+
fs::remove_file,
3+
io::{ErrorKind, Read, Result, Write},
4+
os::unix::net::{UnixListener, UnixStream},
5+
sync::Mutex,
6+
};
7+
8+
use objc2::{
9+
class, declare_class, msg_send, msg_send_id,
10+
rc::{Id, Owned, Shared},
11+
runtime::{NSObject, Object},
12+
sel, ClassType,
13+
};
14+
use once_cell::sync::OnceCell;
15+
16+
use crate::ID;
17+
18+
type THandler = OnceCell<Mutex<Box<dyn FnMut(String) + Send + 'static>>>;
19+
20+
// If the Mutex turns out to be a problem, or FnMut turns out to be useless, we can remove the Mutex and turn FnMut into Fn
21+
static HANDLER: THandler = OnceCell::new();
22+
23+
pub fn register<F, Fut>(_scheme: &str, handler: F) -> Result<()>
24+
where
25+
F: FnMut(String) -> Fut + Send + 'static,
26+
Fut: Future<Output = ()> + Send + 'static,
27+
{
28+
listen(handler)?;
29+
30+
Ok(())
31+
}
32+
33+
pub fn unregister(_scheme: &str) -> Result<()> {
34+
Ok(())
35+
}
36+
37+
// kInternetEventClass
38+
const EVENT_CLASS: u32 = 0x4755524c;
39+
// kAEGetURL
40+
const EVENT_GET_URL: u32 = 0x4755524c;
41+
42+
// Adapted from https://github.com/mrmekon/fruitbasket/blob/aad14e400d710d1d46317c0d8c55ff742bfeaadd/src/osx.rs#L848
43+
fn parse_url_event(event: *mut Object) -> Option<String> {
44+
if event as u64 == 0u64 {
45+
return None;
46+
}
47+
unsafe {
48+
let class: u32 = msg_send![event, eventClass];
49+
let id: u32 = msg_send![event, eventID];
50+
if class != EVENT_CLASS || id != EVENT_GET_URL {
51+
return None;
52+
}
53+
54+
let subevent: *mut Object = msg_send![event, paramDescriptorForKeyword: 0x2d2d2d2d_u32];
55+
let nsstring: *mut Object = msg_send![subevent, stringValue];
56+
let cstr: *const i8 = msg_send![nsstring, UTF8String];
57+
if !cstr.is_null() {
58+
Some(std::ffi::CStr::from_ptr(cstr).to_string_lossy().to_string())
59+
} else {
60+
None
61+
}
62+
}
63+
}
64+
65+
declare_class!(
66+
struct Handler;
67+
68+
unsafe impl ClassType for Handler {
69+
type Super = NSObject;
70+
const NAME: &'static str = "TauriPluginDeepLinkHandler";
71+
}
72+
73+
unsafe impl Handler {
74+
#[method(handleEvent:withReplyEvent:)]
75+
fn handle_event(&self, event: *mut Object, _replace: *const Object) {
76+
let s = parse_url_event(event).unwrap_or_default();
77+
let mut cb = HANDLER.get().unwrap().lock().unwrap();
78+
cb(s);
79+
}
80+
}
81+
);
82+
83+
impl Handler {
84+
pub fn new() -> Id<Self, Owned> {
85+
let cls = Self::class();
86+
unsafe { msg_send_id![msg_send_id![cls, alloc], init] }
87+
}
88+
}
89+
90+
#[cfg(debug_assertions)]
91+
fn secondary_handler(s: String) {
92+
let addr = format!(
93+
"/tmp/{}-deep-link.sock",
94+
ID.get()
95+
.expect("URL event received before prepare() was called")
96+
);
97+
if let Ok(mut stream) = UnixStream::connect(addr) {
98+
if let Err(io_err) = stream.write_all(s.as_bytes()) {
99+
log::error!(
100+
"Error sending message to primary instance: {}",
101+
io_err.to_string()
102+
);
103+
};
104+
}
105+
std::process::exit(0);
106+
}
107+
108+
pub fn listen<F,Fut>(handler: F) -> Result<()>
109+
where
110+
F: FnMut(String) -> Fut + Send + 'static ,
111+
Fut: Future<Output = ()> + Send + 'static,{
112+
#[cfg(debug_assertions)]
113+
let addr = format!(
114+
"/tmp/{}-deep-link.sock",
115+
ID.get().expect("listen() called before prepare()")
116+
);
117+
118+
#[cfg(debug_assertions)]
119+
if HANDLER
120+
.set(match UnixStream::connect(&addr) {
121+
Ok(_) => Mutex::new(Box::new(secondary_handler)),
122+
Err(err) => {
123+
log::error!("Error creating socket listener: {}", err.to_string());
124+
if err.kind() == ErrorKind::ConnectionRefused {
125+
let _ = remove_file(&addr);
126+
}
127+
Mutex::new(Box::new(handler))
128+
}
129+
})
130+
.is_err()
131+
{
132+
return Err(std::io::Error::new(
133+
ErrorKind::AlreadyExists,
134+
"Handler was already set",
135+
));
136+
}
137+
138+
#[cfg(not(debug_assertions))]
139+
if HANDLER.set(Mutex::new(Box::new(handler))).is_err() {
140+
return Err(std::io::Error::new(
141+
ErrorKind::AlreadyExists,
142+
"Handler was already set",
143+
));
144+
}
145+
146+
unsafe {
147+
let event_manager: Id<Object, Shared> =
148+
msg_send_id![class!(NSAppleEventManager), sharedAppleEventManager];
149+
150+
let handler = Handler::new();
151+
let handler_boxed = Box::into_raw(Box::new(handler));
152+
153+
let _: () = msg_send![&event_manager,
154+
setEventHandler: &**handler_boxed
155+
andSelector: sel!(handleEvent:withReplyEvent:)
156+
forEventClass:EVENT_CLASS
157+
andEventID:EVENT_GET_URL];
158+
}
159+
160+
#[cfg(debug_assertions)]
161+
std::thread::spawn(move || {
162+
let listener = UnixListener::bind(addr).expect("Can't create listener");
163+
164+
for stream in listener.incoming() {
165+
match stream {
166+
Ok(mut stream) => {
167+
let mut buffer = String::new();
168+
if let Err(io_err) = stream.read_to_string(&mut buffer) {
169+
log::error!("Error reading incoming connection: {}", io_err.to_string());
170+
};
171+
172+
let mut cb = HANDLER.get().unwrap().lock().unwrap();
173+
cb(buffer);
174+
}
175+
Err(err) => {
176+
log::error!("Incoming connection failed: {}", err);
177+
continue;
178+
}
179+
}
180+
}
181+
});
182+
183+
Ok(())
7184
}
8185

9-
pub fn unregister(scheme: &str) -> Result<(), std::io::Error> {
10-
unimplemented!()
186+
pub fn prepare(identifier: &str) {
187+
ID.set(identifier.to_string())
188+
.expect("prepare() called more than once with different identifiers.");
11189
}

0 commit comments

Comments
 (0)