|
|
|
@ -1,3 +1,24 @@
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
|
|
|
# Drum Pedal Keyboard
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Imagine if you will a keyboard made in the image of drum pedals.
|
|
|
|
|
|
|
|
Consumers of your input certainly will.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
This program offers boilerplate to allow you to write in Rust simple keyboard macros
|
|
|
|
|
|
|
|
for GNU/Linux, utilizing Linux input subsystem userland API, wrapped with
|
|
|
|
|
|
|
|
libevdev and evdev-rs libraries. Focusing on simple 'key hold => repeat action'
|
|
|
|
|
|
|
|
pattern.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
This program works at the basic keypress level, before any translation into
|
|
|
|
|
|
|
|
symbols or meanings takes place.
|
|
|
|
|
|
|
|
As such this program requires access to /dev/input and /dev/uinput interfaces.
|
|
|
|
|
|
|
|
It can be granted by input and uinput user groups or superuser privileges.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Usage
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
./dpk [/PATH/TO/INPUT/DEVICE]
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
use std::io::Write; //for .flush()
|
|
|
|
use std::io::Write; //for .flush()
|
|
|
|
|
|
|
|
|
|
|
|
#[macro_use]
|
|
|
|
#[macro_use]
|
|
|
|
@ -5,7 +26,10 @@ extern crate lazy_static;
|
|
|
|
|
|
|
|
|
|
|
|
mod pedals;
|
|
|
|
mod pedals;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[doc(hidden)]
|
|
|
|
fn main() -> () {
|
|
|
|
fn main() -> () {
|
|
|
|
|
|
|
|
// Open the requested device. Wrap in into lazy evaluated static
|
|
|
|
|
|
|
|
// to share between threads
|
|
|
|
lazy_static! {
|
|
|
|
lazy_static! {
|
|
|
|
static ref INPUT_DEVICE: evdev::Device = {
|
|
|
|
static ref INPUT_DEVICE: evdev::Device = {
|
|
|
|
let d = pick_device();
|
|
|
|
let d = pick_device();
|
|
|
|
@ -23,6 +47,7 @@ fn main() -> () {
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Main program flow.
|
|
|
|
fn run(
|
|
|
|
fn run(
|
|
|
|
input_device: &'static evdev::Device,
|
|
|
|
input_device: &'static evdev::Device,
|
|
|
|
output_device: &mut evdev::uinput::VirtualDevice,
|
|
|
|
output_device: &mut evdev::uinput::VirtualDevice,
|
|
|
|
@ -30,6 +55,7 @@ fn run(
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
let mut handles: Vec<_> = Vec::new();
|
|
|
|
let mut handles: Vec<_> = Vec::new();
|
|
|
|
let (tx, rx) = std::sync::mpsc::channel();
|
|
|
|
let (tx, rx) = std::sync::mpsc::channel();
|
|
|
|
|
|
|
|
// Make a separate thread for each pedal macro
|
|
|
|
for pedal in pedals {
|
|
|
|
for pedal in pedals {
|
|
|
|
let tx = tx.clone();
|
|
|
|
let tx = tx.clone();
|
|
|
|
let handle = std::thread::spawn(move || loop {
|
|
|
|
let handle = std::thread::spawn(move || loop {
|
|
|
|
@ -50,6 +76,8 @@ fn run(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Open the device on the path provided in the first cli argument
|
|
|
|
|
|
|
|
/// or, if none present, allow user to choose one
|
|
|
|
fn pick_device() -> evdev::Device {
|
|
|
|
fn pick_device() -> evdev::Device {
|
|
|
|
let mut args = std::env::args_os();
|
|
|
|
let mut args = std::env::args_os();
|
|
|
|
args.next();
|
|
|
|
args.next();
|
|
|
|
@ -72,8 +100,11 @@ fn pick_device() -> evdev::Device {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
use evdev::{uinput::VirtualDeviceBuilder, AttributeSet, Key};
|
|
|
|
use evdev::{uinput::VirtualDeviceBuilder, AttributeSet, Key};
|
|
|
|
|
|
|
|
/// Create new uinput device to send generated events into
|
|
|
|
|
|
|
|
/// Enables explicitly allowed here key press events to be generated
|
|
|
|
fn create_device() -> Result<evdev::uinput::VirtualDevice, Box<dyn std::error::Error>> {
|
|
|
|
fn create_device() -> Result<evdev::uinput::VirtualDevice, Box<dyn std::error::Error>> {
|
|
|
|
let mut keys = AttributeSet::<Key>::new();
|
|
|
|
let mut keys = AttributeSet::<Key>::new();
|
|
|
|
|
|
|
|
// Addtional buttons go here in `keys.insert(evdev::Key)` format
|
|
|
|
keys.insert(Key::KEY_F);
|
|
|
|
keys.insert(Key::KEY_F);
|
|
|
|
keys.insert(Key::KEY_SPACE);
|
|
|
|
keys.insert(Key::KEY_SPACE);
|
|
|
|
keys.insert(Key::BTN_LEFT);
|
|
|
|
keys.insert(Key::BTN_LEFT);
|
|
|
|
@ -87,6 +118,7 @@ fn create_device() -> Result<evdev::uinput::VirtualDevice, Box<dyn std::error::E
|
|
|
|
Ok(device)
|
|
|
|
Ok(device)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Simple key-action pair container, tying a button to a repeatable macro
|
|
|
|
pub struct Pedal {
|
|
|
|
pub struct Pedal {
|
|
|
|
key: evdev::Key,
|
|
|
|
key: evdev::Key,
|
|
|
|
action: Box<dyn Fn(&std::sync::mpsc::Sender<evdev::InputEvent>) -> () + Send + 'static>,
|
|
|
|
action: Box<dyn Fn(&std::sync::mpsc::Sender<evdev::InputEvent>) -> () + Send + 'static>,
|
|
|
|
|