Merge remote-tracking branch 'upstream/master' into center

This commit is contained in:
Dorota Czaplejewicz
2020-02-05 10:32:01 +00:00
24 changed files with 310 additions and 87 deletions

View File

@ -45,7 +45,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
keysym: "BackSpace" action: "erase"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "special" outline: "special"

View File

@ -45,7 +45,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
keysym: "BackSpace" action: "erase"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "special" outline: "special"

View File

@ -46,7 +46,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
keysym: "BackSpace" action: "erase"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "altline" outline: "altline"

View File

@ -44,7 +44,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
keysym: "BackSpace" action: "erase"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "default" outline: "default"

View File

@ -39,7 +39,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
keysym: "BackSpace" action: "erase"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "altline" outline: "altline"

View File

@ -46,7 +46,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
keysym: "BackSpace" action: "erase"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "default" outline: "default"

View File

@ -195,7 +195,7 @@ buttons:
BackSpace: BackSpace:
outline: "wide" outline: "wide"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
keysym: "BackSpace" action: erase
Return: Return:
outline: "wide" outline: "wide"
icon: "key-enter" icon: "key-enter"

View File

@ -195,7 +195,7 @@ buttons:
BackSpace: BackSpace:
outline: "wide" outline: "wide"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
keysym: "BackSpace" action: erase
Return: Return:
outline: "wide" outline: "wide"
icon: "key-enter" icon: "key-enter"

View File

@ -39,7 +39,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
keysym: "BackSpace" action: erase
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "altline" outline: "altline"

View File

@ -16,7 +16,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
keysym: "BackSpace" action: erase
space: space:
outline: spaceline outline: spaceline
text: " " text: " "

View File

@ -39,7 +39,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
keysym: "BackSpace" action: erase
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "altline" outline: "altline"

View File

@ -45,7 +45,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
keysym: "BackSpace" action: erase
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "special" outline: "special"

View File

@ -39,9 +39,9 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
keysym: "BackSpace" action: erase
preferences: preferences:
action: "show_prefs" action: show_prefs
outline: "special" outline: "special"
icon: "keyboard-mode-symbolic" icon: "keyboard-mode-symbolic"
show_numbers: show_numbers:

View File

@ -39,7 +39,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
keysym: "BackSpace" action: "erase"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "special" outline: "special"

View File

