Merge branch 'central_visible' into 'master'
Central visibility policy See merge request Librem5/squeekboard!409
This commit is contained in:
@ -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
|
||||
@ -42,8 +41,6 @@ pub mod c {
|
||||
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
|
||||
@ -153,7 +150,7 @@ pub mod c {
|
||||
};
|
||||
|
||||
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 +176,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 +329,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 +346,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 +368,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 => {
|
||||
|
||||
@ -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;
|
||||
@ -288,7 +287,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 +296,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 +316,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 +391,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 +415,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_hide_keyboard (ServerContextService *self);
|
||||
void server_context_service_set_enabled (ServerContextService *self, gboolean enabled);
|
||||
G_END_DECLS
|
||||
#endif /* SERVER_CONTEXT_SERVICE_H */
|
||||
|
||||
|
||||
@ -277,8 +277,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 +291,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);
|
||||
}
|
||||
|
||||
@ -4,17 +4,19 @@
|
||||
#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);
|
||||
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(
|
||||
|
||||
@ -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,49 @@
|
||||
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);
|
||||
|
||||
#[no_mangle]
|
||||
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 +119,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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user