section: Move properties into Row

This commit is contained in:
Dorota Czaplejewicz
2019-08-15 17:33:09 +00:00
parent 3689727fc1
commit 4f8de42598
12 changed files with 467 additions and 130 deletions

141
src/float_ord.rs Normal file
View File

@ -0,0 +1,141 @@
//! Order floating point numbers, into this ordering:
//!
//! NaN | -Infinity | x < 0 | -0 | +0 | x > 0 | +Infinity | NaN
/* Adapted from https://github.com/notriddle/rust-float-ord revision e995165f
* maintained by Michael Howell <michael@notriddle.com>
* licensed under MIT / Apache-2.0 licenses
*/
extern crate core;
use ::float_ord::core::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
use ::float_ord::core::hash::{Hash, Hasher};
use ::float_ord::core::mem::transmute;
/// A wrapper for floats, that implements total equality and ordering
/// and hashing.
#[derive(Clone, Copy)]
pub struct FloatOrd<T>(pub T);
macro_rules! float_ord_impl {
($f:ident, $i:ident, $n:expr) => {
impl FloatOrd<$f> {
fn convert(self) -> $i {
let u = unsafe { transmute::<$f, $i>(self.0) };
let bit = 1 << ($n - 1);
if u & bit == 0 {
u | bit
} else {
!u
}
}
}
impl PartialEq for FloatOrd<$f> {
fn eq(&self, other: &Self) -> bool {
self.convert() == other.convert()
}
}
impl Eq for FloatOrd<$f> {}
impl PartialOrd for FloatOrd<$f> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.convert().partial_cmp(&other.convert())
}
}
impl Ord for FloatOrd<$f> {
fn cmp(&self, other: &Self) -> Ordering {
self.convert().cmp(&other.convert())
}
}
impl Hash for FloatOrd<$f> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.convert().hash(state);
}
}
}
}
float_ord_impl!(f32, u32, 32);
float_ord_impl!(f64, u64, 64);
/// Sort a slice of floats.
///
/// # Allocation behavior
///
/// This routine uses a quicksort implementation that does not heap allocate.
///
/// # Example
///
/// ```
/// let mut v = [-5.0, 4.0, 1.0, -3.0, 2.0];
///
/// float_ord::sort(&mut v);
/// assert!(v == [-5.0, -3.0, 1.0, 2.0, 4.0]);
/// ```
#[allow(dead_code)]
pub fn sort<T>(v: &mut [T]) where FloatOrd<T>: Ord {
let v_: &mut [FloatOrd<T>] = unsafe { transmute(v) };
v_.sort_unstable();
}
#[cfg(test)]
mod tests {
extern crate std;
use self::std::collections::hash_map::DefaultHasher;
use self::std::hash::{Hash, Hasher};
use super::FloatOrd;
#[test]
fn test_ord() {
assert!(FloatOrd(1.0f64) < FloatOrd(2.0f64));
assert!(FloatOrd(2.0f32) > FloatOrd(1.0f32));
assert!(FloatOrd(1.0f64) == FloatOrd(1.0f64));
assert!(FloatOrd(1.0f32) == FloatOrd(1.0f32));
assert!(FloatOrd(0.0f64) > FloatOrd(-0.0f64));
assert!(FloatOrd(0.0f32) > FloatOrd(-0.0f32));
assert!(FloatOrd(::float_ord::core::f64::NAN) == FloatOrd(::float_ord::core::f64::NAN));
assert!(FloatOrd(::float_ord::core::f32::NAN) == FloatOrd(::float_ord::core::f32::NAN));
assert!(FloatOrd(-::float_ord::core::f64::NAN) < FloatOrd(::float_ord::core::f64::NAN));
assert!(FloatOrd(-::float_ord::core::f32::NAN) < FloatOrd(::float_ord::core::f32::NAN));
assert!(FloatOrd(-::float_ord::core::f64::INFINITY) < FloatOrd(::float_ord::core::f64::INFINITY));
assert!(FloatOrd(-::float_ord::core::f32::INFINITY) < FloatOrd(::float_ord::core::f32::INFINITY));
assert!(FloatOrd(::float_ord::core::f64::INFINITY) < FloatOrd(::float_ord::core::f64::NAN));
assert!(FloatOrd(::float_ord::core::f32::INFINITY) < FloatOrd(::float_ord::core::f32::NAN));
assert!(FloatOrd(-::float_ord::core::f64::NAN) < FloatOrd(::float_ord::core::f64::INFINITY));
assert!(FloatOrd(-::float_ord::core::f32::NAN) < FloatOrd(::float_ord::core::f32::INFINITY));
}
fn hash<F: Hash>(f: F) -> u64 {
let mut hasher = DefaultHasher::new();
f.hash(&mut hasher);
hasher.finish()
}
#[test]
fn test_hash() {
assert_ne!(hash(FloatOrd(0.0f64)), hash(FloatOrd(-0.0f64)));
assert_ne!(hash(FloatOrd(0.0f32)), hash(FloatOrd(-0.0f32)));
assert_eq!(hash(FloatOrd(-0.0f64)), hash(FloatOrd(-0.0f64)));
assert_eq!(hash(FloatOrd(0.0f32)), hash(FloatOrd(0.0f32)));
assert_ne!(hash(FloatOrd(::float_ord::core::f64::NAN)), hash(FloatOrd(-::float_ord::core::f64::NAN)));
assert_ne!(hash(FloatOrd(::float_ord::core::f32::NAN)), hash(FloatOrd(-::float_ord::core::f32::NAN)));
assert_eq!(hash(FloatOrd(::float_ord::core::f64::NAN)), hash(FloatOrd(::float_ord::core::f64::NAN)));
assert_eq!(hash(FloatOrd(-::float_ord::core::f32::NAN)), hash(FloatOrd(-::float_ord::core::f32::NAN)));
}
#[test]
fn test_sort_nan() {
let nan = ::float_ord::core::f64::NAN;
let mut v = [-1.0, 5.0, 0.0, -0.0, nan, 1.5, nan, 3.7];
super::sort(&mut v);
assert!(v[0] == -1.0);
assert!(v[1] == 0.0 && v[1].is_sign_negative());
assert!(v[2] == 0.0 && !v[2].is_sign_negative());
assert!(v[3] == 1.5);
assert!(v[4] == 3.7);
assert!(v[5] == 5.0);
assert!(v[6].is_nan());
assert!(v[7].is_nan());
}
}

