Merge remote-tracking branch 'upstream/master' into latch
This commit is contained in:
		@ -17,6 +17,7 @@ pub enum Modifier {
 | 
			
		||||
    /// so it's simple to implement as levels are deprecated in squeekboard.
 | 
			
		||||
    Control,
 | 
			
		||||
    Alt,
 | 
			
		||||
    Mod4,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Action to perform on the keypress and, in reverse, on keyrelease
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										145
									
								
								src/data.rs
									
									
									
									
									
								
							
							
						
						
									
										145
									
								
								src/data.rs
									
									
									
									
									
								
							@ -97,6 +97,8 @@ impl fmt::Display for DataSource {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type LayoutSource = (ArrangementKind, DataSource);
 | 
			
		||||
 | 
			
		||||
/// Lists possible sources, with 0 as the most preferred one
 | 
			
		||||
/// Trying order: native lang of the right kind, native base,
 | 
			
		||||
/// fallback lang of the right kind, fallback base
 | 
			
		||||
@ -104,55 +106,76 @@ fn list_layout_sources(
 | 
			
		||||
    name: &str,
 | 
			
		||||
    kind: ArrangementKind,
 | 
			
		||||
    keyboards_path: Option<PathBuf>,
 | 
			
		||||
) -> Vec<(ArrangementKind, DataSource)> {
 | 
			
		||||
    let mut ret = Vec::new();
 | 
			
		||||
    {
 | 
			
		||||
        fn name_with_arrangement(name: String, kind: &ArrangementKind)
 | 
			
		||||
            -> String
 | 
			
		||||
        {
 | 
			
		||||
            match kind {    
 | 
			
		||||
                ArrangementKind::Base => name,
 | 
			
		||||
                ArrangementKind::Wide => name + "_wide",
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let mut add_by_name = |name: &str, kind: &ArrangementKind| {
 | 
			
		||||
            if let Some(path) = keyboards_path.clone() {
 | 
			
		||||
                ret.push((
 | 
			
		||||
                    kind.clone(),
 | 
			
		||||
                    DataSource::File(
 | 
			
		||||
                        path.join(name.to_owned()).with_extension("yaml")
 | 
			
		||||
                    )
 | 
			
		||||
                ))
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
) -> Vec<LayoutSource> {
 | 
			
		||||
    // Just a simplification of often called code.
 | 
			
		||||
    let add_by_name = |
 | 
			
		||||
        mut ret: Vec<LayoutSource>,
 | 
			
		||||
        name: &str,
 | 
			
		||||
        kind: &ArrangementKind,
 | 
			
		||||
    | -> Vec<LayoutSource> {
 | 
			
		||||
        if let Some(path) = keyboards_path.clone() {
 | 
			
		||||
            ret.push((
 | 
			
		||||
                kind.clone(),
 | 
			
		||||
                DataSource::Resource(name.into())
 | 
			
		||||
            ));
 | 
			
		||||
        };
 | 
			
		||||
                DataSource::File(
 | 
			
		||||
                    path.join(name.to_owned()).with_extension("yaml")
 | 
			
		||||
                )
 | 
			
		||||
            ))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        match &kind {
 | 
			
		||||
            ArrangementKind::Base => {},
 | 
			
		||||
        ret.push((
 | 
			
		||||
            kind.clone(),
 | 
			
		||||
            DataSource::Resource(name.into())
 | 
			
		||||
        ));
 | 
			
		||||
        ret
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Another grouping.
 | 
			
		||||
    let add_by_kind = |ret, name: &str, kind| {
 | 
			
		||||
        let ret = match kind {
 | 
			
		||||
            &ArrangementKind::Base => ret,
 | 
			
		||||
            kind => add_by_name(
 | 
			
		||||
                &name_with_arrangement(name.into(), &kind),
 | 
			
		||||
                &kind,
 | 
			
		||||
                ret,
 | 
			
		||||
                &name_with_arrangement(name.into(), kind),
 | 
			
		||||
                kind,
 | 
			
		||||
            ),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        add_by_name(name, &ArrangementKind::Base);
 | 
			
		||||
        add_by_name(ret, name, &ArrangementKind::Base)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
        match &kind {
 | 
			
		||||
            ArrangementKind::Base => {},
 | 
			
		||||
            kind => add_by_name(
 | 
			
		||||
                &name_with_arrangement(FALLBACK_LAYOUT_NAME.into(), &kind),
 | 
			
		||||
                &kind,
 | 
			
		||||
            ),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        add_by_name(FALLBACK_LAYOUT_NAME, &ArrangementKind::Base);
 | 
			
		||||
    fn name_with_arrangement(name: String, kind: &ArrangementKind) -> String {
 | 
			
		||||
        match kind {    
 | 
			
		||||
            ArrangementKind::Base => name,
 | 
			
		||||
            ArrangementKind::Wide => name + "_wide",
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    ret
 | 
			
		||||
 | 
			
		||||
    let ret = Vec::new();
 | 
			
		||||
 | 
			
		||||
    // Name as given takes priority.
 | 
			
		||||
    let ret = add_by_kind(ret, name, &kind);
 | 
			
		||||
 | 
			
		||||
    // Then try non-alternative name if applicable (`us` for `us+colemak`).
 | 
			
		||||
    let ret = {
 | 
			
		||||
        let mut parts = name.splitn(2, '+');
 | 
			
		||||
        match parts.next() {
 | 
			
		||||
            Some(base) => {
 | 
			
		||||
                // The name is already equal to base, so it was already added.
 | 
			
		||||
                if base == name { ret }
 | 
			
		||||
                else {
 | 
			
		||||
                    add_by_kind(ret, base, &kind)
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            // The layout's base name starts with a "+". Weird but OK.
 | 
			
		||||
            None => {
 | 
			
		||||
                log_print!(logging::Level::Surprise, "Base layout name is empty: {}", name);
 | 
			
		||||
                ret
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // No other choices left, so give anything.
 | 
			
		||||
    add_by_kind(ret, FALLBACK_LAYOUT_NAME.into(), &kind)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn load_layout_data(source: DataSource)
 | 
			
		||||
@ -645,6 +668,9 @@ fn create_action<H: logging::Handler>(
 | 
			
		||||
            Modifier::Alt => action::Action::ApplyModifier(
 | 
			
		||||
                action::Modifier::Alt,
 | 
			
		||||
            ),
 | 
			
		||||
            Modifier::Mod4 => action::Action::ApplyModifier(
 | 
			
		||||
                action::Modifier::Mod4,
 | 
			
		||||
            ),
 | 
			
		||||
            unsupported_modifier => {
 | 
			
		||||
                warning_handler.handle(
 | 
			
		||||
                    logging::Level::Bug,
 | 
			
		||||
@ -756,13 +782,21 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
    use ::logging::ProblemPanic;
 | 
			
		||||
 | 
			
		||||
    const THIS_FILE: &str = file!();
 | 
			
		||||
 | 
			
		||||
    fn path_from_root(file: &'static str) -> PathBuf {
 | 
			
		||||
        PathBuf::from(THIS_FILE)
 | 
			
		||||
            .parent().unwrap()
 | 
			
		||||
            .parent().unwrap()
 | 
			
		||||
            .join(file)
 | 
			
		||||
        let source_dir = env::var("SOURCE_DIR")
 | 
			
		||||
            .map(PathBuf::from)
 | 
			
		||||
            .unwrap_or_else(|e| {
 | 
			
		||||
                if let env::VarError::NotPresent = e {
 | 
			
		||||
                    let this_file = file!();
 | 
			
		||||
                    PathBuf::from(this_file)
 | 
			
		||||
                        .parent().unwrap()
 | 
			
		||||
                        .parent().unwrap()
 | 
			
		||||
                        .into()
 | 
			
		||||
                } else {
 | 
			
		||||
                    panic!("{:?}", e);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        source_dir.join(file)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
@ -919,7 +953,26 @@ mod tests {
 | 
			
		||||
            )
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// If layout contains a "+", it should reach for what's in front of it too.
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn fallbacks_order_base() {
 | 
			
		||||
        let sources = list_layout_sources("nb+aliens", ArrangementKind::Base, None);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            sources,
 | 
			
		||||
            vec!(
 | 
			
		||||
                (ArrangementKind::Base, DataSource::Resource("nb+aliens".into())),
 | 
			
		||||
                (ArrangementKind::Base, DataSource::Resource("nb".into())),
 | 
			
		||||
                (
 | 
			
		||||
                    ArrangementKind::Base,
 | 
			
		||||
                    DataSource::Resource(FALLBACK_LAYOUT_NAME.into())
 | 
			
		||||
                ),
 | 
			
		||||
            )
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn unicode_keysym() {
 | 
			
		||||
        let keysym = xkb::keysym_from_name(
 | 
			
		||||
 | 
			
		||||
@ -59,7 +59,7 @@ handle_set_visible(SmPuriOSK0 *object, GDBusMethodInvocation *invocation,
 | 
			
		||||
 | 
			
		||||
    if (service->context) {
 | 
			
		||||
        if (arg_visible) {
 | 
			
		||||
            server_context_service_show_keyboard (service->context);
 | 
			
		||||
            server_context_service_force_show_keyboard (service->context);
 | 
			
		||||
        } else {
 | 
			
		||||
            server_context_service_hide_keyboard (service->context);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -36,7 +36,6 @@ mod c {
 | 
			
		||||
    pub struct GtkStyleContext(*const c_void);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    extern "C" {
 | 
			
		||||
        #[allow(improper_ctypes)]
 | 
			
		||||
        pub fn eek_renderer_get_scale_factor(
 | 
			
		||||
 | 
			
		||||
@ -25,6 +25,7 @@ static const struct zwp_input_method_v2_listener input_method_listener = {
 | 
			
		||||
 | 
			
		||||
struct submission* get_submission(struct zwp_input_method_manager_v2 *immanager,
 | 
			
		||||
                                  struct zwp_virtual_keyboard_manager_v1 *vkmanager,
 | 
			
		||||
                                  struct vis_manager *vis_manager,
 | 
			
		||||
                                  struct wl_seat *seat,
 | 
			
		||||
                                  EekboardContextService *state) {
 | 
			
		||||
    struct zwp_input_method_v2 *im = NULL;
 | 
			
		||||
@ -35,7 +36,7 @@ struct submission* get_submission(struct zwp_input_method_manager_v2 *immanager,
 | 
			
		||||
    if (vkmanager) {
 | 
			
		||||
        vk = zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(vkmanager, seat);
 | 
			
		||||
    }
 | 
			
		||||
    return submission_new(im, vk, state);
 | 
			
		||||
    return submission_new(im, vk, state, vis_manager);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Un-inlined
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,6 @@
 | 
			
		||||
use std::boxed::Box;
 | 
			
		||||
use std::ffi::CString;
 | 
			
		||||
use std::fmt;
 | 
			
		||||
use std::mem;
 | 
			
		||||
use std::num::Wrapping;
 | 
			
		||||
use std::string::String;
 | 
			
		||||
 | 
			
		||||
@ -24,7 +23,7 @@ pub mod c {
 | 
			
		||||
 | 
			
		||||
    use std::os::raw::{c_char, c_void};
 | 
			
		||||
 | 
			
		||||
    pub use ::submission::c::UIManager;
 | 
			
		||||
    pub use ::ui_manager::c::UIManager;
 | 
			
		||||
    pub use ::submission::c::StateManager;
 | 
			
		||||
 | 
			
		||||
    // The following defined in C
 | 
			
		||||
@ -33,17 +32,15 @@ pub mod c {
 | 
			
		||||
    #[repr(transparent)]
 | 
			
		||||
    pub struct InputMethod(*const c_void);
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    extern "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);
 | 
			
		||||
        pub fn server_context_service_set_im_active(imservice: *const UIManager, active: u32);
 | 
			
		||||
        pub fn server_context_service_keyboard_release_visibility(imservice: *const UIManager);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
 | 
			
		||||
@ -152,8 +149,10 @@ pub mod c {
 | 
			
		||||
            ..IMProtocolState::default()
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        imservice.serial += Wrapping(1u32);
 | 
			
		||||
 | 
			
		||||
        if active_changed {
 | 
			
		||||
            imservice.apply_active_to_ui();
 | 
			
		||||
            (imservice.active_callback)(imservice.current.active);
 | 
			
		||||
            if imservice.current.active {
 | 
			
		||||
                unsafe {
 | 
			
		||||
                    eekboard_context_service_set_hint_purpose(
 | 
			
		||||
@ -179,9 +178,7 @@ pub mod c {
 | 
			
		||||
        // the keyboard is already decommissioned
 | 
			
		||||
        imservice.current.active = false;
 | 
			
		||||
 | 
			
		||||
        if let Some(ui) = imservice.ui_manager {
 | 
			
		||||
            unsafe { server_context_service_keyboard_release_visibility(ui); }
 | 
			
		||||
        }
 | 
			
		||||
        (imservice.active_callback)(imservice.current.active);
 | 
			
		||||
    }    
 | 
			
		||||
 | 
			
		||||
    // FIXME: destroy and deallocate
 | 
			
		||||
@ -334,8 +331,7 @@ pub struct IMService {
 | 
			
		||||
    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
 | 
			
		||||
    ui_manager: Option<*const c::UIManager>,
 | 
			
		||||
    active_callback: Box<dyn Fn(bool)>,
 | 
			
		||||
 | 
			
		||||
    pending: IMProtocolState,
 | 
			
		||||
    current: IMProtocolState, // turn current into an idiomatic representation?
 | 
			
		||||
@ -352,12 +348,13 @@ impl IMService {
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        im: *mut c::InputMethod,
 | 
			
		||||
        state_manager: *const c::StateManager,
 | 
			
		||||
        active_callback: Box<dyn Fn(bool)>,
 | 
			
		||||
    ) -> Box<IMService> {
 | 
			
		||||
        // IMService will be referenced to by C,
 | 
			
		||||
        // so it needs to stay in the same place in memory via Box
 | 
			
		||||
        let imservice = Box::new(IMService {
 | 
			
		||||
            im,
 | 
			
		||||
            ui_manager: None,
 | 
			
		||||
            active_callback,
 | 
			
		||||
            state_manager,
 | 
			
		||||
            pending: IMProtocolState::default(),
 | 
			
		||||
            current: IMProtocolState::default(),
 | 
			
		||||
@ -373,26 +370,6 @@ impl IMService {
 | 
			
		||||
        imservice
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn set_ui_manager(&mut self, mut ui_manager: Option<*const c::UIManager>) {
 | 
			
		||||
        mem::swap(&mut self.ui_manager, &mut ui_manager);
 | 
			
		||||
        // Now ui_manager is what was previously self.ui_manager.
 | 
			
		||||
        // If there wasn't any, we need to consider if UI was requested.
 | 
			
		||||
        if let None = ui_manager {
 | 
			
		||||
            self.apply_active_to_ui();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn apply_active_to_ui(&self) {
 | 
			
		||||
        if let Some(ui) = self.ui_manager {
 | 
			
		||||
            unsafe {
 | 
			
		||||
                c::server_context_service_set_im_active(
 | 
			
		||||
                    ui,
 | 
			
		||||
                    self.is_active() as u32,
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn commit_string(&self, text: &CString) -> Result<(), SubmitError> {
 | 
			
		||||
        match self.current.active {
 | 
			
		||||
            true => {
 | 
			
		||||
@ -429,7 +406,6 @@ impl IMService {
 | 
			
		||||
                unsafe {
 | 
			
		||||
                    c::eek_input_method_commit(self.im, self.serial.0)
 | 
			
		||||
                }
 | 
			
		||||
                self.serial += Wrapping(1u32);
 | 
			
		||||
                Ok(())
 | 
			
		||||
            },
 | 
			
		||||
            false => Err(SubmitError::NotActive),
 | 
			
		||||
 | 
			
		||||
@ -50,7 +50,6 @@ pub mod c {
 | 
			
		||||
    #[derive(Copy, Clone)]
 | 
			
		||||
    pub struct EekGtkKeyboard(pub *const gtk_sys::GtkWidget);
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    extern "C" {
 | 
			
		||||
        #[allow(improper_ctypes)]
 | 
			
		||||
        pub fn eek_gtk_keyboard_emit_feedback(
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,6 @@ mod c {
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    pub struct GnomeXkbInfo(*const c_void);
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    extern "C" {
 | 
			
		||||
        // from libc
 | 
			
		||||
        pub fn strcoll(cs: *const c_char, ct: *const c_char) -> c_int;
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,6 @@ pub mod c {
 | 
			
		||||
    #[derive(Clone, Copy)]
 | 
			
		||||
    pub struct Manager(*const c_void);
 | 
			
		||||
    
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    extern "C" {
 | 
			
		||||
        pub fn eekboard_context_service_set_overlay(
 | 
			
		||||
            manager: Manager,
 | 
			
		||||
 | 
			
		||||
@ -80,6 +80,7 @@ test(
 | 
			
		||||
    'rstest',
 | 
			
		||||
    cargo_script,
 | 
			
		||||
    args: ['test'] + cargo_build_flags,
 | 
			
		||||
    env: ['SOURCE_DIR=' + meson.source_root()],
 | 
			
		||||
    # this is a whole Carg-based test suite, let it run for a while
 | 
			
		||||
    timeout: 900,
 | 
			
		||||
    depends: [build_rstests, cargo_toml],
 | 
			
		||||
 | 
			
		||||
@ -29,7 +29,6 @@ use ::logging::Warn;
 | 
			
		||||
mod c {
 | 
			
		||||
    use std::os::raw::c_char;
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    extern "C" {
 | 
			
		||||
        pub fn popover_open_settings_panel(panel: *const c_char);
 | 
			
		||||
    }
 | 
			
		||||
@ -437,7 +436,8 @@ pub fn show(
 | 
			
		||||
 | 
			
		||||
        let settings_action = gio::SimpleAction::new("settings", None);
 | 
			
		||||
        settings_action.connect_activate(move |_, _| {
 | 
			
		||||
            unsafe { c::popover_open_settings_panel(CString::new("region").unwrap().as_ptr()) };
 | 
			
		||||
            let s = CString::new("region").unwrap();
 | 
			
		||||
            unsafe { c::popover_open_settings_panel(s.as_ptr()) };
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        let action_group = gio::SimpleActionGroup::new();
 | 
			
		||||
 | 
			
		||||
@ -10,34 +10,78 @@ use std::iter::FromIterator;
 | 
			
		||||
// TODO: keep a list of what is a language layout,
 | 
			
		||||
// and what a convenience layout. "_wide" is not a layout,
 | 
			
		||||
// neither is "number"
 | 
			
		||||
/// List of builtin layouts
 | 
			
		||||
const KEYBOARDS: &[(*const str, *const str)] = &[
 | 
			
		||||
    // layouts: us must be left as first, as it is the,
 | 
			
		||||
    // fallback layout. The others should be alphabetical.
 | 
			
		||||
    // fallback layout.
 | 
			
		||||
    ("us", include_str!("../data/keyboards/us.yaml")),
 | 
			
		||||
    ("us_wide", include_str!("../data/keyboards/us_wide.yaml")),
 | 
			
		||||
    ("br", include_str!("../data/keyboards/br.yaml")),
 | 
			
		||||
    ("de", include_str!("../data/keyboards/de.yaml")),
 | 
			
		||||
 | 
			
		||||
    // Language layouts: keep alphabetical.
 | 
			
		||||
    ("be", include_str!("../data/keyboards/be.yaml")),
 | 
			
		||||
    ("be_wide", include_str!("../data/keyboards/be_wide.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("bg", include_str!("../data/keyboards/bg.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("br", include_str!("../data/keyboards/br.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("de", include_str!("../data/keyboards/de.yaml")),
 | 
			
		||||
    ("de_wide", include_str!("../data/keyboards/de_wide.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("cz", include_str!("../data/keyboards/cz.yaml")),
 | 
			
		||||
    ("cz_wide", include_str!("../data/keyboards/cz_wide.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("cz+qwerty", include_str!("../data/keyboards/cz+qwerty.yaml")),
 | 
			
		||||
    ("cz+qwerty_wide", include_str!("../data/keyboards/cz+qwerty_wide.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("dk", include_str!("../data/keyboards/dk.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("epo", include_str!("../data/keyboards/epo.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("es", include_str!("../data/keyboards/es.yaml")),
 | 
			
		||||
    ("es+cat", include_str!("../data/keyboards/es+cat.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("fi", include_str!("../data/keyboards/fi.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("fr", include_str!("../data/keyboards/fr.yaml")),
 | 
			
		||||
    ("fr_wide", include_str!("../data/keyboards/fr_wide.yaml")),
 | 
			
		||||
    ("it+fur", include_str!("../data/keyboards/it+fur.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("gr", include_str!("../data/keyboards/gr.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("il", include_str!("../data/keyboards/il.yaml")),
 | 
			
		||||
    
 | 
			
		||||
    ("ir", include_str!("../data/keyboards/ir.yaml")),
 | 
			
		||||
    ("ir_wide", include_str!("../data/keyboards/ir_wide.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("it", include_str!("../data/keyboards/it.yaml")),
 | 
			
		||||
    ("it+fur", include_str!("../data/keyboards/it+fur.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("jp+kana", include_str!("../data/keyboards/jp+kana.yaml")),
 | 
			
		||||
    ("jp+kana_wide", include_str!("../data/keyboards/jp+kana_wide.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("no", include_str!("../data/keyboards/no.yaml")),
 | 
			
		||||
    ("number", include_str!("../data/keyboards/number.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("pl", include_str!("../data/keyboards/pl.yaml")),
 | 
			
		||||
    ("pl_wide", include_str!("../data/keyboards/pl_wide.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("ru", include_str!("../data/keyboards/ru.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("se", include_str!("../data/keyboards/se.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("th", include_str!("../data/keyboards/th.yaml")),
 | 
			
		||||
    ("th_wide", include_str!("../data/keyboards/th_wide.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("ua", include_str!("../data/keyboards/ua.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("us+colemak", include_str!("../data/keyboards/us+colemak.yaml")),
 | 
			
		||||
    ("us+colemak_wide", include_str!("../data/keyboards/us+colemak_wide.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("us+dvorak", include_str!("../data/keyboards/us+dvorak.yaml")),
 | 
			
		||||
    ("us+dvorak_wide", include_str!("../data/keyboards/us+dvorak_wide.yaml")),
 | 
			
		||||
 | 
			
		||||
    // Others
 | 
			
		||||
    ("number", include_str!("../data/keyboards/number.yaml")),
 | 
			
		||||
 | 
			
		||||
    // layout+overlay
 | 
			
		||||
    ("terminal", include_str!("../data/keyboards/terminal.yaml")),
 | 
			
		||||
    ("terminal_wide", include_str!("../data/keyboards/terminal_wide.yaml")),
 | 
			
		||||
@ -78,6 +122,7 @@ const LAYOUT_NAMES: &[(*const str, *const str)] = &[
 | 
			
		||||
    ("en-US", include_str!("../data/langs/en-US.txt")),
 | 
			
		||||
    ("es-ES", include_str!("../data/langs/es-ES.txt")),
 | 
			
		||||
    ("fur-IT", include_str!("../data/langs/fur-IT.txt")),
 | 
			
		||||
    ("he-IL", include_str!("../data/langs/he_IL.txt")),
 | 
			
		||||
    ("ja-JP", include_str!("../data/langs/ja-JP.txt")),
 | 
			
		||||
    ("pl-PL", include_str!("../data/langs/pl-PL.txt")),
 | 
			
		||||
    ("ru-RU", include_str!("../data/langs/ru-RU.txt")),
 | 
			
		||||
 | 
			
		||||
@ -43,10 +43,9 @@ struct _ServerContextService {
 | 
			
		||||
    struct submission *submission; // unowned
 | 
			
		||||
    struct squeek_layout_state *layout;
 | 
			
		||||
    struct ui_manager *manager; // unowned
 | 
			
		||||
    struct vis_manager *vis_manager; // owned
 | 
			
		||||
 | 
			
		||||
    gboolean visible;
 | 
			
		||||
    gboolean enabled;
 | 
			
		||||
    gboolean im_active;
 | 
			
		||||
    PhoshLayerSurface *window;
 | 
			
		||||
    GtkWidget *widget; // nullable
 | 
			
		||||
    guint hiding;
 | 
			
		||||
@ -240,6 +239,13 @@ server_context_service_real_show_keyboard (ServerContextService *self)
 | 
			
		||||
    gtk_widget_show (GTK_WIDGET(self->window));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean
 | 
			
		||||
show_keyboard_source_func(ServerContextService *context)
 | 
			
		||||
{
 | 
			
		||||
    server_context_service_real_show_keyboard(context);
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
server_context_service_real_hide_keyboard (ServerContextService *self)
 | 
			
		||||
{
 | 
			
		||||
@ -247,6 +253,13 @@ server_context_service_real_hide_keyboard (ServerContextService *self)
 | 
			
		||||
    self->visible = FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean
 | 
			
		||||
hide_keyboard_source_func(ServerContextService *context)
 | 
			
		||||
{
 | 
			
		||||
    server_context_service_real_hide_keyboard(context);
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean
 | 
			
		||||
on_hide (ServerContextService *self)
 | 
			
		||||
{
 | 
			
		||||
@ -256,7 +269,7 @@ on_hide (ServerContextService *self)
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
static void
 | 
			
		||||
server_context_service_show_keyboard (ServerContextService *self)
 | 
			
		||||
{
 | 
			
		||||
    g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(self));
 | 
			
		||||
@ -267,17 +280,30 @@ server_context_service_show_keyboard (ServerContextService *self)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!self->visible) {
 | 
			
		||||
        server_context_service_real_show_keyboard (self);
 | 
			
		||||
        g_idle_add((GSourceFunc)show_keyboard_source_func, self);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
server_context_service_force_show_keyboard (ServerContextService *self)
 | 
			
		||||
{
 | 
			
		||||
    if (!submission_hint_available(self->submission)) {
 | 
			
		||||
        eekboard_context_service_set_hint_purpose(
 | 
			
		||||
            self->state,
 | 
			
		||||
            ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE,
 | 
			
		||||
            ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
    server_context_service_show_keyboard(self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
server_context_service_hide_keyboard (ServerContextService *self)
 | 
			
		||||
{
 | 
			
		||||
    g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(self));
 | 
			
		||||
 | 
			
		||||
    if (self->visible) {
 | 
			
		||||
        server_context_service_real_hide_keyboard (self);
 | 
			
		||||
        g_idle_add((GSourceFunc)hide_keyboard_source_func, self);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -288,7 +314,7 @@ server_context_service_hide_keyboard (ServerContextService *self)
 | 
			
		||||
/// In this case, the user doesn't really need the keyboard surface
 | 
			
		||||
/// to disappear completely.
 | 
			
		||||
void
 | 
			
		||||
server_context_service_keyboard_release_visibility (ServerContextService *self)
 | 
			
		||||
server_context_service_release_visibility (ServerContextService *self)
 | 
			
		||||
{
 | 
			
		||||
    g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(self));
 | 
			
		||||
 | 
			
		||||
@ -297,6 +323,13 @@ server_context_service_keyboard_release_visibility (ServerContextService *self)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
server_context_service_set_physical_keyboard_present (ServerContextService *self, gboolean physical_keyboard_present)
 | 
			
		||||
{
 | 
			
		||||
    g_return_if_fail (SERVER_IS_CONTEXT_SERVICE (self));
 | 
			
		||||
    squeek_visman_set_keyboard_present(self->vis_manager, physical_keyboard_present);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
server_context_service_set_property (GObject      *object,
 | 
			
		||||
                                     guint         prop_id,
 | 
			
		||||
@ -310,7 +343,7 @@ server_context_service_set_property (GObject      *object,
 | 
			
		||||
        self->visible = g_value_get_boolean (value);
 | 
			
		||||
        break;
 | 
			
		||||
    case PROP_ENABLED:
 | 
			
		||||
        server_context_service_set_enabled (self, g_value_get_boolean (value));
 | 
			
		||||
        server_context_service_set_physical_keyboard_present (self, !g_value_get_boolean (value));
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 | 
			
		||||
@ -385,12 +418,14 @@ server_context_service_class_init (ServerContextServiceClass *klass)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
server_context_service_init (ServerContextService *self) {
 | 
			
		||||
server_context_service_init (ServerContextService *self) {}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
init (ServerContextService *self) {
 | 
			
		||||
    const char *schema_name = "org.gnome.desktop.a11y.applications";
 | 
			
		||||
    GSettingsSchemaSource *ssrc = g_settings_schema_source_get_default();
 | 
			
		||||
    g_autoptr(GSettingsSchema) schema = NULL;
 | 
			
		||||
 | 
			
		||||
    self->enabled = TRUE;
 | 
			
		||||
    if (!ssrc) {
 | 
			
		||||
        g_warning("No gsettings schemas installed.");
 | 
			
		||||
        return;
 | 
			
		||||
@ -407,37 +442,24 @@ server_context_service_init (ServerContextService *self) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ServerContextService *
 | 
			
		||||
server_context_service_new (EekboardContextService *self, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman)
 | 
			
		||||
server_context_service_new (EekboardContextService *self, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman, struct vis_manager *visman)
 | 
			
		||||
{
 | 
			
		||||
    ServerContextService *ui = g_object_new (SERVER_TYPE_CONTEXT_SERVICE, NULL);
 | 
			
		||||
    ui->submission = submission;
 | 
			
		||||
    ui->state = self;
 | 
			
		||||
    ui->layout = layout;
 | 
			
		||||
    ui->manager = uiman;
 | 
			
		||||
    ui->vis_manager = visman;
 | 
			
		||||
    init(ui);
 | 
			
		||||
    return ui;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
server_context_service_update_visible (ServerContextService *self, gboolean delay) {
 | 
			
		||||
    if (self->enabled && self->im_active) {
 | 
			
		||||
server_context_service_update_visible (ServerContextService *self, gboolean visible) {
 | 
			
		||||
    if (visible) {
 | 
			
		||||
        server_context_service_show_keyboard(self);
 | 
			
		||||
    } else if (delay) {
 | 
			
		||||
        server_context_service_keyboard_release_visibility(self);
 | 
			
		||||
    } else {
 | 
			
		||||
        server_context_service_hide_keyboard(self);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
server_context_service_set_enabled (ServerContextService *self, gboolean enabled)
 | 
			
		||||
{
 | 
			
		||||
    g_return_if_fail (SERVER_IS_CONTEXT_SERVICE (self));
 | 
			
		||||
    self->enabled = enabled;
 | 
			
		||||
    server_context_service_update_visible(self, FALSE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
server_context_service_set_im_active(ServerContextService *self, uint32_t active) {
 | 
			
		||||
    self->im_active = active;
 | 
			
		||||
    server_context_service_update_visible(self, TRUE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -29,11 +29,10 @@ G_BEGIN_DECLS
 | 
			
		||||
/** Manages the lifecycle of the window displaying layouts. */
 | 
			
		||||
G_DECLARE_FINAL_TYPE (ServerContextService, server_context_service, SERVER, CONTEXT_SERVICE, GObject)
 | 
			
		||||
 | 
			
		||||
ServerContextService *server_context_service_new(EekboardContextService *self, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman);
 | 
			
		||||
ServerContextService *server_context_service_new(EekboardContextService *self, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman, struct vis_manager *visman);
 | 
			
		||||
enum squeek_arrangement_kind server_context_service_get_layout_type(ServerContextService *);
 | 
			
		||||
void server_context_service_show_keyboard (ServerContextService *self);
 | 
			
		||||
void server_context_service_force_show_keyboard (ServerContextService *self);
 | 
			
		||||
void server_context_service_hide_keyboard (ServerContextService *self);
 | 
			
		||||
void server_context_service_set_enabled (ServerContextService *self, gboolean enabled);
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
#endif  /* SERVER_CONTEXT_SERVICE_H */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -50,9 +50,16 @@ struct squeekboard {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
GMainLoop *loop;
 | 
			
		||||
static gboolean opt_system = FALSE;
 | 
			
		||||
static gchar *opt_address = NULL;
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
quit (void)
 | 
			
		||||
{
 | 
			
		||||
  g_main_loop_quit (loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// D-Bus
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
@ -131,6 +138,67 @@ static const struct wl_registry_listener registry_listener = {
 | 
			
		||||
#define SESSION_NAME "sm.puri.OSK0"
 | 
			
		||||
 | 
			
		||||
GDBusProxy *_proxy = NULL;
 | 
			
		||||
GDBusProxy *_client_proxy = NULL;
 | 
			
		||||
gchar      *_client_path = NULL;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
send_quit_response (GDBusProxy  *proxy)
 | 
			
		||||
{
 | 
			
		||||
    g_debug ("Calling EndSessionResponse");
 | 
			
		||||
    g_dbus_proxy_call (proxy, "EndSessionResponse",
 | 
			
		||||
        g_variant_new ("(bs)", TRUE, ""), G_DBUS_CALL_FLAGS_NONE,
 | 
			
		||||
        G_MAXINT, NULL, NULL, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
unregister_client (void)
 | 
			
		||||
{
 | 
			
		||||
    g_autoptr (GError) error = NULL;
 | 
			
		||||
 | 
			
		||||
    g_return_if_fail (G_IS_DBUS_PROXY (_proxy));
 | 
			
		||||
    g_return_if_fail (_client_path != NULL);
 | 
			
		||||
 | 
			
		||||
    g_debug ("Unregistering client");
 | 
			
		||||
 | 
			
		||||
    g_dbus_proxy_call_sync (_proxy,
 | 
			
		||||
			    "UnregisterClient",
 | 
			
		||||
			    g_variant_new ("(o)", _client_path),
 | 
			
		||||
			    G_DBUS_CALL_FLAGS_NONE,
 | 
			
		||||
			    G_MAXINT,
 | 
			
		||||
			    NULL,
 | 
			
		||||
			    &error);
 | 
			
		||||
 | 
			
		||||
    if (error) {
 | 
			
		||||
        g_warning ("Failed to unregister client: %s", error->message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    g_clear_object (&_client_proxy);
 | 
			
		||||
    g_clear_pointer (&_client_path, g_free);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void client_proxy_signal (GDBusProxy  *proxy,
 | 
			
		||||
				 const gchar *sender_name,
 | 
			
		||||
				 const gchar *signal_name,
 | 
			
		||||
				 GVariant    *parameters,
 | 
			
		||||
				 gpointer     user_data)
 | 
			
		||||
{
 | 
			
		||||
    if (g_str_equal (signal_name, "QueryEndSession")) {
 | 
			
		||||
        g_debug ("Received QueryEndSession");
 | 
			
		||||
        send_quit_response (proxy);
 | 
			
		||||
    } else if (g_str_equal (signal_name, "CancelEndSession")) {
 | 
			
		||||
        g_debug ("Received CancelEndSession");
 | 
			
		||||
    } else if (g_str_equal (signal_name, "EndSession")) {
 | 
			
		||||
        g_debug ("Received EndSession");
 | 
			
		||||
        send_quit_response (proxy);
 | 
			
		||||
        unregister_client ();
 | 
			
		||||
	quit ();
 | 
			
		||||
    } else if (g_str_equal (signal_name, "Stop")) {
 | 
			
		||||
        g_debug ("Received Stop");
 | 
			
		||||
        unregister_client ();
 | 
			
		||||
        quit ();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
session_register(void) {
 | 
			
		||||
@ -151,7 +219,8 @@ session_register(void) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    g_dbus_proxy_call_sync(_proxy, "RegisterClient",
 | 
			
		||||
    g_autoptr (GVariant) res = NULL;
 | 
			
		||||
    res = g_dbus_proxy_call_sync(_proxy, "RegisterClient",
 | 
			
		||||
        g_variant_new("(ss)", SESSION_NAME, autostart_id),
 | 
			
		||||
        G_DBUS_CALL_FLAGS_NONE, 1000, NULL, &error);
 | 
			
		||||
    if (error) {
 | 
			
		||||
@ -160,6 +229,22 @@ session_register(void) {
 | 
			
		||||
        g_clear_error(&error);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    g_variant_get (res, "(o)", &_client_path);
 | 
			
		||||
    g_debug ("Registered client at '%s'", _client_path);
 | 
			
		||||
 | 
			
		||||
    _client_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
 | 
			
		||||
      0, NULL, "org.gnome.SessionManager", _client_path,
 | 
			
		||||
      "org.gnome.SessionManager.ClientPrivate", NULL, &error);
 | 
			
		||||
    if (error) {
 | 
			
		||||
        g_warning ("Failed to get client proxy: %s", error->message);
 | 
			
		||||
	g_clear_error (&error);
 | 
			
		||||
	g_free (_client_path);
 | 
			
		||||
	_client_path = NULL;
 | 
			
		||||
	return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    g_signal_connect (_client_proxy, "g-signal", G_CALLBACK (client_proxy_signal), NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
@ -277,8 +362,11 @@ main (int argc, char **argv)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct vis_manager *vis_manager = squeek_visman_new();
 | 
			
		||||
 | 
			
		||||
    instance.submission = get_submission(instance.wayland.input_method_manager,
 | 
			
		||||
                                         instance.wayland.virtual_keyboard_manager,
 | 
			
		||||
                                         vis_manager,
 | 
			
		||||
                                         instance.wayland.seat,
 | 
			
		||||
                                         instance.settings_context);
 | 
			
		||||
 | 
			
		||||
@ -288,15 +376,15 @@ main (int argc, char **argv)
 | 
			
		||||
                instance.settings_context,
 | 
			
		||||
                instance.submission,
 | 
			
		||||
                &instance.layout_choice,
 | 
			
		||||
                instance.ui_manager);
 | 
			
		||||
                instance.ui_manager,
 | 
			
		||||
                vis_manager);
 | 
			
		||||
    if (!ui_context) {
 | 
			
		||||
        g_error("Could not initialize GUI");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
    instance.ui_context = ui_context;
 | 
			
		||||
    if (instance.submission) {
 | 
			
		||||
        submission_set_ui(instance.submission, instance.ui_context);
 | 
			
		||||
    }
 | 
			
		||||
    squeek_visman_set_ui(vis_manager, instance.ui_context);
 | 
			
		||||
 | 
			
		||||
    if (instance.dbus_handler) {
 | 
			
		||||
        dbus_handler_set_ui_context(instance.dbus_handler, instance.ui_context);
 | 
			
		||||
    }
 | 
			
		||||
@ -304,8 +392,7 @@ main (int argc, char **argv)
 | 
			
		||||
 | 
			
		||||
    session_register();
 | 
			
		||||
 | 
			
		||||
    GMainLoop *loop = g_main_loop_new (NULL, FALSE);
 | 
			
		||||
 | 
			
		||||
    loop = g_main_loop_new (NULL, FALSE);
 | 
			
		||||
    g_main_loop_run (loop);
 | 
			
		||||
 | 
			
		||||
    if (connection) {
 | 
			
		||||
 | 
			
		||||
@ -4,17 +4,20 @@
 | 
			
		||||
#include "input-method-unstable-v2-client-protocol.h"
 | 
			
		||||
#include "virtual-keyboard-unstable-v1-client-protocol.h"
 | 
			
		||||
#include "eek/eek-types.h"
 | 
			
		||||
#include "src/ui_manager.h"
 | 
			
		||||
 | 
			
		||||
struct submission;
 | 
			
		||||
struct squeek_layout;
 | 
			
		||||
 | 
			
		||||
struct submission* get_submission(struct zwp_input_method_manager_v2 *immanager,
 | 
			
		||||
                                  struct zwp_virtual_keyboard_manager_v1 *vkmanager,
 | 
			
		||||
                                  struct vis_manager *vis_manager,
 | 
			
		||||
                                  struct wl_seat *seat,
 | 
			
		||||
                                  EekboardContextService *state);
 | 
			
		||||
 | 
			
		||||
// Defined in Rust
 | 
			
		||||
struct submission* submission_new(struct zwp_input_method_v2 *im, struct zwp_virtual_keyboard_v1 *vk, EekboardContextService *state);
 | 
			
		||||
struct submission* submission_new(struct zwp_input_method_v2 *im, struct zwp_virtual_keyboard_v1 *vk, EekboardContextService *state, struct vis_manager *vis_manager);
 | 
			
		||||
uint8_t submission_hint_available(struct submission *self);
 | 
			
		||||
void submission_set_ui(struct submission *self, ServerContextService *ui_context);
 | 
			
		||||
void submission_use_layout(struct submission *self, struct squeek_layout *layout, uint32_t time);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,7 @@ use ::imservice;
 | 
			
		||||
use ::imservice::IMService;
 | 
			
		||||
use ::keyboard::{ KeyCode, KeyStateId, Modifiers, PressType };
 | 
			
		||||
use ::layout;
 | 
			
		||||
use ::ui_manager::VisibilityManager;
 | 
			
		||||
use ::util::vec_remove;
 | 
			
		||||
use ::vkeyboard;
 | 
			
		||||
use ::vkeyboard::VirtualKeyboard;
 | 
			
		||||
@ -38,14 +39,11 @@ pub mod c {
 | 
			
		||||
    use std::os::raw::c_void;
 | 
			
		||||
 | 
			
		||||
    use ::imservice::c::InputMethod;
 | 
			
		||||
    use ::util::c::Wrapped;
 | 
			
		||||
    use ::vkeyboard::c::ZwpVirtualKeyboardV1;
 | 
			
		||||
 | 
			
		||||
    // The following defined in C
 | 
			
		||||
 | 
			
		||||
    /// ServerContextService*
 | 
			
		||||
    #[repr(transparent)]
 | 
			
		||||
    pub struct UIManager(*const c_void);
 | 
			
		||||
 | 
			
		||||
    /// EekboardContextService*
 | 
			
		||||
    #[repr(transparent)]
 | 
			
		||||
    pub struct StateManager(*const c_void);
 | 
			
		||||
@ -55,12 +53,18 @@ pub mod c {
 | 
			
		||||
    fn submission_new(
 | 
			
		||||
        im: *mut InputMethod,
 | 
			
		||||
        vk: ZwpVirtualKeyboardV1,
 | 
			
		||||
        state_manager: *const StateManager
 | 
			
		||||
        state_manager: *const StateManager,
 | 
			
		||||
        visibility_manager: Wrapped<VisibilityManager>,
 | 
			
		||||
    ) -> *mut Submission {
 | 
			
		||||
        let imservice = if im.is_null() {
 | 
			
		||||
            None
 | 
			
		||||
        } else {
 | 
			
		||||
            Some(IMService::new(im, state_manager))
 | 
			
		||||
            let visibility_manager = visibility_manager.clone_ref();
 | 
			
		||||
            Some(IMService::new(
 | 
			
		||||
                im,
 | 
			
		||||
                state_manager,
 | 
			
		||||
                Box::new(move |active| visibility_manager.borrow_mut().set_im_active(active)),
 | 
			
		||||
            ))
 | 
			
		||||
        };
 | 
			
		||||
        // TODO: add vkeyboard too
 | 
			
		||||
        Box::<Submission>::into_raw(Box::new(
 | 
			
		||||
@ -75,23 +79,6 @@ pub mod c {
 | 
			
		||||
        ))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Use to initialize the UI reference
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn submission_set_ui(submission: *mut Submission, ui_manager: *const UIManager) {
 | 
			
		||||
        if submission.is_null() {
 | 
			
		||||
            panic!("Null submission pointer");
 | 
			
		||||
        }
 | 
			
		||||
        let submission: &mut Submission = unsafe { &mut *submission };
 | 
			
		||||
        if let Some(ref mut imservice) = &mut submission.imservice {
 | 
			
		||||
            imservice.set_ui_manager(if ui_manager.is_null() {
 | 
			
		||||
                None
 | 
			
		||||
            } else {
 | 
			
		||||
                Some(ui_manager)
 | 
			
		||||
            })
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn submission_use_layout(
 | 
			
		||||
@ -106,6 +93,18 @@ pub mod c {
 | 
			
		||||
        let layout = unsafe { &*layout };
 | 
			
		||||
        submission.use_layout(layout, Timestamp(time));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn submission_hint_available(submission: *mut Submission) -> u8 {
 | 
			
		||||
        if submission.is_null() {
 | 
			
		||||
            panic!("Null submission pointer");
 | 
			
		||||
        }
 | 
			
		||||
        let submission: &mut Submission = unsafe { &mut *submission };
 | 
			
		||||
        let active = submission.imservice.as_ref()
 | 
			
		||||
            .map(|imservice| imservice.is_active());
 | 
			
		||||
        (Some(true) == active) as u8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
@ -271,6 +270,7 @@ impl Submission {
 | 
			
		||||
            .map(|(_id, m)| match m {
 | 
			
		||||
                Modifier::Control => Modifiers::CONTROL,
 | 
			
		||||
                Modifier::Alt => Modifiers::MOD1,
 | 
			
		||||
                Modifier::Mod4 => Modifiers::MOD4,
 | 
			
		||||
            })
 | 
			
		||||
            .fold(Modifiers::empty(), |m, n| m | n);
 | 
			
		||||
        self.virtual_keyboard.set_modifiers_state(raw_modifiers);
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,7 @@
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
 | 
			
		||||
#include "eek/eek-types.h"
 | 
			
		||||
#include "outputs.h"
 | 
			
		||||
 | 
			
		||||
struct ui_manager;
 | 
			
		||||
@ -11,4 +12,9 @@ struct ui_manager *squeek_uiman_new(void);
 | 
			
		||||
void squeek_uiman_set_output(struct ui_manager *uiman, struct squeek_output_handle output);
 | 
			
		||||
uint32_t squeek_uiman_get_perceptual_height(struct ui_manager *uiman);
 | 
			
		||||
 | 
			
		||||
struct vis_manager;
 | 
			
		||||
 | 
			
		||||
struct vis_manager *squeek_visman_new(void);
 | 
			
		||||
void squeek_visman_set_ui(struct vis_manager *visman, ServerContextService *ui_context);
 | 
			
		||||
void squeek_visman_set_keyboard_present(struct vis_manager *visman, uint32_t keyboard_present);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@ -10,9 +10,48 @@
 | 
			
		||||
use std::cmp::min;
 | 
			
		||||
use ::outputs::c::OutputHandle;
 | 
			
		||||
 | 
			
		||||
mod c {
 | 
			
		||||
pub mod c {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use std::os::raw::c_void;
 | 
			
		||||
    use ::util::c::Wrapped;
 | 
			
		||||
    
 | 
			
		||||
    /// ServerContextService*
 | 
			
		||||
    #[repr(transparent)]
 | 
			
		||||
    pub struct UIManager(*const c_void);
 | 
			
		||||
 | 
			
		||||
    extern "C" {
 | 
			
		||||
        pub fn server_context_service_update_visible(imservice: *const UIManager, active: u32);
 | 
			
		||||
        pub fn server_context_service_release_visibility(imservice: *const UIManager);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_visman_new() -> Wrapped<VisibilityManager> {
 | 
			
		||||
        Wrapped::new(VisibilityManager {
 | 
			
		||||
            ui_manager: None,
 | 
			
		||||
            visibility_state: VisibilityFactors {
 | 
			
		||||
                im_active: false,
 | 
			
		||||
                physical_keyboard_present: false,
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Use to initialize the UI reference
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_visman_set_ui(visman: Wrapped<VisibilityManager>, ui_manager: *const UIManager) {
 | 
			
		||||
        let visman = visman.clone_ref();
 | 
			
		||||
        let mut visman = visman.borrow_mut();
 | 
			
		||||
        visman.set_ui_manager(Some(ui_manager))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_visman_set_keyboard_present(visman: Wrapped<VisibilityManager>, present: u32) {
 | 
			
		||||
        let visman = visman.clone_ref();
 | 
			
		||||
        let mut visman = visman.borrow_mut();
 | 
			
		||||
        visman.set_keyboard_present(present != 0)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
@ -79,3 +118,131 @@ impl Manager {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(PartialEq, Debug)]
 | 
			
		||||
enum Visibility {
 | 
			
		||||
    Hidden,
 | 
			
		||||
    Visible,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
enum VisibilityTransition {
 | 
			
		||||
    /// Hide immediately
 | 
			
		||||
    Hide,
 | 
			
		||||
    /// Hide if no show request comes soon
 | 
			
		||||
    Release,
 | 
			
		||||
    /// Show instantly
 | 
			
		||||
    Show,
 | 
			
		||||
    /// Don't do anything
 | 
			
		||||
    NoTransition,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Contains visibility policy
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
struct VisibilityFactors {
 | 
			
		||||
    im_active: bool,
 | 
			
		||||
    physical_keyboard_present: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl VisibilityFactors {
 | 
			
		||||
    /// Static policy.
 | 
			
		||||
    /// Use when transitioning from an undefined state (e.g. no UI before).
 | 
			
		||||
    fn desired(&self) -> Visibility {
 | 
			
		||||
        match self {
 | 
			
		||||
            VisibilityFactors {
 | 
			
		||||
                im_active: true,
 | 
			
		||||
                physical_keyboard_present: false,
 | 
			
		||||
            } => Visibility::Visible,
 | 
			
		||||
            _ => Visibility::Hidden,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /// Stateful policy
 | 
			
		||||
    fn transition_to(&self, next: &Self) -> VisibilityTransition {
 | 
			
		||||
        use self::Visibility::*;
 | 
			
		||||
        let im_deactivation = self.im_active && !next.im_active;
 | 
			
		||||
        match (self.desired(), next.desired(), im_deactivation) {
 | 
			
		||||
            (Visible, Hidden, true) => VisibilityTransition::Release,
 | 
			
		||||
            (Visible, Hidden, _) => VisibilityTransition::Hide,
 | 
			
		||||
            (Hidden, Visible, _) => VisibilityTransition::Show,
 | 
			
		||||
            _ => VisibilityTransition::NoTransition,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Temporary struct for migration. Should be integrated with Manager eventually.
 | 
			
		||||
pub struct VisibilityManager {
 | 
			
		||||
    /// Owned reference. Be careful, it's shared with C at large
 | 
			
		||||
    ui_manager: Option<*const c::UIManager>,
 | 
			
		||||
    visibility_state: VisibilityFactors,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl VisibilityManager {
 | 
			
		||||
    fn set_ui_manager(&mut self, ui_manager: Option<*const c::UIManager>) {
 | 
			
		||||
        let new = VisibilityManager {
 | 
			
		||||
            ui_manager,
 | 
			
		||||
            ..unsafe { self.clone() }
 | 
			
		||||
        };
 | 
			
		||||
        self.apply_changes(new);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn apply_changes(&mut self, new: Self) {
 | 
			
		||||
        if let Some(ui) = &new.ui_manager {
 | 
			
		||||
            if self.ui_manager.is_none() {
 | 
			
		||||
                // Previous state was never applied, so effectively undefined.
 | 
			
		||||
                // Just apply the new one.
 | 
			
		||||
                let new_state = new.visibility_state.desired();
 | 
			
		||||
                unsafe {
 | 
			
		||||
                    c::server_context_service_update_visible(
 | 
			
		||||
                        *ui,
 | 
			
		||||
                        (new_state == Visibility::Visible) as u32,
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                match self.visibility_state.transition_to(&new.visibility_state) {
 | 
			
		||||
                    VisibilityTransition::Hide => unsafe {
 | 
			
		||||
                        c::server_context_service_update_visible(*ui, 0);
 | 
			
		||||
                    },
 | 
			
		||||
                    VisibilityTransition::Show => unsafe {
 | 
			
		||||
                        c::server_context_service_update_visible(*ui, 1);
 | 
			
		||||
                    },
 | 
			
		||||
                    VisibilityTransition::Release => unsafe {
 | 
			
		||||
                        c::server_context_service_release_visibility(*ui);
 | 
			
		||||
                    },
 | 
			
		||||
                    VisibilityTransition::NoTransition => {}
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        *self = new;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn set_im_active(&mut self, im_active: bool) {
 | 
			
		||||
        let new = VisibilityManager {
 | 
			
		||||
            visibility_state: VisibilityFactors {
 | 
			
		||||
                im_active,
 | 
			
		||||
                ..self.visibility_state.clone()
 | 
			
		||||
            },
 | 
			
		||||
            ..unsafe { self.clone() }
 | 
			
		||||
        };
 | 
			
		||||
        self.apply_changes(new);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn set_keyboard_present(&mut self, keyboard_present: bool) {
 | 
			
		||||
        let new = VisibilityManager {
 | 
			
		||||
            visibility_state: VisibilityFactors {
 | 
			
		||||
                physical_keyboard_present: keyboard_present,
 | 
			
		||||
                ..self.visibility_state.clone()
 | 
			
		||||
            },
 | 
			
		||||
            ..unsafe { self.clone() }
 | 
			
		||||
        };
 | 
			
		||||
        self.apply_changes(new);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// The struct is not really safe to clone due to the ui_manager reference.
 | 
			
		||||
    /// This is only a helper for getting desired visibility.
 | 
			
		||||
    unsafe fn clone(&self) -> Self {
 | 
			
		||||
        VisibilityManager {
 | 
			
		||||
            ui_manager: self.ui_manager.clone(),
 | 
			
		||||
            visibility_state: self.visibility_state.clone(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -37,7 +37,6 @@ pub mod c {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    extern "C" {
 | 
			
		||||
        // From libc, to let KeyMap get deallocated.
 | 
			
		||||
        fn close(fd: u32);
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user