Compare commits

..

10 Commits

Author SHA1 Message Date
3af10285b7 squeekboard-test-layout: correct dependencies and version for clap argument parsing 2019-12-13 21:37:50 +01:00
53997abc46 added librust-clap-dev to debian/control 2019-12-08 21:14:56 +01:00
34765be22e squeekboard-test-layout: add argument parsing and some more output 2019-12-08 18:29:54 +01:00
8b4c643d3e Merge branch 'german-wide-keyboard-layout' into 'master'
layout: add German wide layout

See merge request Librem5/squeekboard!271
2019-12-07 14:39:43 +00:00
358b25c431 layout: add German wide layout 2019-12-07 14:39:43 +00:00
2749fdb686 Merge branch 'click' into 'master'
Switch layout on click

Closes #157

See merge request Librem5/squeekboard!266
2019-12-05 23:49:37 +00:00
8e7909e877 Merge branch 'stable' into 'master'
keycodes: Sort to eliminate runtime indeterminism

See merge request Librem5/squeekboard!268
2019-12-05 22:20:43 +00:00
afaacd3f68 Merge branch '1.4' into 'master'
Release 1.4.0 "Nacelle"

See merge request Librem5/squeekboard!273
2019-12-02 19:53:20 +00:00
6a164d8119 Release 1.4.0 "Nacelle"
Major changes:

- "text" property for layouts
- adjusting to user's color scheme
2019-12-02 19:40:14 +00:00
3ac4caa3b9 keycodes: Sort to eliminate runtime indeterminism 2019-11-27 16:18:36 +00:00
14 changed files with 279 additions and 349 deletions

View File

@ -4,6 +4,7 @@ version = "0.1.0"
[dependencies] [dependencies]
bitflags = "1.0.*" bitflags = "1.0.*"
clap = "2.32.*"
maplit = "1.0.*" maplit = "1.0.*"
regex = "1.1.*" regex = "1.1.*"
serde = { version = "1.0.*", features = ["derive"] } serde = { version = "1.0.*", features = ["derive"] }

View File

@ -0,0 +1,88 @@
# Maintained by: Mark Müller <markmueller86@gmail.com>
---
bounds: { x: 0, y: 1, width: 540, height: 168 }
outlines:
default:
bounds: { x: 0, y: 0, width: 48, height: 42 }
altline:
bounds: { x: 0, y: 0, width: 81, height: 42 }
wide:
bounds: { x: 0, y: 0, width: 108, height: 42 }
spaceline:
bounds: { x: 0, y: 0, width: 216, height: 42 }
special:
bounds: { x: 0, y: 0, width: 48, height: 42 }
views:
base:
- "q w e r t z u i o p ü"
- "a s d f g h j k l ö ä"
- "Shift_L y x c v b n m BackSpace"
- "show_numbers preferences space , . Return"
upper:
- "Q W E R T Z U I O P Ü"
- "A S D F G H J K L Ö Ä"
- "Shift_L Y X C V B N M BackSpace"
- "show_numbers preferences space ! ? Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # % & - _ + ( ) ß"
- "show_symbols , \" ' : = < > BackSpace"
- "show_letters preferences space , . Return"
symbols:
- "~ ` ´ · © ® ÷ × ¶"
- "€ £ $ ¥ ^ ° * { } |"
- "show_numbers \\ / § π τ [ ] BackSpace"
- "show_letters preferences space , . Return"
eschars:
- "ä è é ö ü Ä È É Ö Ü"
- "à â ê î ô À Â È Î Ô"
- "show_numbers « » ç Ç æ œ ß BackSpace"
- "show_letters preferences space „ “ Return"
buttons:
Shift_L:
action:
locking:
lock_view: "upper"
unlock_view: "base"
outline: "altline"
icon: "key-shift"
BackSpace:
outline: "altline"
icon: "edit-clear-symbolic"
keysym: "BackSpace"
preferences:
action: "show_prefs"
outline: "special"
icon: "keyboard-mode-symbolic"
show_numbers:
action:
set_view: "numbers"
outline: "altline"
label: "123"
show_letters:
action:
set_view: "base"
outline: "altline"
label: "abc"
show_symbols:
action:
set_view: "symbols"
outline: "altline"
label: "*/="
show_eschars:
action:
locking:
lock_view: "eschars"
unlock_view: "base"
outline: "altline"
label: "äÄ"
space:
outline: "spaceline"
text: " "
Return:
outline: "altline"
icon: "key-enter"
keysym: "Return"

