Improved user-friendliness. Closes #7

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.
This commit is contained in:
Dennis Potter 2018-11-14 00:20:51 +01:00
parent dc9def512c
commit 74e23a5b5f
4 changed files with 215 additions and 81 deletions

View File

@ -1,11 +1,11 @@
[package]
name = "footswitch-rs"
version = "0.1.0"
authors = ["Dennis <dennis@dennispotter.eu>"]
authors = ["Dennis Potter <dennis@dennispotter.eu>"]
[dependencies]
structopt = "0.2.10"
structopt = "0.2.13"
hidapi = "0.5.0"
users = "0.7"
users = "0.8"
colored = "1.6.1"

View File

@ -20,32 +20,43 @@ use colored::*;
#[derive(StructOpt, Debug)]
#[structopt(name = "rust-footswitch")]
struct Opt {
/// Prints a table of all keys with <listkeys> rows
#[structopt(short = "l", long = "listkeys")]
listkeys: Option<usize>,
#[structopt(subcommand)]
cmd: Option<Command>,
}
#[derive(StructOpt, Debug)]
enum Command {
/// Write to the footpedal
#[structopt(name = "write")]
Write {
/// Specify pedal to modify with following command. Possible values: [0 | 1 | 2]
#[structopt(short = "p", long = "pedal")]
pedal: Vec<u8>,
/// Command to apply. Possible values: [set_key | set_mousebutton | del_key | app_key | app_str | app_mod]
#[structopt(short = "c", long = "command")]
command: Vec<String>,
/// Input values to apply
#[structopt(short = "i", long = "input")]
input: Vec<String>,
/// Prints a table of all possible keys
#[structopt(name = "list")]
ListKeys {
/// Specify the number of columns of the table
#[structopt(short = "c", long = "columns")]
columns: usize,
},
/// Set a key or a mousebutton to one or more pedals
#[structopt(name = "set")]
Set {
#[structopt(subcommand)]
cmd: Set
},
/// Append a key, a modifier, or a string to one or more pedals
#[structopt(name = "append")]
Append {
#[structopt(subcommand)]
cmd: Append
},
/// Clear the value of one or more pedals
#[structopt(name = "clear")]
Clear {
/// Specify pedal(s) to clear: [0 | 1 | 2]
#[structopt(short = "p", long = "pedal")]
pedal: Vec<u8>,
},
/// Read from the footpedal
#[structopt(name = "read")]
Read {
/// Read all pedals
@ -58,76 +69,180 @@ enum Command {
}
}
#[derive(StructOpt, Debug)]
enum Set {
/// Set a key value to one or more pedals
#[structopt(name = "key")]
SetKey {
/// Specify pedal(s) to modify: [0 | 1 | 2]
#[structopt(short = "p", long = "pedal")]
pedal: Vec<u8>,
/// Value(s) to apply
#[structopt(short = "i", long = "input")]
input: Vec<String>,
},
/// Set a mousebutton to one or more pedals
#[structopt(name = "mousebutton")]
SetMousebutton {
/// Specify pedal(s) to modify: [0 | 1 | 2]
#[structopt(short = "p", long = "pedal")]
pedal: Vec<u8>,
/// Value(s) to apply
#[structopt(short = "i", long = "input")]
input: Vec<String>,
},
/// Set X, Y, and W movement of the mouse pointer for one or more pedals
#[structopt(name = "mousemovement")]
SetMousemovement {
/// Specify pedal(s) to modify: [0 | 1 | 2]
#[structopt(short = "p", long = "pedal")]
pedal: Vec<u8>,
/// X value(s): [-128,127]
#[structopt(short = "x")]
x: Vec<i8>,
/// Y value(s): [-128,127]
#[structopt(short = "y")]
y: Vec<i8>,
/// W value(s): [-128,127]
#[structopt(short = "w")]
w: Vec<i8>,
}
}
#[derive(StructOpt, Debug)]
enum Append {
/// Append a key value to one or more pedals
#[structopt(name = "key")]
AppendKey {
/// Specify pedal(s) to modify: [0 | 1 | 2]
#[structopt(short = "p", long = "pedal")]
pedal: Vec<u8>,
/// Value(s) to apply
#[structopt(short = "i", long = "input")]
input: Vec<String>,
},
/// Append a string to one or more pedals
#[structopt(name = "string")]
AppendString {
/// Specify pedal(s) to modify: [0 | 1 | 2]
#[structopt(short = "p", long = "pedal")]
pedal: Vec<u8>,
/// Value(s) to apply
#[structopt(short = "i", long = "input")]
input: Vec<String>,
},
/// Append a modifier to one or more pedals
#[structopt(name = "modifier")]
AppendModifier {
/// Specify pedal(s) to modify: [0 | 1 | 2]
#[structopt(short = "p", long = "pedal")]
pedal: Vec<u8>,
/// Value(s) to apply
#[structopt(short = "i", long = "input")]
input: Vec<String>,
}
}
fn main() {
let opt = Opt::from_args();
welcome("footswitch-rs, Dennis Potter <dennis@dennispotter.eu>");
welcome();
check_sudo();
// All options that don't need the device to be open
// Print all keys and exit application
if let Some(x) = opt.listkeys {
key_operations::print_key_map(x);
goodbye();
match opt.cmd {
Some(Command::ListKeys { columns }) => {
key_operations::print_key_map(columns);
goodbye();
},
_ => { /* Do nothing, there are still lots of other options further below */ }
}
let mut pedals = pedal_operations::Pedals::new();
// All options that need the device to be open
match opt.cmd {
Some(Command::Write {pedal: ped_list, command: cmd_list, input: val_list}) => {
if ped_list.len() != cmd_list.len() && ped_list.len() != val_list.len() {
error!("You must define as much pedals as you define commands and as you define input values!");
}
Some(Command::Append { cmd }) => {
match cmd {
Append::AppendKey { pedal, input } =>
{
for (i, cmd) in cmd_list.iter().enumerate() {
match cmd as &str {
"set_key" => {
pedals.set_key(ped_list[i] as usize, val_list[i].as_str());
},
Append::AppendString { pedal, input } =>
{
check_length(&pedal, &input);
for (i, pedal) in pedal.iter().enumerate() {
pedals.set_string(*pedal as usize, input[i].as_str());
}
"set_mousebutton" => {
pedals.set_mousebutton(ped_list[i] as usize, val_list[i].as_str());
}
"del_key" => {
}
"app_key" => {
}
"app_str" => {
pedals.set_string(ped_list[i] as usize, val_list[i].as_str());
}
"app_mod" => {
pedals.set_modifier(ped_list[i] as usize, val_list[i].as_str());
}
"set_x" => {
pedals.set_mouse_xyw(ped_list[i] as usize, val_list[i].as_str(), 5)
}
"set_y" => {
pedals.set_mouse_xyw(ped_list[i] as usize, val_list[i].as_str(), 6)
}
"set_w" => {
pedals.set_mouse_xyw(ped_list[i] as usize, val_list[i].as_str(), 7)
}
_ => {
error!("Unknown command!");
},
Append::AppendModifier { pedal, input } =>
{
check_length(&pedal, &input);
for (i, pedal) in pedal.iter().enumerate() {
pedals.set_modifier(*pedal as usize, input[i].as_str());
}
}
}
// Since we ran the Write command without any errors, we are now writing everything
pedals.write_pedals();
pedals.update_and_close();
},
info!("Successfully wrote everything to footpedal!");
info!("The current state of the device is shown below.");
Some(Command::Set { cmd }) => {
match cmd {
Set::SetKey { pedal, input } =>
{
check_length(&pedal, &input);
// Show user current state of pedal
pedals.read_pedals(vec![0,1,2]);
for (i, pedal) in pedal.iter().enumerate() {
pedals.set_key(*pedal as usize, input[i].as_str());
}
},
Set::SetMousebutton { pedal, input } =>
{
check_length(&pedal, &input);
goodbye();
for (i, pedal) in pedal.iter().enumerate() {
pedals.set_mousebutton(*pedal as usize, input[i].as_str());
}
}
Set::SetMousemovement { pedal, x, y, w } =>
{
if pedal.len() != x.len() || x.len() != y.len() || y.len() != w.len() {
error!("You must define X, Y, and W for every pedal. If a direction is not needed, set it to 0!");
}
for (i, pedal) in pedal.iter().enumerate() {
pedals.set_mouse_xyw(*pedal as usize, x[i], 5);
pedals.set_mouse_xyw(*pedal as usize, y[i], 6);
pedals.set_mouse_xyw(*pedal as usize, w[i], 7);
}
}
}
pedals.update_and_close();
},
Some(Command::Clear { pedal }) => {
},
Some(Command::Read {all: all_var, pedals: ped_list}) => {
if ped_list.len() > 3 {
error!("Number of pedals may not be bigger than 3!");
@ -145,6 +260,8 @@ fn main() {
goodbye();
},
Some(Command::ListKeys { columns:_columns }) => { /* This case will never occur */ },
None => {
error!("You did not specify any command. Run './footswitch-rs --help' for more information.");
}
@ -157,3 +274,9 @@ fn check_sudo() {
error!("Please execute this application as super user!");
}
}
fn check_length(pedal: & Vec<u8>, input: & Vec<String>) {
if pedal.len() != input.len() {
error!("You must define as much pedals as you define input values!");
}
}

View File

@ -26,10 +26,15 @@ macro_rules! error {
};
}
pub fn welcome(string: &str) {
println!("{}", "".repeat(string.len() + 20));
println!("{text:^-width$}", text = string, width = string.len() + 20);
println!("{}", "".repeat(string.len() + 20));
pub fn welcome() {
const AUTHORS: &'static str = env!("CARGO_PKG_AUTHORS");
const NAME: &'static str = env!("CARGO_PKG_NAME");
let name_authors = &[NAME, " | ", AUTHORS].concat();
println!("{}", "".repeat(name_authors.len() + 20));
println!("{text:^-width$}", text = name_authors, width = name_authors.len() + 20);
println!("{}", "".repeat(name_authors.len() + 20));
}
pub fn goodbye() {

View File

@ -4,6 +4,7 @@ extern crate hidapi;
use std::process;
use std::ffi::CString;
use colored::*;
use messages::*;
#[derive(Copy, Clone)]
enum Type {
@ -285,20 +286,12 @@ impl Pedals {
self.ped_data[ped].data[4] |= mousebutton as u8;
}
pub fn set_mouse_xyw(& mut self, ped:usize, value:&str, direction:usize) {
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 value passed by user to integer
let value_i8 = match value.parse::<i8>() {
Ok(x) => x,
Err(x) => {
error!("The value of {} ({}) must be in [-128, 127]! Message: {}.", direction, value, x)
}
};
// Translate to u8
let mut value_u8 = value_i8 as u8;
@ -372,4 +365,17 @@ impl Pedals {
}
/// 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();
}
}