2024-06-18 18:43:00 +00:00
use std ::fs ;
2024-07-08 00:08:20 +00:00
use tauri ::async_runtime ::RwLock ;
2024-06-03 17:11:22 +00:00
use tauri_plugin_dialog ::DialogExt ;
2024-07-08 00:08:20 +00:00
use wasmtime ::AsContextMut ;
2024-05-27 19:24:46 +00:00
use wellen ::simple ::Waveform ;
2024-06-03 17:11:22 +00:00
type Filename = String ;
2024-06-18 18:43:00 +00:00
type JavascriptCode = String ;
2024-06-25 16:29:33 +00:00
type AddedDecodersCount = usize ;
2024-07-07 12:53:30 +00:00
type RemovedDecodersCount = usize ;
2024-06-25 16:29:33 +00:00
type DecoderPath = String ;
mod component_manager ;
2024-06-03 17:11:22 +00:00
2024-05-27 19:24:46 +00:00
#[ derive(Default) ]
struct Store {
2024-07-08 00:08:20 +00:00
waveform : RwLock < Option < Waveform > > ,
2024-05-27 19:24:46 +00:00
}
#[ tauri::command(rename_all = " snake_case " ) ]
2024-06-03 17:11:22 +00:00
async fn show_window ( window : tauri ::Window ) {
2024-05-27 19:24:46 +00:00
window . show ( ) . unwrap ( ) ;
}
#[ tauri::command(rename_all = " snake_case " ) ]
2024-06-03 17:11:22 +00:00
async fn pick_and_load_waveform (
store : tauri ::State < '_ , Store > ,
app : tauri ::AppHandle ,
) -> Result < Option < Filename > , ( ) > {
let Some ( file_response ) = app . dialog ( ) . file ( ) . blocking_pick_file ( ) else {
return Ok ( None ) ;
2024-05-28 10:57:51 +00:00
} ;
2024-06-03 17:11:22 +00:00
let file_path = file_response . path . as_os_str ( ) . to_str ( ) . unwrap ( ) ;
// @TODO `read` should accept `Path` instead of `&str`
let waveform = wellen ::simple ::read ( file_path ) ;
2024-05-27 19:24:46 +00:00
let Ok ( waveform ) = waveform else {
2024-06-03 17:11:22 +00:00
panic! ( " Waveform file reading failed " )
2024-05-27 19:24:46 +00:00
} ;
2024-07-08 00:08:20 +00:00
* store . waveform . write ( ) . await = Some ( waveform ) ;
2024-06-03 17:11:22 +00:00
Ok ( Some ( file_response . name . unwrap ( ) ) )
2024-05-27 19:24:46 +00:00
}
2024-06-18 18:43:00 +00:00
#[ tauri::command(rename_all = " snake_case " ) ]
async fn load_file_with_selected_vars ( app : tauri ::AppHandle ) -> Result < Option < JavascriptCode > , ( ) > {
let Some ( file_response ) = app . dialog ( ) . file ( ) . blocking_pick_file ( ) else {
return Ok ( None ) ;
} ;
// @TODO Tokio's `fs` or a Tauri `fs`?
let Ok ( javascript_code ) = fs ::read_to_string ( file_response . path ) else {
panic! ( " Selected vars file reading failed " )
} ;
Ok ( Some ( javascript_code ) )
}
2024-05-27 19:24:46 +00:00
#[ tauri::command(rename_all = " snake_case " ) ]
2024-06-03 17:11:22 +00:00
async fn get_hierarchy ( store : tauri ::State < '_ , Store > ) -> Result < serde_json ::Value , ( ) > {
2024-07-08 00:08:20 +00:00
let waveform_lock = store . waveform . read ( ) . await ;
let waveform = waveform_lock . as_ref ( ) . unwrap ( ) ;
let hierarchy = waveform . hierarchy ( ) ;
2024-06-03 17:11:22 +00:00
Ok ( serde_json ::to_value ( hierarchy ) . unwrap ( ) )
2024-05-27 19:24:46 +00:00
}
#[ tauri::command(rename_all = " snake_case " ) ]
2024-06-06 22:45:05 +00:00
async fn load_signal_and_get_timeline (
2024-06-06 20:04:57 +00:00
signal_ref_index : usize ,
2024-06-11 20:42:14 +00:00
timeline_zoom : f64 ,
2024-06-11 15:00:29 +00:00
timeline_viewport_width : u32 ,
2024-06-11 20:42:14 +00:00
timeline_viewport_x : i32 ,
2024-06-06 20:45:20 +00:00
block_height : u32 ,
2024-06-09 20:53:02 +00:00
var_format : shared ::VarFormat ,
2024-06-06 20:04:57 +00:00
store : tauri ::State < '_ , Store > ,
) -> Result < serde_json ::Value , ( ) > {
2024-06-07 21:18:06 +00:00
// @TODO run (all?) in a blocking thread?
2024-06-06 20:04:57 +00:00
let signal_ref = wellen ::SignalRef ::from_index ( signal_ref_index ) . unwrap ( ) ;
2024-07-08 00:08:20 +00:00
let mut waveform_lock = store . waveform . write ( ) . await ;
2024-06-06 20:04:57 +00:00
let waveform = waveform_lock . as_mut ( ) . unwrap ( ) ;
waveform . load_signals_multi_threaded ( & [ signal_ref ] ) ;
let signal = waveform . get_signal ( signal_ref ) . unwrap ( ) ;
2024-06-06 20:45:20 +00:00
let time_table = waveform . time_table ( ) ;
2024-06-14 18:28:18 +00:00
let timeline = shared ::signal_to_timeline (
signal ,
time_table ,
timeline_zoom ,
timeline_viewport_width ,
timeline_viewport_x ,
block_height ,
var_format ,
2024-07-08 00:08:20 +00:00
| mut value : String | {
Box ::pin ( async {
2024-07-08 12:30:49 +00:00
// We need to spawn a (non-runtime-specific?) blocking task before calling component methods to prevent this error:
// "Cannot start a runtime from within a runtime. This happens because a function (like `block_on`) attempted to block the current thread while the thread is being used to drive asynchronous tasks."
// @TODO Workaround? Is it a problem only for non-Rust components? Is it needed only when there is a problem in the component (e.g. "`Err` value: wasm trap: cannot enter component instance"?)
// let value = std::thread::spawn(move || {
2024-07-08 12:38:30 +00:00
// futures::executor::block_on(async move {
let decoders = component_manager ::DECODERS . read ( ) . await ;
let mut store_lock = component_manager ::STORE . lock ( ) . await ;
let mut store = store_lock . as_context_mut ( ) ;
2024-07-08 12:30:49 +00:00
2024-07-08 12:38:30 +00:00
for decoder in decoders . iter ( ) {
value = decoder
. component_decoder_decoder ( )
. call_format_signal_value ( & mut store , & value )
// @TODO Resolve panic when running non-Rust components:
// `Err` value: wasm trap: cannot enter component instance
// https://github.com/bytecodealliance/wasmtime/issues/8670 ?
. unwrap ( )
}
// value
// })
2024-07-08 12:30:49 +00:00
// }).join().unwrap();
2024-07-08 00:08:20 +00:00
value
} )
} ,
)
. await ;
2024-06-06 20:04:57 +00:00
Ok ( serde_json ::to_value ( timeline ) . unwrap ( ) )
}
2024-05-27 19:24:46 +00:00
#[ tauri::command(rename_all = " snake_case " ) ]
2024-06-03 17:11:22 +00:00
async fn unload_signal ( signal_ref_index : usize , store : tauri ::State < '_ , Store > ) -> Result < ( ) , ( ) > {
2024-05-27 19:24:46 +00:00
let signal_ref = wellen ::SignalRef ::from_index ( signal_ref_index ) . unwrap ( ) ;
2024-07-08 00:08:20 +00:00
let mut waveform_lock = store . waveform . write ( ) . await ;
2024-05-27 19:24:46 +00:00
let waveform = waveform_lock . as_mut ( ) . unwrap ( ) ;
waveform . unload_signals ( & [ signal_ref ] ) ;
2024-06-03 17:11:22 +00:00
Ok ( ( ) )
2024-05-27 19:24:46 +00:00
}
2024-06-25 16:29:33 +00:00
#[ tauri::command(rename_all = " snake_case " ) ]
async fn add_decoders ( decoder_paths : Vec < DecoderPath > ) -> Result < AddedDecodersCount , ( ) > {
2024-07-07 20:26:44 +00:00
Ok ( component_manager ::add_decoders ( decoder_paths ) . await )
2024-06-25 16:29:33 +00:00
}
2024-07-07 12:53:30 +00:00
#[ tauri::command(rename_all = " snake_case " ) ]
async fn remove_all_decoders ( ) -> Result < RemovedDecodersCount , ( ) > {
2024-07-07 20:26:44 +00:00
Ok ( component_manager ::remove_all_decoders ( ) . await )
2024-07-07 12:53:30 +00:00
}
2024-05-27 19:24:46 +00:00
#[ cfg_attr(mobile, tauri::mobile_entry_point) ]
pub fn run ( ) {
// https://github.com/tauri-apps/tauri/issues/8462
#[ cfg(target_os = " linux " ) ]
std ::env ::set_var ( " WEBKIT_DISABLE_DMABUF_RENDERER " , " 1 " ) ;
tauri ::Builder ::default ( )
. manage ( Store ::default ( ) )
. plugin ( tauri_plugin_window_state ::Builder ::default ( ) . build ( ) )
2024-06-03 17:11:22 +00:00
. plugin ( tauri_plugin_dialog ::init ( ) )
2024-05-27 19:24:46 +00:00
// Npte: Add all handlers to `frontend/src/tauri_bridge.rs`
. invoke_handler ( tauri ::generate_handler! [
show_window ,
2024-06-03 17:11:22 +00:00
pick_and_load_waveform ,
2024-06-18 18:43:00 +00:00
load_file_with_selected_vars ,
2024-05-27 19:24:46 +00:00
get_hierarchy ,
2024-06-06 22:45:05 +00:00
load_signal_and_get_timeline ,
2024-05-27 19:24:46 +00:00
unload_signal ,
2024-06-25 16:29:33 +00:00
add_decoders ,
2024-07-07 12:53:30 +00:00
remove_all_decoders ,
2024-05-27 19:24:46 +00:00
] )
. run ( tauri ::generate_context! ( ) )
. expect ( " error while running tauri application " ) ;
}