Fixed issue where device would reboot when stopping (was sending an invalid clear buffer payload)

Added a manual device list refresh button
Updated device list validator function to not remove a device from the config just because it's not available
This commit is contained in:
2025-03-28 19:34:12 -04:00
parent e2053f0d67
commit 4df9ce4d49
3 changed files with 33 additions and 29 deletions

View File

@@ -1,5 +1,5 @@
use hidapi::{DeviceInfo, HidApi, HidError};
use log::{error, info, warn}; // Use log crate
use log::{error, info, warn, debug, trace}; // Use log crate
use serde::{Deserialize, Serialize};
use std::rc::Rc; // Keep Rc for potential sharing within UI if needed
@@ -98,7 +98,7 @@ pub(crate) fn find_device_index_for_saved(
impl crate::ShiftTool {
/// Refreshes the internal list of available HID devices.
pub(crate) fn refresh_devices(&mut self) {
info!("Refreshing device list...");
trace!("Refreshing device list...");
match HidApi::new() {
Ok(hidapi) => {
let mut current_devices: Vec<VpcDevice> = Vec::new();
@@ -126,7 +126,7 @@ impl crate::ShiftTool {
if crate::util::is_supported(
vpc_device.firmware.to_string(),
) {
info!("Found supported device: {}", vpc_device);
debug!("Found supported device: {}", vpc_device);
current_devices.push(vpc_device);
} else {
warn!(
@@ -153,7 +153,7 @@ impl crate::ShiftTool {
// Update the app's device list
self.device_list = current_devices;
info!(
debug!(
"Device list refresh complete. Found {} unique devices.",
self.device_list.len() - 1 // Exclude default entry
);
@@ -200,29 +200,26 @@ impl crate::ShiftTool {
/// Checks if saved source/receiver devices still exist in the refreshed list.
/// Resets the config entry to default if the device is gone.
fn validate_selected_devices(&mut self) {
let mut changed = false;
for i in 0..self.config.data.sources.len() {
let idx = self.find_source_device_index(i);
let idx = self.find_device_index_for_saved(&self.config.data.sources[i]);
// Check if device *was* configured but is *not* found (idx 0 is default/not found)
if idx == 0 && (self.config.data.sources[i].vendor_id != 0 || self.config.data.sources[i].product_id != 0) {
warn!("Previously selected source device {} not found after refresh. Resetting.", i + 1);
self.config.data.sources[i] = SavedDevice::default();
changed = true;
// Log that the configured device is currently missing, but DO NOT reset config
warn!(
"validate_selected_devices: Configured source device {} (VID={:04X}, PID={:04X}) not found in refreshed list. Keeping configuration.",
i + 1, self.config.data.sources[i].vendor_id, self.config.data.sources[i].product_id
);
}
}
for i in 0..self.config.data.receivers.len() {
let idx = self.find_receiver_device_index(i);
let idx = self.find_device_index_for_saved(&self.config.data.receivers[i]);
if idx == 0 && (self.config.data.receivers[i].vendor_id != 0 || self.config.data.receivers[i].product_id != 0) {
warn!("Previously selected receiver device {} not found after refresh. Resetting.", i + 1);
self.config.data.receivers[i] = SavedDevice::default();
changed = true;
warn!(
"validate_selected_devices: Configured receiver device {} (VID={:04X}, PID={:04X}) not found in refreshed list. Keeping configuration.",
i + 1, self.config.data.receivers[i].vendor_id, self.config.data.receivers[i].product_id
);
}
}
if changed {
// Optionally save the config immediately after validation changes
// if let Err(e) = self.config.save() {
// error!("Failed to save config after device validation: {}", e);
// }
}
}
}

View File

@@ -264,11 +264,8 @@ fn run_hid_worker_loop(hidapi: HidApi, data: WorkerData) {
}
// --- Write to Receiver Devices ---
// We need to determine the correct length to send. Assuming report 4 is always ID + 2 bytes.
const bytes_to_send: usize = 19; // Report ID (1) + Data (2)
let zero_buffer: [u8; bytes_to_send] = {
let mut buf = [0u8; bytes_to_send];
let zero_buffer: [u8; REPORT_BUFFER_SIZE] = {
let mut buf = [0u8; REPORT_BUFFER_SIZE];
buf[0] = FEATURE_REPORT_ID; // Set Report ID 4
// All other bytes (1-18) remain 0 for the zero state
buf
@@ -322,7 +319,7 @@ fn run_hid_worker_loop(hidapi: HidApi, data: WorkerData) {
// Send the potentially filtered feature report
match device.send_feature_report(&filtered_write_buffer[0..bytes_to_send]) {
match device.send_feature_report(&filtered_write_buffer[0..REPORT_BUFFER_SIZE]) {
Ok(_) => {
log::debug!("Worker: Send to receiver[{}] successful.", i);
// Successfully sent. Update UI state for this receiver.
@@ -392,8 +389,13 @@ fn run_hid_worker_loop(hidapi: HidApi, data: WorkerData) {
// --- Cleanup before thread exit ---
log::info!("Worker loop finished. Performing cleanup...");
// Optionally send a 'zero' report to all devices on exit
let cleanup_buffer: [u8; 3] = [FEATURE_REPORT_ID, 0, 0];
// Send a 'zero' report to all devices on exit
let cleanup_buffer: [u8; REPORT_BUFFER_SIZE] = {
let mut buf = [0u8; REPORT_BUFFER_SIZE];
buf[0] = FEATURE_REPORT_ID; // Set Report ID 4
// All other bytes (1-18) remain 0 for the zero state
buf
};
for device_opt in source_devices.iter_mut().chain(receiver_devices.iter_mut()) {
if let Some(device) = device_opt {
if let Err(e) = device.send_feature_report(&cleanup_buffer) {

View File

@@ -550,6 +550,11 @@ fn draw_control_buttons(
// }
// }
if ui.add_enabled(!thread_running, egui::Button::new("Refresh Devices")).clicked() {
log::info!("Refreshing device list manually.");
app.refresh_devices();
}
if ui.button("About").clicked() {
app.state = State::About;
}