7
debian/changelog vendored
View File

@ -1,3 +1,10 @@
squeekboard (1.4.0) amber-phone; urgency=medium
* "text" property in layouts
* Adjusts to user's color scheme
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Mon, 02 Dec 2019 19:37:01 +0000
squeekboard (1.3.2) amber-phone; urgency=medium squeekboard (1.3.2) amber-phone; urgency=medium
* Make sure all key presses get accepted by the compositor * Make sure all key presses get accepted by the compositor

1
debian/control vendored
View File

@ -12,6 +12,7 @@ Build-Depends:
libgtk-3-dev, libgtk-3-dev,
libcroco3-dev, libcroco3-dev,
librust-bitflags-1-dev (>= 1.0), librust-bitflags-1-dev (>= 1.0),
librust-clap-2+default-dev (>= 2.32),
librust-gio+v2-44-dev, librust-gio+v2-44-dev,
librust-glib+v2-44-dev, librust-glib+v2-44-dev,
librust-glib-sys-dev, librust-glib-sys-dev,

View File

@ -1,7 +1,7 @@
project( project(
'squeekboard', 'squeekboard',
'c', 'rust', 'c', 'rust',
version: '1.3.2', version: '1.4.0',
license: 'GPLv3', license: 'GPLv3',
meson_version: '>=0.51.0', meson_version: '>=0.51.0',
default_options: [ default_options: [

View File

@ -6,8 +6,8 @@ use std::ffi::CString;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct KeySym(pub String); pub struct KeySym(pub String);
/// Name of a set of buttons displayed at one time within the layout /// Use to switch layouts
type View = String; type Level = String;
/// Use to send modified keypresses /// Use to send modified keypresses
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -20,12 +20,12 @@ pub enum Modifier {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Action { pub enum Action {
/// Switch to this view /// Switch to this view
SetView(View), SetLevel(Level),
/// Switch to a view and latch /// Switch to a view and latch
LockView { LockLevel {
lock: View, lock: Level,
/// When unlocked by pressing it or emitting a key /// When unlocked by pressing it or emitting a key
unlock: View, unlock: Level,
}, },
/// Set this modifier TODO: release? /// Set this modifier TODO: release?
SetModifier(Modifier), SetModifier(Modifier),

View File

@ -1,8 +1,16 @@
#[macro_use]
extern crate clap;
extern crate rs; extern crate rs;
use rs::tests::check_layout_file; use rs::tests::check_layout_file;
use std::env;
fn main() -> () { fn main() -> () {
check_layout_file(env::args().nth(1).expect("No argument given").as_str()); let matches = clap_app!(test_layout =>
(name: "squeekboard-test-layout")
(about: "Test keyboard layout for errors. Returns OK or an error message containing further information.")
(@arg INPUT: +required "Yaml keyboard layout file to test")
).get_matches();
if check_layout_file(matches.value_of("INPUT").unwrap()) == () {
println!("Test result: OK");
}
} }

View File

@ -504,7 +504,7 @@ fn create_action<H: WarningHandler>(
match submission { match submission {
SubmitData::Action( SubmitData::Action(
Action::SetView(view_name) Action::SetView(view_name)
) => ::action::Action::SetView( ) => ::action::Action::SetLevel(
filter_view_name( filter_view_name(
name, view_name.clone(), &view_names, name, view_name.clone(), &view_names,
warning_handler, warning_handler,
@ -512,7 +512,7 @@ fn create_action<H: WarningHandler>(
), ),
SubmitData::Action(Action::Locking { SubmitData::Action(Action::Locking {
lock_view, unlock_view lock_view, unlock_view
}) => ::action::Action::LockView { }) => ::action::Action::LockLevel {
lock: filter_view_name( lock: filter_view_name(
name, name,
lock_view.clone(), lock_view.clone(),

View File

@ -41,41 +41,23 @@ 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<KeyCode>, pub keycodes: Vec<u32>,
/// 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 { /// Sorts an iterator by converting it to a Vector and back
#[must_use] fn sorted<'a, I: Iterator<Item=&'a str>>(
pub fn into_activated(self) -> KeyState { iter: I
match self.action { ) -> impl Iterator<Item=&'a str> {
Action::LockView { lock: _, unlock: _ } => KeyState { let mut v: Vec<&'a str> = iter.collect();
locked: self.locked ^ true, v.sort();
..self v.into_iter()
},
_ => self,
}
}
#[must_use]
pub fn into_switched(self) -> KeyState {
use self::PressType::*;
KeyState {
pressed: match self.pressed {
Released => Pressed,
Pressed => Released,
},
..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~~
@ -85,7 +67,8 @@ pub fn generate_keycodes<'a, C: IntoIterator<Item=&'a str>>(
key_names: C key_names: C
) -> HashMap<String, u32> { ) -> HashMap<String, u32> {
HashMap::from_iter( HashMap::from_iter(
key_names.into_iter() // sort to remove a source of indeterminism in keycode assignment
sorted(key_names.into_iter())
.map(|name| String::from(name)) .map(|name| String::from(name))
.zip(9..) .zip(9..)
) )

View File

@ -20,7 +20,6 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::{ HashMap, HashSet }; use std::collections::{ HashMap, HashSet };
use std::ffi::CString; use std::ffi::CString;
use std::fmt;
use std::rc::Rc; use std::rc::Rc;
use std::vec::Vec; use std::vec::Vec;
@ -28,11 +27,8 @@ 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 ::util::Stack;
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 {
@ -195,7 +191,10 @@ pub mod c {
pub extern "C" pub extern "C"
fn squeek_layout_get_current_view(layout: *const Layout) -> *const View { fn squeek_layout_get_current_view(layout: *const Layout) -> *const View {
let layout = unsafe { &*layout }; let layout = unsafe { &*layout };
layout.get_current_view().as_ref() as *const View let view_name = layout.current_view.clone();
layout.views.get(&view_name)
.expect("Current view doesn't exist")
.as_ref() as *const View
} }
#[no_mangle] #[no_mangle]
@ -355,25 +354,20 @@ 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
let ui_backend = UIBackend { for key in layout.pressed_keys.clone() {
widget_to_layout,
keyboard: ui_keyboard,
};
let keys = layout.get_pressed_keys();
for key in keys {
let key: &Rc<RefCell<KeyState>> = key.borrow(); let key: &Rc<RefCell<KeyState>> = key.borrow();
seat::release_key( ui::release_key(
layout, layout,
&virtual_keyboard, &virtual_keyboard,
Some(&ui_backend), &widget_to_layout,
time, time,
key, ui_keyboard,
key
); );
} }
} }
/// Release all buttons but don't redraw. /// Release all buittons but don't redraw
/// Use for situations where the UI is gone, e.g. unmapped.
#[no_mangle] #[no_mangle]
pub extern "C" pub extern "C"
fn squeek_layout_release_all_only( fn squeek_layout_release_all_only(
@ -381,18 +375,16 @@ pub mod c {
virtual_keyboard: ZwpVirtualKeyboardV1, // TODO: receive a reference to the backend virtual_keyboard: ZwpVirtualKeyboardV1, // TODO: receive a reference to the backend
time: u32, time: u32,
) { ) {
let mut layout = unsafe { &mut *layout }; let layout = unsafe { &mut *layout };
let virtual_keyboard = VirtualKeyboard(virtual_keyboard); let virtual_keyboard = VirtualKeyboard(virtual_keyboard);
let time = Timestamp(time); // 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();
seat::release_key( layout.release_key(
&mut layout,
&virtual_keyboard, &virtual_keyboard,
None, // don't update anything UI related
time,
&mut key.clone(), &mut key.clone(),
Timestamp(time)
); );
} }
} }
@ -456,12 +448,7 @@ pub mod c {
Point { x: x_widget, y: y_widget } Point { x: x_widget, y: y_widget }
); );
let ui_backend = UIBackend { let pressed = layout.pressed_keys.clone();
keyboard: ui_keyboard,
widget_to_layout,
};
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);
@ -473,16 +460,18 @@ 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 key in pressed { for wrapped_key in pressed {
if Rc::ptr_eq(&state, &key) { let key: &Rc<RefCell<KeyState>> = wrapped_key.borrow();
if Rc::ptr_eq(&state, &wrapped_key.0) {
found = true; found = true;
} else { } else {
seat::release_key( ui::release_key(
layout, layout,
&virtual_keyboard, &virtual_keyboard,
Some(&ui_backend), &widget_to_layout,
time, time,
&key, ui_keyboard,
key,
); );
} }
} }
@ -491,13 +480,15 @@ 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 key in pressed { for wrapped_key in pressed {
seat::release_key( let key: &Rc<RefCell<KeyState>> = wrapped_key.borrow();
ui::release_key(
layout, layout,
&virtual_keyboard, &virtual_keyboard,
Some(&ui_backend), &widget_to_layout,
time, time,
&key, ui_keyboard,
key,
); );
} }
} }
@ -749,32 +740,12 @@ pub enum ArrangementKind {
Wide = 1, Wide = 1,
} }
enum ViewLockKind {
/// Lock gets released the next time something is submitted
OneShot,
/// Lock persists until the lock button is clicked again
Persisting,
}
pub struct ViewLock {
/// Name of the view
view: String,
/// ID of the lock, for unlocking.
/// Ideally unique within the layout
id: String,
kind: ViewLockKind,
}
// TODO: split into sth like // TODO: split into sth like
// Arrangement (views) + details (keymap) + State (keys) // Arrangement (views) + details (keymap) + State (keys)
/// State of the UI, contains the backend as well /// State of the UI, contains the backend as well
pub struct Layout { pub struct Layout {
pub kind: ArrangementKind, pub kind: ArrangementKind,
/// The view after unlocking all locking view locks. pub current_view: String,
pub base_view: String,
/// A stack of views that are locked, later locks with higher indices.
/// Setting a view directly discards all.
pub locked_views: Stack<ViewLock>,
// Views own the actual buttons which have state // Views own the actual buttons which have state
// Maybe they should own UI only, // Maybe they should own UI only,
// and keys should be owned by a dedicated non-UI-State? // and keys should be owned by a dedicated non-UI-State?
@ -784,7 +755,15 @@ 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
// TODO: store clicked buttons per-input point to track dragging. // a Vec would be enough, but who cares, this will be small & fast enough
// 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
@ -793,23 +772,8 @@ pub struct LayoutData {
pub keymap_str: CString, pub keymap_str: CString,
} }
#[derive(Debug)]
struct NoSuchView; struct NoSuchView;
impl fmt::Display for NoSuchView {
fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
write!(out, "No such view")
}
}
struct NoSuchLock;
impl fmt::Display for NoSuchLock {
fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
write!(out, "No such lock")
}
}
// Unfortunately, changes are not atomic due to mutability :( // Unfortunately, changes are not atomic due to mutability :(
// An error will not be recoverable // An error will not be recoverable
// The usage of &mut on Rc<RefCell<KeyState>> doesn't mean anything special. // The usage of &mut on Rc<RefCell<KeyState>> doesn't mean anything special.
@ -818,122 +782,104 @@ impl Layout {
pub fn new(data: LayoutData, kind: ArrangementKind) -> Layout { pub fn new(data: LayoutData, kind: ArrangementKind) -> Layout {
Layout { Layout {
kind, kind,
base_view: "base".to_owned(), current_view: "base".to_owned(),
locked_views: Vec::new(),
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> {
let view_name = self.locked_views.iter().next_back() self.views.get(&self.current_view).expect("Selected nonexistent view")
.map(|lock| &lock.view)
.unwrap_or(&self.base_view);
self.views.get(view_name).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
}
})
}
/// Top oneshot locks can be all removed when a button is pressed
fn get_top_oneshot_locks_idx(&self) -> usize {
let last_persisting_idx = self.locked_views.iter()
.rposition(|lock| match lock.kind {
ViewLockKind::Persisting => true,
ViewLockKind::OneShot => false,
});
match last_persisting_idx {
// don't remove the last persisting item
Some(idx) => idx + 1,
None => 0,
}
}
fn lock_view(&mut self, view: String, id: String)
-> Result<(), NoSuchView>
{
if self.views.contains_key(&view) {
self.locked_views.push(ViewLock {
view,
id,
kind: ViewLockKind::OneShot,
});
Ok(())
} else {
Err(NoSuchView)
}
}
fn unlock_view(&mut self, id: String) -> Result<(), NoSuchLock> {
let lock_idx = self.locked_views.iter()
// find the first occurrence: locking the same key multiple times
// sounds like not something we want to do
.position(|lock| lock.id == id);
lock_idx.map(|idx| self.locked_views.truncate(idx))
.ok_or(NoSuchLock)
} }
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.base_view = view; self.current_view = view;
self.locked_views = Vec::new();
Ok(()) Ok(())
} else { } else {
Err(NoSuchView) Err(NoSuchView)
} }
} }
fn release_key(
&mut self,
virtual_keyboard: &VirtualKeyboard,
mut key: &mut Rc<RefCell<KeyState>>,
time: Timestamp,
) {
if !self.pressed_keys.remove(&::util::Pointer(key.clone())) {
eprintln!("Warning: key {:?} was not pressed", key);
}
virtual_keyboard.switch(
&mut key.borrow_mut(),
PressType::Released,
time,
);
self.set_level_from_press(&mut key);
}
fn press_key( fn press_key(
&mut self, &mut self,
virtual_keyboard: &VirtualKeyboard, virtual_keyboard: &VirtualKeyboard,
key: &mut Rc<RefCell<KeyState>>, key: &mut Rc<RefCell<KeyState>>,
time: Timestamp, time: Timestamp,
) { ) {
let mut bkey = key.borrow_mut(); if !self.pressed_keys.insert(::util::Pointer(key.clone())) {
eprintln!("Warning: key {:?} was already pressed", key);
}
virtual_keyboard.switch( virtual_keyboard.switch(
&bkey.keycodes, &mut key.borrow_mut(),
PressType::Pressed, PressType::Pressed,
time, time,
); );
bkey.pressed = PressType::Pressed; }
fn set_level_from_press(&mut self, key: &Rc<RefCell<KeyState>>) {
let keys = self.locked_keys.clone();
for key in &keys {
self.locked_keys.remove(key);
self.set_state_from_press(key.borrow());
}
// Don't handle the same key twice, but handle it at least once,
// because its press is the reason we're here
if !keys.contains(&::util::Pointer(key.clone())) {
self.set_state_from_press(key);
}
}
fn set_state_from_press(&mut self, key: &Rc<RefCell<KeyState>>) {
// 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 {
Action::SetLevel(name) => {
Some(name.clone())
},
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())
},
_ => None,
};
if let Some(view_name) = view_name {
if let Err(_e) = self.set_view(view_name.clone()) {
eprintln!("No such view: {}, ignoring switch", view_name)
};
};
} }
} }
@ -978,7 +924,8 @@ mod procedures {
key: &Rc<RefCell<KeyState>>, key: &Rc<RefCell<KeyState>>,
ui_keyboard: c::EekGtkKeyboard, ui_keyboard: c::EekGtkKeyboard,
) { ) {
for (_row, button) in find_key_paths(&view, key) { let paths = ::layout::procedures::find_key_paths(&view, key);
for (_row, button) in paths {
unsafe { unsafe {
c::procedures::eek_gtk_on_button_released( c::procedures::eek_gtk_on_button_released(
button.as_ref() as *const Button, button.as_ref() as *const Button,
@ -1065,127 +1012,30 @@ mod procedures {
} }
} }
pub struct UIBackend {
widget_to_layout: c::procedures::Transformation,
keyboard: c::EekGtkKeyboard,
}
/// Top level UI procedures /// Top level UI procedures
mod seat { mod ui {
use super::*; use super::*;
/// Only use from release_key for when the view switch is ignored
fn set_all_unlocked(layout: &mut Layout) {
for key in layout.get_locked_keys() {
let mut key = RefCell::borrow_mut(&key);
match &key.action {
Action::LockView { lock: _, unlock: _ } => {},
a => eprintln!(
"BUG: action {:?} was found inside locked keys",
a
),
};
key.locked = false;
}
}
/// Only use from release_key. Changes state of other keys only.
fn handle_set_view(layout: &mut Layout, view_name: String) {
set_all_unlocked(layout);
layout.set_view(view_name.clone())
.map_err(|e|
eprintln!("Bad view {} ({}), ignoring", view_name, e)
).ok();
}
fn handle_unstick_locks(layout: &mut Layout) {
let oneshot_idx = layout.get_top_oneshot_locks_idx();
let (_permalocks, onetimes) = layout.locked_views.split_at(oneshot_idx);
// Convert into a hashmap for easier finding of elements
let onetime_ids = HashSet::<&String>::from_iter(
onetimes.into_iter().map(|lock| &lock.id)
);
for key in layout.get_locked_keys() {
let mut key = RefCell::borrow_mut(&key);
match &key.action {
Action::LockView { lock: _, unlock: id } => {
if onetime_ids.contains(id) {
key.locked = false;
}
},
a => eprintln!(
"BUG: action {:?} was found inside locked keys",
a
),
};
}
layout.locked_views.truncate(oneshot_idx);
}
// TODO: turn into release_button // TODO: turn into release_button
pub fn release_key( pub fn release_key(
layout: &mut Layout, layout: &mut Layout,
virtual_keyboard: &VirtualKeyboard, virtual_keyboard: &VirtualKeyboard,
ui: Option<&UIBackend>, widget_to_layout: &c::procedures::Transformation,
time: Timestamp, time: Timestamp,
rckey: &Rc<RefCell<KeyState>>, ui_keyboard: c::EekGtkKeyboard,
key: &Rc<RefCell<KeyState>>,
) { ) {
let key: KeyState = { layout.release_key(virtual_keyboard, &mut key.clone(), time);
RefCell::borrow(rckey).clone()
};
let action = key.action.clone();
// update
let key = key.into_switched();
let key = match action {
Action::LockView { lock: _, unlock: _ } => key.into_activated(),
_ => key,
};
// process changes
match action {
Action::Submit { text: _, keys: _ } => {
handle_unstick_locks(layout);
virtual_keyboard.switch(
&key.keycodes,
PressType::Released,
time,
);
},
Action::SetView(view) => {
handle_set_view(layout, view)
},
Action::LockView { lock, unlock } => {
// The button that triggered this will be in the right state
// due to commit at the end.
set_all_unlocked(layout);
match key.locked {
true => {
layout.lock_view(lock.clone(), unlock).map_err(|e|
eprintln!("Bad view {} ({}), ignoring", lock, e)
).ok();
},
false => {
layout.unlock_view(unlock.clone()).map_err(|e| {
eprintln!(
"BUG: Bad id {} ({}), resetting locks",
unlock, e
);
layout.set_view("base".into()).unwrap()
}).ok();
},
// TODO: stuck => stick_view(lock, unlock)
}
},
Action::ShowPreferences => if let Some(ui) = &ui {
let view = layout.get_current_view(); let view = layout.get_current_view();
let action = RefCell::borrow(key).action.clone();
if let Action::ShowPreferences = action {
let paths = ::layout::procedures::find_key_paths( let paths = ::layout::procedures::find_key_paths(
view, &rckey view, key
); );
// Getting first item will cause mispositioning // getting first item will cause mispositioning
// with more than one button with the same key // with more than one button with the same key
// on the keyboard. // on the keyboard
if let Some((row, button)) = paths.get(0) { if let Some((row, button)) = paths.get(0) {
let bounds = ::layout::procedures::get_button_bounds( let bounds = ::layout::procedures::get_button_bounds(
view, row, button view, row, button
@ -1194,21 +1044,13 @@ mod seat {
c::Bounds { x: 0f64, y: 0f64, width: 0f64, height: 0f64 } c::Bounds { x: 0f64, y: 0f64, width: 0f64, height: 0f64 }
}); });
::popover::show( ::popover::show(
ui.keyboard, ui_keyboard,
ui.widget_to_layout.reverse_bounds(bounds) widget_to_layout.reverse_bounds(bounds)
); );
} }
}, }
Action::SetModifier(_) => eprintln!("Modifiers unsupported"),
};
//layout.release_key(virtual_keyboard, &mut key.clone(), time);
if let Some(ui) = ui { procedures::release_ui_buttons(view, key, ui_keyboard);
let view = layout.get_current_view();
procedures::release_ui_buttons(view, &rckey, ui.keyboard);
};
// Commits activated button state changes
RefCell::replace(rckey, key);
} }
} }
@ -1223,7 +1065,7 @@ mod test {
pressed: PressType::Released, pressed: PressType::Released,
locked: false, locked: false,
keycodes: Vec::new(), keycodes: Vec::new(),
action: Action::SetView("default".into()), action: Action::SetLevel("default".into()),
})) }))
} }

