Skip to content

Commit

Permalink
Add button event handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
pixix4 committed Feb 23, 2024
1 parent efdc28c commit 64bb536
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 32 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ev3dev-lang-rust"
version = "0.13.0"
version = "0.14.0"
edition = "2021"
authors = ["Lars Westermann <[email protected]>"]

Expand All @@ -26,6 +26,7 @@ ev3dev-lang-rust-derive = { path = "ev3dev_lang_rust_derive", version="0.10" }
libc = "0.2"
framebuffer = { version = "0.3", optional = true }
image = { version = "0.24", optional = true }
paste = "1.0"

[workspace]
members = [
Expand Down
115 changes: 84 additions & 31 deletions src/ev3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use std::os::unix::io::AsRawFd;
use std::path::Path;
use std::rc::Rc;

use paste::paste;

use crate::driver::DRIVER_PATH;
use crate::utils::OrErr;
use crate::{Attribute, Ev3Result};
Expand Down Expand Up @@ -201,27 +203,41 @@ struct ButtonMapEntry {
pub key_code: u32,
}

type ButtonHandler = Box<dyn Fn(bool)>;

/// This implementation depends on the availability of the EVIOCGKEY ioctl
/// to be able to read the button state buffer. See Linux kernel source
/// in /include/uapi/linux/input.h for details.
#[derive(Debug)]
struct ButtonFileHandler {
file_map: HashMap<String, FileMapEntry>,
button_map: HashMap<String, ButtonMapEntry>,
button_handlers: HashMap<String, ButtonHandler>,
pressed_buttons: HashSet<String>,
}

impl std::fmt::Debug for ButtonFileHandler {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ButtonFileHandler")
.field("file_map", &self.file_map)
.field("button_map", &self.button_map)
.field("button_handlers", &self.button_map.keys())
.field("pressed_buttons", &self.pressed_buttons)
.finish()
}
}

impl ButtonFileHandler {
/// Create a new instance.
fn new() -> Self {
ButtonFileHandler {
file_map: HashMap::new(),
button_map: HashMap::new(),
button_handlers: HashMap::new(),
pressed_buttons: HashSet::new(),
}
}

/// Add a button the the file handler.
/// Add a button to the file handler.
fn add_button(&mut self, name: &str, file_name: &str, key_code: u32) -> Ev3Result<()> {
if !self.file_map.contains_key(file_name) {
let file = File::open(file_name)?;
Expand All @@ -242,6 +258,16 @@ impl ButtonFileHandler {
Ok(())
}

/// Sets an event listener for the given button.
fn set_button_listener(&mut self, name: &str, listener: Option<ButtonHandler>) {
if let Some(listener) = listener {
self.button_handlers.insert(name.to_owned(), listener);
} else {
self.button_handlers.remove(name);
}
}

/// Gets a copy of the currently pressed buttons.
fn get_pressed_buttons(&self) -> HashSet<String> {
self.pressed_buttons.clone()
}
Expand All @@ -264,6 +290,7 @@ impl ButtonFileHandler {
}
}

let old_pressed_buttons = self.pressed_buttons.clone();
self.pressed_buttons.clear();

for (
Expand All @@ -280,6 +307,13 @@ impl ButtonFileHandler {
self.pressed_buttons.insert(btn_name.to_owned());
}
}

let difference = old_pressed_buttons.symmetric_difference(&self.pressed_buttons);
for button in difference {
if self.button_handlers.contains_key(button) {
self.button_handlers[button](self.get_button_state(button));
}
}
}
}