@ -116,6 +116,7 @@ settings_get_layout(GSettings *settings, char **type, char **layout)
GVariant *inputs = g_settings_get_value(settings, "sources"); GVariant *inputs = g_settings_get_value(settings, "sources");
// current layout is always first // current layout is always first
g_variant_get_child(inputs, 0, "(ss)", type, layout); g_variant_get_child(inputs, 0, "(ss)", type, layout);
g_variant_unref(inputs);
} }
void void
@ -139,9 +140,11 @@ eekboard_context_service_update_layout(EekboardContextService *context, enum squ
switch (priv->purpose) { switch (priv->purpose) {
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NUMBER: case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NUMBER:
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PHONE: case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PHONE:
g_free(keyboard_layout);
keyboard_layout = g_strdup("number"); keyboard_layout = g_strdup("number");
break; break;
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL: case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL:
g_free(keyboard_layout);
keyboard_layout = g_strdup("terminal"); keyboard_layout = g_strdup("terminal");
break; break;
default: default:

View File

@ -31,10 +31,29 @@ pub enum Action {
SetModifier(Modifier), SetModifier(Modifier),
/// Submit some text /// Submit some text
Submit { Submit {
/// Text to submit with input-method /// Text to submit with input-method.
/// If None, then keys are to be submitted instead.
text: Option<CString>, text: Option<CString>,
/// The key events this symbol submits when submitting text is not possible /// The key events this symbol submits when submitting text is not possible
keys: Vec<KeySym>, keys: Vec<KeySym>,
}, },
/// Erase a position behind the cursor
Erase,
ShowPreferences, ShowPreferences,
} }
impl Action {
pub fn is_locked(&self, view_name: &str) -> bool {
match self {
Action::LockView { lock, unlock: _ } => lock == view_name,
_ => false,
}
}
pub fn is_active(&self, view_name: &str) -> bool {
match self {
Action::SetView(view) => view == view_name,
Action::LockView { lock, unlock: _ } => lock == view_name,
_ => false,
}
}
}

View File

@ -15,6 +15,7 @@ use std::vec::Vec;
use xkbcommon::xkb; use xkbcommon::xkb;
use ::action;
use ::keyboard::{ use ::keyboard::{
KeyState, PressType, KeyState, PressType,
generate_keymap, generate_keycodes, FormattingError generate_keymap, generate_keycodes, FormattingError
@ -264,6 +265,9 @@ enum Action {
SetView(String), SetView(String),
#[serde(rename="show_prefs")] #[serde(rename="show_prefs")]
ShowPrefs, ShowPrefs,
/// Remove last character
#[serde(rename="erase")]
Erase,
} }
#[derive(Debug, Clone, Deserialize, PartialEq)] #[derive(Debug, Clone, Deserialize, PartialEq)]
@ -386,13 +390,16 @@ impl Layout {
) )
}).collect() }).collect()
}, },
action::Action::Erase => vec![
*keymap.get("BackSpace")
.expect(&format!("BackSpace missing from keymap")),
],
_ => Vec::new(), _ => Vec::new(),
}; };
( (
name.into(), name.into(),
KeyState { KeyState {
pressed: PressType::Released, pressed: PressType::Released,
locked: false,
keycodes, keycodes,
action, action,
} }
@ -574,6 +581,7 @@ fn create_action<H: logging::Handler>(
SubmitData::Action( SubmitData::Action(
Action::ShowPrefs Action::ShowPrefs
) => ::action::Action::ShowPreferences, ) => ::action::Action::ShowPreferences,
SubmitData::Action(Action::Erase) => action::Action::Erase,
SubmitData::Keysym(keysym) => ::action::Action::Submit { SubmitData::Keysym(keysym) => ::action::Action::Submit {
text: None, text: None,
keys: vec!(::action::KeySym( keys: vec!(::action::KeySym(
@ -605,7 +613,7 @@ fn create_action<H: logging::Handler>(
false => format!("U{:04X}", codepoint as u32), false => format!("U{:04X}", codepoint as u32),
}) })
}).collect(), }).collect(),
} },
} }
} }

View File

