Every function is now a separate subcommand: * append (append a key, a modifier, or a string to one or more pedals) * clear (clear one or more pedals) * help (print a help message) * list (print a table with all possible keys) * read (read values from the footpedal) * set (set a key or mousebutton to one or more pedals) A disadvantage is that different subcommands cannot be combined in one call of the application anymore. However, this new structure brings the major benefit that the program is more intuitive for users. A second, small change is the fact that the welcome() function now reads application and authore name from the Cargo file.
382 lines
12 KiB
Rust
382 lines
12 KiB
Rust
#[path = "key_operations.rs"] pub mod key_operations;
|
|
|
|
extern crate hidapi;
|
|
use std::process;
|
|
use std::ffi::CString;
|
|
use colored::*;
|
|
use messages::*;
|
|
|
|
#[derive(Copy, Clone)]
|
|
enum Type {
|
|
Unconfigured = 0,
|
|
Key = 1,
|
|
Mouse = 2,
|
|
MouseKey = 3,
|
|
String = 4
|
|
}
|
|
|
|
impl Type {
|
|
fn u8_to_enum(value:u8) -> Option<Type> {
|
|
match value {
|
|
0 => Some(Type::Unconfigured),
|
|
1 => Some(Type::Key),
|
|
2 => Some(Type::Mouse),
|
|
3 => Some(Type::MouseKey),
|
|
4 => Some(Type::String),
|
|
0x81 => Some(Type::Key),
|
|
_ => None
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct PedalsData {
|
|
header: [u8; 8],
|
|
data: [u8; 48],
|
|
length: u8,
|
|
}
|
|
|
|
pub struct Pedals {
|
|
dev:hidapi::HidDevice,
|
|
|
|
start: [u8; 8],
|
|
ped_data: Vec<PedalsData>,
|
|
}
|
|
|
|
impl Pedals {
|
|
pub fn new() -> Pedals {
|
|
// Open device
|
|
let vld_dev = [
|
|
(0x0c45u16, 0x7403u16),
|
|
(0x0c45 , 0x7404),
|
|
(0x413d , 0x2107)
|
|
];
|
|
|
|
info!("Initializing HID object. This can take a moment.");
|
|
|
|
let api = match hidapi::HidApi::new() {
|
|
Ok(res) => {
|
|
info!("Successfully initialized HID object.");
|
|
res
|
|
},
|
|
Err(_) => {
|
|
error!("Could not initialize HID object.")
|
|
},
|
|
};
|
|
|
|
let mut dev_path = CString::new("").unwrap();
|
|
|
|
for device in api.devices() {
|
|
for val in vld_dev.iter() {
|
|
if *val == (device.vendor_id, device.product_id) && device.interface_number == 1 {
|
|
info!("Found device {:x}:{:x} ({:#?})", device.vendor_id, device.product_id, device.path);
|
|
dev_path = device.path.clone();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Moved this out of loop, because of error of "possibly uninitialized `dev`. Don't try to move it in the loop.
|
|
let dev = match api.open_path(&dev_path) {
|
|
Ok(res) => {
|
|
info!("Successfully opened device.");
|
|
res
|
|
},
|
|
Err(_) => {
|
|
error!("Could not open device. Make sure your device is connected. Maybe try to reconnect it.")
|
|
},
|
|
};
|
|
|
|
// Prepare variables
|
|
let start = [0x01u8, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00];
|
|
|
|
let header_0 = [0x01u8, 0x81, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00];
|
|
let header_1 = [0x01u8, 0x81, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00];
|
|
let header_2 = [0x01u8, 0x81, 0x08, 0x03, 0x00, 0x00, 0x00, 0x00];
|
|
|
|
let mut default_data = [0u8; 48];
|
|
default_data[0] = 0x08;
|
|
|
|
// Initialize actual object
|
|
Pedals {
|
|
dev: dev,
|
|
start: start,
|
|
|
|
ped_data: vec![
|
|
PedalsData {
|
|
header: header_0,
|
|
data: default_data,
|
|
length: 8,
|
|
},
|
|
PedalsData {
|
|
header: header_1,
|
|
data: default_data,
|
|
length: 8,
|
|
},
|
|
PedalsData {
|
|
header: header_2,
|
|
data: default_data,
|
|
length: 8, },
|
|
]
|
|
}
|
|
}
|
|
|
|
pub fn read_pedal(&self, ped:& u8) -> [u8; 8] {
|
|
let mut buf = [0u8; 8];
|
|
let mut query = [0x01u8, 0x82, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00];
|
|
|
|
query[3] += ped;
|
|
|
|
// Write query to device
|
|
self.dev.write(&query).unwrap();
|
|
|
|
// Read answer
|
|
self.dev.read(&mut buf[..]).unwrap();
|
|
|
|
buf
|
|
}
|
|
|
|
/// Read the current values of the pedals
|
|
pub fn read_pedals(&self, peds: Vec<u8>) {
|
|
let total_width = 55 as usize;
|
|
|
|
// Check if passed pedal number is valid
|
|
for i in peds.iter() {
|
|
if *i > 2 {
|
|
error!("Pedal value {} is larger than 2 and thus not valid!", i);
|
|
}
|
|
}
|
|
|
|
// Print header
|
|
println!("├{}┐", "─".repeat(total_width));
|
|
println!("│{name:^width$}│", name = "Programmed Keys", width = total_width);
|
|
println!("╞{}╡", "═".repeat(total_width));
|
|
|
|
// Read and print keys
|
|
for (i, ped) in peds.iter().enumerate() {
|
|
// Read value from pedal and directly translate it to a key
|
|
let mut key_value = self.read_pedal(ped);
|
|
|
|
let key_name_option = match Type::u8_to_enum(key_value[1]) {
|
|
Some(Type::Unconfigured) => None,
|
|
Some(Type::Key) => key_operations::print_key(&key_value),
|
|
Some(Type::Mouse) => key_operations::print_mousebutton(&key_value),
|
|
Some(Type::MouseKey) => key_operations::print_mouse_key(&key_value),
|
|
Some(Type::String) => self.print_string(& mut key_value),
|
|
None => error!("The key type which was returned by the pedal was invalid!")
|
|
};
|
|
|
|
let key_name = match key_name_option {
|
|
Some(key) => key,
|
|
None => "< None >".to_string(),
|
|
};
|
|
|
|
println!("│ Pedal {ped} │ {name:<-width$}│", ped = ped, name = key_name, width = total_width - 14);
|
|
|
|
// Print spacer between lines
|
|
if i != peds.len() - 1 {
|
|
println!("│ {}┼{name:<-width$}│", "─".repeat(10), name = "─".repeat(total_width - 14), width = total_width - 12);
|
|
}
|
|
}
|
|
|
|
// Print simple footer
|
|
println!("├{}┘", "─".repeat(total_width));
|
|
}
|
|
|
|
/// Sets the type of the function. False (0) if everything went fine, True (1) if
|
|
/// an error occurred.
|
|
fn set_type(& mut self, ped:usize, typ:Type) {
|
|
let set_value = if self.ped_data[ped].data[1] == 0 { true } else { false };
|
|
|
|
if set_value {
|
|
self.ped_data[ped].data[1] = typ as u8;
|
|
}
|
|
|
|
let ret = match typ {
|
|
Type::String => {
|
|
// If nothing is set, set type to string and length to 2
|
|
if set_value {
|
|
self.ped_data[ped].length = 2;
|
|
}
|
|
|
|
// Check if pedal type is set to String, otherwise error
|
|
self.ped_data[ped].data[1] != Type::String as u8
|
|
}
|
|
_ => {
|
|
let ret;
|
|
|
|
if self.ped_data[ped].data[1] == Type::String as u8 {
|
|
// if type is Key or Mouse, and String is already set, return false
|
|
ret = true;
|
|
}
|
|
else {
|
|
// else, set type to new type and return true
|
|
self.ped_data[ped].data[1] |= typ as u8;
|
|
ret = false;
|
|
}
|
|
|
|
ret
|
|
}
|
|
};
|
|
|
|
if ret {
|
|
error!("Invalid combination of options!");
|
|
}
|
|
}
|
|
|
|
fn write_pedal(&self, ped:usize) {
|
|
// First, write header
|
|
self.dev.write(&self.ped_data[ped].header).unwrap();
|
|
|
|
// Write data to device in 8 byte chunks
|
|
let mut up:usize = 0;
|
|
|
|
for i in 0..(self.ped_data[ped].length / 8) {
|
|
// Set bounds
|
|
let low = (i * 8) as usize;
|
|
up = 8 * (i + 1) as usize;
|
|
|
|
// Write to device
|
|
self.dev.write(&self.ped_data[ped].data[low..up]).unwrap();
|
|
}
|
|
|
|
// Write remaining values to device
|
|
if self.ped_data[ped].length % 8 > 0 {
|
|
self.dev.write(&self.ped_data[ped].data[up..(self.ped_data[ped].length as usize)]).unwrap();
|
|
}
|
|
}
|
|
|
|
/// This method writes all data from Pedals.peddata to the device
|
|
pub fn write_pedals(&self) {
|
|
self.dev.write(&self.start).unwrap();
|
|
|
|
for (i, _pedal) in self.ped_data.iter().enumerate() {
|
|
self.write_pedal(i)
|
|
}
|
|
}
|
|
|
|
pub fn set_key(& mut self, ped:usize, key:&str) {
|
|
if let Some(encoded_key) = key_operations::encode_byte(key) {
|
|
self.set_type(ped, Type::Key);
|
|
|
|
self.ped_data[ped].data[3] = encoded_key;
|
|
}
|
|
else {
|
|
error!("Key '{}' is not recognized! Please provide a valid key, listed in './footswitch-rs --listkeys 4'", key);
|
|
}
|
|
}
|
|
|
|
pub fn set_modifier(& mut self, ped:usize, modifier:&str) {
|
|
let modifier = match key_operations::Modifier::str_to_enum(modifier) {
|
|
Some(x) => x,
|
|
None => error!("Unknown modifier! Please use one of the following: ctrl, shift, alt, win."),
|
|
};
|
|
|
|
self.set_type(ped, Type::Key);
|
|
|
|
self.ped_data[ped].data[2] |= modifier as u8;
|
|
}
|
|
|
|
pub fn set_mousebutton(& mut self, ped:usize, mousebutton:&str) {
|
|
let mousebutton = match key_operations::MouseButton::str_to_enum(mousebutton) {
|
|
Some(x) => x,
|
|
None => error!("Unknown mousebutton! Please use one of the following: mouse_left, mouse_middle, mouse_right, mouse_double."),
|
|
};
|
|
|
|
self.set_type(ped, Type::Mouse);
|
|
|
|
self.ped_data[ped].data[4] |= mousebutton as u8;
|
|
}
|
|
|
|
pub fn set_mouse_xyw(& mut self, ped:usize, value_i8:i8, direction:usize) {
|
|
// The values of the directions match the array index of ped_data[].data[]
|
|
// X = 5
|
|
// Y = 6
|
|
// W = 7
|
|
|
|
// Translate to u8
|
|
let mut value_u8 = value_i8 as u8;
|
|
|
|
// Check if mouse wheel movement is smaller than 0, if so, add 256
|
|
if (direction == 7) & (value_i8 < 0) {
|
|
value_u8 += value_i8 as u8 + 256;
|
|
}
|
|
|
|
// Set Mouse Type
|
|
self.set_type(ped, Type::Mouse);
|
|
|
|
// Actually write data
|
|
self.ped_data[ped].data[direction] = value_u8;
|
|
}
|
|
|
|
pub fn print_string(&self, response: & mut [u8]) -> Option<String> {
|
|
let mut string = String::new();
|
|
let mut len = response[0] - 2;
|
|
let mut ind = 2;
|
|
|
|
while len > 0 {
|
|
|
|
if ind == 8 {
|
|
self.dev.read(&mut response[..]).unwrap();
|
|
|
|
ind = 0;
|
|
}
|
|
|
|
if let Some(key_str) = key_operations::decode_byte(&response[ind]) {
|
|
string.push_str(&key_str[..]);
|
|
}
|
|
|
|
len -= 1;
|
|
ind += 1;
|
|
}
|
|
|
|
Some(string)
|
|
}
|
|
|
|
|
|
pub fn set_string(& mut self, ped:usize, key:&str) {
|
|
self.set_type(ped, Type::String);
|
|
|
|
if key.len() > 38 {
|
|
error!("The size of each string must be smaller than or equal to 38.");
|
|
}
|
|
|
|
let encoded_vector = match key_operations::encode_string(&key) {
|
|
Some(x) => x,
|
|
None => error!("Could not encode string!"),
|
|
};
|
|
|
|
self.compile_string_data(ped, encoded_vector);
|
|
}
|
|
|
|
fn compile_string_data(& mut self, ped:usize, enc_vec:Vec<u8>) {
|
|
let len = enc_vec.len() as u8;
|
|
|
|
if self.ped_data[ped].length + len > 38 {
|
|
error!("The size of the accumulated string must be smaller than or equal to 38.")
|
|
}
|
|
|
|
let start_byte = self.ped_data[ped].length as usize;
|
|
for (i, c) in enc_vec.iter().enumerate() {
|
|
self.ped_data[ped].data[start_byte + i] = *c;
|
|
}
|
|
|
|
self.ped_data[ped].length += len;
|
|
self.ped_data[ped].header[2] = self.ped_data[ped].length;
|
|
self.ped_data[ped].data[0] = self.ped_data[ped].length;
|
|
|
|
|
|
}
|
|
|
|
/// Update device and close application
|
|
pub fn update_and_close(& mut self) {
|
|
self.write_pedals();
|
|
|
|
info!("Successfully wrote everything to footpedal!");
|
|
info!("The current state of the device is shown below.");
|
|
|
|
// Show user current state of pedal
|
|
self.read_pedals(vec![0,1,2]);
|
|
|
|
goodbye();
|
|
}
|
|
}
|