xous-core/services/keyboard/src/lib.rs
bunnie 67fc21fbdd harmonize hosted mode selection, but retain an explicit hosted flag
In general, hosted mode is assumed "by default" if the target
is not Xous. This allows packages to be built cleanly on command
lines that don't know to pass a --feature flag to specify a target.

However, an explicit "hosted" flag is still retained so that we
can confirm the build system intent. This saves us from the failure
mode that we thought we were building for target A, but accidentally
ommitted the flag, and instead we've been silently building for
"hosted" mode without any warning. The main consumer of the "hosted"
flag is now just the build.rs script inside of the utralib.
2022-10-25 17:21:02 +08:00

126 lines
4.3 KiB
Rust

#![cfg_attr(target_os = "none", no_std)]
use num_traits::*;
pub mod api;
pub use api::*;
use xous::{send_message, Message};
use xous_ipc::{Buffer, String};
#[derive(Debug)]
pub struct Keyboard {
conn: xous::CID,
}
impl Keyboard {
pub fn new(xns: &xous_names::XousNames) -> Result<Self, xous::Error> {
REFCOUNT.fetch_add(1, Ordering::Relaxed);
let conn = xns.request_connection_blocking(api::SERVER_NAME_KBD).expect("Can't connect to KBD");
Ok(Keyboard {
conn,
})
}
pub fn register_listener(&self, server_name: &str, action_opcode: usize) {
let kr = KeyboardRegistration {
server_name: String::<64>::from_str(server_name),
listener_op_id: action_opcode
};
let buf = Buffer::into_buf(kr).unwrap();
buf.lend(self.conn, Opcode::RegisterListener.to_u32().unwrap())
.expect("couldn't register listener");
}
pub fn register_raw_listener(&self, server_name: &str, action_opcode: usize) {
let kr = KeyboardRegistration {
server_name: String::<64>::from_str(server_name),
listener_op_id: action_opcode
};
let buf = Buffer::into_buf(kr).unwrap();
buf.lend(self.conn, Opcode::RegisterRawListener.to_u32().unwrap())
.expect("couldn't register listener");
}
pub fn register_observer(&self, server_name: &str, action_opcode: usize) {
let kr = KeyboardRegistration {
server_name: String::<64>::from_str(server_name),
listener_op_id: action_opcode
};
let buf = Buffer::into_buf(kr).unwrap();
buf.lend(self.conn, Opcode::RegisterKeyObserver.to_u32().unwrap())
.expect("couldn't register listener");
}
pub fn set_vibe(&self, enable: bool) -> Result<(), xous::Error> {
let ena =
if enable { 1 }
else { 0 };
send_message(self.conn,
Message::new_scalar(Opcode::Vibe.to_usize().unwrap(),
ena, 0, 0, 0,)
).map(|_| ())
}
pub fn set_keymap(&self, map: KeyMap) -> Result<(), xous::Error> {
send_message(self.conn,
Message::new_scalar(Opcode::SelectKeyMap.to_usize().unwrap(),
map.into(),
0, 0, 0)
).map(|_| ())
}
pub fn get_keymap(&self) -> Result<KeyMap, xous::Error> {
match send_message(self.conn,
Message::new_blocking_scalar(Opcode::GetKeyMap.to_usize().unwrap(),
0, 0, 0, 0)
) {
Ok(xous::Result::Scalar1(code)) => {
Ok(code.into())
}
_ => Err(xous::Error::InternalError)
}
}
/// Blocks until a key is hit. Does not block the keyboard server, just the caller.
/// Returns a `Vec::<char>`, as the user can press more than one key at a time.
/// The specific order of a simultaneous key hit event is not defined.
pub fn get_keys_blocking(&self) -> Vec<char> {
match send_message(self.conn,
Message::new_blocking_scalar(
Opcode::BlockingKeyListener.to_usize().unwrap(),
0, 0, 0, 0
)
) {
Ok(xous::Result::Scalar2(k1, k2)) => {
let mut ret = Vec::<char>::new();
if let Some(c) = core::char::from_u32(k1 as u32) {
ret.push(c)
}
if let Some(c) = core::char::from_u32(k2 as u32) {
ret.push(c)
}
ret
}
Ok(_) | Err(_) => panic!("internal error: Incorrect return type")
}
}
/// Reveal the connection ID for use with unsafe FFI calls
pub fn conn(&self) -> xous::CID { self.conn }
#[cfg(not(target_os = "xous"))]
pub fn hostmode_inject_key(&self, c: char) {
send_message(self.conn,
Message::new_scalar(Opcode::InjectKey.to_usize().unwrap(),
c as u32 as usize, 0, 0, 0
)).unwrap();
}
}
use core::sync::atomic::{AtomicU32, Ordering};
static REFCOUNT: AtomicU32 = AtomicU32::new(0);
impl Drop for Keyboard {
fn drop(&mut self) {
// now de-allocate myself. It's unsafe because we are responsible to make sure nobody else is using the connection.
if REFCOUNT.fetch_sub(1, Ordering::Relaxed) == 1 {
unsafe{xous::disconnect(self.conn).unwrap();}
}
}
}