11use std:: {
22 env,
3- path:: { Path , PathBuf } ,
3+ path:: Path ,
4+ process,
45 rc:: Rc ,
5- sync:: Mutex ,
6+ sync:: {
7+ Mutex ,
8+ atomic:: { AtomicUsize , Ordering } ,
9+ } ,
610} ;
711
8- use wxdragon:: prelude:: * ;
12+ use wxdragon:: { prelude:: * , translations :: translate as t } ;
913
1014use super :: MainWindow ;
11- use crate :: { config:: ConfigManager , translation_manager:: TranslationManager } ;
15+ use crate :: {
16+ config:: ConfigManager ,
17+ ipc:: {
18+ IPC_COMMAND_ACTIVATE , IPC_HOST_LOCALHOST , IPC_SERVICE , IPC_TOPIC_OPEN_FILE , IpcCommand , SINGLE_INSTANCE_NAME ,
19+ decode_execute_payload, normalize_cli_path,
20+ } ,
21+ translation_manager:: TranslationManager ,
22+ } ;
1223
1324pub struct PaperbackApp {
1425 _config : Rc < Mutex < ConfigManager > > ,
15- _main_window : MainWindow ,
26+ _main_window : Rc < MainWindow > ,
27+ _ipc_server : Option < IPCServer > ,
28+ _single_instance_checker : Option < SingleInstanceChecker > ,
1629}
1730
31+ static MAIN_WINDOW_PTR : AtomicUsize = AtomicUsize :: new ( 0 ) ;
32+
1833impl PaperbackApp {
1934 pub fn new ( _app : App ) -> Self {
2035 let mut config = ConfigManager :: new ( ) ;
@@ -28,14 +43,28 @@ impl PaperbackApp {
2843 }
2944 }
3045 let config = Rc :: new ( Mutex :: new ( config) ) ;
31- let main_window = MainWindow :: new ( Rc :: clone ( & config) ) ;
46+ let single_instance_checker = SingleInstanceChecker :: new ( SINGLE_INSTANCE_NAME , None ) ;
47+ if let Some ( checker) = single_instance_checker. as_ref ( ) {
48+ if checker. is_another_running ( ) {
49+ send_ipc_command ( ipc_command_from_cli ( ) ) ;
50+ process:: exit ( 0 ) ;
51+ }
52+ }
53+ let main_window = Rc :: new ( MainWindow :: new ( Rc :: clone ( & config) ) ) ;
54+ MAIN_WINDOW_PTR . store ( Rc :: as_ptr ( & main_window) as usize , Ordering :: SeqCst ) ;
3255 wxdragon:: app:: set_top_window ( main_window. frame ( ) ) ;
56+ let ipc_server = start_ipc_server ( Rc :: clone ( & main_window) ) ;
3357 main_window. show ( ) ;
3458 open_from_command_line ( & main_window) ;
3559 if config. lock ( ) . unwrap ( ) . get_app_bool ( "check_for_updates_on_startup" , true ) {
3660 MainWindow :: check_for_updates ( true ) ;
3761 }
38- Self { _config : config, _main_window : main_window }
62+ Self {
63+ _config : config,
64+ _main_window : main_window,
65+ _ipc_server : ipc_server,
66+ _single_instance_checker : single_instance_checker,
67+ }
3968 }
4069}
4170
@@ -46,12 +75,63 @@ fn open_from_command_line(main_window: &MainWindow) {
4675 }
4776}
4877
49- fn normalize_cli_path ( path : & Path ) -> PathBuf {
50- if let Ok ( normalized) = path. canonicalize ( ) {
51- return normalized;
78+ fn main_window_from_ptr ( ) -> Option < & ' static MainWindow > {
79+ let ptr = MAIN_WINDOW_PTR . load ( Ordering :: SeqCst ) ;
80+ if ptr == 0 {
81+ return None ;
5282 }
53- if path. is_absolute ( ) {
54- return path. to_path_buf ( ) ;
83+ unsafe { ( ptr as * const MainWindow ) . as_ref ( ) }
84+ }
85+
86+ fn ipc_command_from_cli ( ) -> IpcCommand {
87+ if let Some ( path) = env:: args ( ) . nth ( 1 ) {
88+ let normalized = normalize_cli_path ( Path :: new ( & path) ) ;
89+ return IpcCommand :: OpenFile ( normalized) ;
90+ }
91+ IpcCommand :: Activate
92+ }
93+
94+ fn send_ipc_command ( command : IpcCommand ) {
95+ let client = IPCClient :: new ( ) ;
96+ let Some ( conn) = client. make_connection ( IPC_HOST_LOCALHOST , IPC_SERVICE , IPC_TOPIC_OPEN_FILE ) else {
97+ return ;
98+ } ;
99+ let payload = match command {
100+ IpcCommand :: Activate => IPC_COMMAND_ACTIVATE . to_string ( ) ,
101+ IpcCommand :: OpenFile ( path) => path. to_string_lossy ( ) . to_string ( ) ,
102+ } ;
103+ let _ = conn. execute_string ( & payload) ;
104+ let _ = conn. disconnect ( ) ;
105+ }
106+
107+ fn start_ipc_server ( main_window : Rc < MainWindow > ) -> Option < IPCServer > {
108+ let server = IPCServer :: new ( move |topic| {
109+ if topic != IPC_TOPIC_OPEN_FILE {
110+ return None ;
111+ }
112+ Some (
113+ IPCConnection :: builder ( )
114+ . on_execute ( {
115+ move |_topic, data, _format| {
116+ let Some ( command) = decode_execute_payload ( data) else {
117+ return false ;
118+ } ;
119+ wxdragon:: call_after ( Box :: new ( move || {
120+ if let Some ( window) = main_window_from_ptr ( ) {
121+ window. handle_ipc_command ( command) ;
122+ }
123+ } ) ) ;
124+ true
125+ }
126+ } )
127+ . build ( ) ,
128+ )
129+ } ) ;
130+ if !server. create ( IPC_SERVICE ) {
131+ let dialog = MessageDialog :: builder ( main_window. frame ( ) , & t ( "Failed to create IPC server" ) , & t ( "Warning" ) )
132+ . with_style ( MessageDialogStyle :: OK | MessageDialogStyle :: IconWarning | MessageDialogStyle :: Centre )
133+ . build ( ) ;
134+ dialog. show_modal ( ) ;
55135 }
56- env :: current_dir ( ) . map_or_else ( |_| path . to_path_buf ( ) , |cwd| cwd . join ( path ) )
136+ Some ( server )
57137}
0 commit comments