View File

@ -13,6 +13,7 @@ const KEYBOARDS: &[(*const str, *const str)] = &[
("us", include_str!("../data/keyboards/us.yaml")), ("us", include_str!("../data/keyboards/us.yaml")),
("us_wide", include_str!("../data/keyboards/us_wide.yaml")), ("us_wide", include_str!("../data/keyboards/us_wide.yaml")),
("de", include_str!("../data/keyboards/de.yaml")), ("de", include_str!("../data/keyboards/de.yaml")),
("de_wide", include_str!("../data/keyboards/de_wide.yaml")),
("el", include_str!("../data/keyboards/el.yaml")), ("el", include_str!("../data/keyboards/el.yaml")),
("es", include_str!("../data/keyboards/es.yaml")), ("es", include_str!("../data/keyboards/es.yaml")),
("fi", include_str!("../data/keyboards/fi.yaml")), ("fi", include_str!("../data/keyboards/fi.yaml")),

View File

@ -1,6 +1,6 @@
/*! Managing the events belonging to virtual-keyboard interface. */ /*! Managing the events belonging to virtual-keyboard interface. */
use ::keyboard::{ KeyCode, PressType }; use ::keyboard::{ KeyState, 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,14 +33,16 @@ impl VirtualKeyboard {
// TODO: split out keyboard state management // TODO: split out keyboard state management
pub fn switch( pub fn switch(
&self, &self,
keycodes: &Vec<KeyCode>, key: &mut KeyState,
action: PressType, action: PressType,
timestamp: Timestamp, timestamp: Timestamp,
) { ) {
let keycodes_count = keycodes.len(); key.pressed = action.clone();
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 (action, keycodes_count) { match (&key.pressed, 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 {

View File

@ -198,9 +198,6 @@ pub trait WarningHandler {
fn handle(&mut self, warning: &str); fn handle(&mut self, warning: &str);
} }
/// TODO: only allow indexing, push, peek, and pop
pub type Stack<T> = Vec<T>;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -49,7 +49,7 @@ endforeach
# and the need to call it manually # and the need to call it manually
foreach layout : [ foreach layout : [
'us', 'us_wide', 'us', 'us_wide',
'de', 'de', 'de_wide',
'el', 'el',
'es', 'es',
'fi', 'fi',