From 71798deb148a182b9f791d6c9644072c28bb0633 Mon Sep 17 00:00:00 2001 From: Suyog Tandel Date: Mon, 12 Jan 2026 22:53:07 +0530 Subject: [PATCH] fix: move window state logic to backend --- .gitignore | 1 + src-tauri/src/error.rs | 22 ++++++++++++++++++++ src-tauri/src/fido/mod.rs | 17 +++++++++------- src-tauri/src/io.rs | 10 ++++----- src-tauri/src/lib.rs | 32 ++++++++++++++++++++++++++++- src-tauri/src/rescue/mod.rs | 38 +++++++++++++++++++---------------- src-tauri/src/types.rs | 29 +++++--------------------- src/lib/layout/sidebar.svelte | 13 +++++++----- 8 files changed, 103 insertions(+), 59 deletions(-) create mode 100644 src-tauri/src/error.rs diff --git a/.gitignore b/.gitignore index c4a20a8..5bf4dbb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .DS_Store node_modules +/target /build /.svelte-kit /package diff --git a/src-tauri/src/error.rs b/src-tauri/src/error.rs new file mode 100644 index 0000000..530387b --- /dev/null +++ b/src-tauri/src/error.rs @@ -0,0 +1,22 @@ +/// Custom error types for Pico Forge application. +#[derive(Debug, thiserror::Error)] +pub enum PFError { + #[error("PCSC Error: {0}")] + Pcsc(#[from] pcsc::Error), + #[error("IO/Hex Error: {0}")] + Io(String), + #[error("Device Error: {0}")] + Device(String), +} + +// Allow error to be serialized to string for Tauri +impl serde::Serialize for PFError { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +// pub type Result = std::result::Result; diff --git a/src-tauri/src/fido/mod.rs b/src-tauri/src/fido/mod.rs index 3b11a6a..67cc2d3 100644 --- a/src-tauri/src/fido/mod.rs +++ b/src-tauri/src/fido/mod.rs @@ -3,7 +3,10 @@ pub mod constants; pub mod hid; -use crate::types::{AppConfig, AppError, DeviceInfo, FidoDeviceInfo, FullDeviceStatus}; +use crate::{ + error::PFError, + types::{AppConfig, DeviceInfo, FidoDeviceInfo, FullDeviceStatus}, +}; use constants::*; use ctap_hid_fido2::{Cfg, FidoKeyHidFactory}; use hid::*; @@ -80,12 +83,12 @@ pub(crate) fn set_min_pin_length( // Custom Fido functions ( works only with pico-fido firmware ) -pub fn read_device_details() -> Result { +pub fn read_device_details() -> Result { log::info!("Starting FIDO device details read..."); let transport = HidTransport::open().map_err(|e| { log::error!("Failed to open HID transport: {}", e); - AppError::Device(e.to_string()) + PFError::Device(e.to_string()) })?; // --- 1. Get Info --- @@ -95,14 +98,14 @@ pub fn read_device_details() -> Result { .send_cbor(CTAPHID_CBOR, &info_payload) .map_err(|e| { log::error!("GetInfo CTAP command failed: {}", e); - AppError::Device(format!("GetInfo failed: {}", e)) + PFError::Device(format!("GetInfo failed: {}", e)) })?; log::debug!("GetInfo response received ({} bytes)", info_res.len()); let info_val: Value = from_slice(&info_res).map_err(|e| { log::error!("Failed to parse GetInfo CBOR: {}", e); - AppError::Io(e.to_string()) + PFError::Io(e.to_string()) })?; // NOTE: Key 0x03 is AAGUID, not the unique device Serial. @@ -158,7 +161,7 @@ pub fn read_device_details() -> Result { let mem_cbor = to_vec(&Value::Map(mem_req)).map_err(|e| { log::error!("Failed to encode Memory Stats CBOR: {}", e); - AppError::Io(format!("CBOR encode error: {}", e)) + PFError::Io(format!("CBOR encode error: {}", e)) })?; // FIX: Prepend the Vendor Command ID (0x06 for Memory) to the payload @@ -216,7 +219,7 @@ pub fn read_device_details() -> Result { let phy_cbor = to_vec(&Value::Map(phy_params)).map_err(|e| { log::error!("Failed to encode Physical Config CBOR: {}", e); - AppError::Io(format!("CBOR encode error: {}", e)) + PFError::Io(format!("CBOR encode error: {}", e)) })?; // FIX: Prepend Vendor Command ID (0x05 for PhysicalOptions) diff --git a/src-tauri/src/io.rs b/src-tauri/src/io.rs index 28c3e78..91bafa2 100644 --- a/src-tauri/src/io.rs +++ b/src-tauri/src/io.rs @@ -1,19 +1,19 @@ //! Tauri Commands to interact with the pico-fido firmware via rescue and fido protocols. -use crate::{fido, rescue, types::*}; +use crate::{error::PFError, fido, rescue, types::*}; #[tauri::command] -pub fn read_device_details() -> Result { +pub fn read_device_details() -> Result { rescue::read_device_details() // fido::read_device_details() } #[tauri::command] -pub fn write_config(config: AppConfigInput) -> Result { +pub fn write_config(config: AppConfigInput) -> Result { rescue::write_config(config) } #[tauri::command] -pub fn enable_secure_boot(lock: bool) -> Result { +pub fn enable_secure_boot(lock: bool) -> Result { rescue::enable_secure_boot(lock) } @@ -40,6 +40,6 @@ pub(crate) fn set_min_pin_length( } #[tauri::command] -pub fn reboot(to_bootsel: bool) -> Result { +pub fn reboot(to_bootsel: bool) -> Result { rescue::reboot_device(to_bootsel) } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index dd7c388..2ee15ae 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,11 +1,34 @@ -// use tauri::State; +use serde::Serialize; +use tauri::{Emitter, Listener, Manager, WebviewWindow}; +mod error; mod fido; mod io; mod logging; mod rescue; mod types; +// This will be temporary here untill moved to a dedicated module: + +#[derive(Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct WindowState { + pub is_maximized: bool, +} + +/// Sets up window state listener that emits events to the frontend +pub fn setup_window_state_listener(window: &WebviewWindow) { + let window_clone = window.clone(); + + window.on_window_event(move |event| { + if let tauri::WindowEvent::Resized(_) = event { + if let Ok(is_maximized) = window_clone.is_maximized() { + let _ = window_clone.emit("window-state-changed", WindowState { is_maximized }); + } + } + }); +} + #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { logging::logger_init(); @@ -14,6 +37,13 @@ pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_opener::init()) + .setup(|app| { + if let Some(window) = app.get_webview_window("main") { + setup_window_state_listener(&window); + log::info!("Window state listener initialized"); + } + Ok(()) + }) .invoke_handler(tauri::generate_handler![ io::read_device_details, io::write_config, diff --git a/src-tauri/src/rescue/mod.rs b/src-tauri/src/rescue/mod.rs index 9881bcc..2f61265 100644 --- a/src-tauri/src/rescue/mod.rs +++ b/src-tauri/src/rescue/mod.rs @@ -1,13 +1,17 @@ +//! Implements communication with the pico-fido firmware via the `Rescue API`. +//! +//! For more details checkout the [pico-key-sdk](https://github.com/polhenarejos/pico-keys-sdk/blob/main/src/rescue.c) + pub mod constants; -use crate::{rescue::constants::*, types::*}; +use crate::{error::PFError, rescue::constants::*, types::*}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use log; use pcsc::{Context, Protocols, Scope, ShareMode}; use std::io::Cursor; /// Connects to the first available reader and selects the Rescue Applet -fn connect_and_select() -> Result<(pcsc::Card, Vec), AppError> { +fn connect_and_select() -> Result<(pcsc::Card, Vec), PFError> { let ctx = Context::establish(Scope::User)?; let mut readers_buf = [0; 2048]; @@ -16,7 +20,7 @@ fn connect_and_select() -> Result<(pcsc::Card, Vec), AppError> { // Use the first reader found let reader = readers.next().ok_or_else(|| { log::error!("No Smart Card Reader found"); - AppError::Device("No Smart Card Reader found.".into()) + PFError::Device("No Smart Card Reader found.".into()) })?; let card = ctx.connect(reader, ShareMode::Shared, Protocols::ANY)?; @@ -37,7 +41,7 @@ fn connect_and_select() -> Result<(pcsc::Card, Vec), AppError> { // Check Success (0x90 0x00) if !rx.ends_with(&[0x90, 0x00]) { log::error!("Rescue Applet not found on the device!"); - return Err(AppError::Device( + return Err(PFError::Device( // There is no such mode as fido, i tink the rescue applet stays active and at the same time fido mode works? // Need to study this more. "Rescue Applet not found on device. Is it in FIDO mode?".into(), @@ -48,12 +52,12 @@ fn connect_and_select() -> Result<(pcsc::Card, Vec), AppError> { Ok((card, rx.to_vec())) } -pub fn read_device_details() -> Result { +pub fn read_device_details() -> Result { log::info!("Reading full device details"); let (card, select_resp) = connect_and_select()?; if select_resp.len() < 14 { - return Err(AppError::Device("Invalid select response".into())); + return Err(PFError::Device("Invalid select response".into())); } let version_major = select_resp[2]; let version_minor = select_resp[3]; @@ -73,7 +77,7 @@ pub fn read_device_details() -> Result { )?; if !rx_flash.ends_with(&SW_SUCCESS) { - return Err(AppError::Device("Failed to read flash".into())); + return Err(PFError::Device("Failed to read flash".into())); } let mut rdr = Cursor::new(&rx_flash[..rx_flash.len() - 2]); @@ -116,7 +120,7 @@ pub fn read_device_details() -> Result { )?; if !rx_phy.ends_with(&[0x90, 0x00]) { - return Err(AppError::Device("Failed to read config".into())); + return Err(PFError::Device("Failed to read config".into())); } // Parse TLV @@ -215,7 +219,7 @@ pub fn read_device_details() -> Result { }) } -pub fn write_config(config: AppConfigInput) -> Result { +pub fn write_config(config: AppConfigInput) -> Result { log::info!("Writing configuration to device"); log::debug!("Config input: {:?}", config); @@ -225,9 +229,9 @@ pub fn write_config(config: AppConfigInput) -> Result { // VID:PID (Tag 0x00) if let (Some(vid_str), Some(pid_str)) = (&config.vid, &config.pid) { let vid = - u16::from_str_radix(vid_str, 16).map_err(|_| AppError::Io("Invalid VID".into()))?; + u16::from_str_radix(vid_str, 16).map_err(|_| PFError::Io("Invalid VID".into()))?; let pid = - u16::from_str_radix(pid_str, 16).map_err(|_| AppError::Io("Invalid PID".into()))?; + u16::from_str_radix(pid_str, 16).map_err(|_| PFError::Io("Invalid PID".into()))?; tlv.push(PhyTag::VidPid as u8); tlv.push(0x04); @@ -303,7 +307,7 @@ pub fn write_config(config: AppConfigInput) -> Result { let name_bytes = name.as_bytes(); let len = name_bytes.len() + 1; if len > 32 { - return Err(AppError::Io("Product name too long".into())); + return Err(PFError::Io("Product name too long".into())); } tlv.push(PhyTag::UsbProduct as u8); @@ -341,11 +345,11 @@ pub fn write_config(config: AppConfigInput) -> Result { Ok("Configuration Applied Successfully".into()) } else { log::error!("Configuration write failed: {:02X?}", rx); - Err(AppError::Device(format!("Write failed: {:02X?}", rx))) + Err(PFError::Device(format!("Write failed: {:02X?}", rx))) } } -pub fn reboot_device(to_bootsel: bool) -> Result { +pub fn reboot_device(to_bootsel: bool) -> Result { let (card, _) = connect_and_select()?; let param = if to_bootsel { @@ -368,12 +372,12 @@ pub fn reboot_device(to_bootsel: bool) -> Result { if rx.ends_with(&SW_SUCCESS) { Ok("Reboot command sent".into()) } else { - Err(AppError::Device(format!("Reboot failed: {:02X?}", rx))) + Err(PFError::Device(format!("Reboot failed: {:02X?}", rx))) } } /// UNSTABLE! (WIP) -pub fn enable_secure_boot(lock: bool) -> Result { +pub fn enable_secure_boot(lock: bool) -> Result { let (card, _) = connect_and_select()?; // APDU: 80 1D [KeyIndex] [LockBool] 00 @@ -394,6 +398,6 @@ pub fn enable_secure_boot(lock: bool) -> Result { if rx.ends_with(&[0x90, 0x00]) { Ok("Secure Boot Enabled".into()) } else { - Err(AppError::Device(format!("Secure Boot failed: {:02X?}", rx))) + Err(PFError::Device(format!("Secure Boot failed: {:02X?}", rx))) } } diff --git a/src-tauri/src/types.rs b/src-tauri/src/types.rs index 70640a9..d0e9f8a 100644 --- a/src-tauri/src/types.rs +++ b/src-tauri/src/types.rs @@ -1,6 +1,10 @@ +#![allow(unused)] + use serde::{Deserialize, Serialize}; -// --- Data Structures --- +struct PForgeState { + device_info: DeviceInfo, +} #[derive(Serialize)] #[serde(rename_all = "camelCase")] @@ -22,7 +26,6 @@ pub struct AppConfig { pub touch_timeout: u8, #[serde(skip_serializing_if = "Option::is_none")] pub led_driver: Option, - // New Options pub led_dimmable: bool, pub power_cycle_on_reset: bool, pub led_steady: bool, @@ -69,25 +72,3 @@ pub struct FidoDeviceInfo { pub min_pin_length: u32, pub firmware_version: String, } - -// Error stuff: - -#[derive(Debug, thiserror::Error)] -pub enum AppError { - #[error("PCSC Error: {0}")] - Pcsc(#[from] pcsc::Error), - #[error("IO/Hex Error: {0}")] - Io(String), - #[error("Device Error: {0}")] - Device(String), -} - -// Allow error to be serialized to string for Tauri -impl serde::Serialize for AppError { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&self.to_string()) - } -} diff --git a/src/lib/layout/sidebar.svelte b/src/lib/layout/sidebar.svelte index 8ca4fc6..8a52d63 100644 --- a/src/lib/layout/sidebar.svelte +++ b/src/lib/layout/sidebar.svelte @@ -1,6 +1,7 @@