diff --git a/src/keyboard.rs b/src/keyboard.rs index d675017c..cb9cb8ae 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -15,59 +15,20 @@ use std::iter::{ FromIterator, IntoIterator }; /// Gathers stuff defined in C or called by C pub mod c { use super::*; + use ::util::c; use ::util::c::as_cstr; use std::ffi::CString; use std::os::raw::c_char; - // traits - - use std::borrow::ToOwned; - - /// The wrapped structure for KeyState suitable for handling in C - /// Since C doesn't respect borrowing rules, - /// RefCell will enforce them dynamically (only 1 writer/many readers) - /// Rc is implied and will ensure timely dropping - #[repr(transparent)] - pub struct CKeyState(*const RefCell); - - impl Clone for CKeyState { - fn clone(&self) -> Self { - CKeyState(self.0.clone()) - } - } - - impl CKeyState { - pub fn wrap(state: Rc>) -> CKeyState { - CKeyState(Rc::into_raw(state)) - } - pub fn unwrap(self) -> Rc> { - unsafe { Rc::from_raw(self.0) } - } - fn to_owned(self) -> KeyState { - let rc = self.unwrap(); - let state = rc.borrow().to_owned(); - Rc::into_raw(rc); // Prevent dropping - state - } - fn borrow_mut(self, f: F) -> T where F: FnOnce(&mut KeyState) -> T { - let rc = self.unwrap(); - let ret = { - let mut state = rc.borrow_mut(); - f(&mut state) - }; - Rc::into_raw(rc); // Prevent dropping - ret - } - } - - // TODO: unwrapping + pub type CKeyState = c::Wrapped; // The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers + #[no_mangle] pub extern "C" fn squeek_key_free(key: CKeyState) { - key.unwrap(); // reference dropped + unsafe { key.unwrap() }; // reference dropped } #[no_mangle] @@ -80,7 +41,9 @@ pub mod c { #[no_mangle] pub extern "C" fn squeek_key_set_pressed(key: CKeyState, pressed: u32) { - key.borrow_mut(|key| key.pressed = pressed != 0); + let key = key.clone_ref(); + let mut key = key.borrow_mut(); + key.pressed = pressed != 0; } #[no_mangle] @@ -92,7 +55,9 @@ pub mod c { #[no_mangle] pub extern "C" fn squeek_key_set_locked(key: CKeyState, locked: u32) { - key.borrow_mut(|key| key.locked = locked != 0); + let key = key.clone_ref(); + let mut key = key.borrow_mut(); + key.locked = locked != 0; } #[no_mangle] @@ -116,10 +81,10 @@ pub mod c { Action::Submit { text: Some(text), .. } => { Some( text.clone() - .into_string().expect("Bad symbol text") + .into_string().expect("Bad symbol") ) }, - _ => None + _ => None, }; let inner = match symbol_name { diff --git a/src/layout.rs b/src/layout.rs index e406e638..e72a6937 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -194,9 +194,8 @@ pub mod c { state: ::keyboard::c::CKeyState, ) -> u32 { let button = unsafe { &*button }; - let state = state.unwrap(); + let state = state.clone_ref(); let equal = Rc::ptr_eq(&button.state, &state); - Rc::into_raw(state); // Prevent dropping equal as u32 } @@ -333,13 +332,10 @@ pub mod c { needle: ::keyboard::c::CKeyState, ) -> ButtonPlace { let view = unsafe { &*view }; - let state = needle.unwrap(); + let state = needle.clone_ref(); let paths = ::layout::procedures::find_key_paths(view, &state); - // Iterators used up, can turn the reference back into pointer - Rc::into_raw(state); - // Can only return 1 entry back to C let (row, button) = match paths.get(0) { Some((row, button)) => ( diff --git a/src/util.rs b/src/util.rs index 4d7d6378..9289a40d 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,7 +1,13 @@ pub mod c { + use std::cell::RefCell; use std::ffi::{ CStr, CString }; use std::os::raw::c_char; + use std::rc::Rc; use std::str::Utf8Error; + + // traits + + use std::borrow::ToOwned; pub fn as_str(s: &*const c_char) -> Result, Utf8Error> { if s.is_null() { @@ -46,4 +52,52 @@ pub mod c { assert_eq!(as_str(&ptr::null()), Ok(None)) } } + + /// Wraps structures to pass them safely to/from C + /// Since C doesn't respect borrowing rules, + /// RefCell will enforce them dynamically (only 1 writer/many readers) + /// Rc is implied and will ensure timely dropping + #[repr(transparent)] + pub struct Wrapped(*const RefCell); + + // It would be nice to implement `Borrow` + // directly on the raw pointer to avoid one conversion call, + // but the `borrow()` call needs to extract a `Rc`, + // and at the same time return a reference to it (`std::cell::Ref`) + // to take advantage of `Rc::borrow()` runtime checks. + // Unfortunately, that needs a `Ref` struct with self-referential fields, + // which is a bit too complex for now. + + impl Wrapped { + pub fn wrap(state: Rc>) -> Wrapped { + Wrapped(Rc::into_raw(state)) + } + /// Extracts the reference to the data. + /// It may cause problems if attempted in more than one place + pub unsafe fn unwrap(self) -> Rc> { + Rc::from_raw(self.0) + } + + /// Creates a new Rc reference to the same data + pub fn clone_ref(&self) -> Rc> { + // A bit dangerous: the Rc may be in use elsewhere + let used_rc = unsafe { Rc::from_raw(self.0) }; + let rc = used_rc.clone(); + Rc::into_raw(used_rc); // prevent dropping the original reference + rc + } + + /// Create a copy of the underlying data + pub fn to_owned(&self) -> T { + let rc = self.clone_ref(); + let r = rc.borrow(); + r.to_owned() + } + } + + impl Clone for Wrapped { + fn clone(&self) -> Wrapped { + Wrapped::wrap(self.clone_ref()) + } + } }