Expand All @@ -297,6 +331,10 @@ impl ButtonFileHandler {
/// # fn main() -> ev3dev_lang_rust::Ev3Result<()> {
/// let button = Button::new()?;
///
/// button.set_down_handler(|is_pressed| {
/// println("Is 'down' pressed: {is_pressed}");
/// });
///
/// loop {
/// button.process();
///
Expand Down Expand Up @@ -335,42 +373,57 @@ impl Button {

/// Check for currently pressed buttons. If the new state differs from the
/// old state, call the appropriate button event handlers.
/// ```no_run
/// use ev3dev_lang_rust::Button;
/// use std::thread;
/// use std::time::Duration;
///
/// # fn main() -> ev3dev_lang_rust::Ev3Result<()> {
/// let button = Button::new()?;
///
/// button.set_down_handler(|is_pressed| {
/// println("Is 'down' pressed: {is_pressed}");
/// });
///
/// loop {
/// button.process();
///
/// println!("Is 'up' pressed: {}", button.is_up());
/// println!("Pressed buttons: {:?}", button.get_pressed_buttons());
///
/// thread::sleep(Duration::from_millis(100));
/// }
/// # }
/// ```
pub fn process(&self) {
self.button_handler.borrow_mut().process()
}

/// Get all pressed buttons by name.
///
/// ```no_run
/// use ev3dev_lang_rust::Button;
/// use std::thread;
/// use std::time::Duration;
///
/// # fn main() -> ev3dev_lang_rust::Ev3Result<()> {
/// let button = Button::new()?;
///
/// loop {
/// button.process();
/// println!("Pressed buttons: {:?}", button.get_pressed_buttons());
/// thread::sleep(Duration::from_millis(100));
/// }
/// # }
/// ```
pub fn get_pressed_buttons(&self) -> HashSet<String> {
self.button_handler.borrow().get_pressed_buttons()
}

/// Check if 'up' button is pressed.
pub fn is_up(&self) -> bool {
self.button_handler.borrow().get_button_state("up")
}

/// Check if 'down' button is pressed.
pub fn is_down(&self) -> bool {
self.button_handler.borrow().get_button_state("down")
}

/// Check if 'left' button is pressed.
pub fn is_left(&self) -> bool {
self.button_handler.borrow().get_button_state("left")
}

/// Check if 'right' button is pressed.
pub fn is_right(&self) -> bool {
self.button_handler.borrow().get_button_state("right")
}

/// Check if 'enter' button is pressed.
pub fn is_enter(&self) -> bool {
self.button_handler.borrow().get_button_state("enter")
}

/// Check if 'backspace' button is pressed.
pub fn is_backspace(&self) -> bool {
self.button_handler.borrow().get_button_state("backspace")
}
ev3_button_functions!(up);
ev3_button_functions!(down);
ev3_button_functions!(left);
ev3_button_functions!(right);
ev3_button_functions!(enter);
ev3_button_functions!(backspace);
}
59 changes: 59 additions & 0 deletions src/ev3_button_functions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/// Helper macro to create all necessary functions for a button
#[macro_export]
macro_rules! ev3_button_functions {
($button_name:ident) => {
paste! {
#[doc = "Check if the `" $button_name "` button is pressed."]
#[doc = "```no_run"]
#[doc = "use ev3dev_lang_rust::Button;"]
#[doc = "use std::thread;"]
#[doc = "use std::time::Duration;"]
#[doc = ""]
#[doc = "# fn main() -> ev3dev_lang_rust::Ev3Result<()> {"]
#[doc = "let button = Button::new()?;"]
#[doc = ""]
#[doc = "loop {"]
#[doc = " button.process();"]
#[doc = " println(\"Is '" $button_name "' pressed: {}\", button.is_" $button_name "());"]
#[doc = " thread::sleep(Duration::from_millis(100));"]
#[doc = "}"]
#[doc = "# }"]
#[doc = "```"]
pub fn [<is_ $button_name>] (&self) -> bool {
self.button_handler.borrow().get_button_state("$button_name")
}

#[doc = "Set an event handler for changes in the pressed state of the `" $button_name "` button."]
#[doc = "```no_run"]
#[doc = "use ev3dev_lang_rust::Button;"]
#[doc = "use std::thread;"]
#[doc = "use std::time::Duration;"]
#[doc = ""]
#[doc = "# fn main() -> ev3dev_lang_rust::Ev3Result<()> {"]
#[doc = "let button = Button::new()?;"]
#[doc = ""]
#[doc = "button.set_" $button_name "_handler(|is_pressed| {"]
#[doc = " println(\"Is '" $button_name "' pressed: {is_pressed}\");"]
#[doc = "});"]
#[doc = ""]
#[doc = "loop {"]
#[doc = " button.process();"]
#[doc = " thread::sleep(Duration::from_millis(100));"]
#[doc = "}"]
#[doc = "# }"]
#[doc = "```"]
pub fn [<set_ $button_name _handler>](&mut self, handler: impl Fn(bool) + 'static) {
self.button_handler
.borrow_mut()
.set_button_listener("$button_name", Some(Box::new(handler)));
}

#[doc = "Removes the event handler of the `" $button_name "` button."]
pub fn [<remove_ $button_name _handler>](&mut self) {
self.button_handler
.borrow_mut()
.set_button_listener("$button_name", None);
}
}
};
}
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,14 @@ extern crate image;
#[macro_use]
extern crate ev3dev_lang_rust_derive;
extern crate libc;
extern crate paste;

#[macro_use]
mod findable;

#[macro_use]
mod ev3_button_functions;

mod attribute;
pub use attribute::Attribute;
mod driver;
Expand Down

0 comments on commit 64bb536

Please sign in to comment.