diff --git a/src/data/loading.rs b/src/data/loading.rs index b173477a..cd2ad3a2 100644 --- a/src/data/loading.rs +++ b/src/data/loading.rs @@ -192,7 +192,7 @@ fn iter_layout_sources( } fn load_layout_data(source: DataSource) - -> Result<::layout::LayoutData, LoadError> + -> Result<::layout::LayoutParseData, LoadError> { let handler = logging::Print {}; match source { @@ -217,7 +217,7 @@ fn load_layout_data_with_fallback( kind: ArrangementKind, purpose: ContentPurpose, overlay: Option<&str>, -) -> (ArrangementKind, layout::LayoutData) { +) -> (ArrangementKind, layout::LayoutParseData) { // Build the path to the right keyboard layout subdirectory let path = env::var_os("SQUEEKBOARD_KEYBOARDSDIR") diff --git a/src/data/parsing.rs b/src/data/parsing.rs index 820362cc..a1d84e44 100644 --- a/src/data/parsing.rs +++ b/src/data/parsing.rs @@ -4,12 +4,10 @@ /*! Parsing of the data files containing layouts */ -use std::cell::RefCell; use std::collections::{ HashMap, HashSet }; use std::ffi::CString; use std::fs; use std::path::PathBuf; -use std::rc::Rc; use std::vec::Vec; use xkbcommon::xkb; @@ -17,13 +15,11 @@ use xkbcommon::xkb; use super::{ Error, LoadError }; use ::action; -use ::keyboard::{ - KeyState, PressType, - generate_keymaps, generate_keycodes, KeyCode, FormattingError +use crate::keyboard::{ + Key, generate_keymaps, generate_keycodes, KeyCode, FormattingError }; use ::layout; use ::logging; -use ::util::hash_map_map; use ::resources; // traits, derives @@ -157,7 +153,7 @@ impl Layout { } pub fn build(self, mut warning_handler: H) - -> (Result<::layout::LayoutData, FormattingError>, H) + -> (Result<::layout::LayoutParseData, FormattingError>, H) { let button_names = self.views.values() .flat_map(|rows| { @@ -183,7 +179,7 @@ impl Layout { extract_symbol_names(&button_actions) ); - let button_states = HashMap::::from_iter( + let button_states = HashMap::::from_iter( button_actions.into_iter().map(|(name, action)| { let keycodes = match &action { ::action::Action::Submit { text: _, keys } => { @@ -208,8 +204,7 @@ impl Layout { }; ( name.into(), - KeyState { - pressed: PressType::Released, + Key { keycodes, action, } @@ -222,20 +217,14 @@ impl Layout { Ok(v) => v, }; - let button_states_cache = hash_map_map( - button_states, - |name, state| {( - name, - Rc::new(RefCell::new(state)) - )} - ); + let button_states_cache = button_states; let views: Vec<_> = self.views.iter() .map(|(name, view)| { let rows = view.iter().map(|row| { let buttons = row.split_ascii_whitespace() .map(|name| { - Box::new(create_button( + create_button( &self.buttons, &self.outlines, name, @@ -243,7 +232,7 @@ impl Layout { .expect("Button state not created") .clone(), &mut warning_handler, - )) + ) }); layout::Row::new( add_offsets( @@ -279,7 +268,7 @@ impl Layout { }; ( - Ok(::layout::LayoutData { + Ok(layout::LayoutParseData { views: views, keymaps: keymaps.into_iter().map(|keymap_str| CString::new(keymap_str) @@ -461,7 +450,7 @@ fn create_button( button_info: &HashMap, outlines: &HashMap, name: &str, - state: Rc>, + data: Key, warning_handler: &mut H, ) -> ::layout::Button { let cname = CString::new(name.clone()) @@ -523,7 +512,8 @@ fn create_button( height: outline.height, }, label: label, - state: state, + action: data.action, + keycodes: data.keycodes, } } @@ -677,7 +667,6 @@ mod tests { out.views["base"].1 .get_rows()[0].1 .get_buttons()[0].1 - .state.borrow() .keycodes.len(), 2 ); @@ -694,7 +683,6 @@ mod tests { out.views["base"].1 .get_rows()[0].1 .get_buttons()[0].1 - .state.borrow() .keycodes.len(), 1 ); diff --git a/src/drawing.rs b/src/drawing.rs index 6ccf925e..c792b5de 100644 --- a/src/drawing.rs +++ b/src/drawing.rs @@ -1,11 +1,10 @@ /*! Drawing the UI */ use cairo; -use std::cell::RefCell; use ::action::{ Action, Modifier }; use ::keyboard; -use ::layout::{ Button, Label, LatchedState, Layout }; +use crate::layout::{ Button, ButtonPosition, Label, LatchedState, Layout }; use ::layout::c::{ Bounds, EekGtkKeyboard, Point }; use ::submission::c::Submission as CSubmission; @@ -84,14 +83,21 @@ mod c { let cr = unsafe { cairo::Context::from_raw_none(cr) }; let active_modifiers = submission.get_active_modifiers(); - layout.foreach_visible_button(|offset, button| { - let state = RefCell::borrow(&button.state).clone(); + layout.foreach_visible_button(|offset, button, (row, position_in_row)| { + // TODO: this iterator copies string indices way too much. + // For efficiency, it would be better to draw pressed buttons from the list first, + // and then iterate the rest without having to look up their indices. + let state = layout.state.active_buttons.get(&ButtonPosition { + view: layout.state.current_view.clone(), + row, + position_in_row, + }); let locked = LockedStyle::from_action( - &state.action, + &button.action, &active_modifiers, layout.get_view_latched(), - &layout.current_view, + &layout.state.current_view, ); if state.pressed == keyboard::PressType::Pressed || locked != LockedStyle::Free @@ -99,7 +105,7 @@ mod c { render_button_at_position( renderer, &cr, offset, - button.as_ref(), + button, state.pressed, locked, ); } @@ -116,11 +122,11 @@ mod c { let layout = unsafe { &mut *layout }; let cr = unsafe { cairo::Context::from_raw_none(cr) }; - layout.foreach_visible_button(|offset, button| { + layout.foreach_visible_button(|offset, button, _index| { render_button_at_position( renderer, &cr, offset, - button.as_ref(), + button, keyboard::PressType::Released, LockedStyle::Free, ); diff --git a/src/keyboard.rs b/src/keyboard.rs index da650c55..8c3f5d35 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -1,18 +1,16 @@ /*! State of the emulated keyboard and keys. * Regards the keyboard as if it was composed of switches. */ -use std::cell::RefCell; +use crate::action::Action; +use crate::layout; +use crate::util; use std::collections::HashMap; use std::fmt; use std::io; use std::mem; use std::ptr; -use std::rc::Rc; use std::string::FromUtf8Error; -use ::action::Action; -use ::util; - // Traits use std::io::Write; use std::iter::{ FromIterator, IntoIterator }; @@ -24,7 +22,7 @@ pub enum PressType { } /// The extended, unambiguous layout-keycode -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct KeyCode { pub code: u32, pub keymap_idx: usize, @@ -49,19 +47,30 @@ bitflags!{ } /// When the submitted actions of keys need to be tracked, -/// they need a stable, comparable ID +/// they need a stable, comparable ID. +/// With layout::ButtonPosition, the IDs are unique within layouts. #[derive(Clone, PartialEq)] -pub struct KeyStateId(*const KeyState); +pub struct KeyStateId(layout::ButtonPosition); -#[derive(Debug, Clone)] -pub struct KeyState { - pub pressed: PressType, +impl From<&layout::ButtonPosition> for KeyStateId { + fn from(v: &layout::ButtonPosition) -> Self { + Self(v.clone()) + } +} + +#[derive(Clone)] +pub struct Key { /// A cache of raw keycodes derived from Action::Submit given a keymap pub keycodes: Vec, /// Static description of what the key does when pressed or released pub action: Action, } +#[derive(Debug, Clone)] +pub struct KeyState { + pub pressed: PressType, +} + impl KeyState { #[must_use] pub fn into_released(self) -> KeyState { @@ -78,12 +87,6 @@ impl KeyState { ..self } } - - /// KeyStates instances are the unique identifiers of pressed keys, - /// and the actions submitted with them. - pub fn get_id(keystate: &Rc>) -> KeyStateId { - KeyStateId(keystate.as_ptr() as *const KeyState) - } } /// Sorts an iterator by converting it to a Vector and back diff --git a/src/layout.rs b/src/layout.rs index b472c373..b76e634e 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -17,19 +17,17 @@ * and let the renderer scale and center it within the widget. */ -use std::cell::RefCell; use std::cmp; -use std::collections::{ HashMap, HashSet }; +use std::collections::HashMap; use std::ffi::CString; use std::fmt; -use std::rc::Rc; use std::vec::Vec; use crate::action::Action; use crate::actors; use crate::drawing; use crate::float_ord::FloatOrd; -use crate::keyboard::KeyState; +use crate::keyboard::{KeyState, KeyCode, PressType}; use crate::logging; use crate::popover; use crate::receiver; @@ -39,7 +37,6 @@ use crate::util::find_max_double; use crate::imservice::ContentPurpose; // Traits -use std::borrow::Borrow; use crate::logging::Warn; /// Gathers stuff defined in C or called by C @@ -182,7 +179,7 @@ pub mod c { allocation_height: f64, ) -> Transformation { let layout = unsafe { &*layout }; - layout.calculate_transformation(Size { + layout.shape.calculate_transformation(Size { width: allocation_width, height: allocation_height, }) @@ -192,14 +189,14 @@ pub mod c { pub extern "C" fn squeek_layout_get_kind(layout: *const Layout) -> u32 { let layout = unsafe { &*layout }; - layout.kind.clone() as u32 + layout.shape.kind.clone() as u32 } #[no_mangle] pub extern "C" fn squeek_layout_get_purpose(layout: *const Layout) -> u32 { let layout = unsafe { &*layout }; - layout.purpose.clone() as u32 + layout.shape.purpose.clone() as u32 } #[no_mangle] @@ -238,15 +235,16 @@ pub mod c { // The list must be copied, // because it will be mutated in the loop - for key in layout.pressed_keys.clone() { - let key: &Rc> = key.borrow(); + let pressed_buttons + = layout.state.active_buttons.clone(); + for (button, _key_state) in pressed_buttons.iter_pressed() { seat::handle_release_key( layout, &mut submission, Some(&ui_backend), time, Some((&popover_state, app_state.clone())), - key, + button, ); } drawing::queue_redraw(ui_keyboard); @@ -265,15 +263,15 @@ pub mod c { let mut submission = submission.borrow_mut(); // The list must be copied, // because it will be mutated in the loop - for key in layout.pressed_keys.clone() { - let key: &Rc> = key.borrow(); + let pressed_buttons = layout.state.active_buttons.clone(); + for (button, _key_state) in pressed_buttons.iter_pressed() { seat::handle_release_key( layout, &mut submission, None, // don't update UI Timestamp(time), None, // don't switch layouts - &mut key.clone(), + button, ); } } @@ -295,15 +293,19 @@ pub mod c { Point { x: x_widget, y: y_widget } ); - let state = layout.find_button_by_position(point) - .map(|place| place.button.state.clone()); + let index = layout.find_index_by_position(point); - if let Some(state) = state { + if let Some((row, position_in_row)) = index { + let button = ButtonPosition { + view: layout.state.current_view.clone(), + row, + position_in_row, + }; seat::handle_press_key( layout, &mut submission, Timestamp(time), - &state, + &button, ); // maybe TODO: draw on the display buffer here drawing::queue_redraw(ui_keyboard); @@ -344,21 +346,19 @@ pub mod c { Point { x: x_widget, y: y_widget } ); - let pressed = layout.pressed_keys.clone(); - let button_info = { - let place = layout.find_button_by_position(point); - place.map(|place| {( - place.button.state.clone(), - place.button.clone(), - place.offset, - )}) - }; + let pressed_buttons = layout.state.active_buttons.clone(); + let pressed_buttons = pressed_buttons.iter_pressed(); + let button_info = layout.find_index_by_position(point); - if let Some((state, _button, _view_position)) = button_info { + if let Some((row, position_in_row)) = button_info { + let current_pos = ButtonPosition { + view: layout.state.current_view.clone(), + row, + position_in_row, + }; let mut found = false; - for wrapped_key in pressed { - let key: &Rc> = wrapped_key.borrow(); - if Rc::ptr_eq(&state, &wrapped_key.0) { + for (button, _key_state) in pressed_buttons { + if button == ¤t_pos { found = true; } else { seat::handle_release_key( @@ -367,16 +367,21 @@ pub mod c { Some(&ui_backend), time, Some((&popover_state, app_state.clone())), - key, + button, ); } } if !found { + let button = ButtonPosition { + view: layout.state.current_view.clone(), + row, + position_in_row, + }; seat::handle_press_key( layout, &mut submission, time, - &state, + &button, ); // maybe TODO: draw on the display buffer here unsafe { @@ -384,15 +389,14 @@ pub mod c { } } } else { - for wrapped_key in pressed { - let key: &Rc> = wrapped_key.borrow(); + for (button, _key_state) in pressed_buttons { seat::handle_release_key( layout, &mut submission, Some(&ui_backend), time, Some((&popover_state, app_state.clone())), - key, + button, ); } } @@ -424,11 +428,6 @@ pub mod c { } } -pub struct ButtonPlace<'a> { - button: &'a Button, - offset: c::Point, -} - #[derive(Debug, Clone, PartialEq)] pub struct Size { pub width: f64, @@ -443,8 +442,8 @@ pub enum Label { IconName(CString), } -/// The graphical representation of a button -#[derive(Clone, Debug)] +/// The definition of an interactive button +#[derive(Clone, Debug, PartialEq)] pub struct Button { /// ID string, e.g. for CSS pub name: CString, @@ -453,8 +452,11 @@ pub struct Button { pub size: Size, /// The name of the visual class applied pub outline_name: CString, - /// current state, shared with other buttons - pub state: Rc>, + // action-related stuff + /// A cache of raw keycodes derived from Action::Submit given a keymap + pub keycodes: Vec, + /// Static description of what the key does when pressed or released + pub action: Action, } impl Button { @@ -466,19 +468,19 @@ impl Button { } } -/// The graphical representation of a row of buttons +/// The representation of a row of buttons #[derive(Clone, Debug)] pub struct Row { /// Buttons together with their offset from the left relative to the row. /// ie. the first button always start at 0. - buttons: Vec<(f64, Box