Merge remote-tracking branch 'upstream/master' into center
This commit is contained in:
		@ -45,7 +45,7 @@ buttons:
 | 
			
		||||
    BackSpace:
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
        icon: "edit-clear-symbolic"
 | 
			
		||||
        keysym: "BackSpace"
 | 
			
		||||
        action: "erase"
 | 
			
		||||
    preferences:
 | 
			
		||||
        action: "show_prefs"
 | 
			
		||||
        outline: "special"
 | 
			
		||||
 | 
			
		||||
@ -45,7 +45,7 @@ buttons:
 | 
			
		||||
    BackSpace:
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
        icon: "edit-clear-symbolic"
 | 
			
		||||
        keysym: "BackSpace"
 | 
			
		||||
        action: "erase"
 | 
			
		||||
    preferences:
 | 
			
		||||
        action: "show_prefs"
 | 
			
		||||
        outline: "special"
 | 
			
		||||
 | 
			
		||||
@ -46,7 +46,7 @@ buttons:
 | 
			
		||||
    BackSpace:
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
        icon: "edit-clear-symbolic"
 | 
			
		||||
        keysym: "BackSpace"
 | 
			
		||||
        action: "erase"
 | 
			
		||||
    preferences:
 | 
			
		||||
        action: "show_prefs"
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
 | 
			
		||||
@ -44,7 +44,7 @@ buttons:
 | 
			
		||||
    BackSpace:
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
        icon: "edit-clear-symbolic"
 | 
			
		||||
        keysym: "BackSpace"
 | 
			
		||||
        action: "erase"
 | 
			
		||||
    preferences:
 | 
			
		||||
        action: "show_prefs"
 | 
			
		||||
        outline: "default"
 | 
			
		||||
 | 
			
		||||
@ -39,7 +39,7 @@ buttons:
 | 
			
		||||
    BackSpace:
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
        icon: "edit-clear-symbolic"
 | 
			
		||||
        keysym: "BackSpace"
 | 
			
		||||
        action: "erase"
 | 
			
		||||
    preferences:
 | 
			
		||||
        action: "show_prefs"
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
 | 
			
		||||
@ -46,7 +46,7 @@ buttons:
 | 
			
		||||
    BackSpace:
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
        icon: "edit-clear-symbolic"
 | 
			
		||||
        keysym: "BackSpace"
 | 
			
		||||
        action: "erase"
 | 
			
		||||
    preferences:
 | 
			
		||||
        action: "show_prefs"
 | 
			
		||||
        outline: "default"
 | 
			
		||||
 | 
			
		||||
@ -195,7 +195,7 @@ buttons:
 | 
			
		||||
    BackSpace:
 | 
			
		||||
        outline: "wide"
 | 
			
		||||
        icon: "edit-clear-symbolic"
 | 
			
		||||
        keysym: "BackSpace"
 | 
			
		||||
        action: erase
 | 
			
		||||
    Return:
 | 
			
		||||
        outline: "wide"
 | 
			
		||||
        icon: "key-enter"
 | 
			
		||||
 | 
			
		||||
@ -195,7 +195,7 @@ buttons:
 | 
			
		||||
    BackSpace:
 | 
			
		||||
        outline: "wide"
 | 
			
		||||
        icon: "edit-clear-symbolic"
 | 
			
		||||
        keysym: "BackSpace"
 | 
			
		||||
        action: erase
 | 
			
		||||
    Return:
 | 
			
		||||
        outline: "wide"
 | 
			
		||||
        icon: "key-enter"
 | 
			
		||||
 | 
			
		||||
@ -39,7 +39,7 @@ buttons:
 | 
			
		||||
    BackSpace:
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
        icon: "edit-clear-symbolic"
 | 
			
		||||
        keysym: "BackSpace"
 | 
			
		||||
        action: erase
 | 
			
		||||
    preferences:
 | 
			
		||||
        action: "show_prefs"
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,7 @@ buttons:
 | 
			
		||||
    BackSpace:
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
        icon: "edit-clear-symbolic"
 | 
			
		||||
        keysym: "BackSpace"
 | 
			
		||||
        action: erase
 | 
			
		||||
    space:
 | 
			
		||||
        outline: spaceline
 | 
			
		||||
        text: " "
 | 
			
		||||
 | 
			
		||||
