diff --git a/eek/eek-keyboard.c b/eek/eek-keyboard.c index 5c52fccb..7ff978f9 100644 --- a/eek/eek-keyboard.c +++ b/eek/eek-keyboard.c @@ -1,17 +1,17 @@ -/* +/* * Copyright (C) 2010-2011 Daiki Ueno * Copyright (C) 2010-2011 Red Hat, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. - * + * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA @@ -432,9 +432,9 @@ EekKeyboard *level_keyboard_current(LevelKeyboard *keyboard) } struct GetSectionData { - const struct squeek_button *button; + struct squeek_button *button; EekSection *section; - const struct squeek_key *needle; + struct squeek_key *needle; }; void find_button_in_section(EekElement *element, gpointer user_data) { @@ -449,7 +449,7 @@ void find_button_in_section(EekElement *element, gpointer user_data) { } EekSection *eek_keyboard_get_section(EekKeyboard *keyboard, - const struct squeek_button *button) { + struct squeek_button *button) { struct GetSectionData data = { .button = button, .section = NULL, @@ -473,7 +473,7 @@ void find_key_in_section(EekElement *element, gpointer user_data) { // TODO: return multiple struct button_place eek_keyboard_get_button_by_state(EekKeyboard *keyboard, - const struct squeek_key *key) { + struct squeek_key *key) { struct GetSectionData data = { .section = NULL, .button = NULL, @@ -482,7 +482,7 @@ struct button_place eek_keyboard_get_button_by_state(EekKeyboard *keyboard, eek_container_foreach_child(EEK_CONTAINER(keyboard), find_key_in_section, &data); struct button_place ret = { .section = data.section, - .button = (struct squeek_button*)data.button, + .button = data.button, }; return ret; } diff --git a/eek/eek-keyboard.h b/eek/eek-keyboard.h index 652a99e7..c152e72a 100644 --- a/eek/eek-keyboard.h +++ b/eek/eek-keyboard.h @@ -158,7 +158,7 @@ EekSection *eek_keyboard_create_section (EekKeyboard *keyboard); EekSection *eek_keyboard_get_section (EekKeyboard *keyboard, - const struct squeek_button *button); + struct squeek_button *button); struct squeek_button *eek_keyboard_find_button_by_name(LevelKeyboard *keyboard, const gchar *name); @@ -169,7 +169,7 @@ struct button_place { }; struct button_place eek_keyboard_get_button_by_state(EekKeyboard *keyboard, - const struct squeek_key *key); + struct squeek_key *key); EekOutline *level_keyboard_get_outline (LevelKeyboard *keyboard, @@ -196,5 +196,9 @@ void level_keyboard_free(LevelKeyboard *self); EekSection * eek_keyboard_real_create_section (EekKeyboard *self); +struct squeek_row * +eek_keyboard_real_create_row (EekKeyboard *self); + + G_END_DECLS #endif /* EEK_KEYBOARD_H */ diff --git a/eek/eek-renderer.c b/eek/eek-renderer.c index 64f244b5..7e39c8ff 100644 --- a/eek/eek-renderer.c +++ b/eek/eek-renderer.c @@ -93,10 +93,9 @@ struct _CreateKeyboardSurfaceCallbackData { typedef struct _CreateKeyboardSurfaceCallbackData CreateKeyboardSurfaceCallbackData; static void -create_keyboard_surface_button_callback (gpointer item, +create_keyboard_surface_button_callback (struct squeek_button *button, gpointer user_data) { - struct squeek_button *button = item; CreateKeyboardSurfaceCallbackData *data = user_data; EekBounds bounds = squeek_button_get_bounds(button); @@ -978,10 +977,9 @@ sign (EekPoint *p1, EekPoint *p2, EekPoint *p3) } static void -find_button_by_position_key_callback (gpointer item, +find_button_by_position_key_callback (struct squeek_button *button, gpointer user_data) { - struct squeek_button *button = item; FindKeyByPositionCallbackData *data = user_data; if (data->button) { return; diff --git a/eek/eek-section.c b/eek/eek-section.c index 25d7dadd..a958e7ae 100644 --- a/eek/eek-section.c +++ b/eek/eek-section.c @@ -44,36 +44,11 @@ enum { typedef struct _EekSectionPrivate { - gint angle; - EekModifierType modifiers; - GPtrArray *buttons; // struct squeek_button* + struct squeek_row *row; } EekSectionPrivate; G_DEFINE_TYPE_WITH_PRIVATE (EekSection, eek_section, EEK_TYPE_ELEMENT) -struct squeek_button* -eek_section_create_button (EekSection *self, - const gchar *name, - guint keycode, - guint oref) -{ - struct squeek_button *button = squeek_button_new(keycode, oref); - g_return_val_if_fail (button, NULL); - EekSectionPrivate *priv = eek_section_get_instance_private (self); - g_ptr_array_add(priv->buttons, button); - return button; -} - -struct squeek_button *eek_section_create_button_with_state(EekSection *self, - const gchar *name, - struct squeek_button *source) { - struct squeek_button *button = squeek_button_new_with_state(source); - g_return_val_if_fail (button, NULL); - EekSectionPrivate *priv = eek_section_get_instance_private (self); - g_ptr_array_add(priv->buttons, button); - return button; -} - static void eek_section_finalize (GObject *object) { @@ -143,7 +118,7 @@ static void eek_section_init (EekSection *self) { EekSectionPrivate *priv = eek_section_get_instance_private (self); - priv->buttons = g_ptr_array_new(); + priv->row = squeek_row_new(0); } /** @@ -161,10 +136,7 @@ eek_section_set_angle (EekSection *section, EekSectionPrivate *priv = eek_section_get_instance_private (section); - if (priv->angle != angle) { - priv->angle = angle; - g_object_notify (G_OBJECT(section), "angle"); - } + squeek_row_set_angle(priv->row, angle); } /** @@ -180,7 +152,16 @@ eek_section_get_angle (EekSection *section) EekSectionPrivate *priv = eek_section_get_instance_private (section); - return priv->angle; + return squeek_row_get_angle(priv->row); +} + +struct squeek_row * +eek_section_get_row (EekSection *section) +{ + g_return_val_if_fail (EEK_IS_SECTION(section), NULL); + + EekSectionPrivate *priv = eek_section_get_instance_private (section); + return priv->row; } const double keyspacing = 4.0; @@ -191,14 +172,7 @@ struct keys_info { double biggest_height; }; -/// Set button size to match the outline. Reset position -static void -buttonsizer(gpointer item, gpointer user_data) -{ - struct squeek_button *button = item; - - LevelKeyboard *keyboard = user_data; - uint oref = squeek_button_get_oref(button); +EekBounds eek_get_outline_size(LevelKeyboard *keyboard, uint32_t oref) { EekOutline *outline = level_keyboard_get_outline (keyboard, oref); if (outline && outline->num_points > 0) { double minx = outline->points[0].x; @@ -225,81 +199,47 @@ buttonsizer(gpointer item, gpointer user_data) .x = 0, .y = 0, }; - squeek_button_set_bounds(button, key_bounds); + return key_bounds; } + EekBounds bounds = {0, 0, 0, 0}; + return bounds; } -static void -buttoncounter (gpointer item, gpointer user_data) -{ - struct squeek_button *button = item; - struct keys_info *data = user_data; - data->count++; - EekBounds key_bounds = squeek_button_get_bounds(button); - data->total_width += key_bounds.width; - if (key_bounds.height > data->biggest_height) { - data->biggest_height = key_bounds.height; - } -} - -static void -buttonplacer(gpointer item, gpointer user_data) -{ - struct squeek_button *button = item; - double *current_offset = user_data; - EekBounds key_bounds = squeek_button_get_bounds(button); - key_bounds.x = *current_offset; - key_bounds.y = 0; - squeek_button_set_bounds(button, key_bounds); - *current_offset += key_bounds.width + keyspacing; +void eek_section_set_bounds(EekSection *section, EekBounds bounds) { + eek_element_set_bounds(EEK_ELEMENT(section), &bounds); } void eek_section_place_keys(EekSection *section, LevelKeyboard *keyboard) { EekSectionPrivate *priv = eek_section_get_instance_private (section); - - g_ptr_array_foreach(priv->buttons, buttonsizer, keyboard); - - struct keys_info keyinfo = {0}; - g_ptr_array_foreach(priv->buttons, buttoncounter, &keyinfo); - + EekBounds section_size = squeek_row_place_keys(priv->row, keyboard); EekBounds section_bounds = {0}; eek_element_get_bounds(EEK_ELEMENT(section), §ion_bounds); - - double key_offset = (section_bounds.width - (keyinfo.total_width + (keyinfo.count - 1) * keyspacing)) / 2; - g_ptr_array_foreach(priv->buttons, buttonplacer, &key_offset); - - section_bounds.height = keyinfo.biggest_height; + // FIXME: do centering of each section based on keyboard dimensions, + // one level up the iterators + // now centering by comparing previous width to the new, calculated one + section_bounds.x = (section_bounds.width - section_size.width) / 2; + section_bounds.width = section_size.width; + section_bounds.height = section_size.height; eek_element_set_bounds(EEK_ELEMENT(section), §ion_bounds); } void eek_section_foreach (EekSection *section, - GFunc func, + ButtonCallback func, gpointer user_data) { EekSectionPrivate *priv = eek_section_get_instance_private (section); - g_ptr_array_foreach(priv->buttons, func, user_data); + squeek_row_foreach(priv->row, func, user_data); } gboolean eek_section_find(EekSection *section, - const struct squeek_button *button) { + struct squeek_button *button) { EekSectionPrivate *priv = eek_section_get_instance_private (section); - return g_ptr_array_find(priv->buttons, button, NULL); -} - -static gboolean button_has_key(gconstpointer buttonptr, gconstpointer keyptr) { - const struct squeek_button *button = buttonptr; - const struct squeek_key *key = keyptr; - return squeek_button_has_key((struct squeek_button*)button, key) != 0; + return squeek_row_contains(priv->row, button) != 0; } struct squeek_button *eek_section_find_key(EekSection *section, - const struct squeek_key *key) { + struct squeek_key *key) { EekSectionPrivate *priv = eek_section_get_instance_private (section); - guint index; - gboolean ret = g_ptr_array_find_with_equal_func(priv->buttons, key, button_has_key, &index); - if (ret) { - return g_ptr_array_index(priv->buttons, index); - } - return NULL;// TODO: store keys in locked_keys, pressed_keys + return squeek_row_find_key(priv->row, key); } diff --git a/eek/eek-section.h b/eek/eek-section.h index 84375b17..12397f00 100644 --- a/eek/eek-section.h +++ b/eek/eek-section.h @@ -64,6 +64,8 @@ GType eek_section_get_type (void) G_GNUC_CONST; void eek_section_set_angle (EekSection *section, gint angle); gint eek_section_get_angle (EekSection *section); +struct squeek_row * +eek_section_get_row (EekSection *section); struct squeek_button *eek_section_create_button (EekSection *section, const gchar *name, @@ -73,13 +75,13 @@ struct squeek_button *eek_section_create_button_with_state(EekSection *self, struct squeek_button *source); void eek_section_place_keys (EekSection *section, LevelKeyboard *keyboard); void eek_section_foreach (EekSection *section, - GFunc func, + ButtonCallback func, gpointer user_data); gboolean eek_section_find(EekSection *section, - const struct squeek_button *button); + struct squeek_button *button); struct squeek_button *eek_section_find_key(EekSection *section, - const struct squeek_key *key); + struct squeek_key *key); G_END_DECLS #endif /* EEK_SECTION_H */ diff --git a/eek/eek-xml-layout.c b/eek/eek-xml-layout.c index 44e669f1..316ebe9d 100644 --- a/eek/eek-xml-layout.c +++ b/eek/eek-xml-layout.c @@ -235,6 +235,7 @@ struct _GeometryParseData { EekKeyboard **views; guint view_idx; EekSection *section; + struct squeek_row *row; gint num_rows; EekOrientation orientation; gdouble corner_radius; @@ -375,6 +376,7 @@ geometry_start_element_callback (GMarkupParseContext *pcontext, if (g_strcmp0 (element_name, "section") == 0) { data->section = eek_keyboard_real_create_section (data->views[data->view_idx]); + data->row = eek_section_get_row(data->section); attribute = get_attribute (attribute_names, attribute_values, "id"); if (attribute != NULL) @@ -384,7 +386,7 @@ geometry_start_element_callback (GMarkupParseContext *pcontext, if (attribute != NULL) { gint angle; angle = strtol (attribute, NULL, 10); - eek_section_set_angle (data->section, angle); + squeek_row_set_angle (data->row, angle); } goto out; @@ -566,15 +568,12 @@ geometry_end_element_callback (GMarkupParseContext *pcontext, guint oref = GPOINTER_TO_UINT(g_hash_table_lookup(data->keyname_oref_hash, name)); // default value gives idx 0, which is guaranteed to be occupied - button = eek_section_create_button (data->section, - name, - keycode, - oref); + button = squeek_row_create_button (data->row, keycode, oref); g_hash_table_insert (data->name_button_hash, g_strdup(name), button); } else { - struct squeek_button *new_button = eek_section_create_button_with_state(data->section, name, button); + struct squeek_button *new_button = squeek_row_create_button_with_state(data->row, button); if (!new_button) { g_set_error (error, G_MARKUP_ERROR, @@ -586,6 +585,7 @@ geometry_end_element_callback (GMarkupParseContext *pcontext, } data->section = NULL; + data->row = NULL; data->num_rows = 0; return; } diff --git a/src/float_ord.rs b/src/float_ord.rs new file mode 100644 index 00000000..c27ac5e9 --- /dev/null +++ b/src/float_ord.rs @@ -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 + * 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(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 { + 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(&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(v: &mut [T]) where FloatOrd: Ord { + let v_: &mut [FloatOrd] = 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: 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()); + } +} diff --git a/src/layout.h b/src/layout.h index 9b308025..2cc3fd7a 100644 --- a/src/layout.h +++ b/src/layout.h @@ -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 diff --git a/src/layout.rs b/src/layout.rs index 711d4e56..e44a0e3b 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -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> = 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>, } + +pub struct Row { + buttons: Vec>, + angle: i32, +} diff --git a/src/lib.rs b/src/lib.rs index f6b3b319..e5620f7c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ #[macro_use] mod bitflags; +mod float_ord; mod imservice; mod keyboard; mod layout; diff --git a/src/util.rs b/src/util.rs index e2846b31..53290e7a 100644 --- a/src/util.rs +++ b/src/util.rs @@ -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, Utf8Error> { if s.is_null() { Ok(None) diff --git a/tests/eek-simple-test.c b/tests/eek-simple-test.c index de2a491b..72cdd391 100644 --- a/tests/eek-simple-test.c +++ b/tests/eek-simple-test.c @@ -31,9 +31,11 @@ test_create (void) keyboard = g_object_new (EEK_TYPE_KEYBOARD, NULL); section = eek_keyboard_real_create_section (keyboard); g_assert (EEK_IS_SECTION(section)); - button0 = eek_section_create_button (section, "key0", 1, 0); + struct squeek_row *row = eek_section_get_row(section); + g_assert (row); + button0 = squeek_row_create_button (row, 1, 0); g_assert (button0); - button1 = eek_section_create_button (section, "key1", 2, 0); + button1 = squeek_row_create_button (row, 2, 0); g_assert (button1); }