diff --git a/eek/eek-key.c b/eek/eek-key.c index cadb8ac4..4268f22f 100644 --- a/eek/eek-key.c +++ b/eek/eek-key.c @@ -31,6 +31,7 @@ #include "eek-section.h" #include "eek-keyboard.h" +#include "src/keyboard.h" #include "src/symbol.h" #include "eek-key.h" @@ -53,9 +54,8 @@ static guint signals[LAST_SIGNAL] = { 0, }; typedef struct _EekKeyPrivate { guint keycode; - struct squeek_symbols *symbols; gulong oref; // UI outline reference - gboolean is_pressed; + struct squeek_key *state; gboolean is_locked; } EekKeyPrivate; @@ -89,7 +89,7 @@ eek_key_finalize (GObject *object) EekKey *self = EEK_KEY (object); EekKeyPrivate *priv = eek_key_get_instance_private (self); - squeek_symbols_free (priv->symbols); + squeek_key_free (priv->state); G_OBJECT_CLASS (eek_key_parent_class)->finalize (object); } @@ -211,7 +211,7 @@ static void eek_key_init (EekKey *self) { EekKeyPrivate *priv = eek_key_get_instance_private (self); - priv->symbols = squeek_symbols_new (); + priv->state = squeek_key_new (0); } /** @@ -253,23 +253,6 @@ eek_key_get_keycode (EekKey *key) return priv->keycode; } -/** - * eek_key_get_symbol_matrix: - * @key: an #EekKey - * - * Get the symbol matrix of @key. - * Returns: (transfer none): #EekSymbolMatrix or %NULL - */ -struct squeek_symbols * -eek_key_get_symbol_matrix (EekKey *key) -{ - g_return_val_if_fail (EEK_IS_KEY(key), NULL); - - EekKeyPrivate *priv = eek_key_get_instance_private (key); - - return priv->symbols; -} - /** * eek_key_get_symbol_at_index: * @key: an #EekKey @@ -286,8 +269,8 @@ eek_key_get_symbol_at_index (EekKey *key, gint group, guint level) { - struct squeek_symbols *symbols = eek_key_get_symbol_matrix(key); - return squeek_symbols_get(symbols, level); + EekKeyPrivate *priv = eek_key_get_instance_private (key); + return squeek_key_get_symbol(priv->state, level); } /** @@ -341,7 +324,7 @@ eek_key_is_pressed (EekKey *key) EekKeyPrivate *priv = eek_key_get_instance_private (key); - return priv->is_pressed; + return (bool)squeek_key_is_pressed(priv->state); } /** @@ -366,5 +349,10 @@ void eek_key_set_pressed(EekKey *key, gboolean value) EekKeyPrivate *priv = eek_key_get_instance_private (key); - priv->is_pressed = value; + squeek_key_set_pressed(priv->state, value); +} + +struct squeek_key *eek_key_get_state(EekKey *key) { + EekKeyPrivate *priv = eek_key_get_instance_private (key); + return priv->state; } diff --git a/eek/eek-key.h b/eek/eek-key.h index 799d92bc..1213548e 100644 --- a/eek/eek-key.h +++ b/eek/eek-key.h @@ -58,8 +58,7 @@ GType eek_key_get_type (void) G_GNUC_CONST; void eek_key_set_keycode (EekKey *key, guint keycode); guint eek_key_get_keycode (EekKey *key); -struct squeek_symbols * -eek_key_get_symbol_matrix (EekKey *key); +struct squeek_key *eek_key_get_state(EekKey *key); struct squeek_symbol *eek_key_get_symbol_at_index (EekKey *key, gint group, guint level); diff --git a/eek/eek-keyboard.c b/eek/eek-keyboard.c index 313c37f5..83d40fe7 100644 --- a/eek/eek-keyboard.c +++ b/eek/eek-keyboard.c @@ -763,7 +763,7 @@ eek_keyboard_get_keymap(EekKeyboard *keyboard) // FIXME: free const char *key_str = squeek_key_to_keymap_entry( (char*)key_name, - eek_key_get_symbol_matrix(key) + eek_key_get_state(key) ); current = symbols; symbols = g_strconcat(current, key_str, NULL); diff --git a/eek/eek-renderer.c b/eek/eek-renderer.c index 1e777814..a8d64995 100644 --- a/eek/eek-renderer.c +++ b/eek/eek-renderer.c @@ -86,10 +86,6 @@ static void render_key (EekRenderer *self, cairo_t *cr, EekKey *key, guint level, gboolean active); -static void on_symbol_index_changed (EekKeyboard *keyboard, - gint group, - gint level, - gpointer user_data); struct _CreateKeyboardSurfaceCallbackData { cairo_t *cr; @@ -715,16 +711,6 @@ invalidate (EekRenderer *renderer) } } -static void -on_symbol_index_changed (EekKeyboard *keyboard, - gint group, - gint level, - gpointer user_data) -{ - EekRenderer *renderer = user_data; - invalidate (renderer); -} - EekRenderer * eek_renderer_new (EekKeyboard *keyboard, PangoContext *pcontext, diff --git a/eek/eek-xml-layout.c b/eek/eek-xml-layout.c index 74c327f5..408eb455 100644 --- a/eek/eek-xml-layout.c +++ b/eek/eek-xml-layout.c @@ -30,6 +30,7 @@ #include "eek-keyboard.h" #include "eek-section.h" #include "eek-key.h" +#include "src/keyboard.h" #include "src/symbol.h" #include "squeekboard-resources.h" @@ -748,8 +749,8 @@ symbols_end_element_callback (GMarkupParseContext *pcontext, g_strcmp0 (element_name, "keysym") == 0 || g_strcmp0 (element_name, "text") == 0) { - squeek_symbols_add( - eek_key_get_symbol_matrix(data->key), + squeek_key_add_symbol( + eek_key_get_state(data->key), element_name, text, data->keyval, diff --git a/src/keyboard.h b/src/keyboard.h new file mode 100644 index 00000000..c9929ca2 --- /dev/null +++ b/src/keyboard.h @@ -0,0 +1,19 @@ +#ifndef __KEYBOARD_H +#define __KYBOARD_H + +#include "stdbool.h" +#include "inttypes.h" + +struct squeek_key; + +struct squeek_key *squeek_key_new(uint32_t keycode); +void squeek_key_free(struct squeek_key *key); +void squeek_key_add_symbol(struct squeek_key* key, + const char *element_name, + const char *text, uint32_t keyval, + const char *label, const char *icon, + const char *tooltip); +uint32_t squeek_key_is_pressed(struct squeek_key *key); +void squeek_key_set_pressed(struct squeek_key *key, uint32_t pressed); +struct squeek_symbol *squeek_key_get_symbol(struct squeek_key* key, uint32_t level); +#endif diff --git a/src/keyboard.rs b/src/keyboard.rs new file mode 100644 index 00000000..0618606c --- /dev/null +++ b/src/keyboard.rs @@ -0,0 +1,216 @@ +use std::vec::Vec; + +use super::symbol; + +/// Gathers stuff defined in C or called by C +pub mod c { + use super::*; + use ::util::c::{ as_cstr, into_cstring }; + + use std::ffi::CString; + use std::os::raw::c_char; + + // The following defined in C + #[no_mangle] + extern "C" { + fn eek_keysym_from_name(name: *const c_char) -> u32; + } + + + // The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers + + // TODO: this will receive data from the filesystem, + // so it should handle garbled strings in the future + #[no_mangle] + pub extern "C" + fn squeek_key_new(keycode: u32) -> *mut KeyState { + Box::into_raw(Box::new( + KeyState { + pressed: false, + keycode: keycode, + symbols: Vec::new(), + } + )) + } + + #[no_mangle] + pub extern "C" + fn squeek_key_free(key: *mut KeyState) { + unsafe { Box::from_raw(key) }; // gets dropped + } + + #[no_mangle] + pub extern "C" + fn squeek_key_is_pressed(key: *const KeyState) -> u32{ + let key = unsafe { &*key }; + return key.pressed as u32; + } + + #[no_mangle] + pub extern "C" + fn squeek_key_set_pressed(key: *mut KeyState, pressed: u32) { + let key = unsafe { &mut *key }; + key.pressed = pressed != 0; + } + + // TODO: this will receive data from the filesystem, + // so it should handle garbled strings in the future + #[no_mangle] + pub extern "C" + fn squeek_key_add_symbol( + key: *mut KeyState, + element: *const c_char, + text_raw: *const c_char, keyval: u32, + label: *const c_char, icon: *const c_char, + tooltip: *const c_char, + ) { + let element = as_cstr(&element) + .expect("Missing element name"); + + let text = into_cstring(text_raw) + .unwrap_or_else(|e| { + eprintln!("Text unreadable: {}", e); + None + }) + .and_then(|text| { + if text.as_bytes() == b"" { + None + } else { + Some(text) + } + }); + + let icon = into_cstring(icon) + .unwrap_or_else(|e| { + eprintln!("Icon name unreadable: {}", e); + None + }); + + use symbol::*; + // Only read label if there's no icon + let label = match icon { + Some(icon) => Label::IconName(icon), + None => Label::Text( + into_cstring(label) + .unwrap_or_else(|e| { + eprintln!("Label unreadable: {}", e); + Some(CString::new(" ").unwrap()) + }) + .unwrap_or_else(|| { + eprintln!("Label missing"); + CString::new(" ").unwrap() + }) + ), + }; + + let tooltip = into_cstring(tooltip) + .unwrap_or_else(|e| { + eprintln!("Tooltip unreadable: {}", e); + None + }); + + let symbol = match element.to_bytes() { + b"symbol" => Symbol { + action: Action::Submit { + text: text, + keys: Vec::new(), + }, + label: label, + tooltip: tooltip, + }, + b"keysym" => { + let keysym = XKeySym( + if keyval == 0 { + unsafe { eek_keysym_from_name(text_raw) } + } else { + keyval + } + ); + Symbol { + action: match KeySym::from_u32(keysym.0) { + KeySym::Shift => Action::SetLevel(1), + _ => Action::Submit { + text: text, + keys: vec![keysym], + } + }, + label: label, + tooltip: tooltip, + } + }, + _ => panic!("unsupported element type {:?}", element), + }; + + let key = unsafe { &mut *key }; + key.symbols.push(symbol); + } + + #[no_mangle] + pub extern "C" + fn squeek_key_get_symbol( + key: *const KeyState, index: u32 + ) -> *const symbol::Symbol { + let key = unsafe { &*key }; + let index = index as usize; + &key.symbols[ + if index < key.symbols.len() { index } else { 0 } + ] as *const symbol::Symbol + } + + #[no_mangle] + pub extern "C" + fn squeek_key_to_keymap_entry( + key_name: *const c_char, + key: *const KeyState, + ) -> *const c_char { + let key_name = as_cstr(&key_name) + .expect("Missing key name") + .to_str() + .expect("Bad key name"); + + let key = unsafe { &*key }; + let symbol_names = key.symbols.iter() + .map(|symbol| { + match &symbol.action { + symbol::Action::Submit { text: Some(text), .. } => { + Some( + text.clone() + .into_string().expect("Bad symbol") + ) + }, + _ => None + } + }) + .collect::>(); + + let inner = match symbol_names.len() { + 1 => match &symbol_names[0] { + Some(name) => format!("[ {} ]", name), + _ => format!("[ ]"), + }, + 4 => { + let first = match (&symbol_names[0], &symbol_names[1]) { + (Some(left), Some(right)) => format!("{}, {}", left, right), + _ => format!(""), + }; + let second = match (&symbol_names[2], &symbol_names[3]) { + (Some(left), Some(right)) => format!("{}, {}", left, right), + _ => format!(""), + }; + format!("[ {} ], [ {} ]", first, second) + }, + _ => panic!("Unsupported number of symbols"), + }; + + CString::new(format!(" key <{}> {{ {} }};\n", key_name, inner)) + .expect("Couldn't convert string") + .into_raw() + } +} + +#[derive(Debug)] +pub struct KeyState { + pressed: bool, + keycode: u32, + symbols: Vec, +} diff --git a/src/lib.rs b/src/lib.rs index 5b3168f0..c863f5df 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,4 +2,6 @@ mod bitflags; mod imservice; +mod keyboard; mod symbol; +mod util; diff --git a/src/symbol.h b/src/symbol.h index 98eca502..d186ede4 100644 --- a/src/symbol.h +++ b/src/symbol.h @@ -22,9 +22,5 @@ uint32_t squeek_symbol_get_modifier_mask(struct squeek_symbol* symbol); void squeek_symbol_print(struct squeek_symbol* symbol); -struct squeek_symbols* squeek_symbols_new(); -void squeek_symbols_free(struct squeek_symbols*); -struct squeek_symbol *squeek_symbols_get(struct squeek_symbols*, uint32_t level); - const char* squeek_key_to_keymap_entry(const char *key_name, struct squeek_symbols *symbols); #endif diff --git a/src/symbol.rs b/src/symbol.rs index a57335a3..ae5bcec3 100644 --- a/src/symbol.rs +++ b/src/symbol.rs @@ -1,67 +1,16 @@ -use std::boxed::Box; use std::ffi::CString; /// Gathers stuff defined in C or called by C pub mod c { use super::*; - + use std::ffi::CStr; use std::os::raw::c_char; use std::ptr; - use std::str::Utf8Error; - - - fn as_str(s: &*const c_char) -> Result, Utf8Error> { - if s.is_null() { - Ok(None) - } else { - unsafe {CStr::from_ptr(*s)} - .to_str() - .map(Some) - } - } - - fn as_cstr(s: &*const c_char) -> Option<&CStr> { - if s.is_null() { - None - } else { - Some(unsafe {CStr::from_ptr(*s)}) - } - } - - fn into_cstring(s: *const c_char) -> Result, std::ffi::NulError> { - if s.is_null() { - Ok(None) - } else { - CString::new( - unsafe {CStr::from_ptr(s)}.to_bytes() - ).map(Some) - } - } - - #[cfg(test)] - mod tests { - use super::*; - - #[test] - fn test_null_cstring() { - assert_eq!(into_cstring(ptr::null()), Ok(None)) - } - - #[test] - fn test_null_str() { - assert_eq!(as_str(&ptr::null()), Ok(None)) - } - } // The following defined in C - #[no_mangle] - extern "C" { - fn eek_keysym_from_name(name: *const c_char) -> u32; - } - // Legacy; Will never be used in Rust as a bit field enum ModifierMask { Nothing = 0, @@ -72,98 +21,6 @@ pub mod c { // TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers // Symbols are owned by Rust and will move towards no C manipulation, so it may make sense not to wrap them - - // TODO: this will receive data from the filesystem, - // so it should handle garbled strings in the future - #[no_mangle] - pub extern "C" - fn squeek_symbols_add( - v: *mut Vec, - element: *const c_char, - text_raw: *const c_char, keyval: u32, - label: *const c_char, icon: *const c_char, - tooltip: *const c_char, - ) { - let element = as_cstr(&element) - .expect("Missing element name"); - - let text = into_cstring(text_raw) - .unwrap_or_else(|e| { - eprintln!("Text unreadable: {}", e); - None - }) - .and_then(|text| { - if text.as_bytes() == b"" { - None - } else { - Some(text) - } - }); - - let icon = into_cstring(icon) - .unwrap_or_else(|e| { - eprintln!("Icon name unreadable: {}", e); - None - }); - - // Only read label if there's no icon - let label = match icon { - Some(icon) => Label::IconName(icon), - None => Label::Text( - into_cstring(label) - .unwrap_or_else(|e| { - eprintln!("Label unreadable: {}", e); - Some(CString::new(" ").unwrap()) - }) - .unwrap_or_else(|| { - eprintln!("Label missing"); - CString::new(" ").unwrap() - }) - ), - }; - - let tooltip = into_cstring(tooltip) - .unwrap_or_else(|e| { - eprintln!("Tooltip unreadable: {}", e); - None - }); - - let symbol = match element.to_bytes() { - b"symbol" => Symbol { - action: Action::Submit { - text: text, - keys: Vec::new(), - }, - label: label, - tooltip: tooltip, - }, - b"keysym" => { - let keysym = XKeySym( - if keyval == 0 { - unsafe { eek_keysym_from_name(text_raw) } - } else { - keyval - } - ); - Symbol { - action: match KeySym::from_u32(keysym.0) { - KeySym::Shift => Action::SetLevel(1), - _ => Action::Submit { - text: text, - keys: vec![keysym], - } - }, - label: label, - tooltip: tooltip, - } - }, - _ => panic!("unsupported element type {:?}", element), - }; - - let v = unsafe { &mut *v }; - v.push(symbol); - } - #[no_mangle] pub extern "C" fn squeek_symbol_get_name(symbol: *const Symbol) -> *const c_char { @@ -214,88 +71,17 @@ pub mod c { let symbol = unsafe { &*symbol }; println!("{:?}", symbol); } - - #[no_mangle] - pub extern "C" - fn squeek_symbols_new() -> *const Vec { - Box::into_raw(Box::new(Vec::new())) - } - - #[no_mangle] - pub extern "C" - fn squeek_symbols_get(v: *mut Vec, index: u32) -> *const Symbol { - let v = unsafe { &mut *v }; - let index = index as usize; - &v[ - if index < v.len() { index } else { 0 } - ] as *const Symbol - } - - #[no_mangle] - pub extern "C" - fn squeek_symbols_free(symbols: *mut Vec) { - unsafe { Box::from_raw(symbols) }; // Will just get dropped, together with the contents - } - - #[no_mangle] - pub extern "C" - fn squeek_key_to_keymap_entry( - key_name: *const c_char, - symbols: *const Vec, - ) -> *const c_char { - let key_name = as_cstr(&key_name) - .expect("Missing key name") - .to_str() - .expect("Bad key name"); - let symbols = unsafe { &*symbols }; - let symbol_names = symbols.iter() - .map(|symbol| { - match &symbol.action { - Action::Submit { text: Some(text), .. } => { - Some( - text.clone() - .into_string().expect("Bad symbol") - ) - }, - _ => None - } - }) - .collect::>(); - - let inner = match symbol_names.len() { - 1 => match &symbol_names[0] { - Some(name) => format!("[ {} ]", name), - _ => format!("[ ]"), - }, - 4 => { - let first = match (&symbol_names[0], &symbol_names[1]) { - (Some(left), Some(right)) => format!("{}, {}", left, right), - _ => format!(""), - }; - let second = match (&symbol_names[2], &symbol_names[3]) { - (Some(left), Some(right)) => format!("{}, {}", left, right), - _ => format!(""), - }; - format!("[ {} ], [ {} ]", first, second) - }, - _ => panic!("Unsupported number of symbols"), - }; - - CString::new(format!(" key <{}> {{ {} }};\n", key_name, inner)) - .expect("Couldn't convert string") - .into_raw() - } } /// Just defines some int->identifier mappings for convenience #[derive(Debug)] -enum KeySym { +pub enum KeySym { Unknown = 0, Shift = 0xffe1, } impl KeySym { - fn from_u32(num: u32) -> KeySym { + pub fn from_u32(num: u32) -> KeySym { match num { 0xffe1 => KeySym::Shift, _ => KeySym::Unknown, @@ -304,7 +90,7 @@ impl KeySym { } #[derive(Debug)] -pub struct XKeySym(u32); +pub struct XKeySym(pub u32); #[derive(Debug)] pub enum Label { @@ -344,9 +130,9 @@ pub enum Action { #[derive(Debug)] pub struct Symbol { /// The action that this key performs - action: Action, + pub action: Action, /// Label to display to the user - label: Label, + pub label: Label, // FIXME: is it used? - tooltip: Option, + pub tooltip: Option, } diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 00000000..e2846b31 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,49 @@ +pub mod c { + use std::ffi::{ CStr, CString }; + use std::os::raw::c_char; + use std::str::Utf8Error; + + pub fn as_str(s: &*const c_char) -> Result, Utf8Error> { + if s.is_null() { + Ok(None) + } else { + unsafe {CStr::from_ptr(*s)} + .to_str() + .map(Some) + } + } + + pub fn as_cstr(s: &*const c_char) -> Option<&CStr> { + if s.is_null() { + None + } else { + Some(unsafe {CStr::from_ptr(*s)}) + } + } + + pub fn into_cstring(s: *const c_char) -> Result, std::ffi::NulError> { + if s.is_null() { + Ok(None) + } else { + CString::new( + unsafe {CStr::from_ptr(s)}.to_bytes() + ).map(Some) + } + } + + #[cfg(test)] + mod tests { + use super::*; + use std::ptr; + + #[test] + fn test_null_cstring() { + assert_eq!(into_cstring(ptr::null()), Ok(None)) + } + + #[test] + fn test_null_str() { + assert_eq!(as_str(&ptr::null()), Ok(None)) + } + } +}