Skip to content

Commit 64bb536

Browse files
committed
Add button event handlers
1 parent efdc28c commit 64bb536

File tree

4 files changed

+149
-32
lines changed

4 files changed

+149
-32
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "ev3dev-lang-rust"
3-
version = "0.13.0"
3+
version = "0.14.0"
44
edition = "2021"
55
authors = ["Lars Westermann <[email protected]>"]
66

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

3031
[workspace]
3132
members = [

src/ev3.rs

Lines changed: 84 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ use std::os::unix::io::AsRawFd;
1111
use std::path::Path;
1212
use std::rc::Rc;
1313

14+
use paste::paste;
15+
1416
use crate::driver::DRIVER_PATH;
1517
use crate::utils::OrErr;
1618
use crate::{Attribute, Ev3Result};
@@ -201,27 +203,41 @@ struct ButtonMapEntry {
201203
pub key_code: u32,
202204
}
203205

206+
type ButtonHandler = Box<dyn Fn(bool)>;
207+
204208
/// This implementation depends on the availability of the EVIOCGKEY ioctl
205209
/// to be able to read the button state buffer. See Linux kernel source
206210
/// in /include/uapi/linux/input.h for details.
207-
#[derive(Debug)]
208211
struct ButtonFileHandler {
209212
file_map: HashMap<String, FileMapEntry>,
210213
button_map: HashMap<String, ButtonMapEntry>,
214+
button_handlers: HashMap<String, ButtonHandler>,
211215
pressed_buttons: HashSet<String>,
212216
}
213217

218+
impl std::fmt::Debug for ButtonFileHandler {
219+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
220+
f.debug_struct("ButtonFileHandler")
221+
.field("file_map", &self.file_map)
222+
.field("button_map", &self.button_map)
223+
.field("button_handlers", &self.button_map.keys())
224+
.field("pressed_buttons", &self.pressed_buttons)
225+
.finish()
226+
}
227+
}
228+
214229
impl ButtonFileHandler {
215230
/// Create a new instance.
216231
fn new() -> Self {
217232
ButtonFileHandler {
218233
file_map: HashMap::new(),
219234
button_map: HashMap::new(),
235+
button_handlers: HashMap::new(),
220236
pressed_buttons: HashSet::new(),
221237
}
222238
}
223239

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

261+
/// Sets an event listener for the given button.
262+
fn set_button_listener(&mut self, name: &str, listener: Option<ButtonHandler>) {
263+
if let Some(listener) = listener {
264+
self.button_handlers.insert(name.to_owned(), listener);
265+
} else {
266+
self.button_handlers.remove(name);
267+
}
268+
}
269+
270+
/// Gets a copy of the currently pressed buttons.
245271
fn get_pressed_buttons(&self) -> HashSet<String> {
246272
self.pressed_buttons.clone()
247273
}
@@ -264,6 +290,7 @@ impl ButtonFileHandler {
264290
}
265291
}
266292

293+
let old_pressed_buttons = self.pressed_buttons.clone();
267294
self.pressed_buttons.clear();
268295

269296
for (
@@ -280,6 +307,13 @@ impl ButtonFileHandler {
280307
self.pressed_buttons.insert(btn_name.to_owned());
281308
}
282309
}
310+
311+
let difference = old_pressed_buttons.symmetric_difference(&self.pressed_buttons);
312+
for button in difference {
313+
if self.button_handlers.contains_key(button) {
314+
self.button_handlers[button](self.get_button_state(button));
315+
}
316+
}
283317
}
284318
}
285319

