mirror of
https://github.com/lights0123/n-link.git
synced 2025-07-07 19:40:33 +00:00
Driver warnings
This commit is contained in:
parent
50a5496e31
commit
e60a0f5972
10
src-tauri/Cargo.lock
generated
10
src-tauri/Cargo.lock
generated
|
@ -21,6 +21,12 @@ version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.4.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0adac150c2dd5a9c864d054e07bda5e6bc010cd10036ea5f17e82a2f5867f735"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "0.7.13"
|
version = "0.7.13"
|
||||||
|
@ -561,6 +567,9 @@ name = "hashbrown"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "00d63df3d41950fb462ed38308eea019113ad1508da725bbedcd0fa5a85ef5f7"
|
checksum = "00d63df3d41950fb462ed38308eea019113ad1508da725bbedcd0fa5a85ef5f7"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
|
@ -841,6 +850,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
|
"hashbrown",
|
||||||
"indicatif",
|
"indicatif",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libnspire",
|
"libnspire",
|
||||||
|
|
|
@ -16,11 +16,12 @@ libnspire = "0.2.2"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
rusb = "0.6.4"
|
rusb = "0.6.4"
|
||||||
serde = { version = "1.0", features = [ "derive" ] }
|
serde = { version = "1.0", features = [ "derive" ] }
|
||||||
tauri = { version = "0.9", features = [ "event", "notification" ] }
|
tauri = { version = "0.9", features = [ "event", "notification", "open" ] }
|
||||||
native-dialog = "0.4.3"
|
native-dialog = "0.4.3"
|
||||||
clap = "3.0.0-beta.2"
|
clap = "3.0.0-beta.2"
|
||||||
indicatif = "0.15.0"
|
indicatif = "0.15.0"
|
||||||
libusb1-sys = { version = "0.4.2", features = [ "vendored" ] }
|
libusb1-sys = { version = "0.4.2", features = [ "vendored" ] }
|
||||||
|
hashbrown = "0.9.0"
|
||||||
|
|
||||||
[target."cfg(windows)".build-dependencies]
|
[target."cfg(windows)".build-dependencies]
|
||||||
winres = "0.1"
|
winres = "0.1"
|
||||||
|
|
|
@ -2,10 +2,12 @@ use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use libnspire::{PID, PID_CX2, VID};
|
use libnspire::{PID, PID_CX2, VID};
|
||||||
use rusb::{GlobalContext, Error};
|
use rusb::{DeviceHandle, Error, GlobalContext};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{Device, DeviceState};
|
use crate::{Device, DeviceState};
|
||||||
|
use tauri::WebviewMut;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Promise {
|
pub struct Promise {
|
||||||
|
@ -150,32 +152,93 @@ pub fn add_device(dev: Arc<rusb::Device<GlobalContext>>) -> rusb::Result<((u8, u
|
||||||
if !(descriptor.vendor_id() == VID && matches!(descriptor.product_id(), PID | PID_CX2)) {
|
if !(descriptor.vendor_id() == VID && matches!(descriptor.product_id(), PID | PID_CX2)) {
|
||||||
return Err(rusb::Error::Other);
|
return Err(rusb::Error::Other);
|
||||||
}
|
}
|
||||||
let handle = dev.open()?;
|
|
||||||
|
let (name, needs_drivers) = match dev.open() {
|
||||||
|
Ok(handle) => (
|
||||||
|
handle.read_product_string(
|
||||||
|
handle.read_languages(Duration::from_millis(100))?[0],
|
||||||
|
&descriptor,
|
||||||
|
Duration::from_millis(100),
|
||||||
|
)?,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
Err(rusb::Error::NotSupported) => (
|
||||||
|
if descriptor.product_id() == PID_CX2 {
|
||||||
|
"TI Nspire CX II"
|
||||||
|
} else {
|
||||||
|
"TI Nspire"
|
||||||
|
}
|
||||||
|
.to_string(),
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
Err(other) => return Err(other),
|
||||||
|
};
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
(dev.bus_number(), dev.address()),
|
(dev.bus_number(), dev.address()),
|
||||||
Device {
|
Device {
|
||||||
name: dbg!(handle.read_product_string(
|
name,
|
||||||
handle.read_languages(Duration::from_millis(100))?[0],
|
|
||||||
&descriptor,
|
|
||||||
Duration::from_millis(100),
|
|
||||||
))?,
|
|
||||||
device: dev,
|
device: dev,
|
||||||
state: DeviceState::Closed,
|
state: DeviceState::Closed,
|
||||||
|
needs_drivers,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enumerate() -> Result<(), libnspire::Error> {
|
pub fn enumerate(handle: &mut WebviewMut) -> Result<Vec<AddDevice>, libnspire::Error> {
|
||||||
crate::DEVICES.write().unwrap().extend(
|
let devices: Vec<_> = rusb::devices()?.iter().collect();
|
||||||
rusb::devices()?
|
let mut map = crate::DEVICES.write().unwrap();
|
||||||
|
map
|
||||||
|
.drain_filter(|k, _v| {
|
||||||
|
devices
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|dev| match add_device(Arc::new(dev)){
|
.all(|d| d.bus_number() != k.0 || d.address() != k.1)
|
||||||
|
})
|
||||||
|
.for_each(|d| {
|
||||||
|
if let Err(msg) = tauri::event::emit(
|
||||||
|
handle,
|
||||||
|
"removeDevice",
|
||||||
|
Some(DevId {
|
||||||
|
bus_number: (d.0).0,
|
||||||
|
address: (d.0).1,
|
||||||
|
}),
|
||||||
|
) {
|
||||||
|
eprintln!("{}", msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let filtered: Vec<_> = devices
|
||||||
|
.into_iter()
|
||||||
|
.filter(|d| !map.contains_key(&(d.bus_number(), d.address())))
|
||||||
|
.collect();
|
||||||
|
Ok(
|
||||||
|
filtered
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|dev| {
|
||||||
|
match add_device(Arc::new(dev)) {
|
||||||
Ok(d) => Ok(d),
|
Ok(d) => Ok(d),
|
||||||
Err(e) => Err(dbg!(e)),
|
Err(e) => Err(dbg!(e)),
|
||||||
}.ok()),
|
}
|
||||||
);
|
.ok()
|
||||||
Ok(())
|
})
|
||||||
|
.map(|dev| {
|
||||||
|
let msg = AddDevice {
|
||||||
|
dev: dbg!(DevId {
|
||||||
|
bus_number: (dev.0).0,
|
||||||
|
address: (dev.0).1,
|
||||||
|
}),
|
||||||
|
name: (dev.1).name.clone(),
|
||||||
|
is_cx_ii: (dev.1)
|
||||||
|
.device
|
||||||
|
.device_descriptor()
|
||||||
|
.map(|d| d.product_id() == PID_CX2)
|
||||||
|
.unwrap_or(false),
|
||||||
|
needs_drivers: (dev.1).needs_drivers,
|
||||||
|
};
|
||||||
|
map.insert(dev.0, dev.1);
|
||||||
|
msg
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
|
@ -185,6 +248,7 @@ pub struct AddDevice {
|
||||||
pub dev: DevId,
|
pub dev: DevId,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub is_cx_ii: bool,
|
pub is_cx_ii: bool,
|
||||||
|
pub needs_drivers: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
// windows_subsystem = "windows"
|
// windows_subsystem = "windows"
|
||||||
// )]
|
// )]
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use hashbrown::HashMap;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
@ -35,6 +35,7 @@ pub struct Device {
|
||||||
name: String,
|
name: String,
|
||||||
device: Arc<rusb::Device<GlobalContext>>,
|
device: Arc<rusb::Device<GlobalContext>>,
|
||||||
state: DeviceState,
|
state: DeviceState,
|
||||||
|
needs_drivers: bool,
|
||||||
}
|
}
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
static ref DEVICES: RwLock<HashMap<(u8, u8), Device>> = RwLock::new(HashMap::new());
|
static ref DEVICES: RwLock<HashMap<(u8, u8), Device>> = RwLock::new(HashMap::new());
|
||||||
|
@ -55,6 +56,7 @@ impl Hotplug<GlobalContext> for DeviceMon {
|
||||||
match add_device(device.clone()) {
|
match add_device(device.clone()) {
|
||||||
Ok(dev) => {
|
Ok(dev) => {
|
||||||
let name = (dev.1).name.clone();
|
let name = (dev.1).name.clone();
|
||||||
|
let needs_drivers = (dev.1).needs_drivers;
|
||||||
DEVICES.write().unwrap().insert(dev.0, dev.1);
|
DEVICES.write().unwrap().insert(dev.0, dev.1);
|
||||||
if let Err(msg) = tauri::event::emit(
|
if let Err(msg) = tauri::event::emit(
|
||||||
&mut handle,
|
&mut handle,
|
||||||
|
@ -66,6 +68,7 @@ impl Hotplug<GlobalContext> for DeviceMon {
|
||||||
},
|
},
|
||||||
name,
|
name,
|
||||||
is_cx_ii,
|
is_cx_ii,
|
||||||
|
needs_drivers,
|
||||||
}),
|
}),
|
||||||
) {
|
) {
|
||||||
eprintln!("{}", msg);
|
eprintln!("{}", msg);
|
||||||
|
@ -85,23 +88,43 @@ impl Hotplug<GlobalContext> for DeviceMon {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn device_left(&mut self, device: rusb::Device<GlobalContext>) {
|
fn device_left(&mut self, device: rusb::Device<GlobalContext>) {
|
||||||
if let Some((dev, _)) = DEVICES
|
// if let Some((dev, _)) = DEVICES
|
||||||
|
// .write()
|
||||||
|
// .unwrap()
|
||||||
|
// .remove_entry(&(device.bus_number(), device.address()))
|
||||||
|
// {
|
||||||
|
// if let Err(msg) = tauri::event::emit(
|
||||||
|
// &mut self.handle,
|
||||||
|
// "removeDevice",
|
||||||
|
// Some(DevId {
|
||||||
|
// bus_number: dev.0,
|
||||||
|
// address: dev.1,
|
||||||
|
// }),
|
||||||
|
// ) {
|
||||||
|
// eprintln!("{}", msg);
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn err_wrap<T>(
|
||||||
|
res: Result<T, libnspire::Error>,
|
||||||
|
dev: DevId,
|
||||||
|
handle: &mut WebviewMut,
|
||||||
|
) -> Result<T, libnspire::Error> {
|
||||||
|
if let Err(ref e) = res {
|
||||||
|
dbg!(e);
|
||||||
|
}
|
||||||
|
if let Err(libnspire::Error::NoDevice) = res {
|
||||||
|
DEVICES
|
||||||
.write()
|
.write()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.remove_entry(&(device.bus_number(), device.address()))
|
.remove(&(dev.bus_number, dev.address));
|
||||||
{
|
if let Err(msg) = tauri::event::emit(handle, "removeDevice", Some(dev)) {
|
||||||
if let Err(msg) = tauri::event::emit(
|
|
||||||
&mut self.handle,
|
|
||||||
"removeDevice",
|
|
||||||
Some(DevId {
|
|
||||||
bus_number: dev.0,
|
|
||||||
address: dev.1,
|
|
||||||
}),
|
|
||||||
) {
|
|
||||||
eprintln!("{}", msg);
|
eprintln!("{}", msg);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn progress_sender<'a>(
|
fn progress_sender<'a>(
|
||||||
|
@ -180,28 +203,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
promise_fn(
|
promise_fn(
|
||||||
webview,
|
webview,
|
||||||
move || {
|
move || Ok(cmd::enumerate(&mut wv_handle)?),
|
||||||
let _ = cmd::enumerate();
|
|
||||||
Ok(
|
|
||||||
DEVICES
|
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.iter()
|
|
||||||
.map(|dev| AddDevice {
|
|
||||||
dev: dbg!(DevId {
|
|
||||||
bus_number: (dev.0).0,
|
|
||||||
address: (dev.0).1,
|
|
||||||
}),
|
|
||||||
name: (dev.1).name.clone(),
|
|
||||||
is_cx_ii: (dev.1)
|
|
||||||
.device
|
|
||||||
.device_descriptor()
|
|
||||||
.map(|d| d.product_id() == PID_CX2)
|
|
||||||
.unwrap_or(false),
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
promise,
|
promise,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -253,8 +255,7 @@ fn main() {
|
||||||
move || {
|
move || {
|
||||||
let handle = get_open_dev(&dev)?;
|
let handle = get_open_dev(&dev)?;
|
||||||
let handle = handle.lock().unwrap();
|
let handle = handle.lock().unwrap();
|
||||||
|
let info = err_wrap(handle.info(), dev, &mut wv_handle)?;
|
||||||
let info = handle.info()?;
|
|
||||||
Ok(info)
|
Ok(info)
|
||||||
},
|
},
|
||||||
promise,
|
promise,
|
||||||
|
@ -266,7 +267,7 @@ fn main() {
|
||||||
move || {
|
move || {
|
||||||
let handle = get_open_dev(&dev)?;
|
let handle = get_open_dev(&dev)?;
|
||||||
let handle = handle.lock().unwrap();
|
let handle = handle.lock().unwrap();
|
||||||
let dir = handle.list_dir(&path)?;
|
let dir = err_wrap(handle.list_dir(&path), dev, &mut wv_handle)?;
|
||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
dir
|
dir
|
||||||
|
@ -296,10 +297,14 @@ fn main() {
|
||||||
let handle = get_open_dev(&dev)?;
|
let handle = get_open_dev(&dev)?;
|
||||||
let handle = handle.lock().unwrap();
|
let handle = handle.lock().unwrap();
|
||||||
let mut buf = vec![0; size as usize];
|
let mut buf = vec![0; size as usize];
|
||||||
|
err_wrap(
|
||||||
handle.read_file(
|
handle.read_file(
|
||||||
&file,
|
&file,
|
||||||
&mut buf,
|
&mut buf,
|
||||||
&mut progress_sender(&mut wv_handle, dev, size as usize),
|
&mut progress_sender(&mut wv_handle.clone(), dev, size as usize),
|
||||||
|
),
|
||||||
|
dev,
|
||||||
|
&mut wv_handle,
|
||||||
)?;
|
)?;
|
||||||
if let Some(name) = file.split('/').last() {
|
if let Some(name) = file.split('/').last() {
|
||||||
File::create(dest.join(name))?.write_all(&buf)?;
|
File::create(dest.join(name))?.write_all(&buf)?;
|
||||||
|
@ -328,10 +333,14 @@ fn main() {
|
||||||
.ok_or_else(|| anyhow::anyhow!("Failed to get file name"))?
|
.ok_or_else(|| anyhow::anyhow!("Failed to get file name"))?
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.to_string();
|
.to_string();
|
||||||
|
err_wrap(
|
||||||
handle.write_file(
|
handle.write_file(
|
||||||
&format!("{}/{}", path, name),
|
&format!("{}/{}", path, name),
|
||||||
&buf,
|
&buf,
|
||||||
&mut progress_sender(&mut wv_handle, dev, buf.len()),
|
&mut progress_sender(&mut wv_handle.clone(), dev, buf.len()),
|
||||||
|
),
|
||||||
|
dev,
|
||||||
|
&mut wv_handle,
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
|
@ -346,7 +355,14 @@ fn main() {
|
||||||
let handle = handle.lock().unwrap();
|
let handle = handle.lock().unwrap();
|
||||||
let mut buf = vec![];
|
let mut buf = vec![];
|
||||||
File::open(&src)?.read_to_end(&mut buf)?;
|
File::open(&src)?.read_to_end(&mut buf)?;
|
||||||
handle.send_os(&buf, &mut progress_sender(&mut wv_handle, dev, buf.len()))?;
|
err_wrap(
|
||||||
|
handle.send_os(
|
||||||
|
&buf,
|
||||||
|
&mut progress_sender(&mut wv_handle.clone(), dev, buf.len()),
|
||||||
|
),
|
||||||
|
dev,
|
||||||
|
&mut wv_handle,
|
||||||
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
promise,
|
promise,
|
||||||
|
@ -358,7 +374,7 @@ fn main() {
|
||||||
move || {
|
move || {
|
||||||
let handle = get_open_dev(&dev)?;
|
let handle = get_open_dev(&dev)?;
|
||||||
let handle = handle.lock().unwrap();
|
let handle = handle.lock().unwrap();
|
||||||
handle.delete_file(&path)?;
|
err_wrap(handle.delete_file(&path), dev, &mut wv_handle)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
promise,
|
promise,
|
||||||
|
@ -370,7 +386,7 @@ fn main() {
|
||||||
move || {
|
move || {
|
||||||
let handle = get_open_dev(&dev)?;
|
let handle = get_open_dev(&dev)?;
|
||||||
let handle = handle.lock().unwrap();
|
let handle = handle.lock().unwrap();
|
||||||
handle.delete_dir(&path)?;
|
err_wrap(handle.delete_dir(&path), dev, &mut wv_handle)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
promise,
|
promise,
|
||||||
|
@ -382,7 +398,7 @@ fn main() {
|
||||||
move || {
|
move || {
|
||||||
let handle = get_open_dev(&dev)?;
|
let handle = get_open_dev(&dev)?;
|
||||||
let handle = handle.lock().unwrap();
|
let handle = handle.lock().unwrap();
|
||||||
handle.create_dir(&path)?;
|
err_wrap(handle.create_dir(&path), dev, &mut wv_handle)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
promise,
|
promise,
|
||||||
|
@ -399,7 +415,7 @@ fn main() {
|
||||||
move || {
|
move || {
|
||||||
let handle = get_open_dev(&dev)?;
|
let handle = get_open_dev(&dev)?;
|
||||||
let handle = handle.lock().unwrap();
|
let handle = handle.lock().unwrap();
|
||||||
handle.move_file(&src, &dest)?;
|
err_wrap(handle.move_file(&src, &dest), dev, &mut wv_handle)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
promise,
|
promise,
|
||||||
|
@ -416,7 +432,7 @@ fn main() {
|
||||||
move || {
|
move || {
|
||||||
let handle = get_open_dev(&dev)?;
|
let handle = get_open_dev(&dev)?;
|
||||||
let handle = handle.lock().unwrap();
|
let handle = handle.lock().unwrap();
|
||||||
handle.copy_file(&src, &dest)?;
|
err_wrap(handle.copy_file(&src, &dest), dev, &mut wv_handle)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
promise,
|
promise,
|
||||||
|
|
|
@ -30,7 +30,8 @@
|
||||||
},
|
},
|
||||||
"allowlist": {
|
"allowlist": {
|
||||||
"event": true,
|
"event": true,
|
||||||
"notification": true
|
"notification": true,
|
||||||
|
"open": true
|
||||||
},
|
},
|
||||||
"window": {
|
"window": {
|
||||||
"title": "N-Link",
|
"title": "N-Link",
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="header border-b px-2 py-2">
|
<div class="header border-b px-2 py-2 flex w-full">
|
||||||
<el-popover width="239" :visible-arrow="false" popper-class="focus:outline-none dev-select-pop" v-model="active">
|
<button @click="$devices.enumerate()" class="flex-shrink-0 mr-2 focus:outline-none" :class="$devices.enumerating && 'cursor-not-allowed opacity-25'" :disabled="$devices.enumerating">
|
||||||
<div slot="reference" class="inline-block relative w-full focus:outline-none">
|
<img src="~feather-icons/dist/icons/refresh-cw.svg" class="w-5"/>
|
||||||
|
</button>
|
||||||
|
<el-popover width="239" :visible-arrow="false" popper-class="focus:outline-none dev-select-pop" v-model="active" class="w-full overflow-hidden">
|
||||||
|
<div slot="reference" class="relative w-full focus:outline-none">
|
||||||
<div
|
<div
|
||||||
class="block w-full bg-white border border-gray-400 hover:border-gray-500 px-4 py-3/2 pr-8 rounded shadow leading-tight focus:outline-none focus:shadow-outline h-8 truncate">
|
class="block w-full bg-white border border-gray-400 hover:border-gray-500 px-4 py-3/2 pr-8 rounded shadow leading-tight focus:outline-none focus:shadow-outline h-8 truncate">
|
||||||
<span v-if="selectedCalculator">
|
<span v-if="selectedCalculator">
|
||||||
|
@ -10,7 +13,7 @@
|
||||||
<span v-else> {{ calc.name }}</span>
|
<span v-else> {{ calc.name }}</span>
|
||||||
</span>
|
</span>
|
||||||
<span v-else class="text-gray-700 text-sm">
|
<span v-else class="text-gray-700 text-sm">
|
||||||
Select...
|
Select a device...
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700">
|
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700">
|
||||||
|
|
|
@ -98,6 +98,7 @@ export default class FileBrowser extends Vue {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
height: 80px;
|
height: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lds-dual-ring:after {
|
.lds-dual-ring:after {
|
||||||
$color: theme('colors.gray.400');
|
$color: theme('colors.gray.400');
|
||||||
content: " ";
|
content: " ";
|
||||||
|
|
|
@ -50,7 +50,7 @@ export type PartialCmd = { action: 'download'; path: [string, number]; dest: str
|
||||||
|
|
||||||
export type Cmd = { id: number } & PartialCmd;
|
export type Cmd = { id: number } & PartialCmd;
|
||||||
|
|
||||||
export type Device = { name: string; info?: Info; progress?: Progress; queue?: Cmd[]; running?: boolean };
|
export type Device = { name: string; isCxIi: boolean; needsDrivers: boolean; info?: Info; progress?: Progress; queue?: Cmd[]; running?: boolean };
|
||||||
|
|
||||||
async function downloadFile(dev: DevId | string, path: [string, number], dest: string) {
|
async function downloadFile(dev: DevId | string, path: [string, number], dest: string) {
|
||||||
if (typeof dev === 'string') dev = stringToDev(dev);
|
if (typeof dev === 'string') dev = stringToDev(dev);
|
||||||
|
@ -118,18 +118,16 @@ let queueId = 0;
|
||||||
@Component
|
@Component
|
||||||
class Devices extends Vue {
|
class Devices extends Vue {
|
||||||
devices: Record<string, Device> = {};
|
devices: Record<string, Device> = {};
|
||||||
|
enumerating = false;
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
promisified({cmd: 'enumerate'}).then(devs => {
|
this.enumerate().catch(console.error);
|
||||||
for (const dev of devs as (Device & DevId)[]) {
|
|
||||||
this.$set(this.devices, devToString(dev as DevId), dev);
|
|
||||||
}
|
|
||||||
}, console.error);
|
|
||||||
listen('addDevice', dev => {
|
listen('addDevice', dev => {
|
||||||
const payload = dev.payload as Device & DevId;
|
const payload = dev.payload as Device & DevId;
|
||||||
const str = devToString(payload);
|
const str = devToString(payload);
|
||||||
const existing = this.devices[str] || {};
|
const existing = this.devices[str] || {};
|
||||||
this.$set(this.devices, str, {...existing, ...payload});
|
this.$set(this.devices, str, {...existing, ...payload});
|
||||||
|
console.log(dev);
|
||||||
});
|
});
|
||||||
listen('removeDevice', dev => {
|
listen('removeDevice', dev => {
|
||||||
this.$delete(this.devices, devToString(dev.payload as DevId));
|
this.$delete(this.devices, devToString(dev.payload as DevId));
|
||||||
|
@ -193,6 +191,18 @@ class Devices extends Vue {
|
||||||
this.runQueue(dev);
|
this.runQueue(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async enumerate() {
|
||||||
|
this.enumerating = true;
|
||||||
|
try {
|
||||||
|
for (const dev of await promisified({cmd: 'enumerate'}) as (Device & DevId)[]) {
|
||||||
|
this.$set(this.devices, devToString(dev as DevId), dev);
|
||||||
|
console.log(dev);
|
||||||
|
}
|
||||||
|
}finally {
|
||||||
|
this.enumerating = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async open(dev: DevId | string) {
|
async open(dev: DevId | string) {
|
||||||
if (typeof dev === 'string') dev = stringToDev(dev);
|
if (typeof dev === 'string') dev = stringToDev(dev);
|
||||||
const info = await promisified({...dev, cmd: 'openDevice'});
|
const info = await promisified({...dev, cmd: 'openDevice'});
|
||||||
|
|
|
@ -4,7 +4,17 @@
|
||||||
<div class="flex-shrink-0 border-r w-64">
|
<div class="flex-shrink-0 border-r w-64">
|
||||||
<device-select :selected.sync="selectedCalculator"/>
|
<device-select :selected.sync="selectedCalculator"/>
|
||||||
<div class="overflow-auto h-full px-4 py-4">
|
<div class="overflow-auto h-full px-4 py-4">
|
||||||
<div v-if="calculator && calculator.info">
|
<div v-if="needsDrivers">
|
||||||
|
<h1 class="text-3xl">Drivers required</h1>
|
||||||
|
<p>The WinUSB driver is required to use this device.</p>
|
||||||
|
<p class="text-center mt-2">
|
||||||
|
<a href="#" @click.prevent="installDrivers" class="text-blue-600">See installation instructions</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="calculator && !calculator.info" class="flex items-center justify-center h-full">
|
||||||
|
<div class="lds-dual-ring" />
|
||||||
|
</div>
|
||||||
|
<div v-else-if="calculator && calculator.info">
|
||||||
<calc-info :info="calculator.info" :dev="selectedCalculator"/>
|
<calc-info :info="calculator.info" :dev="selectedCalculator"/>
|
||||||
<label class="inline-flex items-center cursor-pointer mr-2 mt-4">
|
<label class="inline-flex items-center cursor-pointer mr-2 mt-4">
|
||||||
<input type="checkbox" class="form-checkbox h-5 w-5 text-blue-600 cursor-pointer" v-model="showHidden">
|
<input type="checkbox" class="form-checkbox h-5 w-5 text-blue-600 cursor-pointer" v-model="showHidden">
|
||||||
|
@ -24,6 +34,7 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {Component, Vue, Watch} from 'vue-property-decorator';
|
import {Component, Vue, Watch} from 'vue-property-decorator';
|
||||||
|
import {open} from 'tauri/api/window';
|
||||||
import CalcInfo from '@/components/CalcInfo.vue';
|
import CalcInfo from '@/components/CalcInfo.vue';
|
||||||
import FileBrowser from '@/components/FileBrowser.vue';
|
import FileBrowser from '@/components/FileBrowser.vue';
|
||||||
import DeviceSelect from "@/components/DeviceSelect.vue";
|
import DeviceSelect from "@/components/DeviceSelect.vue";
|
||||||
|
@ -52,15 +63,49 @@ export default class Home extends Vue {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Watch('selectedCalculator')
|
@Watch('selectedCalculator')
|
||||||
onSelectCalculator(dev: string | null) {
|
async onSelectCalculator(dev: string | null) {
|
||||||
if (dev && !this.$devices.devices[dev].info) {
|
if (dev && !this.$devices.devices[dev].info && !this.$devices.devices[dev].needsDrivers) {
|
||||||
console.log('open', dev);
|
console.log('open', dev);
|
||||||
this.$devices.open(dev);
|
try {
|
||||||
|
await this.$devices.open(dev);
|
||||||
|
}catch(e) {
|
||||||
|
console.error(e);
|
||||||
|
this.selectedCalculator = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get calculator() {
|
get calculator() {
|
||||||
return this.selectedCalculator && this.$devices.devices[this.selectedCalculator];
|
return this.selectedCalculator && this.$devices.devices[this.selectedCalculator];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get needsDrivers() {
|
||||||
|
return this.selectedCalculator && this.$devices.devices[this.selectedCalculator]?.needsDrivers;
|
||||||
|
}
|
||||||
|
|
||||||
|
installDrivers() {
|
||||||
|
open('https://github.com');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.lds-dual-ring {
|
||||||
|
display: inline-block;
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lds-dual-ring:after {
|
||||||
|
$color: theme('colors.gray.400');
|
||||||
|
content: " ";
|
||||||
|
display: block;
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
margin: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 6px solid $color;
|
||||||
|
border-color: $color transparent $color transparent;
|
||||||
|
animation: lds-dual-ring 1.2s linear infinite;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
Loading…
Reference in a new issue