1
1
#![ cfg_attr( all( not( debug_assertions) , windows) , windows_subsystem = "windows" ) ]
2
+ use std:: env;
2
3
use std:: panic;
3
4
use std:: sync:: atomic:: AtomicBool ;
4
5
use std:: sync:: atomic:: Ordering ;
@@ -68,88 +69,125 @@ fn main() -> Result<()> {
68
69
let tauri_context = tauri:: generate_context!( ) ;
69
70
70
71
// Set up loggers and global handlers
71
- let _logger = {
72
- use flexi_logger:: {
73
- Age , Cleanup , Criterion , Duplicate , FileSpec , Logger , Naming , WriteMode ,
74
- } ;
75
- use tauri:: Error ;
76
-
77
- // Based on https://docs.rs/tauri/2.0.0-alpha.10/src/tauri/path/desktop.rs.html#238-256
78
- #[ cfg( target_os = "macos" ) ]
79
- let path = dirs_next:: home_dir ( ) . ok_or ( Error :: UnknownPath ) . map ( |dir| {
80
- dir. join ( "Library/Logs" )
81
- . join ( & tauri_context. config ( ) . identifier )
82
- } ) ;
83
-
84
- #[ cfg( not( target_os = "macos" ) ) ]
85
- let path = dirs_next:: data_dir ( )
86
- . ok_or ( Error :: UnknownPath )
87
- . map ( |dir| dir. join ( & tauri_context. config ( ) . identifier ) . join ( "logs" ) ) ;
88
-
89
- Logger :: try_with_env_or_str ( "info" ) ?
90
- . log_to_file (
91
- FileSpec :: default ( ) . directory ( path. expect ( "We need a log dir" ) ) ,
92
- )
93
- . format_for_files ( |w, now, record| {
94
- util:: logger_format ( w, now, record, false )
95
- } )
96
- . format_for_stderr ( |w, now, record| {
97
- util:: logger_format ( w, now, record, true )
98
- } )
99
- . rotate (
100
- Criterion :: Age ( Age :: Day ) ,
101
- Naming :: Timestamps ,
102
- Cleanup :: KeepLogFiles ( 2 ) ,
103
- )
104
- . duplicate_to_stderr ( Duplicate :: All )
105
- . write_mode ( WriteMode :: BufferAndFlush )
106
- . start ( ) ?
107
- } ;
72
+ let _logger = setup_logger ( & tauri_context) ;
108
73
109
74
// Ensure child processes die when spawned on windows
110
75
// and then check for WebView2's existence
111
76
#[ cfg( windows) ]
112
- {
113
- use crate :: util:: webview2_exists;
114
- use win32job:: { ExtendedLimitInfo , Job } ;
115
-
116
- let mut info = ExtendedLimitInfo :: new ( ) ;
117
- info. limit_kill_on_job_close ( ) ;
118
- let job = Job :: create_with_limit_info ( & mut info) . expect ( "Failed to create Job" ) ;
119
- job. assign_current_process ( )
120
- . expect ( "Failed to assign current process to Job" ) ;
121
-
122
- // We don't do anything with the job anymore, but we shouldn't drop it because that would
123
- // terminate our process tree. So we intentionally leak it instead.
124
- std:: mem:: forget ( job) ;
125
-
126
- if !webview2_exists ( ) {
127
- // This makes a dialog appear which let's you press Ok or Cancel
128
- // If you press Ok it will open the SlimeVR installer documentation
129
- use rfd:: {
130
- MessageButtons , MessageDialog , MessageDialogResult , MessageLevel ,
131
- } ;
77
+ setup_webview2 ( ) ?;
132
78
133
- let confirm = MessageDialog :: new ( )
134
- . set_title ( "SlimeVR" )
135
- . set_description ( "Couldn't find WebView2 installed. You can install it with the SlimeVR installer" )
136
- . set_buttons ( MessageButtons :: OkCancel )
137
- . set_level ( MessageLevel :: Error )
138
- . show ( ) ;
139
- if confirm == MessageDialogResult :: Ok {
140
- open:: that ( "https://docs.slimevr.dev/server-setup/installing-and-connecting.html#install-the-latest-slimevr-installer" ) . unwrap ( ) ;
141
- }
142
- return Ok ( ( ) ) ;
143
- }
144
- }
79
+ // Check for environment variables that can affect the server, and if so, warn in log and GUI
80
+ check_environment_variables ( ) ;
145
81
146
82
// Spawn server process
147
83
let exit_flag = Arc :: new ( AtomicBool :: new ( false ) ) ;
148
84
let backend = Arc :: new ( Mutex :: new ( Option :: < CommandChild > :: None ) ) ;
149
- let backend_termination = backend. clone ( ) ;
150
- let run_path = get_launch_path ( cli) ;
151
85
152
- let server_info = if let Some ( p) = run_path {
86
+ let server_info = execute_server ( cli) ?;
87
+ let build_result = setup_tauri (
88
+ tauri_context,
89
+ server_info,
90
+ exit_flag. clone ( ) ,
91
+ backend. clone ( ) ,
92
+ ) ;
93
+
94
+ tauri_build_result ( build_result, exit_flag, backend) ;
95
+
96
+ Ok ( ( ) )
97
+ }
98
+
99
+ fn setup_logger ( context : & tauri:: Context ) -> Result < flexi_logger:: LoggerHandle > {
100
+ use flexi_logger:: {
101
+ Age , Cleanup , Criterion , Duplicate , FileSpec , Logger , Naming , WriteMode ,
102
+ } ;
103
+ use tauri:: Error ;
104
+
105
+ // Based on https://docs.rs/tauri/2.0.0-alpha.10/src/tauri/path/desktop.rs.html#238-256
106
+ #[ cfg( target_os = "macos" ) ]
107
+ let path = dirs_next:: home_dir ( )
108
+ . ok_or ( Error :: UnknownPath )
109
+ . map ( |dir| dir. join ( "Library/Logs" ) . join ( & context. config ( ) . identifier ) ) ;
110
+
111
+ #[ cfg( not( target_os = "macos" ) ) ]
112
+ let path = dirs_next:: data_dir ( )
113
+ . ok_or ( Error :: UnknownPath )
114
+ . map ( |dir| dir. join ( & context. config ( ) . identifier ) . join ( "logs" ) ) ;
115
+
116
+ Ok ( Logger :: try_with_env_or_str ( "info" ) ?
117
+ . log_to_file ( FileSpec :: default ( ) . directory ( path. expect ( "We need a log dir" ) ) )
118
+ . format_for_files ( |w, now, record| util:: logger_format ( w, now, record, false ) )
119
+ . format_for_stderr ( |w, now, record| util:: logger_format ( w, now, record, true ) )
120
+ . rotate (
121
+ Criterion :: Age ( Age :: Day ) ,
122
+ Naming :: Timestamps ,
123
+ Cleanup :: KeepLogFiles ( 2 ) ,
124
+ )
125
+ . duplicate_to_stderr ( Duplicate :: All )
126
+ . write_mode ( WriteMode :: BufferAndFlush )
127
+ . start ( ) ?)
128
+ }
129
+
130
+ #[ cfg( windows) ]
131
+ fn setup_webview2 ( ) -> Result < ( ) > {
132
+ use crate :: util:: webview2_exists;
133
+ use win32job:: { ExtendedLimitInfo , Job } ;
134
+
135
+ let mut info = ExtendedLimitInfo :: new ( ) ;
136
+ info. limit_kill_on_job_close ( ) ;
137
+ let job = Job :: create_with_limit_info ( & mut info) . expect ( "Failed to create Job" ) ;
138
+ job. assign_current_process ( )
139
+ . expect ( "Failed to assign current process to Job" ) ;
140
+
141
+ // We don't do anything with the job anymore, but we shouldn't drop it because that would
142
+ // terminate our process tree. So we intentionally leak it instead.
143
+ std:: mem:: forget ( job) ;
144
+
145
+ if !webview2_exists ( ) {
146
+ // This makes a dialog appear which let's you press Ok or Cancel
147
+ // If you press Ok it will open the SlimeVR installer documentation
148
+ use rfd:: { MessageButtons , MessageDialog , MessageDialogResult , MessageLevel } ;
149
+
150
+ let confirm = MessageDialog :: new ( )
151
+ . set_title ( "SlimeVR" )
152
+ . set_description ( "Couldn't find WebView2 installed. You can install it with the SlimeVR installer" )
153
+ . set_buttons ( MessageButtons :: OkCancel )
154
+ . set_level ( MessageLevel :: Error )
155
+ . show ( ) ;
156
+ if confirm == MessageDialogResult :: Ok {
157
+ open:: that ( "https://docs.slimevr.dev/server-setup/installing-and-connecting.html#install-the-latest-slimevr-installer" ) . unwrap ( ) ;
158
+ }
159
+ }
160
+ Ok ( ( ) )
161
+ }
162
+
163
+ fn check_environment_variables ( ) {
164
+ use itertools:: Itertools ;
165
+ const ENVS_TO_CHECK : & [ & str ] = & [ "_JAVA_OPTIONS" , "JAVA_TOOL_OPTIONS" ] ;
166
+ let checked_envs = ENVS_TO_CHECK
167
+ . into_iter ( )
168
+ . filter_map ( |e| {
169
+ let Ok ( data) = env:: var ( e) else {
170
+ return None ;
171
+ } ;
172
+ log:: warn!( "{e} is set to: {data}" ) ;
173
+ Some ( e)
174
+ } )
175
+ . join ( ", " ) ;
176
+
177
+ if !checked_envs. is_empty ( ) {
178
+ rfd:: MessageDialog :: new ( )
179
+ . set_title ( "SlimeVR" )
180
+ . set_description ( format ! ( "You have environment variables {} set, which may cause the SlimeVR Server to fail to launch properly." , checked_envs) )
181
+ . set_level ( rfd:: MessageLevel :: Warning )
182
+ . show ( ) ;
183
+ }
184
+ }
185
+
186
+ fn execute_server (
187
+ cli : Cli ,
188
+ ) -> Result < Option < ( std:: ffi:: OsString , std:: path:: PathBuf ) > > {
189
+ use const_format:: formatcp;
190
+ if let Some ( p) = get_launch_path ( cli) {
153
191
log:: info!( "Server found on path: {}" , p. to_str( ) . unwrap( ) ) ;
154
192
155
193
// Check if any Java already installed is compatible
@@ -159,19 +197,29 @@ fn main() -> Result<()> {
159
197
. then ( || jre. into_os_string ( ) )
160
198
. or_else ( || valid_java_paths ( ) . first ( ) . map ( |x| x. 0 . to_owned ( ) ) ) ;
161
199
let Some ( java_bin) = java_bin else {
162
- show_error ( & format ! ( "Couldn't find a compatible Java version, please download Java {} or higher" , MINIMUM_JAVA_VERSION ) ) ;
163
- return Ok ( ( ) ) ;
200
+ show_error ( formatcp ! (
201
+ "Couldn't find a compatible Java version, please download Java {} or higher" ,
202
+ MINIMUM_JAVA_VERSION
203
+ ) ) ;
204
+ return Ok ( None ) ;
164
205
} ;
165
206
166
207
log:: info!( "Using Java binary: {:?}" , java_bin) ;
167
- Some ( ( java_bin, p) )
208
+ Ok ( Some ( ( java_bin, p) ) )
168
209
} else {
169
210
log:: warn!( "No server found. We will not start the server." ) ;
170
- None
171
- } ;
211
+ Ok ( None )
212
+ }
213
+ }
172
214
215
+ fn setup_tauri (
216
+ context : tauri:: Context ,
217
+ server_info : Option < ( std:: ffi:: OsString , std:: path:: PathBuf ) > ,
218
+ exit_flag : Arc < AtomicBool > ,
219
+ backend : Arc < Mutex < Option < CommandChild > > > ,
220
+ ) -> Result < tauri:: App , tauri:: Error > {
173
221
let exit_flag_terminated = exit_flag. clone ( ) ;
174
- let build_result = tauri:: Builder :: default ( )
222
+ tauri:: Builder :: default ( )
175
223
. plugin ( tauri_plugin_dialog:: init ( ) )
176
224
. plugin ( tauri_plugin_fs:: init ( ) )
177
225
. plugin ( tauri_plugin_os:: init ( ) )
@@ -281,7 +329,14 @@ fn main() -> Result<()> {
281
329
// WindowEvent::Resized(_) => std::thread::sleep(std::time::Duration::from_nanos(1)),
282
330
_ => ( ) ,
283
331
} )
284
- . build ( tauri_context) ;
332
+ . build ( context)
333
+ }
334
+
335
+ fn tauri_build_result (
336
+ build_result : Result < tauri:: App , tauri:: Error > ,
337
+ exit_flag : Arc < AtomicBool > ,
338
+ backend : Arc < Mutex < Option < CommandChild > > > ,
339
+ ) {
285
340
match build_result {
286
341
Ok ( app) => {
287
342
app. run ( move |app_handle, event| match event {
@@ -295,7 +350,7 @@ fn main() -> Result<()> {
295
350
Err ( e) => log:: error!( "failed to save window state: {}" , e) ,
296
351
}
297
352
298
- let mut lock = backend_termination . lock ( ) . unwrap ( ) ;
353
+ let mut lock = backend . lock ( ) . unwrap ( ) ;
299
354
let Some ( ref mut child) = * lock else { return } ;
300
355
let write_result = child. write ( b"exit\n " ) ;
301
356
match write_result {
@@ -339,6 +394,4 @@ fn main() -> Result<()> {
339
394
show_error ( & error. to_string ( ) ) ;
340
395
}
341
396
}
342
-
343
- Ok ( ( ) )
344
397
}
0 commit comments