@ -37,6 +37,7 @@ mod c {
); );
} }
/// Draws all buttons that are not in the base state
#[no_mangle] #[no_mangle]
pub extern "C" pub extern "C"
fn squeek_layout_draw_all_changed( fn squeek_layout_draw_all_changed(
@ -49,12 +50,13 @@ mod c {
layout.foreach_visible_button(|offset, button| { layout.foreach_visible_button(|offset, button| {
let state = RefCell::borrow(&button.state).clone(); let state = RefCell::borrow(&button.state).clone();
if state.pressed == keyboard::PressType::Pressed || state.locked { let locked = state.action.is_active(&layout.current_view);
if state.pressed == keyboard::PressType::Pressed || locked {
render_button_at_position( render_button_at_position(
renderer, &cr, renderer, &cr,
offset, offset,
button.as_ref(), button.as_ref(),
state.pressed, state.locked, state.pressed, locked,
); );
} }
}) })

View File

@ -49,6 +49,23 @@ void imservice_connect_listeners(struct zwp_input_method_v2 *im, struct imservic
zwp_input_method_v2_add_listener(im, &input_method_listener, imservice); zwp_input_method_v2_add_listener(im, &input_method_listener, imservice);
} }
void
eek_input_method_commit_string(struct zwp_input_method_v2 *zwp_input_method_v2, const char *text)
{
zwp_input_method_v2_commit_string(zwp_input_method_v2, text);
}
void
eek_input_method_delete_surrounding_text(struct zwp_input_method_v2 *zwp_input_method_v2, uint32_t before_length, uint32_t after_length) {
zwp_input_method_v2_delete_surrounding_text(zwp_input_method_v2, before_length, after_length);
};
void
eek_input_method_commit(struct zwp_input_method_v2 *zwp_input_method_v2, uint32_t serial)
{
zwp_input_method_v2_commit(zwp_input_method_v2, serial);
}
/// Declared explicitly because _destroy is inline, /// Declared explicitly because _destroy is inline,
/// making it unavailable in Rust /// making it unavailable in Rust
void imservice_destroy_im(struct zwp_input_method_v2 *im) { void imservice_destroy_im(struct zwp_input_method_v2 *im) {

View File

@ -1,3 +1,8 @@
/*! Manages zwp_input_method_v2 protocol.
*
* Library module.
*/
use std::boxed::Box; use std::boxed::Box;
use std::ffi::CString; use std::ffi::CString;
use std::fmt; use std::fmt;
@ -32,6 +37,9 @@ pub mod c {
fn imservice_destroy_im(im: *mut c::InputMethod); fn imservice_destroy_im(im: *mut c::InputMethod);
#[allow(improper_ctypes)] // IMService will never be dereferenced in C #[allow(improper_ctypes)] // IMService will never be dereferenced in C
pub fn imservice_connect_listeners(im: *mut InputMethod, imservice: *const IMService); pub fn imservice_connect_listeners(im: *mut InputMethod, imservice: *const IMService);
pub fn eek_input_method_commit_string(im: *mut InputMethod, text: *const c_char);
pub fn eek_input_method_delete_surrounding_text(im: *mut InputMethod, before: u32, after: u32);
pub fn eek_input_method_commit(im: *mut InputMethod, serial: u32);
fn eekboard_context_service_set_hint_purpose(state: *const StateManager, hint: u32, purpose: u32); fn eekboard_context_service_set_hint_purpose(state: *const StateManager, hint: u32, purpose: u32);
fn server_context_service_show_keyboard(imservice: *const UIManager); fn server_context_service_show_keyboard(imservice: *const UIManager);
fn server_context_service_hide_keyboard(imservice: *const UIManager); fn server_context_service_hide_keyboard(imservice: *const UIManager);
@ -328,7 +336,7 @@ impl Default for IMProtocolState {
pub struct IMService { pub struct IMService {
/// Owned reference (still created and destroyed in C) /// Owned reference (still created and destroyed in C)
pub im: *const c::InputMethod, pub im: *mut c::InputMethod,
/// Unowned reference. Be careful, it's shared with C at large /// Unowned reference. Be careful, it's shared with C at large
state_manager: *const c::StateManager, state_manager: *const c::StateManager,
/// Unowned reference. Be careful, it's shared with C at large /// Unowned reference. Be careful, it's shared with C at large
@ -340,6 +348,11 @@ pub struct IMService {
serial: Wrapping<u32>, serial: Wrapping<u32>,
} }
pub enum SubmitError {
/// The input method had not been activated
NotActive,
}
impl IMService { impl IMService {
pub fn new( pub fn new(
im: *mut c::InputMethod, im: *mut c::InputMethod,
@ -364,4 +377,51 @@ impl IMService {
} }
imservice imservice
} }
pub fn commit_string(&self, text: &CString) -> Result<(), SubmitError> {
match self.current.active {
true => {
unsafe {
c::eek_input_method_commit_string(self.im, text.as_ptr())
}
Ok(())
},
false => Err(SubmitError::NotActive),
}
}
pub fn delete_surrounding_text(
&self,
before: u32, after: u32,
) -> Result<(), SubmitError> {
match self.current.active {
true => {
unsafe {
c::eek_input_method_delete_surrounding_text(
self.im,
before, after,
)
}
Ok(())
},
false => Err(SubmitError::NotActive),
}
}
pub fn commit(&mut self) -> Result<(), SubmitError> {
match self.current.active {
true => {
unsafe {
c::eek_input_method_commit(self.im, self.serial.0)
}
self.serial += Wrapping(1u32);
Ok(())
},
false => Err(SubmitError::NotActive),
}
}
pub fn is_active(&self) -> bool {
self.current.active
}
} }

View File

@ -1,14 +1,17 @@
/*! State of the emulated keyboard and keys. /*! State of the emulated keyboard and keys.
* Regards the keyboard as if it was composed of switches. */ * Regards the keyboard as if it was composed of switches. */
use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt; use std::fmt;
use std::io; use std::io;
use std::rc::Rc;
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
use ::action::Action; use ::action::Action;
use ::logging; use ::logging;
// Traits
use std::io::Write; use std::io::Write;
use std::iter::{ FromIterator, IntoIterator }; use std::iter::{ FromIterator, IntoIterator };
@ -20,10 +23,14 @@ pub enum PressType {
pub type KeyCode = u32; pub type KeyCode = u32;
/// When the submitted actions of keys need to be tracked,
/// they need a stable, comparable ID
#[derive(PartialEq)]
pub struct KeyStateId(*const KeyState);
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct KeyState { pub struct KeyState {
pub pressed: PressType, pub pressed: PressType,
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<KeyCode>,
/// Static description of what the key does when pressed or released /// Static description of what the key does when pressed or released
@ -31,17 +38,6 @@ pub struct KeyState {
} }
impl KeyState { impl KeyState {
#[must_use]
pub fn into_activated(self) -> KeyState {
match self.action {
Action::LockView { lock: _, unlock: _ } => KeyState {
locked: self.locked ^ true,
..self
},
_ => self,
}
}
#[must_use] #[must_use]
pub fn into_released(self) -> KeyState { pub fn into_released(self) -> KeyState {
KeyState { KeyState {
@ -49,6 +45,12 @@ impl KeyState {
..self ..self
} }
} }
/// KeyStates instances are the unique identifiers of pressed keys,
/// and the actions submitted with them.
pub fn get_id(keystate: &Rc<RefCell<KeyState>>) -> KeyStateId {
KeyStateId(keystate.as_ptr() as *const KeyState)
}
} }
/// Sorts an iterator by converting it to a Vector and back /// Sorts an iterator by converting it to a Vector and back
@ -66,9 +68,10 @@ fn sorted<'a, I: Iterator<Item=&'a str>>(
pub fn generate_keycodes<'a, C: IntoIterator<Item=&'a str>>( pub fn generate_keycodes<'a, C: IntoIterator<Item=&'a str>>(
key_names: C key_names: C
) -> HashMap<String, u32> { ) -> HashMap<String, u32> {
let special_keysyms = ["BackSpace", "Return"].iter().map(|&s| s);
HashMap::from_iter( HashMap::from_iter(
// sort to remove a source of indeterminism in keycode assignment // sort to remove a source of indeterminism in keycode assignment
sorted(key_names.into_iter()) sorted(key_names.into_iter().chain(special_keysyms))
.map(|name| String::from(name)) .map(|name| String::from(name))
.zip(9..) .zip(9..)
) )
@ -95,7 +98,10 @@ impl From<io::Error> for FormattingError {
} }
} }
/// Generates a de-facto single level keymap. TODO: actually drop second level /// Generates a de-facto single level keymap.
// TODO: don't rely on keys and their order,
// but rather on what keysyms and keycodes are in use.
// Iterating actions makes it hard to deduplicate keysyms.
pub fn generate_keymap( pub fn generate_keymap(
keystates: &HashMap::<String, KeyState> keystates: &HashMap::<String, KeyState>
) -> Result<String, FormattingError> { ) -> Result<String, FormattingError> {
@ -110,22 +116,40 @@ pub fn generate_keymap(
)?; )?;
for (name, state) in keystates.iter() { for (name, state) in keystates.iter() {
if let Action::Submit { text: _, keys } = &state.action { match &state.action {
if let 0 = keys.len() { Action::Submit { text: _, keys } => {
log_print!( if let 0 = keys.len() {
logging::Level::Warning, log_print!(
"Key {} has no keysyms", name, logging::Level::Warning,
); "Key {} has no keysyms", name,
}; );
for (named_keysym, keycode) in keys.iter().zip(&state.keycodes) { };
for (named_keysym, keycode) in keys.iter().zip(&state.keycodes) {
write!(
buf,
"
<{}> = {};",
named_keysym.0,
keycode,
)?;
}
},
Action::Erase => {
let mut keycodes = state.keycodes.iter();
write!( write!(
buf, buf,
" "
<{}> = {};", <BackSpace> = {};",
named_keysym.0, keycodes.next().expect("Erase key has no keycode"),
keycode,
)?; )?;
} if let Some(_) = keycodes.next() {
log_print!(
logging::Level::Bug,
"Erase key has multiple keycodes",
);
}
},
_ => {},
} }
} }
@ -137,7 +161,9 @@ pub fn generate_keymap(
xkb_symbols \"squeekboard\" {{ xkb_symbols \"squeekboard\" {{
name[Group1] = \"Letters\"; name[Group1] = \"Letters\";
name[Group2] = \"Numbers/Symbols\";" name[Group2] = \"Numbers/Symbols\";
key <BackSpace> {{ [ BackSpace ] }};"
)?; )?;
for (name, state) in keystates.iter() { for (name, state) in keystates.iter() {
@ -195,7 +221,6 @@ mod tests {
keys: vec!(KeySym("a".into()), KeySym("c".into())), keys: vec!(KeySym("a".into()), KeySym("c".into())),
}, },
keycodes: vec!(9, 10), keycodes: vec!(9, 10),
locked: false,
pressed: PressType::Released, pressed: PressType::Released,
}, },
}).unwrap(); }).unwrap();

View File

@ -629,7 +629,6 @@ pub struct Layout {
// When the list tracks actual location, // When the list tracks actual location,
// it becomes possible to place popovers and other UI accurately. // it becomes possible to place popovers and other UI accurately.
pub pressed_keys: HashSet<::util::Pointer<RefCell<KeyState>>>, 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
@ -661,7 +660,6 @@ impl Layout {
views: data.views, views: data.views,
keymap_str: data.keymap_str, keymap_str: data.keymap_str,
pressed_keys: HashSet::new(), pressed_keys: HashSet::new(),
locked_keys: HashSet::new(),
margins: data.margins, margins: data.margins,
} }
} }
@ -672,7 +670,7 @@ impl Layout {
} }
pub fn get_current_view(&self) -> &View { pub fn get_current_view(&self) -> &View {
&self.get_current_view_position().1 &self.views.get(&self.current_view).expect("Selected nonexistent view").1
} }
fn set_view(&mut self, view: String) -> Result<(), NoSuchView> { fn set_view(&mut self, view: String) -> Result<(), NoSuchView> {
@ -742,6 +740,23 @@ impl Layout {
} }
} }
} }
pub fn get_locked_keys(&self) -> Vec<Rc<RefCell<KeyState>>> {
let mut out = Vec::new();
let view = self.get_current_view();
for (_, row) in &view.get_rows() {
for (_, button) in &row.buttons {
let locked = {
let state = RefCell::borrow(&button.state).clone();
state.action.is_locked(&self.current_view)
};
if locked {
out.push(button.state.clone());
}
}
}
out
}
} }
mod procedures { mod procedures {
@ -868,9 +883,9 @@ mod seat {
#[must_use] #[must_use]
fn unstick_locks(layout: &mut Layout) -> ViewChange { fn unstick_locks(layout: &mut Layout) -> ViewChange {
let mut new_view = None; let mut new_view = None;
for key in layout.locked_keys.clone() { for key in layout.get_locked_keys().clone() {
let key: &Rc<RefCell<KeyState>> = key.borrow(); let key: &Rc<RefCell<KeyState>> = key.borrow();
let mut key = RefCell::borrow_mut(key); let key = RefCell::borrow(key);
match &key.action { match &key.action {
Action::LockView { lock: _, unlock: view } => { Action::LockView { lock: _, unlock: view } => {
new_view = Some(view.clone()); new_view = Some(view.clone());
@ -881,7 +896,6 @@ mod seat {
a, a,
), ),
}; };
key.locked = false;
} }
ViewChange { ViewChange {
@ -903,11 +917,7 @@ mod seat {
); );
} }
let mut key = rckey.borrow_mut(); let mut key = rckey.borrow_mut();
submission.virtual_keyboard.switch( submission.handle_press(&key, KeyState::get_id(rckey), time);
&key.keycodes,
PressType::Pressed,
time,
);
key.pressed = PressType::Pressed; key.pressed = PressType::Pressed;
} }
@ -926,32 +936,26 @@ mod seat {
// update // update
let key = key.into_released(); let key = key.into_released();
let key = match action { let mut locked = key.action.is_locked(&layout.current_view);
Action::LockView { lock: _, unlock: _ } => key.into_activated(),
_ => key,
};
// process changes // process changes
match action { match action {
Action::Submit { text: _, keys: _ } => { Action::Submit { text: _, keys: _ }
| Action::Erase
=> {
unstick_locks(layout).apply(); unstick_locks(layout).apply();
submission.virtual_keyboard.switch( submission.handle_release(KeyState::get_id(rckey), time);
&key.keycodes,
PressType::Released,
time,
);
}, },
Action::SetView(view) => { Action::SetView(view) => {
try_set_view(layout, view) try_set_view(layout, view)
}, },
Action::LockView { lock, unlock } => { Action::LockView { lock, unlock } => {
// The button that triggered this will be in the right state locked ^= true;
// due to commit at the end.
unstick_locks(layout) unstick_locks(layout)
// It doesn't matter what the resulting view should be, // It doesn't matter what the resulting view should be,
// it's getting changed anyway. // it's getting changed anyway.
.choose_view( .choose_view(
match key.locked { match locked {
true => lock.clone(), true => lock.clone(),
false => unlock.clone(), false => unlock.clone(),
} }
@ -993,11 +997,6 @@ mod seat {
let pointer = ::util::Pointer(rckey.clone()); let pointer = ::util::Pointer(rckey.clone());
// Apply state changes // Apply state changes
layout.pressed_keys.remove(&pointer); layout.pressed_keys.remove(&pointer);
if key.locked {
layout.locked_keys.insert(pointer);
} else {
layout.locked_keys.remove(&pointer);
}
// Commit activated button state changes // Commit activated button state changes
RefCell::replace(rckey, key); RefCell::replace(rckey, key);
} }
@ -1012,7 +1011,6 @@ mod test {
pub fn make_state() -> Rc<RefCell<::keyboard::KeyState>> { pub fn make_state() -> Rc<RefCell<::keyboard::KeyState>> {
Rc::new(RefCell::new(::keyboard::KeyState { Rc::new(RefCell::new(::keyboard::KeyState {
pressed: PressType::Released, pressed: PressType::Released,
locked: false,
keycodes: Vec::new(), keycodes: Vec::new(),
action: Action::SetView("default".into()), action: Action::SetView("default".into()),
})) }))
@ -1088,7 +1086,6 @@ mod test {
current_view: String::new(), current_view: String::new(),
keymap_str: CString::new("").unwrap(), keymap_str: CString::new("").unwrap(),
kind: ArrangementKind::Base, kind: ArrangementKind::Base,
locked_keys: HashSet::new(),
pressed_keys: HashSet::new(), pressed_keys: HashSet::new(),
// Lots of bottom margin // Lots of bottom margin
margins: Margins { margins: Margins {

View File

@ -91,6 +91,11 @@ mod variants {
unsafe { unsafe {
let ret = glib_sys::g_variant_builder_end(builder); let ret = glib_sys::g_variant_builder_end(builder);
glib_sys::g_variant_builder_unref(builder); glib_sys::g_variant_builder_unref(builder);
// HACK: This is to prevent C taking ownership
// of "floating" Variants,
// where Rust gets to keep a stale reference
// and crash when trying to drop it.
glib_sys::g_variant_ref_sink(ret);
glib::Variant::from_glib_full(ret) glib::Variant::from_glib_full(ret)
} }
} }
@ -141,7 +146,7 @@ fn set_layout(kind: String, name: String) {
.chain(inputs).collect(); .chain(inputs).collect();
settings.set_value( settings.set_value(
"sources", "sources",
&variants::ArrayPairString(inputs).to_variant() &variants::ArrayPairString(inputs).to_variant(),
); );
settings.apply(); settings.apply();
} }

View File

@ -17,7 +17,11 @@
* and those events SHOULD NOT cause any lost events. * and those events SHOULD NOT cause any lost events.
* */ * */
use ::action::Action;
use ::imservice;
use ::imservice::IMService; use ::imservice::IMService;
use ::keyboard::{ KeyCode, KeyState, KeyStateId, PressType };
use ::logging;
use ::vkeyboard::VirtualKeyboard; use ::vkeyboard::VirtualKeyboard;
/// Gathers stuff defined in C or called by C /// Gathers stuff defined in C or called by C
@ -57,6 +61,7 @@ pub mod c {
Submission { Submission {
imservice, imservice,
virtual_keyboard: VirtualKeyboard(vk), virtual_keyboard: VirtualKeyboard(vk),
pressed: Vec::new(),
} }
)) ))
} }
@ -92,9 +97,91 @@ pub mod c {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct Timestamp(pub u32); pub struct Timestamp(pub u32);
pub struct Submission { enum SubmittedAction {
// used by C callbacks internally, TODO: make use with virtual keyboard /// A collection of keycodes that were pressed
#[allow(dead_code)] VirtualKeyboard(Vec<KeyCode>),
imservice: Option<Box<IMService>>, IMService,
pub virtual_keyboard: VirtualKeyboard, }
pub struct Submission {
imservice: Option<Box<IMService>>,
virtual_keyboard: VirtualKeyboard,
pressed: Vec<(KeyStateId, SubmittedAction)>,
}
impl Submission {
/// Sends a submit text event if possible;
/// otherwise sends key press and makes a note of it
pub fn handle_press(
&mut self,
key: &KeyState, key_id: KeyStateId,
time: Timestamp,
) {
match &key.action {
Action::Submit { text: _, keys: _ }
| Action::Erase
=> (),
_ => {
log_print!(
logging::Level::Bug,
"Submitted key with action other than Submit or Erase",
);
return;
},
};
let was_committed_as_text = match (&mut self.imservice, &key.action) {
(Some(imservice), Action::Submit { text: Some(text), keys: _ }) => {
let submit_result = imservice.commit_string(text)
.and_then(|_| imservice.commit());
match submit_result {
Ok(()) => true,
Err(imservice::SubmitError::NotActive) => false,
}
},
(Some(imservice), Action::Erase) => {
let submit_result = imservice.delete_surrounding_text(1, 0)
.and_then(|_| imservice.commit());
match submit_result {
Ok(()) => true,
Err(imservice::SubmitError::NotActive) => false,
}
}
(_, _) => false,
};
let submit_action = match was_committed_as_text {
true => SubmittedAction::IMService,
false => {
self.virtual_keyboard.switch(
&key.keycodes,
PressType::Pressed,
time,
);
SubmittedAction::VirtualKeyboard(key.keycodes.clone())
},
};
self.pressed.push((key_id, submit_action));
}
pub fn handle_release(&mut self, key_id: KeyStateId, time: Timestamp) {
let index = self.pressed.iter().position(|(id, _)| *id == key_id);
if let Some(index) = index {
let (_id, action) = self.pressed.remove(index);
match action {
// string already sent, nothing to do
SubmittedAction::IMService => {},
// no matter if the imservice got activated,
// keys must be released
SubmittedAction::VirtualKeyboard(keycodes) => {
self.virtual_keyboard.switch(
&keycodes,
PressType::Released,
time,
)
},
}
};
}
} }