@@ -297,6 +331,10 @@ impl ButtonFileHandler {
297331
/// # fn main() -> ev3dev_lang_rust::Ev3Result<()> {
298332
/// let button = Button::new()?;
299333
///
334+
/// button.set_down_handler(|is_pressed| {
335+
/// println("Is 'down' pressed: {is_pressed}");
336+
/// });
337+
///
300338
/// loop {
301339
/// button.process();
302340
///
@@ -335,42 +373,57 @@ impl Button {
335373

336374
/// Check for currently pressed buttons. If the new state differs from the
337375
/// old state, call the appropriate button event handlers.
376+
/// ```no_run
377+
/// use ev3dev_lang_rust::Button;
378+
/// use std::thread;
379+
/// use std::time::Duration;
380+
///
381+
/// # fn main() -> ev3dev_lang_rust::Ev3Result<()> {
382+
/// let button = Button::new()?;
383+
///
384+
/// button.set_down_handler(|is_pressed| {
385+
/// println("Is 'down' pressed: {is_pressed}");
386+
/// });
387+
///
388+
/// loop {
389+
/// button.process();
390+
///
391+
/// println!("Is 'up' pressed: {}", button.is_up());
392+
/// println!("Pressed buttons: {:?}", button.get_pressed_buttons());
393+
///
394+
/// thread::sleep(Duration::from_millis(100));
395+
/// }
396+
/// # }
397+
/// ```
338398
pub fn process(&self) {
339399
self.button_handler.borrow_mut().process()
340400
}
341401

342402
/// Get all pressed buttons by name.
403+
///
404+
/// ```no_run
405+
/// use ev3dev_lang_rust::Button;
406+
/// use std::thread;
407+
/// use std::time::Duration;
408+
///
409+
/// # fn main() -> ev3dev_lang_rust::Ev3Result<()> {
410+
/// let button = Button::new()?;
411+
///
412+
/// loop {
413+
/// button.process();
414+
/// println!("Pressed buttons: {:?}", button.get_pressed_buttons());
415+
/// thread::sleep(Duration::from_millis(100));
416+
/// }
417+
/// # }
418+
/// ```
343419
pub fn get_pressed_buttons(&self) -> HashSet<String> {
344420
self.button_handler.borrow().get_pressed_buttons()
345421
}
346422

347-
/// Check if 'up' button is pressed.
348-
pub fn is_up(&self) -> bool {
349-
self.button_handler.borrow().get_button_state("up")
350-
}
351-
352-
/// Check if 'down' button is pressed.
353-
pub fn is_down(&self) -> bool {
354-
self.button_handler.borrow().get_button_state("down")
355-
}
356-
357-
/// Check if 'left' button is pressed.
358-
pub fn is_left(&self) -> bool {
359-
self.button_handler.borrow().get_button_state("left")
360-
}
361-
362-
/// Check if 'right' button is pressed.
363-
pub fn is_right(&self) -> bool {
364-
self.button_handler.borrow().get_button_state("right")
365-
}
366-
367-
/// Check if 'enter' button is pressed.
368-
pub fn is_enter(&self) -> bool {
369-
self.button_handler.borrow().get_button_state("enter")
370-
}
371-
372-
/// Check if 'backspace' button is pressed.
373-
pub fn is_backspace(&self) -> bool {
374-
self.button_handler.borrow().get_button_state("backspace")
375-
}
423+
ev3_button_functions!(up);
424+
ev3_button_functions!(down);
425+
ev3_button_functions!(left);
426+
ev3_button_functions!(right);
427+
ev3_button_functions!(enter);
428+
ev3_button_functions!(backspace);
376429
}

src/ev3_button_functions.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/// Helper macro to create all necessary functions for a button
2+
#[macro_export]
3+
macro_rules! ev3_button_functions {
4+
($button_name:ident) => {
5+
paste! {
6+
#[doc = "Check if the `" $button_name "` button is pressed."]
7+
#[doc = "```no_run"]
8+
#[doc = "use ev3dev_lang_rust::Button;"]
9+
#[doc = "use std::thread;"]
10+
#[doc = "use std::time::Duration;"]
11+
#[doc = ""]
12+
#[doc = "# fn main() -> ev3dev_lang_rust::Ev3Result<()> {"]
13+
#[doc = "let button = Button::new()?;"]
14+
#[doc = ""]
15+
#[doc = "loop {"]
16+
#[doc = " button.process();"]
17+
#[doc = " println(\"Is '" $button_name "' pressed: {}\", button.is_" $button_name "());"]
18+
#[doc = " thread::sleep(Duration::from_millis(100));"]
19+
#[doc = "}"]
20+
#[doc = "# }"]
21+
#[doc = "```"]
22+
pub fn [<is_ $button_name>] (&self) -> bool {
23+
self.button_handler.borrow().get_button_state("$button_name")
24+
}
25+
26+
#[doc = "Set an event handler for changes in the pressed state of the `" $button_name "` button."]
27+
#[doc = "```no_run"]
28+
#[doc = "use ev3dev_lang_rust::Button;"]
29+
#[doc = "use std::thread;"]
30+
#[doc = "use std::time::Duration;"]
31+
#[doc = ""]
32+
#[doc = "# fn main() -> ev3dev_lang_rust::Ev3Result<()> {"]
33+
#[doc = "let button = Button::new()?;"]
34+
#[doc = ""]
35+
#[doc = "button.set_" $button_name "_handler(|is_pressed| {"]
36+
#[doc = " println(\"Is '" $button_name "' pressed: {is_pressed}\");"]
37+
#[doc = "});"]
38+
#[doc = ""]
39+
#[doc = "loop {"]
40+
#[doc = " button.process();"]
41+
#[doc = " thread::sleep(Duration::from_millis(100));"]
42+
#[doc = "}"]
43+
#[doc = "# }"]
44+
#[doc = "```"]
45+
pub fn [<set_ $button_name _handler>](&mut self, handler: impl Fn(bool) + 'static) {
46+
self.button_handler
47+
.borrow_mut()
48+
.set_button_listener("$button_name", Some(Box::new(handler)));
49+
}
50+
51+
#[doc = "Removes the event handler of the `" $button_name "` button."]
52+
pub fn [<remove_ $button_name _handler>](&mut self) {
53+
self.button_handler
54+
.borrow_mut()
55+
.set_button_listener("$button_name", None);
56+
}
57+
}
58+
};
59+
}

src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,14 @@ extern crate image;
4343
#[macro_use]
4444
extern crate ev3dev_lang_rust_derive;
4545
extern crate libc;
46+
extern crate paste;
4647

4748
#[macro_use]
4849
mod findable;
4950

51+
#[macro_use]
52+
mod ev3_button_functions;
53+
5054
mod attribute;
5155
pub use attribute::Attribute;
5256
mod driver;

0 commit comments

Comments
 (0)