From 023283b807a47fb222376e2ed94105dfedab0096 Mon Sep 17 00:00:00 2001 From: "Sebastian H. Gabrielli" Date: Thu, 16 Jun 2022 00:10:16 +0200 Subject: [PATCH] Initial commit --- .gitignore | 1 + Cargo.lock | 125 ++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 11 ++++ numpad_config.json | 25 +++++++++ src/main.rs | 131 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 293 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 numpad_config.json create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..cd3d3c5 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,125 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "bit-set" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e1e6fb1c9e3d6fcdec57216a74eaa03e41f52a22f13a16438251d8e88b89da" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "colored" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +dependencies = [ + "atty", + "lazy_static", + "winapi", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "json" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" + +[[package]] +name = "keyboard_control_software" +version = "0.1.0" +dependencies = [ + "colored", + "json", + "libusb", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "libusb" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f990ddd929cbe53de4ecd6cf26e1f4e0c5b9796e4c629d9046570b03738aa53" +dependencies = [ + "bit-set", + "libc", + "libusb-sys", +] + +[[package]] +name = "libusb-sys" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c53b6582563d64ad3e692f54ef95239c3ea8069e82c9eb70ca948869a7ad767" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "pkg-config" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..0535058 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "keyboard_control_software" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +libusb = "0.3.0" +colored = "2.0.0" +json = "0.12.4" \ No newline at end of file diff --git a/numpad_config.json b/numpad_config.json new file mode 100644 index 0000000..1b7cdc5 --- /dev/null +++ b/numpad_config.json @@ -0,0 +1,25 @@ +{ + "name": "Sebaweb numpad", + "vendor_id": "CAFE", + "product_id": "53BA", + + "layout": { + "rows": 5, + "cols": 4, + "keys": [ + ["NumLk", "/", "*", "-"], + ["7", "8", "9", "+"], + ["4", "5", "6", "&+"], + ["1", "2", "3", "Enter"], + ["0", "&0", ",", "&Enter"] + ] + }, + + "lighting": { + "type": "RGB", + "layout": { + "rows": 1, + "cols": 4 + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..1626a92 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,131 @@ +extern crate libusb; +extern crate colored; +extern crate json; + +use colored::Colorize; +use std::path::Path; + +#[derive(Debug)] +struct KeyboardLighting { + lighting_type: String, + rows: u16, + cols: u16 +} + +#[derive(Debug)] +struct KeyboardLayout { + rows: u16, + cols: u16, + keys: Vec +} + +#[derive(Debug)] +struct KeyboardConfig { + name: String, + vendor_id: u16, + product_id: u16, + layout: KeyboardLayout, + lighting: KeyboardLighting +} + +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() { + let config: KeyboardConfig = parse_config(&Path::new("numpad_config.json")); + + println!("{:#?}", config); + + let context = libusb::Context::new().unwrap(); + + for device in context.devices().unwrap().iter() { + let device_desc = device.device_descriptor().unwrap(); + + println!("Bus {:03} Device {:03} ID {:04x}:{:04x}", + device.bus_number(), + device.address(), + device_desc.vendor_id(), + device_desc.product_id()); + + + if device_desc.vendor_id() == config.vendor_id && device_desc.product_id() == config.product_id { + println!("Woah, this is my device! I'm going to open it!"); + let config_desc = device.active_config_descriptor().unwrap(); + + let mut device_handle: libusb::DeviceHandle = device.open().unwrap(); + println!("Number of interfaces: {}", config_desc.num_interfaces()); + //println!("The device supports these languages: {:?}", &device_handle.read_languages(std::time::Duration::from_millis(100))); + + if device_handle.kernel_driver_active(0).unwrap() { + println!("Interface has kernel driver, detaching..."); + device_handle.detach_kernel_driver(0).unwrap(); + } + device_handle.claim_interface(0).unwrap(); + + match device_handle.write_control(libusb::request_type(libusb::Direction::Out, libusb::RequestType::Class, libusb::Recipient::Interface), 0x09, 0x0300, 0, &[0x00], std::time::Duration::from_secs(1)) { + 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(); + device_handle.attach_kernel_driver(0).unwrap(); + + println!("Goodbye!"); + std::process::exit(0); + } + } + +}