View File

@ -7,17 +7,28 @@
#include "src/keyboard.h"
struct squeek_button;
struct squeek_row;
/*
struct squeek_buttons;
struct squeek_row *squeek_row_new(int32_t angle);
struct squeek_button *squeek_row_create_button (struct squeek_row *row,
guint keycode, guint oref);
struct squeek_button *squeek_row_create_button_with_state(struct squeek_row *row,
struct squeek_button *source);
void squeek_row_set_angle(struct squeek_row *row, int32_t angle);
int32_t squeek_row_get_angle(struct squeek_row*);
uint32_t squeek_row_contains(struct squeek_row*, struct squeek_button *button);
struct squeek_button* squeek_row_find_key(struct squeek_row*, struct squeek_key *state);
typedef void (*ButtonCallback) (struct squeek_button *button, gpointer user_data);
struct squeek_buttons *squeek_buttons_new();
void squeek_buttons_free(struct squeek_buttons*);
void squeek_buttons_foreach(const struct squeek_buttons*,
void squeek_row_foreach(struct squeek_row*,
ButtonCallback callback,
gpointer user_data);
void squeek_row_free(struct squeek_row*);
/*
struct squeek_button *squeek_buttons_find_by_position(
const struct squeek_buttons *buttons,
double x, double y,
@ -26,6 +37,7 @@ struct squeek_button *squeek_buttons_find_by_position(
void squeek_buttons_add(struct squeek_buttons*, const struct squeek_button* button);
void squeek_buttons_remove_key(struct squeek_buttons*, const struct squeek_key* key);
*/
struct squeek_button *squeek_button_new(uint32_t keycode, uint32_t oref);
struct squeek_button *squeek_button_new_with_state(const struct squeek_button* source);
uint32_t squeek_button_get_oref(const struct squeek_button*);
@ -37,4 +49,8 @@ struct squeek_symbol *squeek_button_get_symbol (
struct squeek_key *squeek_button_get_key(struct squeek_button*);
uint32_t *squeek_button_has_key(const struct squeek_button* button,
const struct squeek_key *key);
void squeek_button_print(const struct squeek_button* button);
EekBounds squeek_row_place_keys(struct squeek_row *row, LevelKeyboard *keyboard);
#endif

View File

@ -1,31 +1,145 @@
use std::cell::RefCell;
use std::rc::Rc;
use std::vec::Vec;
use ::symbol::*;
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)]
#[derive(Clone, Debug)]
pub struct OutlineRef(u32);
/// Defined in eek-types.h
#[repr(C)]
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct Bounds {
x: f64,
y: f64,
width: f64,
height: f64
}
type ButtonCallback = unsafe extern "C" fn(button: *mut ::layout::Button, 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_row_new(angle: i32) -> *mut ::layout::Row {
Box::into_raw(Box::new(::layout::Row {
buttons: Vec::new(),
angle: 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<RefCell<::keyboard::KeyState>> = 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_contains(
row: *mut ::layout::Row,
needle: *const ::layout::Button,
) -> u32 {
let row = unsafe { &mut *row };
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() as u32
}
#[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"
@ -44,7 +158,7 @@ pub mod c {
state: state,
}))
}
#[no_mangle]
pub extern "C"
fn squeek_button_new_with_state(source: *mut ::layout::Button) -> *mut ::layout::Button {
@ -117,6 +231,108 @@ pub mod c {
equal as u32
}
#[no_mangle]
pub extern "C"
fn squeek_button_print(button: *const ::layout::Button) {
let button = unsafe { &*button };
println!("{:?}", button);
}
/// More complex procedures and algoithms which span multiple modules
mod procedures {
use super::*;
use std::convert::TryFrom;
#[repr(transparent)]
pub struct LevelKeyboard(*const c_void);
#[no_mangle]
extern "C" {
fn eek_get_outline_size(
keyboard: *const LevelKeyboard,
outline: u32
) -> Bounds;
}
const BUTTON_SPACING: f64 = 4.0;
/// Places each button in order, starting from 0 on the left,
/// keeping the spacing.
/// Sizes each button according to outline dimensions.
/// Returns the width and height of the resulting row.
#[no_mangle]
pub extern "C"
fn squeek_row_place_keys(
row: *mut ::layout::Row,
keyboard: *const LevelKeyboard,
) -> Bounds {
let row = unsafe { &mut *row };
// Size buttons
for mut button in &mut row.buttons {
button.bounds = Some(
unsafe { eek_get_outline_size(keyboard, button.oref.0) }
);
}
// Place buttons
let cumulative_width: f64 = row.buttons.iter().map(
|button| button.bounds.as_ref().unwrap().width
).sum();
let max_height = row.buttons.iter().map(
|button| FloatOrd(
button.bounds.as_ref().unwrap().height
)
).max()
.unwrap_or(FloatOrd(0f64))
.0;
row.buttons.iter_mut().fold(0f64, |acc, button| {
let mut bounds = button.bounds.as_mut().unwrap();
bounds.x = acc;
acc + bounds.width + BUTTON_SPACING
});
// Total size
let total_width = match row.buttons.is_empty() {
true => 0f64,
false => {
let last_button = &row.buttons[row.buttons.len() - 1];
let bounds = last_button.bounds.as_ref().unwrap();
bounds.x + bounds.width
},
};
Bounds {
x: 0f64,
y: 0f64,
width: total_width,
height: max_height,
}
}
/// Finds a button sharing this state
#[no_mangle]
pub extern "C"
fn squeek_row_find_key(
row: *mut ::layout::Row,
state: ::keyboard::c::CKeyState,
) -> *mut Button {
let row = unsafe { &mut *row };
let needle = state.unwrap();
let found = row.buttons.iter_mut().find(
|button| Rc::ptr_eq(&button.state, &needle)
);
Rc::into_raw(needle); // Prevent dropping
match found {
Some(button) => button.as_mut() as *mut Button,
None => ptr::null_mut(),
}
}
}
#[cfg(test)]
mod test {
use super::*;
@ -133,11 +349,22 @@ pub mod c {
let shared_button = squeek_button_new_with_state(button);
assert_eq!(squeek_button_has_key(shared_button, state), 1);
}
#[test]
fn row_has_button() {
let row = squeek_row_new(0);
let button = squeek_row_create_button(row, 0, 0);
assert_eq!(squeek_row_contains(row, button), 1);
let shared_button = squeek_row_create_button_with_state(row, button);
assert_eq!(squeek_row_contains(row, shared_button), 1);
let row = squeek_row_new(0);
assert_eq!(squeek_row_contains(row, button), 0);
}
}
}
/// The graphical representation of a button
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct Button {
oref: c::OutlineRef,
/// TODO: abolish Option, buttons should be created with bounds fully formed
@ -145,3 +372,8 @@ pub struct Button {
/// current state, shared with other buttons
pub state: Rc<RefCell<KeyState>>,
}
pub struct Row {
buttons: Vec<Box<Button>>,
angle: i32,
}

View File

@ -1,6 +1,7 @@
#[macro_use]
mod bitflags;
mod float_ord;
mod imservice;
mod keyboard;
mod layout;

View File

@ -3,6 +3,7 @@ pub mod c {
use std::os::raw::c_char;
use std::str::Utf8Error;
#[allow(dead_code)]
pub fn as_str(s: &*const c_char) -> Result<Option<&str>, Utf8Error> {
if s.is_null() {
Ok(None)