diff --git a/Cargo.toml b/Cargo.toml index 76791d69..7dcec49e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ libtock_platform = { path = "platform" } libtock_proximity = { path = "apis/proximity" } libtock_runtime = { path = "runtime" } libtock_temperature = { path = "apis/temperature" } +libtock_touch = { path = "apis/touch" } [profile.dev] panic = "abort" @@ -43,6 +44,7 @@ members = [ "apis/low_level_debug", "apis/proximity", "apis/temperature", + "apis/touch", "panic_handlers/debug_panic", "panic_handlers/small_panic", "platform", diff --git a/apis/touch/Cargo.toml b/apis/touch/Cargo.toml new file mode 100644 index 00000000..da18f925 --- /dev/null +++ b/apis/touch/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "libtock_touch" +version = "0.1.0" +authors = [ + "Tock Project Developers ", + "dcz ", +] +license = "MIT/Apache-2.0" +edition = "2021" +repository = "https://www.github.com/tock/libtock-rs" +description = "libtock touch driver" + +[dependencies] +libtock_platform = { path = "../../platform" } + +[dev-dependencies] +libtock_unittest = { path = "../../unittest" } diff --git a/apis/touch/src/lib.rs b/apis/touch/src/lib.rs new file mode 100644 index 00000000..eb3b4d71 --- /dev/null +++ b/apis/touch/src/lib.rs @@ -0,0 +1,170 @@ +#![no_std] + +use core::cell::Cell; +use libtock_platform::{share, DefaultConfig, ErrorCode, Subscribe, Syscalls}; +pub struct Touch(S); + +pub enum TouchStatus { + Unstarted, + Pressed, + Released, + Moved, +} + +impl TouchStatus { + fn from_u32(value: u32) -> TouchStatus { + match value { + 0 => TouchStatus::Released, + 1 => TouchStatus::Pressed, + 2 => TouchStatus::Moved, + _ => TouchStatus::Unstarted, + } + } +} + +pub struct TouchEvent { + /// touch event type + pub status: TouchStatus, + /// touch (x, y) position + pub x: u16, + pub y: u16, + /// A scaled value for the size of the touch, if the touchscreen offers that information, + /// or None otherwise. + /// A larger value corresponds to a "fatter" touch. + pub area: Option, + /// A scaled value for the pressure of the touch, if the touchscreen offers that information, + /// or None otherwise. + /// A larger value corresponds to a "firmer" press. + pub pressure: Option, +} + +pub enum GestureEvent { + SwipeUp, + SwipeDown, + SwipeLeft, + SwipeRight, + ZoomIn, + ZoomOut, +} + +impl GestureEvent { + fn from_u32(value: u32) -> GestureEvent { + match value { + 1 => GestureEvent::SwipeUp, + 2 => GestureEvent::SwipeDown, + 3 => GestureEvent::SwipeLeft, + 4 => GestureEvent::SwipeRight, + 5 => GestureEvent::ZoomIn, + _ => GestureEvent::ZoomOut, + } + } +} + +impl Touch { + pub fn exists() -> Result<(), ErrorCode> { + S::command(DRIVER_NUM, command::DRIVER_CHECK, 0, 0).to_result() + } + + pub fn enable_single_touch() -> Result<(), ErrorCode> { + S::command(DRIVER_NUM, command::ENABLE_SINGLE, 0, 0).to_result() + } + + pub fn disable_single_touch() -> Result<(), ErrorCode> { + S::command(DRIVER_NUM, command::DISABLE_SINGLE, 0, 0).to_result() + } + + pub fn register_single_touch_listener<'share>( + listener: &'share Cell>, + subscribe: share::Handle>, + ) -> Result<(), ErrorCode> { + S::subscribe::<_, _, DefaultConfig, DRIVER_NUM, { subscribe::SINGLE }>(subscribe, listener) + } + + pub fn register_gestures_listener<'share>( + listener: &'share Cell>, + subscribe: share::Handle>, + ) -> Result<(), ErrorCode> { + S::subscribe::<_, _, DefaultConfig, DRIVER_NUM, { subscribe::GESTURES }>( + subscribe, listener, + ) + } + + /// Waits for a single touch event, returning details about it in a TouchEvent structure: + pub fn wait_for_single_touch() -> Result { + Self::enable_single_touch()?; + let listener: Cell> = Cell::new(None); + share::scope(|subscribe| { + if let Ok(()) = Self::register_single_touch_listener(&listener, subscribe) { + while listener.get().is_none() { + S::yield_wait(); + } + } + }); + match listener.get() { + None => Err(ErrorCode::Fail), + Some(tuple) => Ok(TouchEvent { + status: TouchStatus::from_u32(tuple.0), + x: (tuple.1 >> 16) as u16, + y: tuple.1 as u16, + area: match tuple.2 as u16 { + 0 => None, + val => Some(val), + }, + pressure: match (tuple.2 >> 16) as u16 { + 0 => None, + val => Some(val), + }, + }), + } + } + + /// Waits for a gesture event, returning it's type + pub fn wait_for_gesture() -> Result { + let listener: Cell> = Cell::new(None); + share::scope(|subscribe| { + if let Ok(()) = Self::register_gestures_listener(&listener, subscribe) { + while listener.get().is_none() { + S::yield_wait(); + } + } + }); + + match listener.get() { + None => Err(ErrorCode::Fail), + Some(val) => Ok(GestureEvent::from_u32(val.0)), + } + } + + /// Returns the number of available touches + pub fn get_number_of_touches() -> Result { + S::command(DRIVER_NUM, command::TOUCHES_NUM, 0, 0).to_result() + } +} + +// ----------------------------------------------------------------------------- +// Driver number and command IDs +// ----------------------------------------------------------------------------- + +const DRIVER_NUM: u32 = 589826; + +// Command IDs + +mod command { + pub const DRIVER_CHECK: u32 = 0; + pub const ENABLE_SINGLE: u32 = 1; + pub const DISABLE_SINGLE: u32 = 2; + pub const ACK_MULTI: u32 = 10; + pub const ENABLE_MULTI: u32 = 11; + pub const DISABLE_MULTI: u32 = 12; + pub const TOUCHES_NUM: u32 = 100; +} + +mod subscribe { + pub const SINGLE: u32 = 0; + pub const GESTURES: u32 = 1; + pub const MULTI: u32 = 2; +} + +mod allow_rw { + pub const MULTI: u32 = 0; +} diff --git a/examples/touch.rs b/examples/touch.rs new file mode 100644 index 00000000..2a91023e --- /dev/null +++ b/examples/touch.rs @@ -0,0 +1,34 @@ +//! A simple libtock-rs example. Checks for touch driver +//! and waits for single touch events, printing their location + +#![no_main] +#![no_std] + +use core::fmt::Write; +use libtock::console::Console; + +use libtock::runtime::{set_main, stack_size}; +use libtock::touch::Touch; + +set_main! {main} +stack_size! {0x200} + +fn main() { + if Touch::exists().is_err() { + writeln!(Console::writer(), "touch driver unavailable").unwrap(); + return; + } + + loop { + match Touch::wait_for_single_touch() { + Err(_) => writeln!(Console::writer(), "Error in getting touch event").unwrap(), + Ok(event) => writeln!( + Console::writer(), + "Touch event at ({}, {})", + event.x, + event.y + ) + .unwrap(), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index fda15c86..86f0a834 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,3 +47,7 @@ pub mod temperature { pub type Temperature = temperature::Temperature; pub use temperature::TemperatureListener; } +pub mod touch { + use libtock_touch as touch; + pub type Touch = touch::Touch; +}