I'm trying to read out a HID device (barcode scanner) using the hidapi crate in a Tauri App.
I have this scenario in mind:
- Scan for the device on app startup -> if not there emit event to UI :
device-disconnected - If device is there, open device and emit event :
device-datawith the payload when a scan happens - If device is disconnected trigger a
Tauri commandfrom the UI to rescan for the device
Now I have this code
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
extern crate hidapi;
use hidapi::HidApi;
use tauri::Manager;
#[derive(Clone, serde::Serialize)]
struct Payload {
message: String,
}
#[tauri::command]
fn detect_barcode_scanner(app: &mut tauri::App) -> () {
let vid = 0x1234;
let pid = 0x5678;
match HidApi::new() {
Ok(api) => {
let mut device_found = false;
for device_info in api.device_list() {
// Check if the scanner is in the list
if device_info.vendor_id() == vid && device_info.product_id() == pid {
device_found = true;
if let Ok(device) = device_info.open_device(&api) {
println!("Barcode scanner found & opened successfully.");
// Allocate mem buffer for data
let mut mem_buf = Vec::<u8>::with_capacity(256);
// Start reading data
loop {
match device.read(&mut mem_buf) {
Ok(count) => {
let data = &mem_buf[..count];
println!("Scanned data: {:?}", data);
app.emit_all("scanner-data", Payload { message: "Tauri is awesome!".into() }).unwrap();
}
Err(e) => {
eprintln!("Error reading from HID device: {:?}", e);
break;
}
}
}
}
}
}
if !device_found {
println!("Barcode scanner not found.");
}
}
Err(e) => {
eprintln!("Error initializing HID API: {}", e);
}
}
}
fn main() {
tauri::Builder::default()
.setup(|app|{
detect_barcode_scanner(app);
Ok(())
})
.invoke_handler(tauri::generate_handler![detect_barcode_scanner])
.run(tauri::generate_context!())
.expect("failed to run app");
}
I now have the problem that the emit_all function is only available on the App Handle, so I call the function detect_barcode_scanner from the setup function.
But this gives that the events send by emit_all function get lost because the UI is not ready.
This also prevents me from keeping the connection open to the device to continue to receive messages after the setup.
Any guidelines / tips on how to solve this ?
You'd want to manage the state differently, and in
asyncway.Rust's paradigm of managing the state without sharing memory differs from other languages.
I'd recommend to do a slightly simpler version:
Here's a quick code (not tested):