layout: Eliminate locked and presed key lists
The lists duplicated a source of truth, and were complicating the code, which can now be separated cleaner by effect areas.
This commit is contained in:
@ -41,16 +41,31 @@ pub enum PressType {
|
|||||||
Pressed = 1,
|
Pressed = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type KeyCode = u32;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct KeyState {
|
pub struct KeyState {
|
||||||
pub pressed: PressType,
|
pub pressed: PressType,
|
||||||
pub locked: bool,
|
pub locked: bool,
|
||||||
/// A cache of raw keycodes derived from Action::Sumbit given a keymap
|
/// A cache of raw keycodes derived from Action::Sumbit given a keymap
|
||||||
pub keycodes: Vec<u32>,
|
pub keycodes: Vec<KeyCode>,
|
||||||
/// Static description of what the key does when pressed or released
|
/// Static description of what the key does when pressed or released
|
||||||
pub action: Action,
|
pub action: Action,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl KeyState {
|
||||||
|
#[must_use]
|
||||||
|
pub fn activate(self) -> KeyState {
|
||||||
|
match self.action {
|
||||||
|
Action::LockLevel { lock: _, unlock: _ } => KeyState {
|
||||||
|
locked: self.locked ^ true,
|
||||||
|
..self
|
||||||
|
},
|
||||||
|
_ => self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Generates a mapping where each key gets a keycode, starting from ~~8~~
|
/// Generates a mapping where each key gets a keycode, starting from ~~8~~
|
||||||
/// HACK: starting from 9, because 8 results in keycode 0,
|
/// HACK: starting from 9, because 8 results in keycode 0,
|
||||||
/// which the compositor likes to discard
|
/// which the compositor likes to discard
|
||||||
|
|||||||
135
src/layout.rs
135
src/layout.rs
@ -27,8 +27,10 @@ use ::action::Action;
|
|||||||
use ::float_ord::FloatOrd;
|
use ::float_ord::FloatOrd;
|
||||||
use ::keyboard::{ KeyState, PressType };
|
use ::keyboard::{ KeyState, PressType };
|
||||||
use ::submission::{ Timestamp, VirtualKeyboard };
|
use ::submission::{ Timestamp, VirtualKeyboard };
|
||||||
|
use ::util;
|
||||||
|
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
|
use std::iter::FromIterator;
|
||||||
|
|
||||||
/// Gathers stuff defined in C or called by C
|
/// Gathers stuff defined in C or called by C
|
||||||
pub mod c {
|
pub mod c {
|
||||||
@ -354,7 +356,8 @@ pub mod c {
|
|||||||
let virtual_keyboard = VirtualKeyboard(virtual_keyboard);
|
let virtual_keyboard = VirtualKeyboard(virtual_keyboard);
|
||||||
// The list must be copied,
|
// The list must be copied,
|
||||||
// because it will be mutated in the loop
|
// because it will be mutated in the loop
|
||||||
for key in layout.pressed_keys.clone() {
|
let keys = layout.get_pressed_keys();
|
||||||
|
for key in keys {
|
||||||
let key: &Rc<RefCell<KeyState>> = key.borrow();
|
let key: &Rc<RefCell<KeyState>> = key.borrow();
|
||||||
ui::release_key(
|
ui::release_key(
|
||||||
layout,
|
layout,
|
||||||
@ -377,9 +380,8 @@ pub mod c {
|
|||||||
) {
|
) {
|
||||||
let layout = unsafe { &mut *layout };
|
let layout = unsafe { &mut *layout };
|
||||||
let virtual_keyboard = VirtualKeyboard(virtual_keyboard);
|
let virtual_keyboard = VirtualKeyboard(virtual_keyboard);
|
||||||
// The list must be copied,
|
|
||||||
// because it will be mutated in the loop
|
for key in layout.get_pressed_keys() {
|
||||||
for key in layout.pressed_keys.clone() {
|
|
||||||
let key: &Rc<RefCell<KeyState>> = key.borrow();
|
let key: &Rc<RefCell<KeyState>> = key.borrow();
|
||||||
layout.release_key(
|
layout.release_key(
|
||||||
&virtual_keyboard,
|
&virtual_keyboard,
|
||||||
@ -448,7 +450,7 @@ pub mod c {
|
|||||||
Point { x: x_widget, y: y_widget }
|
Point { x: x_widget, y: y_widget }
|
||||||
);
|
);
|
||||||
|
|
||||||
let pressed = layout.pressed_keys.clone();
|
let pressed = layout.get_pressed_keys();
|
||||||
let state_place = {
|
let state_place = {
|
||||||
let view = layout.get_current_view();
|
let view = layout.get_current_view();
|
||||||
let place = view.find_button_by_position(point);
|
let place = view.find_button_by_position(point);
|
||||||
@ -460,9 +462,8 @@ pub mod c {
|
|||||||
|
|
||||||
if let Some((mut state, c_place)) = state_place {
|
if let Some((mut state, c_place)) = state_place {
|
||||||
let mut found = false;
|
let mut found = false;
|
||||||
for wrapped_key in pressed {
|
for key in pressed {
|
||||||
let key: &Rc<RefCell<KeyState>> = wrapped_key.borrow();
|
if Rc::ptr_eq(&state, &key) {
|
||||||
if Rc::ptr_eq(&state, &wrapped_key.0) {
|
|
||||||
found = true;
|
found = true;
|
||||||
} else {
|
} else {
|
||||||
ui::release_key(
|
ui::release_key(
|
||||||
@ -471,7 +472,7 @@ pub mod c {
|
|||||||
&widget_to_layout,
|
&widget_to_layout,
|
||||||
time,
|
time,
|
||||||
ui_keyboard,
|
ui_keyboard,
|
||||||
key,
|
&key,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -480,15 +481,14 @@ pub mod c {
|
|||||||
unsafe { eek_gtk_on_button_pressed(c_place, ui_keyboard) };
|
unsafe { eek_gtk_on_button_pressed(c_place, ui_keyboard) };
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for wrapped_key in pressed {
|
for key in pressed {
|
||||||
let key: &Rc<RefCell<KeyState>> = wrapped_key.borrow();
|
|
||||||
ui::release_key(
|
ui::release_key(
|
||||||
layout,
|
layout,
|
||||||
&virtual_keyboard,
|
&virtual_keyboard,
|
||||||
&widget_to_layout,
|
&widget_to_layout,
|
||||||
time,
|
time,
|
||||||
ui_keyboard,
|
ui_keyboard,
|
||||||
key,
|
&key,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -755,15 +755,7 @@ pub struct Layout {
|
|||||||
/// xkb keymap applicable to the contained keys. Unchangeable
|
/// xkb keymap applicable to the contained keys. Unchangeable
|
||||||
pub keymap_str: CString,
|
pub keymap_str: CString,
|
||||||
// Changeable state
|
// Changeable state
|
||||||
// a Vec would be enough, but who cares, this will be small & fast enough
|
// TODO: store clicked buttons per-input point to track dragging.
|
||||||
// TODO: turn those into per-input point *_buttons to track dragging.
|
|
||||||
// The renderer doesn't need the list of pressed keys any more,
|
|
||||||
// because it needs to iterate
|
|
||||||
// through all buttons of the current view anyway.
|
|
||||||
// When the list tracks actual location,
|
|
||||||
// it becomes possible to place popovers and other UI accurately.
|
|
||||||
pub pressed_keys: HashSet<::util::Pointer<RefCell<KeyState>>>,
|
|
||||||
pub locked_keys: HashSet<::util::Pointer<RefCell<KeyState>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A builder structure for picking up layout data from storage
|
/// A builder structure for picking up layout data from storage
|
||||||
@ -785,14 +777,53 @@ impl Layout {
|
|||||||
current_view: "base".to_owned(),
|
current_view: "base".to_owned(),
|
||||||
views: data.views,
|
views: data.views,
|
||||||
keymap_str: data.keymap_str,
|
keymap_str: data.keymap_str,
|
||||||
pressed_keys: HashSet::new(),
|
|
||||||
locked_keys: HashSet::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn get_current_view(&self) -> &Box<View> {
|
fn get_current_view(&self) -> &Box<View> {
|
||||||
self.views.get(&self.current_view).expect("Selected nonexistent view")
|
self.views.get(&self.current_view).expect("Selected nonexistent view")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns all keys matching filter, without duplicates
|
||||||
|
fn get_filtered_keys<F>(&self, pred: F) -> Vec<Rc<RefCell<KeyState>>>
|
||||||
|
where F: Fn(&Box<Button>) -> Option<Rc<RefCell<KeyState>>>
|
||||||
|
{
|
||||||
|
let keys = self.views.iter().flat_map(|(_name, view)| {
|
||||||
|
view.rows.iter().flat_map(|row| {
|
||||||
|
row.buttons.iter().filter_map(|x| pred(x))
|
||||||
|
})
|
||||||
|
});
|
||||||
|
// Key states can be attached to multiple buttons, so duplicates must
|
||||||
|
// be removed
|
||||||
|
let unique: HashSet<util::Pointer<RefCell<KeyState>>>
|
||||||
|
= HashSet::from_iter(
|
||||||
|
keys.map(|key| util::Pointer(key.clone()))
|
||||||
|
);
|
||||||
|
|
||||||
|
unique.into_iter()
|
||||||
|
.map(|ptr| ptr.0)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_pressed_keys(&self) -> Vec<Rc<RefCell<KeyState>>> {
|
||||||
|
self.get_filtered_keys(|button| {
|
||||||
|
let pressed = RefCell::borrow(&button.state).pressed;
|
||||||
|
match pressed {
|
||||||
|
PressType::Pressed => Some(button.state.clone()),
|
||||||
|
PressType::Released => None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_locked_keys(&self) -> Vec<Rc<RefCell<KeyState>>> {
|
||||||
|
self.get_filtered_keys(|button| {
|
||||||
|
let locked = RefCell::borrow(&button.state).locked;
|
||||||
|
match locked {
|
||||||
|
true => Some(button.state.clone()),
|
||||||
|
false => None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn set_view(&mut self, view: String) -> Result<(), NoSuchView> {
|
fn set_view(&mut self, view: String) -> Result<(), NoSuchView> {
|
||||||
if self.views.contains_key(&view) {
|
if self.views.contains_key(&view) {
|
||||||
self.current_view = view;
|
self.current_view = view;
|
||||||
@ -808,14 +839,15 @@ impl Layout {
|
|||||||
mut key: &mut Rc<RefCell<KeyState>>,
|
mut key: &mut Rc<RefCell<KeyState>>,
|
||||||
time: Timestamp,
|
time: Timestamp,
|
||||||
) {
|
) {
|
||||||
if !self.pressed_keys.remove(&::util::Pointer(key.clone())) {
|
{
|
||||||
eprintln!("Warning: key {:?} was not pressed", key);
|
let mut bkey = key.borrow_mut();
|
||||||
|
virtual_keyboard.switch(
|
||||||
|
&bkey.keycodes,
|
||||||
|
PressType::Released,
|
||||||
|
time,
|
||||||
|
);
|
||||||
|
bkey.pressed = PressType::Released;
|
||||||
}
|
}
|
||||||
virtual_keyboard.switch(
|
|
||||||
&mut key.borrow_mut(),
|
|
||||||
PressType::Released,
|
|
||||||
time,
|
|
||||||
);
|
|
||||||
self.set_level_from_press(&mut key);
|
self.set_level_from_press(&mut key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -825,51 +857,44 @@ impl Layout {
|
|||||||
key: &mut Rc<RefCell<KeyState>>,
|
key: &mut Rc<RefCell<KeyState>>,
|
||||||
time: Timestamp,
|
time: Timestamp,
|
||||||
) {
|
) {
|
||||||
if !self.pressed_keys.insert(::util::Pointer(key.clone())) {
|
let mut bkey = key.borrow_mut();
|
||||||
eprintln!("Warning: key {:?} was already pressed", key);
|
|
||||||
}
|
|
||||||
virtual_keyboard.switch(
|
virtual_keyboard.switch(
|
||||||
&mut key.borrow_mut(),
|
&bkey.keycodes,
|
||||||
PressType::Pressed,
|
PressType::Pressed,
|
||||||
time,
|
time,
|
||||||
);
|
);
|
||||||
|
bkey.pressed = PressType::Pressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_level_from_press(&mut self, key: &Rc<RefCell<KeyState>>) {
|
fn set_level_from_press(&mut self, key: &Rc<RefCell<KeyState>>) {
|
||||||
let keys = self.locked_keys.clone();
|
fn activate(layout: &mut Layout, key: &Rc<RefCell<KeyState>>) {
|
||||||
|
let updated = {
|
||||||
|
let keyref = RefCell::borrow(key);
|
||||||
|
keyref.clone().activate()
|
||||||
|
};
|
||||||
|
RefCell::replace(key, updated.clone());
|
||||||
|
layout.set_state_from_press(updated.action.clone(), updated.locked);
|
||||||
|
};
|
||||||
|
|
||||||
|
// unlock all
|
||||||
|
let keys = self.get_locked_keys();
|
||||||
for key in &keys {
|
for key in &keys {
|
||||||
self.locked_keys.remove(key);
|
activate(self, key);
|
||||||
self.set_state_from_press(key.borrow());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't handle the same key twice, but handle it at least once,
|
// Don't handle the same key twice, but handle it at least once,
|
||||||
// because its press is the reason we're here
|
// because its press is the reason we're here
|
||||||
if !keys.contains(&::util::Pointer(key.clone())) {
|
if let None = keys.iter().find(|k| Rc::ptr_eq(k, &key)) {
|
||||||
self.set_state_from_press(key);
|
activate(self, key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_state_from_press(&mut self, key: &Rc<RefCell<KeyState>>) {
|
fn set_state_from_press(&mut self, action: Action, locked: bool) {
|
||||||
// Action should not hold a reference to key,
|
|
||||||
// because key is later borrowed for mutation. So action is cloned.
|
|
||||||
// RefCell::borrow() is covered up by (dyn Borrow)::borrow()
|
|
||||||
// if used like key.borrow() :(
|
|
||||||
let action = RefCell::borrow(key).action.clone();
|
|
||||||
let view_name = match action {
|
let view_name = match action {
|
||||||
Action::SetLevel(name) => {
|
Action::SetLevel(name) => {
|
||||||
Some(name.clone())
|
Some(name.clone())
|
||||||
},
|
},
|
||||||
Action::LockLevel { lock, unlock } => {
|
Action::LockLevel { lock, unlock } => {
|
||||||
let locked = {
|
|
||||||
let mut key = key.borrow_mut();
|
|
||||||
key.locked ^= true;
|
|
||||||
key.locked
|
|
||||||
};
|
|
||||||
|
|
||||||
if locked {
|
|
||||||
self.locked_keys.insert(::util::Pointer(key.clone()));
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(if locked { lock } else { unlock }.clone())
|
Some(if locked { lock } else { unlock }.clone())
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/*! Managing the events belonging to virtual-keyboard interface. */
|
/*! Managing the events belonging to virtual-keyboard interface. */
|
||||||
|
|
||||||
use ::keyboard::{ KeyState, PressType };
|
use ::keyboard::{ KeyCode, PressType };
|
||||||
|
|
||||||
/// Gathers stuff defined in C or called by C
|
/// Gathers stuff defined in C or called by C
|
||||||
pub mod c {
|
pub mod c {
|
||||||
@ -33,16 +33,14 @@ impl VirtualKeyboard {
|
|||||||
// TODO: split out keyboard state management
|
// TODO: split out keyboard state management
|
||||||
pub fn switch(
|
pub fn switch(
|
||||||
&self,
|
&self,
|
||||||
key: &mut KeyState,
|
keycodes: &Vec<KeyCode>,
|
||||||
action: PressType,
|
action: PressType,
|
||||||
timestamp: Timestamp,
|
timestamp: Timestamp,
|
||||||
) {
|
) {
|
||||||
key.pressed = action.clone();
|
let keycodes_count = keycodes.len();
|
||||||
|
for keycode in keycodes.iter() {
|
||||||
let keycodes_count = key.keycodes.len();
|
|
||||||
for keycode in key.keycodes.iter() {
|
|
||||||
let keycode = keycode - 8;
|
let keycode = keycode - 8;
|
||||||
match (&key.pressed, keycodes_count) {
|
match (action, keycodes_count) {
|
||||||
// Pressing a key made out of a single keycode is simple:
|
// Pressing a key made out of a single keycode is simple:
|
||||||
// press on press, release on release.
|
// press on press, release on release.
|
||||||
(_, 1) => unsafe {
|
(_, 1) => unsafe {
|
||||||
|
|||||||
Reference in New Issue
Block a user