From 32267df99cefaba44c55da0b089c0c5a959a51d0 Mon Sep 17 00:00:00 2001 From: sebgab Date: Mon, 6 Feb 2023 11:46:27 +0100 Subject: [PATCH] Moved USB and Config functions to their own files --- src/config_parsing.rs | 78 +++++++++++++++++++++ src/main.rs | 143 +++++--------------------------------- src/usb_communications.rs | 47 +++++++++++++ 3 files changed, 141 insertions(+), 127 deletions(-) create mode 100644 src/config_parsing.rs create mode 100644 src/usb_communications.rs diff --git a/src/config_parsing.rs b/src/config_parsing.rs new file mode 100644 index 0000000..8c491a3 --- /dev/null +++ b/src/config_parsing.rs @@ -0,0 +1,78 @@ +extern crate json; +extern crate colored; +use std::path::Path; + +#[derive(Debug)] +pub struct KeyboardLighting { + pub lighting_type: String, + pub rows: u16, + pub cols: u16 +} + +#[derive(Debug, Clone)] +pub struct KeyboardLayout { + pub rows: u16, + pub cols: u16, + pub keys: Vec +} + +#[derive(Debug)] +pub struct KeyboardConfig { + pub name: String, + pub vendor_id: u16, + pub product_id: u16, + pub layout: KeyboardLayout, + pub lighting: KeyboardLighting +} + +pub fn parse_config(config_path: &Path) -> KeyboardConfig{ + let mut parsed_json = json::parse(&std::fs::read_to_string(config_path).unwrap()).unwrap(); + + // General info + let extracted_name: String = parsed_json["name"].as_str().expect("ERROR: Failed to parse name").to_string(); + let extracted_vendor_id: u16 = u16::from_str_radix(parsed_json["vendor_id"].as_str().expect("ERROR: Failed to parse name"), 16).unwrap(); + let extracted_product_id: u16 = u16::from_str_radix(parsed_json["product_id"].as_str().expect("ERROR: Failed to parse name"), 16).unwrap(); + + // Layout + let extracted_layout_rows: u16 = parsed_json["layout"]["rows"].as_u16().expect("ERROR: Failed to parse layout rows"); + let extracted_layout_cols: u16 = parsed_json["layout"]["cols"].as_u16().expect("ERROR: Failed to parse layout cols"); + let mut extracted_layout_keys: Vec = Vec::new(); + let mut keys: std::collections::VecDeque = std::collections::VecDeque::new(); + for i in 0..extracted_layout_rows { + let mut row: json::JsonValue = parsed_json["layout"]["keys"].pop(); + for j in 0..extracted_layout_cols { + keys.push_front(row.pop().as_str().expect("ERROR: Failed to extract key names").to_string()); + } + } + for i in 0..keys.len() { + extracted_layout_keys.push(keys.pop_front().expect("ERROR: Empty key value")); + } + + let keyboard_layout = KeyboardLayout { + rows: extracted_layout_rows, + cols: extracted_layout_cols, + keys: extracted_layout_keys + }; + + // Lighting + let extracted_lighting_type: String = parsed_json["lighting"]["type"].as_str().expect("ERROR: Failed to parse lighting type").to_string(); + let extracted_lighting_rows: u16 = parsed_json["lighting"]["layout"]["rows"].as_u16().expect("ERROR: Failed to parse lighting rows"); + let extracted_lighting_cols: u16 = parsed_json["lighting"]["layout"]["cols"].as_u16().expect("ERROR: Failed to parse lighting cols"); + + let keyboard_lighting = KeyboardLighting { + lighting_type: extracted_lighting_type, + rows: extracted_lighting_rows, + cols: extracted_lighting_cols + }; + + + let keyboard_config = KeyboardConfig { + name: extracted_name, + vendor_id: extracted_vendor_id, + product_id: extracted_product_id, + layout: keyboard_layout, + lighting: keyboard_lighting + }; + + return keyboard_config; +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index e3f000a..1f0539e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,38 +1,16 @@ -extern crate libusb; extern crate colored; -extern crate json; +// Config +mod config_parsing; +use config_parsing::*; use std::path::Path; -use std::{cell::Cell, rc::Rc}; -use colored::Colorize; +// USB +mod usb_communications; +use usb_communications::*; use iced::{Sandbox, Settings, Vector}; -use iced::widget::{Row, Column, Button, Text, Container, Slider}; - - -#[derive(Debug)] -struct KeyboardLighting { - lighting_type: String, - rows: u16, - cols: u16 -} - -#[derive(Debug, Clone)] -struct KeyboardLayout { - rows: u16, - cols: u16, - keys: Vec -} - -#[derive(Debug)] -struct KeyboardConfig { - name: String, - vendor_id: u16, - product_id: u16, - layout: KeyboardLayout, - lighting: KeyboardLighting -} +use iced::widget::{Row, Column, Button, Text, Slider}; /* #[derive(Debug)] @@ -64,63 +42,12 @@ enum MainPageMessages { row: u8, col: u8 }, - UpdatedSlider_R(u8), - UpdatedSlider_G(u8), - UpdatedSlider_B(u8) + UpdatedSliderRed(u8), + UpdatedSliderGreen(u8), + UpdatedSliderBlue(u8) } -fn parse_config(config_path: &Path) -> KeyboardConfig{ - let mut parsed_json = json::parse(&std::fs::read_to_string(config_path).unwrap()).unwrap(); - - // General info - let extracted_name: String = parsed_json["name"].as_str().expect("ERROR: Failed to parse name").to_string(); - let extracted_vendor_id: u16 = u16::from_str_radix(parsed_json["vendor_id"].as_str().expect("ERROR: Failed to parse name"), 16).unwrap(); - let extracted_product_id: u16 = u16::from_str_radix(parsed_json["product_id"].as_str().expect("ERROR: Failed to parse name"), 16).unwrap(); - - // Layout - let extracted_layout_rows: u16 = parsed_json["layout"]["rows"].as_u16().expect("ERROR: Failed to parse layout rows"); - let extracted_layout_cols: u16 = parsed_json["layout"]["cols"].as_u16().expect("ERROR: Failed to parse layout cols"); - let mut extracted_layout_keys: Vec = Vec::new(); - let mut keys: std::collections::VecDeque = std::collections::VecDeque::new(); - for i in 0..extracted_layout_rows { - let mut row: json::JsonValue = parsed_json["layout"]["keys"].pop(); - for j in 0..extracted_layout_cols { - keys.push_front(row.pop().as_str().expect("ERROR: Failed to extract key names").to_string()); - } - } - for i in 0..keys.len() { - extracted_layout_keys.push(keys.pop_front().expect("ERROR: Empty key value")); - } - - let keyboard_layout = KeyboardLayout { - rows: extracted_layout_rows, - cols: extracted_layout_cols, - keys: extracted_layout_keys - }; - - // Lighting - let extracted_lighting_type: String = parsed_json["lighting"]["type"].as_str().expect("ERROR: Failed to parse lighting type").to_string(); - let extracted_lighting_rows: u16 = parsed_json["lighting"]["layout"]["rows"].as_u16().expect("ERROR: Failed to parse lighting rows"); - let extracted_lighting_cols: u16 = parsed_json["lighting"]["layout"]["cols"].as_u16().expect("ERROR: Failed to parse lighting cols"); - - let keyboard_lighting = KeyboardLighting { - lighting_type: extracted_lighting_type, - rows: extracted_lighting_rows, - cols: extracted_lighting_cols - }; - - - let keyboard_config = KeyboardConfig { - name: extracted_name, - vendor_id: extracted_vendor_id, - product_id: extracted_product_id, - layout: keyboard_layout, - lighting: keyboard_lighting - }; - - return keyboard_config; -} fn main() -> Result<(), iced::Error> { // Printing the parsed config for debug purposes @@ -131,44 +58,6 @@ fn main() -> Result<(), iced::Error> { MainPage::run(Settings::default()) } -// USB Code -fn send_lighting_update(col: u8, row: u8, r: u8, g: u8, b: u8, _a: u8) { - println!("Sending lighting update!"); - - let config: KeyboardConfig = parse_config(&Path::new("numpad_config.json")); - - let context = libusb::Context::new().unwrap(); // Get a new USB context - for device in context.devices().unwrap().iter() { // Iterate through all the devices - let device_desc = device.device_descriptor().unwrap(); // Get a descriptor for the device - - if device_desc.vendor_id() == config.vendor_id && device_desc.product_id() == config.product_id { // Check if the devie is the device we are looking for - println!("Supported keyboard discovered, claiming it."); - - let mut device_handle: libusb::DeviceHandle = device.open().unwrap(); // Get a handle for the device - - if device_handle.kernel_driver_active(0).unwrap() { // If the device has an active kernel driver, detach it - println!("Interface has kernel driver, detaching..."); - device_handle.detach_kernel_driver(0).unwrap(); - } - device_handle.claim_interface(0).unwrap(); // Now that the device is free we claim it - - match device_handle.write_control( - libusb::request_type(libusb::Direction::Out, libusb::RequestType::Class, libusb::Recipient::Interface), // The request is outband and uses HID specific features tergeting an interface - 0x09, 0x0301, 0, // We want to send a SET_REPORT request, that is a custom feature, with the feature being lighting, we want to send it to endpoint 0 - &[r, g, b, row, col], std::time::Duration::from_secs(1)) { // First we send the row, then the column, then the Red, Green, Blue, and lastly the brightness - Ok(n) => { - println!("{}", format!("Successfully wrote {} bytes!", n).green()); - }, - Err(e) => { - eprintln!("{}{:?}", format!("Failed to write with error: ").red().bold(), e); - } - } - - device_handle.release_interface(0).unwrap(); // Release the interface - device_handle.attach_kernel_driver(0).unwrap(); // Re-attach the kernel driver - } - } -} // UI Code impl Sandbox for MainPage { @@ -200,9 +89,9 @@ impl Sandbox for MainPage { fn update(&mut self, message: Self::Message) { match message { - MainPageMessages::UpdatedSlider_R(value) => { self.slider_values.slider_r = value; } - MainPageMessages::UpdatedSlider_G(value) => { self.slider_values.slider_g = value; } - MainPageMessages::UpdatedSlider_B(value) => { self.slider_values.slider_b = value; } + MainPageMessages::UpdatedSliderRed(value) => { self.slider_values.slider_r = value; } + MainPageMessages::UpdatedSliderGreen(value) => { self.slider_values.slider_g = value; } + MainPageMessages::UpdatedSliderBlue(value) => { self.slider_values.slider_b = value; } MainPageMessages::UpdateColor { row, col } => { println!("{:?}", message); send_lighting_update(col, row, self.slider_values.slider_r, self.slider_values.slider_g, self.slider_values.slider_b, 0x0); @@ -283,9 +172,9 @@ impl Sandbox for MainPage { // Create the color selector - let slider_r = Slider::new(0..=255, self.slider_values.slider_r, MainPageMessages::UpdatedSlider_R); - let slider_g = Slider::new(0..=255, self.slider_values.slider_g, MainPageMessages::UpdatedSlider_G); - let slider_b = Slider::new(0..=255, self.slider_values.slider_b, MainPageMessages::UpdatedSlider_B); + let slider_r = Slider::new(0..=255, self.slider_values.slider_r, MainPageMessages::UpdatedSliderRed); + let slider_g = Slider::new(0..=255, self.slider_values.slider_g, MainPageMessages::UpdatedSliderGreen); + let slider_b = Slider::new(0..=255, self.slider_values.slider_b, MainPageMessages::UpdatedSliderBlue); let color_selector_col = Column::new() .push(slider_r) .push(slider_g) diff --git a/src/usb_communications.rs b/src/usb_communications.rs new file mode 100644 index 0000000..77b041c --- /dev/null +++ b/src/usb_communications.rs @@ -0,0 +1,47 @@ +extern crate libusb; +extern crate colored; + +use crate::config_parsing::*; + +use std::path::Path; +use colored::Colorize; + + +// USB Code +pub fn send_lighting_update(col: u8, row: u8, r: u8, g: u8, b: u8, _a: u8) { + println!("Sending lighting update!"); + + let config: KeyboardConfig = parse_config(&Path::new("numpad_config.json")); + + let context = libusb::Context::new().unwrap(); // Get a new USB context + for device in context.devices().unwrap().iter() { // Iterate through all the devices + let device_desc = device.device_descriptor().unwrap(); // Get a descriptor for the device + + if device_desc.vendor_id() == config.vendor_id && device_desc.product_id() == config.product_id { // Check if the devie is the device we are looking for + println!("Supported keyboard discovered, claiming it."); + + let mut device_handle: libusb::DeviceHandle = device.open().unwrap(); // Get a handle for the device + + if device_handle.kernel_driver_active(0).unwrap() { // If the device has an active kernel driver, detach it + println!("Interface has kernel driver, detaching..."); + device_handle.detach_kernel_driver(0).unwrap(); + } + device_handle.claim_interface(0).unwrap(); // Now that the device is free we claim it + + match device_handle.write_control( + libusb::request_type(libusb::Direction::Out, libusb::RequestType::Class, libusb::Recipient::Interface), // The request is outband and uses HID specific features tergeting an interface + 0x09, 0x0301, 0, // We want to send a SET_REPORT request, that is a custom feature, with the feature being lighting, we want to send it to endpoint 0 + &[r, g, b, row, col], std::time::Duration::from_secs(1)) { // First we send the row, then the column, then the Red, Green, Blue, and lastly the brightness + Ok(n) => { + println!("{}", format!("Successfully wrote {} bytes!", n).green()); + }, + Err(e) => { + eprintln!("{}{:?}", format!("Failed to write with error: ").red().bold(), e); + } + } + + device_handle.release_interface(0).unwrap(); // Release the interface + device_handle.attach_kernel_driver(0).unwrap(); // Re-attach the kernel driver + } + } +} \ No newline at end of file