@ -39,7 +39,7 @@ buttons:
 | 
			
		||||
    BackSpace:
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
        icon: "edit-clear-symbolic"
 | 
			
		||||
        keysym: "BackSpace"
 | 
			
		||||
        action: erase
 | 
			
		||||
    preferences:
 | 
			
		||||
        action: "show_prefs"
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
 | 
			
		||||
@ -45,7 +45,7 @@ buttons:
 | 
			
		||||
    BackSpace:
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
        icon: "edit-clear-symbolic"
 | 
			
		||||
        keysym: "BackSpace"
 | 
			
		||||
        action: erase
 | 
			
		||||
    preferences:
 | 
			
		||||
        action: "show_prefs"
 | 
			
		||||
        outline: "special"
 | 
			
		||||
 | 
			
		||||
@ -39,9 +39,9 @@ buttons:
 | 
			
		||||
    BackSpace:
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
        icon: "edit-clear-symbolic"
 | 
			
		||||
        keysym: "BackSpace"
 | 
			
		||||
        action: erase
 | 
			
		||||
    preferences:
 | 
			
		||||
        action: "show_prefs"
 | 
			
		||||
        action: show_prefs
 | 
			
		||||
        outline: "special"
 | 
			
		||||
        icon: "keyboard-mode-symbolic"
 | 
			
		||||
    show_numbers:
 | 
			
		||||
 | 
			
		||||
@ -39,7 +39,7 @@ buttons:
 | 
			
		||||
    BackSpace:
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
        icon: "edit-clear-symbolic"
 | 
			
		||||
        keysym: "BackSpace"
 | 
			
		||||
        action: "erase"
 | 
			
		||||
    preferences:
 | 
			
		||||
        action: "show_prefs"
 | 
			
		||||
        outline: "special"
 | 
			
		||||
 | 
			
		||||
