mirror of
https://github.com/lights0123/n-link.git
synced 2026-08-07 18:23:28 +00:00
feat(desktop): wire Tauri 2 builder, hotplug, and capabilities
Hotplug registration moves from on_page_load to setup (runs once, so the
AtomicBool guard is dropped). v1 allowlist replaced by a capability file
granting dialog:allow-open and shell:allow-open. CSP from 0472908 carried
forward to app.security.csp; bundle targets narrowed to deb + appimage.
Also derives Clone on device::AddDevice: Tauri 2's Emitter::emit requires
Serialize + Clone (v1's did not), and the addDevice event payload was
missing the derive. No wire-format or IPC contract change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
b54976fd28
commit
4c629aaf53
|
|
@ -1,9 +1,11 @@
|
|||
{
|
||||
"$schema": "../gen/schemas/desktop-schema.json",
|
||||
"identifier": "default",
|
||||
"description": "Capability for the main window",
|
||||
"description": "Core, dialog, and shell permissions for the main window",
|
||||
"windows": ["main"],
|
||||
"permissions": [
|
||||
"core:default"
|
||||
"core:default",
|
||||
"dialog:allow-open",
|
||||
"shell:allow-open"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ pub struct DevId {
|
|||
pub address: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AddDevice {
|
||||
#[serde(flatten)]
|
||||
|
|
|
|||
|
|
@ -1,11 +1,125 @@
|
|||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use libnspire::{PID_CX2, VID};
|
||||
use rusb::{GlobalContext, Hotplug, UsbContext};
|
||||
use tauri::{Emitter, Manager, Runtime, WebviewWindow};
|
||||
|
||||
use crate::device::{add_device, AddDevice, DevId, DEVICES};
|
||||
|
||||
mod commands;
|
||||
mod device;
|
||||
mod error;
|
||||
|
||||
struct DeviceMon<R: Runtime> {
|
||||
window: WebviewWindow<R>,
|
||||
}
|
||||
|
||||
impl<R: Runtime> Hotplug<GlobalContext> for DeviceMon<R> {
|
||||
fn device_arrived(&mut self, device: rusb::Device<GlobalContext>) {
|
||||
let handle = self.window.clone();
|
||||
let is_cx_ii = device
|
||||
.device_descriptor()
|
||||
.map(|d| d.product_id() == PID_CX2)
|
||||
.unwrap_or(false);
|
||||
let device = Arc::new(device);
|
||||
// The calculator often reports Busy immediately after enumeration, so
|
||||
// retry until it settles.
|
||||
std::thread::spawn(move || loop {
|
||||
match add_device(device.clone()) {
|
||||
Ok(dev) => {
|
||||
let name = (dev.1).name.clone();
|
||||
let needs_drivers = (dev.1).needs_drivers;
|
||||
DEVICES.write().unwrap().insert(dev.0, dev.1);
|
||||
if let Err(msg) = handle.emit(
|
||||
"addDevice",
|
||||
AddDevice {
|
||||
dev: DevId {
|
||||
bus_number: (dev.0).0,
|
||||
address: (dev.0).1,
|
||||
},
|
||||
name,
|
||||
is_cx_ii,
|
||||
needs_drivers,
|
||||
},
|
||||
) {
|
||||
eprintln!("{}", msg);
|
||||
};
|
||||
return;
|
||||
}
|
||||
Err(rusb::Error::Busy) => {
|
||||
println!("busy");
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(250));
|
||||
});
|
||||
}
|
||||
|
||||
fn device_left(&mut self, device: rusb::Device<GlobalContext>) {
|
||||
if let Some((dev, _)) = DEVICES
|
||||
.write()
|
||||
.unwrap()
|
||||
.remove_entry(&(device.bus_number(), device.address()))
|
||||
{
|
||||
if let Err(msg) = self.window.emit(
|
||||
"removeDevice",
|
||||
DevId {
|
||||
bus_number: dev.0,
|
||||
address: dev.1,
|
||||
},
|
||||
) {
|
||||
eprintln!("{}", msg);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.setup(|app| {
|
||||
// Tauri 1 registered this in on_page_load, guarded by an AtomicBool to
|
||||
// avoid double-registering on reload. In v2, setup runs exactly once,
|
||||
// so the guard is unnecessary.
|
||||
let window = app
|
||||
.get_webview_window("main")
|
||||
.expect("main window not found");
|
||||
if rusb::has_hotplug() {
|
||||
if let Err(msg) =
|
||||
GlobalContext::default().register_callback(Some(VID), None, None, Box::new(DeviceMon { window }))
|
||||
{
|
||||
eprintln!("{}", msg);
|
||||
};
|
||||
std::thread::spawn(|| loop {
|
||||
GlobalContext::default().handle_events(None).unwrap();
|
||||
});
|
||||
} else {
|
||||
println!("no hotplug");
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
commands::enumerate,
|
||||
commands::open_device,
|
||||
commands::close_device,
|
||||
commands::update_device,
|
||||
commands::list_dir,
|
||||
commands::download_file,
|
||||
commands::upload_file,
|
||||
commands::upload_os,
|
||||
commands::delete_file,
|
||||
commands::delete_dir,
|
||||
commands::create_nspire_dir,
|
||||
commands::move_file,
|
||||
commands::copy,
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "n-link",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.6",
|
||||
"identifier": "com.lights0123.n-link",
|
||||
"build": {
|
||||
"beforeDevCommand": "npm run dev",
|
||||
|
|
@ -12,13 +12,15 @@
|
|||
"app": {
|
||||
"windows": [
|
||||
{
|
||||
"title": "n-link",
|
||||
"title": "N-Link",
|
||||
"width": 800,
|
||||
"height": 600
|
||||
"height": 600,
|
||||
"resizable": true,
|
||||
"fullscreen": false
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": null
|
||||
"csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'; img-src 'self' asset: http://asset.localhost blob: data:"
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
|
|
@ -30,6 +32,12 @@
|
|||
"icons/128x128@2x.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
]
|
||||
],
|
||||
"resources": [],
|
||||
"externalBin": [],
|
||||
"copyright": "Copyright (c) 2021 Ben Schattinger. Licensed under GPL-3.0",
|
||||
"category": "Utility",
|
||||
"shortDescription": "Free, cross-platform, CX-II compatible computer linking program for the TI-Nspire",
|
||||
"longDescription": "Free, cross-platform, CX-II compatible computer linking program for the TI-Nspire"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue