use std::cell::RefCell; use std::rc::Rc; use std::vec::Vec; use ::keyboard::*; use ::float_ord::FloatOrd; use ::symbol::*; /// Gathers stuff defined in C or called by C pub mod c { use super::*; use std::os::raw::c_void; use std::ptr; // The following defined in C #[repr(transparent)] pub struct UserData(*const c_void); /// The index in the relevant outline table #[repr(C)] #[derive(Clone, Debug)] pub struct OutlineRef(u32); /// Defined in eek-types.h #[repr(C)] #[derive(Clone, Debug)] pub struct Point { pub x: f64, pub y: f64, } /// Defined in eek-types.h #[repr(C)] #[derive(Clone, Debug)] pub struct Bounds { pub x: f64, pub y: f64, pub width: f64, pub height: f64 } impl Bounds { pub fn zero() -> Bounds { Bounds { x: 0f64, y: 0f64, width: 0f64, height: 0f64 } } } type ButtonCallback = unsafe extern "C" fn(button: *mut ::layout::Button, data: *mut UserData); type RowCallback = unsafe extern "C" fn(row: *mut ::layout::Row, data: *mut UserData); // 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_view_new(bounds: Bounds) -> *mut ::layout::View { Box::into_raw(Box::new(::layout::View { rows: Vec::new(), bounds: bounds, })) } #[no_mangle] pub extern "C" fn squeek_view_get_bounds(view: *const ::layout::View) -> Bounds { unsafe { &*view }.bounds.clone() } #[no_mangle] pub extern "C" fn squeek_view_set_bounds(view: *mut ::layout::View, bounds: Bounds) { unsafe { &mut *view }.bounds = bounds; } /// Places a row into the view and returns a reference to it #[no_mangle] pub extern "C" fn squeek_view_create_row( view: *mut ::layout::View, angle: i32, ) -> *mut ::layout::Row { let view = unsafe { &mut *view }; view.rows.push(Box::new(::layout::Row::new(angle))); // Return the reference directly instead of a Box, it's not on the stack // It will live as long as the Vec let last_idx = view.rows.len() - 1; // Caution: Box can't be returned directly, // so returning a reference to its innards view.rows[last_idx].as_mut() as *mut ::layout::Row } #[no_mangle] pub extern "C" fn squeek_view_foreach( view: *mut ::layout::View, callback: RowCallback, data: *mut UserData, ) { let view = unsafe { &mut *view }; for row in view.rows.iter_mut() { let row = row.as_mut() as *mut ::layout::Row; unsafe { callback(row, data) }; } } #[no_mangle] pub extern "C" fn squeek_row_new(angle: i32) -> *mut ::layout::Row { Box::into_raw(Box::new(::layout::Row::new(angle))) } /// Places a button into the row and returns a reference to it #[no_mangle] pub extern "C" fn squeek_row_create_button( row: *mut ::layout::Row, keycode: u32, oref: u32 ) -> *mut ::layout::Button { let row = unsafe { &mut *row }; let state: Rc> = Rc::new(RefCell::new( ::keyboard::KeyState { pressed: false, locked: false, keycode: keycode, symbol: None, } )); row.buttons.push(Box::new(::layout::Button { oref: OutlineRef(oref), bounds: None, state: state, })); // Return the reference directly instead of a Box, it's not on the stack // It will live as long as the Vec let last_idx = row.buttons.len() - 1; // Caution: Box can't be returned directly, // so returning a reference to its innards row.buttons[last_idx].as_mut() as *mut ::layout::Button } /// Places a button into the row, copying its state, /// and returns a reference to it #[no_mangle] pub extern "C" fn squeek_row_create_button_with_state( row: *mut ::layout::Row, button: *const ::layout::Button, ) -> *mut ::layout::Button { let row = unsafe { &mut *row }; let source = unsafe { &*button }; row.buttons.push(Box::new(source.clone())); // Return the reference directly instead of a Box, it's not on the stack // It will live as long as the Vec let last_idx = row.buttons.len() - 1; // Caution: Box can't be returned directly, // so returning a reference to its innards directly row.buttons[last_idx].as_mut() as *mut ::layout::Button } #[no_mangle] pub extern "C" fn squeek_row_set_angle(row: *mut ::layout::Row, angle: i32) { let row = unsafe { &mut *row }; row.angle = angle; } #[no_mangle] pub extern "C" fn squeek_row_get_angle(row: *const ::layout::Row) -> i32 { let row = unsafe { &*row }; row.angle } #[no_mangle] pub extern "C" fn squeek_row_get_bounds(row: *const ::layout::Row) -> Bounds { let row = unsafe { &*row }; match &row.bounds { Some(bounds) => bounds.clone(), None => panic!("Row doesn't have any bounds yet"), } } /// Set bounds by consuming the value #[no_mangle] pub extern "C" fn squeek_row_set_bounds(row: *mut ::layout::Row, bounds: Bounds) { let row = unsafe { &mut *row }; row.bounds = Some(bounds); } #[no_mangle] pub extern "C" fn squeek_row_foreach( row: *mut ::layout::Row, callback: ButtonCallback, data: *mut UserData, ) { let row = unsafe { &mut *row }; for button in row.buttons.iter_mut() { let button = button.as_mut() as *mut ::layout::Button; unsafe { callback(button, data) }; } } #[no_mangle] pub extern "C" fn squeek_row_free(row: *mut ::layout::Row) { unsafe { Box::from_raw(row) }; // gets dropped } #[no_mangle] pub extern "C" fn squeek_button_new(keycode: u32, oref: u32) -> *mut ::layout::Button { let state: Rc> = Rc::new(RefCell::new( ::keyboard::KeyState { pressed: false, locked: false, keycode: keycode, symbol: None, } )); Box::into_raw(Box::new(::layout::Button { oref: OutlineRef(oref), bounds: None, state: state, })) } #[no_mangle] pub extern "C" fn squeek_button_new_with_state(source: *mut ::layout::Button) -> *mut ::layout::Button { let source = unsafe { &*source }; let button = Box::new(source.clone()); Box::into_raw(button) } #[no_mangle] pub extern "C" fn squeek_button_get_oref(button: *const ::layout::Button) -> u32 { let button = unsafe { &*button }; button.oref.0 } // Bounds transparently mapped to C, therefore no pointer needed #[no_mangle] pub extern "C" fn squeek_button_get_bounds(button: *const ::layout::Button) -> Bounds { let button = unsafe { &*button }; match &button.bounds { Some(bounds) => bounds.clone(), None => panic!("Button doesn't have any bounds yet"), } } /// Set bounds by consuming the value #[no_mangle] pub extern "C" fn squeek_button_set_bounds(button: *mut ::layout::Button, bounds: Bounds) { let button = unsafe { &mut *button }; button.bounds = Some(bounds); } /// Borrow a new reference to key state. Doesn't need freeing #[no_mangle] pub extern "C" fn squeek_button_get_key( button: *const ::layout::Button ) -> ::keyboard::c::CKeyState { let button = unsafe { &*button }; ::keyboard::c::CKeyState::wrap(button.state.clone()) } /// Really should just return the label #[no_mangle] pub extern "C" fn squeek_button_get_symbol( button: *const ::layout::Button, ) -> *const Symbol { let button = unsafe { &*button }; let state = button.state.borrow(); match state.symbol { Some(ref symbol) => symbol as *const Symbol, None => ptr::null(), } } #[no_mangle] pub extern "C" fn squeek_button_has_key( button: *const ::layout::Button, state: ::keyboard::c::CKeyState, ) -> u32 { let button = unsafe { &*button }; let state = state.unwrap(); let equal = Rc::ptr_eq(&button.state, &state); Rc::into_raw(state); // Prevent dropping equal as u32 } #[no_mangle] pub extern "C" fn squeek_button_print(button: *const ::layout::Button) { let button = unsafe { &*button }; println!("{:?}", button); } /// Entry points for more complex procedures and algoithms which span multiple modules mod procedures { use super::*; #[repr(transparent)] pub struct LevelKeyboard(*const c_void); #[repr(C)] #[derive(PartialEq, Debug)] pub struct ButtonPlace { row: *const Row, button: *const Button, } #[no_mangle] extern "C" { fn eek_get_outline_size( keyboard: *const LevelKeyboard, outline: u32 ) -> Bounds; /// Checks if point falls within bounds, /// which are relative to origin and rotated by angle (I think) fn eek_are_bounds_inside (bounds: Bounds, point: Point, origin: Point, angle: i32 ) -> u32; } fn squeek_buttons_get_outlines( buttons: &Vec>, keyboard: *const LevelKeyboard, ) -> Vec { buttons.iter().map(|button| { unsafe { eek_get_outline_size(keyboard, button.oref.0) } }).collect() } /// Places each button in order, starting from 0 on the left, /// keeping the spacing. /// Sizes each button according to outline dimensions. /// Places each row in order, starting from 0 on the top, /// keeping the spacing. /// Sets button and row sizes according to their contents. #[no_mangle] pub extern "C" fn squeek_view_place_contents( view: *mut ::layout::View, keyboard: *const LevelKeyboard, // source of outlines ) { let view = unsafe { &mut *view }; let sizes: Vec> = view.rows.iter().map(|row| squeek_buttons_get_outlines(&row.buttons, keyboard) ).collect(); view.place_buttons_with_sizes(sizes); } #[no_mangle] pub extern "C" fn squeek_row_find_button_by_position( row: *mut Row, point: Point, origin: Point ) -> *mut Button { let row = unsafe { &mut *row }; let row_bounds = row.bounds .as_ref().expect("Missing bounds on row"); let origin = Point { x: origin.x + row_bounds.x, y: origin.y + row_bounds.y, }; let angle = row.angle; let result = row.buttons.iter_mut().find(|button| { let bounds = button.bounds .as_ref().expect("Missing bounds on button") .clone(); let point = point.clone(); let origin = origin.clone(); unsafe { eek_are_bounds_inside(bounds, point, origin, angle) == 1 } }); match result { Some(button) => button.as_mut() as *mut Button, None => ptr::null_mut(), } } fn squeek_row_contains(row: &Row, needle: *const Button) -> bool { row.buttons.iter().position( // TODO: wrap Button properly in Rc; this comparison is unreliable |button| button.as_ref() as *const ::layout::Button == needle ).is_some() } #[no_mangle] pub extern "C" fn squeek_view_get_row( view: *mut View, needle: *const ::layout::Button, ) -> *mut Row { let view = unsafe { &mut *view }; let result = view.rows.iter_mut().find(|row| { squeek_row_contains(row, needle) }); match result { Some(row) => row.as_mut() as *mut Row, None => ptr::null_mut(), } } #[no_mangle] pub extern "C" fn squeek_view_find_key( view: *const View, needle: ::keyboard::c::CKeyState, ) -> ButtonPlace { let view = unsafe { &*view }; let state = needle.unwrap(); 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)) => ( row.as_ref() as *const Row, button.as_ref() as *const Button, ), None => ( ptr::null(), ptr::null() ), }; ButtonPlace { row, button } } #[cfg(test)] mod test { use super::*; #[test] fn row_has_button() { let mut row = Row::new(0); let button = squeek_row_create_button(&mut row as *mut Row, 0, 0); assert_eq!(squeek_row_contains(&row, button), true); let shared_button = squeek_row_create_button_with_state( &mut row as *mut Row, button ); assert_eq!(squeek_row_contains(&row, shared_button), true); let row = Row::new(0); assert_eq!(squeek_row_contains(&row, button), false); } #[test] fn view_has_button() { let state = Rc::new(RefCell::new(::keyboard::KeyState { pressed: false, locked: false, keycode: 0, symbol: None, })); let state_clone = ::keyboard::c::CKeyState::wrap(state.clone()); let button = Box::new(Button { oref: OutlineRef(0), bounds: None, state: state, }); let button_ptr = button.as_ref() as *const Button; let row = Box::new(Row { buttons: vec!(button), angle: 0, bounds: None }); let row_ptr = row.as_ref() as *const Row; let view = View { bounds: Bounds { x: 0f64, y: 0f64, width: 0f64, height: 0f64 }, rows: vec!(row), }; assert_eq!( squeek_view_find_key( &view as *const View, state_clone.clone(), ), ButtonPlace { row: row_ptr, button: button_ptr, } ); let view = View { bounds: Bounds { x: 0f64, y: 0f64, width: 0f64, height: 0f64 }, rows: Vec::new(), }; assert_eq!( squeek_view_find_key( &view as *const View, state_clone.clone() ), ButtonPlace { row: ptr::null(), button: ptr::null(), } ); } } } #[cfg(test)] mod test { use super::*; #[test] fn button_has_key() { let button = squeek_button_new(0, 0); let state = squeek_button_get_key(button); assert_eq!(squeek_button_has_key(button, state.clone()), 1); let other_button = squeek_button_new(0, 0); assert_eq!(squeek_button_has_key(other_button, state.clone()), 0); let other_state = ::keyboard::c::squeek_key_new(0); assert_eq!(squeek_button_has_key(button, other_state), 0); let shared_button = squeek_button_new_with_state(button); assert_eq!(squeek_button_has_key(shared_button, state), 1); } } } #[derive(Debug)] pub struct Size { pub width: f64, pub height: f64, } /// The graphical representation of a button #[derive(Clone, Debug)] pub struct Button { oref: c::OutlineRef, /// TODO: abolish Option, buttons should be created with bounds fully formed bounds: Option, /// current state, shared with other buttons pub state: Rc>, } const BUTTON_SPACING: f64 = 4.0; const ROW_SPACING: f64 = 7.0; /// The graphical representation of a row of buttons pub struct Row { buttons: Vec>, /// Angle is not really used anywhere... angle: i32, bounds: Option, } impl Row { fn new(angle: i32) -> Row { Row { buttons: Vec::new(), angle: angle, bounds: None, } } fn last(positions: &Vec) -> Option<&c::Bounds> { let len = positions.len(); match len { 0 => None, l => Some(&positions[l - 1]) } } fn calculate_button_positions(outlines: Vec) -> Vec { let mut x_offset = 0f64; outlines.iter().map(|outline| { x_offset += outline.x; // account for offset outlines let position = c::Bounds { x: x_offset, ..outline.clone() }; x_offset += outline.width + BUTTON_SPACING; position }).collect() } fn calculate_row_size(positions: &Vec) -> Size { let max_height = positions.iter().map( |bounds| FloatOrd(bounds.height) ).max() .unwrap_or(FloatOrd(0f64)) .0; let total_width = match Row::last(positions) { Some(position) => position.x + position.width, None => 0f64, }; Size { width: total_width, height: max_height } } } pub struct View { /// Position relative to keyboard origin bounds: c::Bounds, rows: Vec>, } impl View { /// Determines the positions of rows based on their sizes /// Each row will be centered horizontally /// The collection of rows will not be altered vertically /// (TODO: make view bounds a constraint, /// and derive a scaling factor that lets contents fit into view) fn calculate_row_positions(&self, sizes: Vec) -> Vec { let mut y_offset = self.bounds.y; sizes.into_iter().map(|size| { let position = c::Bounds { x: (self.bounds.width - size.width) / 2f64, y: y_offset, width: size.width, height: size.height, }; y_offset += size.height + ROW_SPACING; position }).collect() } /// Uses button outline information to place all buttons and rows inside. /// The view itself will not be affected by the sizes fn place_buttons_with_sizes( &mut self, button_outlines: Vec> ) { // Determine all positions let button_positions: Vec<_> = button_outlines.into_iter() .map(Row::calculate_button_positions) .collect(); let row_sizes = button_positions.iter() .map(Row::calculate_row_size) .collect(); let row_positions = self.calculate_row_positions(row_sizes); // Apply all positions for ((mut row, row_position), button_positions) in self.rows.iter_mut() .zip(row_positions) .zip(button_positions) { row.bounds = Some(row_position); for (mut button, button_position) in row.buttons.iter_mut() .zip(button_positions) { button.bounds = Some(button_position); } } } } mod procedures { use super::*; type Path<'v> = (&'v Box, &'v Box