@ -116,6 +116,7 @@ settings_get_layout(GSettings *settings, char **type, char **layout)
 | 
			
		||||
    GVariant *inputs = g_settings_get_value(settings, "sources");
 | 
			
		||||
    // current layout is always first
 | 
			
		||||
    g_variant_get_child(inputs, 0, "(ss)", type, layout);
 | 
			
		||||
    g_variant_unref(inputs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
@ -139,9 +140,11 @@ eekboard_context_service_update_layout(EekboardContextService *context, enum squ
 | 
			
		||||
    switch (priv->purpose) {
 | 
			
		||||
    case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NUMBER:
 | 
			
		||||
    case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PHONE:
 | 
			
		||||
        g_free(keyboard_layout);
 | 
			
		||||
        keyboard_layout = g_strdup("number");
 | 
			
		||||
        break;
 | 
			
		||||
    case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL:
 | 
			
		||||
        g_free(keyboard_layout);
 | 
			
		||||
        keyboard_layout = g_strdup("terminal");
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
 | 
			
		||||
@ -31,10 +31,29 @@ pub enum Action {
 | 
			
		||||
    SetModifier(Modifier),
 | 
			
		||||
    /// Submit some text
 | 
			
		||||
    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>,
 | 
			
		||||
        /// The key events this symbol submits when submitting text is not possible
 | 
			
		||||
        keys: Vec<KeySym>,
 | 
			
		||||
    },
 | 
			
		||||
    /// Erase a position behind the cursor
 | 
			
		||||
    Erase,
 | 
			
		||||
    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,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										12
									
								
								src/data.rs
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								src/data.rs
									
									
									
									
									
								
							@ -15,6 +15,7 @@ use std::vec::Vec;
 | 
			
		||||
 | 
			
		||||
use xkbcommon::xkb;
 | 
			
		||||
 | 
			
		||||
use ::action;
 | 
			
		||||
use ::keyboard::{
 | 
			
		||||
    KeyState, PressType,
 | 
			
		||||
    generate_keymap, generate_keycodes, FormattingError
 | 
			
		||||
@ -264,6 +265,9 @@ enum Action {
 | 
			
		||||
    SetView(String),
 | 
			
		||||
    #[serde(rename="show_prefs")]
 | 
			
		||||
    ShowPrefs,
 | 
			
		||||
    /// Remove last character
 | 
			
		||||
    #[serde(rename="erase")]
 | 
			
		||||
    Erase,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, PartialEq)]
 | 
			
		||||
@ -386,13 +390,16 @@ impl Layout {
 | 
			
		||||
                            )
 | 
			
		||||
                    }).collect()
 | 
			
		||||
                },
 | 
			
		||||
                action::Action::Erase => vec![
 | 
			
		||||
                    *keymap.get("BackSpace")
 | 
			
		||||
                        .expect(&format!("BackSpace missing from keymap")),
 | 
			
		||||
                ],
 | 
			
		||||
                _ => Vec::new(),
 | 
			
		||||
            };
 | 
			
		||||
            (
 | 
			
		||||
                name.into(),
 | 
			
		||||
                KeyState {
 | 
			
		||||
                    pressed: PressType::Released,
 | 
			
		||||
                    locked: false,
 | 
			
		||||
                    keycodes,
 | 
			
		||||
                    action,
 | 
			
		||||
                }
 | 
			
		||||
@ -574,6 +581,7 @@ fn create_action<H: logging::Handler>(
 | 
			
		||||
        SubmitData::Action(
 | 
			
		||||
            Action::ShowPrefs
 | 
			
		||||
        ) => ::action::Action::ShowPreferences,
 | 
			
		||||
        SubmitData::Action(Action::Erase) => action::Action::Erase,
 | 
			
		||||
        SubmitData::Keysym(keysym) => ::action::Action::Submit {
 | 
			
		||||
            text: None,
 | 
			
		||||
            keys: vec!(::action::KeySym(
 | 
			
		||||
@ -605,7 +613,7 @@ fn create_action<H: logging::Handler>(
 | 
			
		||||
                    false => format!("U{:04X}", codepoint as u32),
 | 
			
		||||
                })
 | 
			
		||||
            }).collect(),
 | 
			
		||||
        }
 | 
			
		||||
        },
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -37,6 +37,7 @@ mod c {
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Draws all buttons that are not in the base state
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_layout_draw_all_changed(
 | 
			
		||||
@ -49,12 +50,13 @@ mod c {
 | 
			
		||||
 | 
			
		||||
        layout.foreach_visible_button(|offset, button| {
 | 
			
		||||
            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(
 | 
			
		||||
                    renderer, &cr,
 | 
			
		||||
                    offset,
 | 
			
		||||
                    button.as_ref(),
 | 
			
		||||
                    state.pressed, state.locked,
 | 
			
		||||
                    state.pressed, locked,
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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,
 | 
			
		||||
/// making it unavailable in Rust
 | 
			
		||||
void imservice_destroy_im(struct zwp_input_method_v2 *im) {
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,8 @@
 | 
			
		||||
/*! Manages zwp_input_method_v2 protocol.
 | 
			
		||||
 * 
 | 
			
		||||
 * Library module.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
use std::boxed::Box;
 | 
			
		||||
use std::ffi::CString;
 | 
			
		||||
use std::fmt;
 | 
			
		||||
@ -32,6 +37,9 @@ pub mod c {
 | 
			
		||||
        fn imservice_destroy_im(im: *mut c::InputMethod);
 | 
			
		||||
        #[allow(improper_ctypes)] // IMService will never be dereferenced in C
 | 
			
		||||
        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 server_context_service_show_keyboard(imservice: *const UIManager);
 | 
			
		||||
        fn server_context_service_hide_keyboard(imservice: *const UIManager);
 | 
			
		||||
@ -328,7 +336,7 @@ impl Default for IMProtocolState {
 | 
			
		||||
 | 
			
		||||
pub struct IMService {
 | 
			
		||||
    /// 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
 | 
			
		||||
    state_manager: *const c::StateManager,
 | 
			
		||||
    /// Unowned reference. Be careful, it's shared with C at large
 | 
			
		||||
@ -340,6 +348,11 @@ pub struct IMService {
 | 
			
		||||
    serial: Wrapping<u32>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub enum SubmitError {
 | 
			
		||||
    /// The input method had not been activated
 | 
			
		||||
    NotActive,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl IMService {
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        im: *mut c::InputMethod,
 | 
			
		||||
@ -364,4 +377,51 @@ impl 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
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,14 +1,17 @@
 | 
			
		||||
/*! State of the emulated keyboard and keys.
 | 
			
		||||
 * Regards the keyboard as if it was composed of switches. */
 | 
			
		||||
 | 
			
		||||
use std::cell::RefCell;
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
use std::fmt;
 | 
			
		||||
use std::io;
 | 
			
		||||
use std::rc::Rc;
 | 
			
		||||
use std::string::FromUtf8Error;
 | 
			
		||||
 | 
			
		||||
use ::action::Action;
 | 
			
		||||
use ::logging;
 | 
			
		||||
 | 
			
		||||
// Traits
 | 
			
		||||
use std::io::Write;
 | 
			
		||||
use std::iter::{ FromIterator, IntoIterator };
 | 
			
		||||
 | 
			
		||||
@ -20,10 +23,14 @@ pub enum PressType {
 | 
			
		||||
 | 
			
		||||
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)]
 | 
			
		||||
pub struct KeyState {
 | 
			
		||||
    pub pressed: PressType,
 | 
			
		||||
    pub locked: bool,
 | 
			
		||||
    /// A cache of raw keycodes derived from Action::Sumbit given a keymap
 | 
			
		||||
    pub keycodes: Vec<KeyCode>,
 | 
			
		||||
    /// Static description of what the key does when pressed or released
 | 
			
		||||
@ -31,17 +38,6 @@ pub struct 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]
 | 
			
		||||
    pub fn into_released(self) -> KeyState {
 | 
			
		||||
        KeyState {
 | 
			
		||||
@ -49,6 +45,12 @@ impl KeyState {
 | 
			
		||||
            ..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
 | 
			
		||||
@ -66,9 +68,10 @@ fn sorted<'a, I: Iterator<Item=&'a str>>(
 | 
			
		||||
pub fn generate_keycodes<'a, C: IntoIterator<Item=&'a str>>(
 | 
			
		||||
    key_names: C
 | 
			
		||||
) -> HashMap<String, u32> {
 | 
			
		||||
    let special_keysyms = ["BackSpace", "Return"].iter().map(|&s| s);
 | 
			
		||||
    HashMap::from_iter(
 | 
			
		||||
        // 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))
 | 
			
		||||
            .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(
 | 
			
		||||
    keystates: &HashMap::<String, KeyState>
 | 
			
		||||
) -> Result<String, FormattingError> {
 | 
			
		||||
@ -110,7 +116,8 @@ pub fn generate_keymap(
 | 
			
		||||
    )?;
 | 
			
		||||
    
 | 
			
		||||
    for (name, state) in keystates.iter() {
 | 
			
		||||
        if let Action::Submit { text: _, keys } = &state.action {
 | 
			
		||||
        match &state.action {
 | 
			
		||||
            Action::Submit { text: _, keys } => {
 | 
			
		||||
                if let 0 = keys.len() {
 | 
			
		||||
                    log_print!(
 | 
			
		||||
                        logging::Level::Warning,
 | 
			
		||||
@ -126,6 +133,23 @@ pub fn generate_keymap(
 | 
			
		||||
                        keycode,
 | 
			
		||||
                    )?;
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            Action::Erase => {
 | 
			
		||||
                let mut keycodes = state.keycodes.iter();
 | 
			
		||||
                write!(
 | 
			
		||||
                    buf,
 | 
			
		||||
                    "
 | 
			
		||||
        <BackSpace> = {};",
 | 
			
		||||
                    keycodes.next().expect("Erase key has no 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\" {{
 | 
			
		||||
 | 
			
		||||
        name[Group1] = \"Letters\";
 | 
			
		||||
        name[Group2] = \"Numbers/Symbols\";"
 | 
			
		||||
        name[Group2] = \"Numbers/Symbols\";
 | 
			
		||||
        
 | 
			
		||||
        key <BackSpace> {{ [ BackSpace ] }};"
 | 
			
		||||
    )?;
 | 
			
		||||
    
 | 
			
		||||
    for (name, state) in keystates.iter() {
 | 
			
		||||
@ -195,7 +221,6 @@ mod tests {
 | 
			
		||||
                    keys: vec!(KeySym("a".into()), KeySym("c".into())),
 | 
			
		||||
                },
 | 
			
		||||
                keycodes: vec!(9, 10),
 | 
			
		||||
                locked: false,
 | 
			
		||||
                pressed: PressType::Released,
 | 
			
		||||
            },
 | 
			
		||||
        }).unwrap();
 | 
			
		||||
 | 
			
		||||
@ -629,7 +629,6 @@ pub struct Layout {
 | 
			
		||||
    // 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
 | 
			
		||||
@ -661,7 +660,6 @@ impl Layout {
 | 
			
		||||
            views: data.views,
 | 
			
		||||
            keymap_str: data.keymap_str,
 | 
			
		||||
            pressed_keys: HashSet::new(),
 | 
			
		||||
            locked_keys: HashSet::new(),
 | 
			
		||||
            margins: data.margins,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -672,7 +670,7 @@ impl Layout {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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> {
 | 
			
		||||
@ -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 {
 | 
			
		||||
@ -868,9 +883,9 @@ mod seat {
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    fn unstick_locks(layout: &mut Layout) -> ViewChange {
 | 
			
		||||
        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 mut key = RefCell::borrow_mut(key);
 | 
			
		||||
            let key = RefCell::borrow(key);
 | 
			
		||||
            match &key.action {
 | 
			
		||||
                Action::LockView { lock: _, unlock: view } => {
 | 
			
		||||
                    new_view = Some(view.clone());
 | 
			
		||||
@ -881,7 +896,6 @@ mod seat {
 | 
			
		||||
                    a,
 | 
			
		||||
                ),
 | 
			
		||||
            };
 | 
			
		||||
            key.locked = false;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        ViewChange {
 | 
			
		||||
@ -903,11 +917,7 @@ mod seat {
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        let mut key = rckey.borrow_mut();
 | 
			
		||||
        submission.virtual_keyboard.switch(
 | 
			
		||||
            &key.keycodes,
 | 
			
		||||
            PressType::Pressed,
 | 
			
		||||
            time,
 | 
			
		||||
        );
 | 
			
		||||
        submission.handle_press(&key, KeyState::get_id(rckey), time);
 | 
			
		||||
        key.pressed = PressType::Pressed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -926,32 +936,26 @@ mod seat {
 | 
			
		||||
 | 
			
		||||
        // update
 | 
			
		||||
        let key = key.into_released();
 | 
			
		||||
        let key = match action {
 | 
			
		||||
            Action::LockView { lock: _, unlock: _ } => key.into_activated(),
 | 
			
		||||
            _ => key,
 | 
			
		||||
        };
 | 
			
		||||
        let mut locked = key.action.is_locked(&layout.current_view);
 | 
			
		||||
 | 
			
		||||
        // process changes
 | 
			
		||||
        match action {
 | 
			
		||||
            Action::Submit { text: _, keys: _ } => {
 | 
			
		||||
            Action::Submit { text: _, keys: _ }
 | 
			
		||||
                | Action::Erase
 | 
			
		||||
            => {
 | 
			
		||||
                unstick_locks(layout).apply();
 | 
			
		||||
                submission.virtual_keyboard.switch(
 | 
			
		||||
                    &key.keycodes,
 | 
			
		||||
                    PressType::Released,
 | 
			
		||||
                    time,
 | 
			
		||||
                );
 | 
			
		||||
                submission.handle_release(KeyState::get_id(rckey), time);
 | 
			
		||||
            },
 | 
			
		||||
            Action::SetView(view) => {
 | 
			
		||||
                try_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.
 | 
			
		||||
                locked ^= true;
 | 
			
		||||
                unstick_locks(layout)
 | 
			
		||||
                    // It doesn't matter what the resulting view should be,
 | 
			
		||||
                    // it's getting changed anyway.
 | 
			
		||||
                    .choose_view(
 | 
			
		||||
                        match key.locked {
 | 
			
		||||
                        match locked {
 | 
			
		||||
                            true => lock.clone(),
 | 
			
		||||
                            false => unlock.clone(),
 | 
			
		||||
                        }
 | 
			
		||||
@ -993,11 +997,6 @@ mod seat {
 | 
			
		||||
        let pointer = ::util::Pointer(rckey.clone());
 | 
			
		||||
        // Apply state changes
 | 
			
		||||
        layout.pressed_keys.remove(&pointer);
 | 
			
		||||
        if key.locked {
 | 
			
		||||
            layout.locked_keys.insert(pointer);
 | 
			
		||||
        } else {
 | 
			
		||||
            layout.locked_keys.remove(&pointer);
 | 
			
		||||
        }
 | 
			
		||||
        // Commit activated button state changes
 | 
			
		||||
        RefCell::replace(rckey, key);
 | 
			
		||||
    }
 | 
			
		||||
@ -1012,7 +1011,6 @@ mod test {
 | 
			
		||||
    pub fn make_state() -> Rc<RefCell<::keyboard::KeyState>> {
 | 
			
		||||
        Rc::new(RefCell::new(::keyboard::KeyState {
 | 
			
		||||
            pressed: PressType::Released,
 | 
			
		||||
            locked: false,
 | 
			
		||||
            keycodes: Vec::new(),
 | 
			
		||||
            action: Action::SetView("default".into()),
 | 
			
		||||
        }))
 | 
			
		||||
@ -1088,7 +1086,6 @@ mod test {
 | 
			
		||||
            current_view: String::new(),
 | 
			
		||||
            keymap_str: CString::new("").unwrap(),
 | 
			
		||||
            kind: ArrangementKind::Base,
 | 
			
		||||
            locked_keys: HashSet::new(),
 | 
			
		||||
            pressed_keys: HashSet::new(),
 | 
			
		||||
            // Lots of bottom margin
 | 
			
		||||
            margins: Margins {
 | 
			
		||||
 | 
			
		||||
@ -91,6 +91,11 @@ mod variants {
 | 
			
		||||
            unsafe {
 | 
			
		||||
                let ret = glib_sys::g_variant_builder_end(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)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@ -141,7 +146,7 @@ fn set_layout(kind: String, name: String) {
 | 
			
		||||
        .chain(inputs).collect();
 | 
			
		||||
    settings.set_value(
 | 
			
		||||
        "sources",
 | 
			
		||||
        &variants::ArrayPairString(inputs).to_variant()
 | 
			
		||||
        &variants::ArrayPairString(inputs).to_variant(),
 | 
			
		||||
    );
 | 
			
		||||
    settings.apply();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,11 @@
 | 
			
		||||
 * and those events SHOULD NOT cause any lost events.
 | 
			
		||||
 * */
 | 
			
		||||
 
 | 
			
		||||
use ::action::Action;
 | 
			
		||||
use ::imservice;
 | 
			
		||||
use ::imservice::IMService;
 | 
			
		||||
use ::keyboard::{ KeyCode, KeyState, KeyStateId, PressType };
 | 
			
		||||
use ::logging;
 | 
			
		||||
use ::vkeyboard::VirtualKeyboard;
 | 
			
		||||
 | 
			
		||||
/// Gathers stuff defined in C or called by C
 | 
			
		||||
@ -57,6 +61,7 @@ pub mod c {
 | 
			
		||||
            Submission {
 | 
			
		||||
                imservice,
 | 
			
		||||
                virtual_keyboard: VirtualKeyboard(vk),
 | 
			
		||||
                pressed: Vec::new(),
 | 
			
		||||
            }
 | 
			
		||||
        ))
 | 
			
		||||
    }
 | 
			
		||||
@ -92,9 +97,91 @@ pub mod c {
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub struct Timestamp(pub u32);
 | 
			
		||||
 | 
			
		||||
pub struct Submission {
 | 
			
		||||
    // used by C callbacks internally, TODO: make use with virtual keyboard
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    imservice: Option<Box<IMService>>,
 | 
			
		||||
    pub virtual_keyboard: VirtualKeyboard,
 | 
			
		||||
enum SubmittedAction {
 | 
			
		||||
    /// A collection of keycodes that were pressed
 | 
			
		||||
    VirtualKeyboard(Vec<KeyCode>),
 | 
			
		||||
    IMService,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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,
 | 
			
		||||
                    )
 | 
			
		||||
                },
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user