1818
1919use std:: {
2020 fs:: OpenOptions ,
21- io:: { ErrorKind , Result , Write } ,
22- sync:: Mutex ,
21+ io:: Write ,
2322} ;
2423
25- use objc2:: {
26- class, define_class,
27- ffi:: NSInteger ,
28- msg_send,
29- rc:: Retained ,
30- runtime:: { AnyObject , NSObject } ,
31- sel, ClassType ,
32- } ;
33- use once_cell:: sync:: OnceCell ;
3424use tauri:: { menu:: { Menu , MenuItem , PredefinedMenuItem , Submenu } , AppHandle } ;
3525
36- type THandler = OnceCell < Mutex < Box < dyn FnMut ( Vec < String > ) + Send + ' static > > > ;
37-
38- // 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
39- static HANDLER : THandler = OnceCell :: new ( ) ;
40-
41- // keyDirectObject
42- const KEY_DIRECT_OBJECT : u32 = 0x2d2d2d2d ;
43-
44- // kInternetEventClass
45- const GURL_EVENT_CLASS : u32 = 0x4755524c ;
46- // kAEGetURL
47- const EVENT_GET_URL : u32 = 0x4755524c ;
48-
49- // kCoreEventClass
50- const CORE_EVENT_CLASS : u32 = 0x61657674 ;
5126// kAEOpenDocuments
5227const EVENT_OPEN_DOCUMENTS : u32 = 0x6F646F63 ;
53- // kAEReopenApplication
54- const EVENT_REOPEN_APP : u32 = 0x72617070 ;
55-
56- // Adapted from https://github.com/mrmekon/fruitbasket/blob/aad14e400d710d1d46317c0d8c55ff742bfeaadd/src/osx.rs#L848
57- fn parse_event ( event : * mut AnyObject ) -> ( u32 , Vec < String > ) {
58- if event as u64 == 0u64 {
59- return ( 0 , vec ! [ ] ) ;
60- }
61- unsafe {
62- let class: u32 = msg_send ! [ event, eventClass] ;
63- let id: u32 = msg_send ! [ event, eventID] ;
64-
65- let payload = match ( class, id) {
66- ( GURL_EVENT_CLASS , EVENT_GET_URL ) => {
67- let url: * mut AnyObject =
68- msg_send ! [ event, paramDescriptorForKeyword: KEY_DIRECT_OBJECT ] ;
69- let nsstring: * mut AnyObject = msg_send ! [ url, stringValue] ;
70- let cstr: * const i8 = msg_send ! [ nsstring, UTF8String ] ;
71-
72- if !cstr. is_null ( ) {
73- vec ! [ std:: ffi:: CStr :: from_ptr( cstr) . to_string_lossy( ) . to_string( ) ]
74- } else {
75- vec ! [ ]
76- }
77- }
78- ( CORE_EVENT_CLASS , EVENT_OPEN_DOCUMENTS ) => {
79- let documents: * mut AnyObject =
80- msg_send ! [ event, paramDescriptorForKeyword: KEY_DIRECT_OBJECT ] ;
81- let count: NSInteger = msg_send ! [ documents, numberOfItems] ;
82-
83- let mut paths = Vec :: < String > :: new ( ) ;
84-
85- for i in 1 ..count + 1 {
86- let path: * mut AnyObject = msg_send ! [ documents, descriptorAtIndex: i] ;
87- let nsstring: * mut AnyObject = msg_send ! [ path, stringValue] ;
88- let cstr: * const i8 = msg_send ! [ nsstring, UTF8String ] ;
89-
90- if !cstr. is_null ( ) {
91- let path_str = std:: ffi:: CStr :: from_ptr ( cstr) . to_string_lossy ( ) . to_string ( ) ;
92- paths. push ( path_str) ;
93- }
94- }
95-
96- paths
97- }
98- // reopen app event has no useful payload
99- ( _, _) => {
100- vec ! [ ]
101- }
102- } ;
103-
104- ( id, payload)
105- }
106- }
28+ // kAEGetURL
29+ const EVENT_GET_URL : u32 = 0x4755524c ;
10730
10831fn log_event ( event_id : u32 , payload : & [ String ] ) {
10932 let Some ( path) = dirs:: home_dir ( ) . map ( |home| home. join ( "trgui.log" ) ) else {
@@ -116,73 +39,12 @@ fn log_event(event_id: u32, payload: &[String]) {
11639}
11740
11841pub fn log_opened_urls ( urls : & [ String ] ) {
119- log_event ( EVENT_OPEN_DOCUMENTS , urls) ;
120- }
121-
122- define_class ! (
123- #[ unsafe ( super ( NSObject ) ) ]
124- #[ name = "TauriPluginDeepLinkHandler" ]
125- struct Handler ;
126-
127- impl Handler {
128- #[ unsafe ( method( handleEvent: withReplyEvent: ) ) ]
129- fn handle_event( & self , event: * mut AnyObject , _replace: * const AnyObject ) {
130- let ( event_id, payload) = parse_event( event) ;
131- log_event( event_id, & payload) ;
132- let mut cb = HANDLER . get( ) . unwrap( ) . lock( ) . unwrap( ) ;
133- cb( payload) ;
134- }
135- }
136- ) ;
137-
138- impl Handler {
139- pub fn new ( ) -> Retained < Self > {
140- let cls = Self :: class ( ) ;
141- unsafe { msg_send ! [ msg_send![ cls, alloc] , init] }
142- }
143- }
144-
145- // Call this once early in app main() or setup hook
146- pub fn set_handler < F : FnMut ( Vec < String > ) + Send + ' static > ( handler : F ) -> Result < ( ) > {
147- if HANDLER . set ( Mutex :: new ( Box :: new ( handler) ) ) . is_err ( ) {
148- return Err ( std:: io:: Error :: new (
149- ErrorKind :: AlreadyExists ,
150- "Handler was already set" ,
151- ) ) ;
152- }
153-
154- Ok ( ( ) )
155- }
156-
157- fn listen_apple_event ( event_class : u32 , event_id : u32 ) {
158- unsafe {
159- let event_manager: Retained < AnyObject > =
160- msg_send ! [ class!( NSAppleEventManager ) , sharedAppleEventManager] ;
161-
162- let handler = Handler :: new ( ) ;
163- let handler_boxed = Box :: into_raw ( Box :: new ( handler) ) ;
164-
165- let _: ( ) = msg_send ! [ & event_manager,
166- setEventHandler: & * * handler_boxed,
167- andSelector: sel!( handleEvent: withReplyEvent: ) ,
168- forEventClass: event_class,
169- andEventID: event_id] ;
170- }
171- }
172-
173- // Call this in app setup hook
174- pub fn listen_url ( ) {
175- listen_apple_event ( GURL_EVENT_CLASS , EVENT_GET_URL ) ;
176- }
177-
178- // Call this in app setup hook
179- pub fn listen_open_documents ( ) {
180- listen_apple_event ( CORE_EVENT_CLASS , EVENT_OPEN_DOCUMENTS ) ;
181- }
182-
183- // Call this in app setup hook
184- pub fn listen_reopen_app ( ) {
185- listen_apple_event ( CORE_EVENT_CLASS , EVENT_REOPEN_APP ) ;
42+ let event_id = if urls. first ( ) . is_some_and ( |url| url. starts_with ( "file:" ) ) {
43+ EVENT_OPEN_DOCUMENTS
44+ } else {
45+ EVENT_GET_URL
46+ } ;
47+ log_event ( event_id, urls) ;
18648}
18749
18850pub fn make_menu < R > ( app : & AppHandle < R > ) -> tauri:: Result < Menu < R > >
0 commit comments