diff --git a/eek/eek-gtk-keyboard.c b/eek/eek-gtk-keyboard.c index 19b0837d..bb6a6427 100644 --- a/eek/eek-gtk-keyboard.c +++ b/eek/eek-gtk-keyboard.c @@ -35,6 +35,7 @@ #include "eekboard/eekboard-context-service.h" #include "src/layout.h" +#include "src/popover.h" #include "src/submission.h" #define LIBFEEDBACK_USE_UNSTABLE_API @@ -48,6 +49,8 @@ typedef struct _EekGtkKeyboardPrivate struct render_geometry render_geometry; // mutable EekboardContextService *eekboard_context; // unowned reference + struct squeek_popover *popover; // shared reference + struct squeek_state_manager *state_manager; // shared reference struct submission *submission; // unowned reference struct squeek_layout_state *layout; // unowned @@ -118,15 +121,6 @@ eek_gtk_keyboard_real_draw (GtkWidget *self, return FALSE; } -// Units of virtual pixels size -static enum squeek_arrangement_kind get_type(uint32_t width, uint32_t height) { - (void)height; - if (width < 540) { - return ARRANGEMENT_KIND_BASE; - } - return ARRANGEMENT_KIND_WIDE; -} - static void eek_gtk_keyboard_real_size_allocate (GtkWidget *self, GtkAllocation *allocation) @@ -134,15 +128,6 @@ eek_gtk_keyboard_real_size_allocate (GtkWidget *self, EekGtkKeyboard *keyboard = EEK_GTK_KEYBOARD (self); EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (keyboard); - // check if the change would switch types - enum squeek_arrangement_kind new_type = get_type( - (uint32_t)(allocation->width - allocation->x), - (uint32_t)(allocation->height - allocation->y)); - if (priv->layout->arrangement != new_type) { - priv->layout->arrangement = new_type; - uint32_t time = gdk_event_get_time(NULL); - eekboard_context_service_use_layout(priv->eekboard_context, priv->layout, time); - } if (priv->renderer) { set_allocation_size (keyboard, priv->keyboard->layout, @@ -158,6 +143,7 @@ on_event_triggered (LfbEvent *event, GAsyncResult *res, gpointer unused) { + (void)unused; g_autoptr (GError) err = NULL; if (!lfb_event_trigger_feedback_finish (event, res, &err)) { @@ -188,7 +174,7 @@ static void drag(EekGtkKeyboard *self, squeek_layout_drag(eekboard_context_service_get_keyboard(priv->eekboard_context)->layout, priv->submission, x, y, priv->render_geometry.widget_to_layout, time, - priv->eekboard_context, self); + priv->popover, priv->state_manager, self); } static void release(EekGtkKeyboard *self, guint32 time) @@ -199,7 +185,7 @@ static void release(EekGtkKeyboard *self, guint32 time) } squeek_layout_release(eekboard_context_service_get_keyboard(priv->eekboard_context)->layout, priv->submission, priv->render_geometry.widget_to_layout, time, - priv->eekboard_context, self); + priv->popover, priv->state_manager, self); } static gboolean @@ -406,13 +392,15 @@ on_notify_keyboard (GObject *object, GtkWidget * eek_gtk_keyboard_new (EekboardContextService *eekservice, struct submission *submission, - struct squeek_layout_state *layout) + struct squeek_state_manager *state_manager, + struct squeek_popover *popover) { EekGtkKeyboard *ret = EEK_GTK_KEYBOARD(g_object_new (EEK_TYPE_GTK_KEYBOARD, NULL)); EekGtkKeyboardPrivate *priv = (EekGtkKeyboardPrivate*)eek_gtk_keyboard_get_instance_private (ret); + priv->popover = popover; priv->eekboard_context = eekservice; priv->submission = submission; - priv->layout = layout; + priv->state_manager = state_manager; priv->renderer = NULL; // This should really be done on initialization. // Before the widget is allocated, diff --git a/eek/eek-gtk-keyboard.h b/eek/eek-gtk-keyboard.h index faccd1fa..558d5849 100644 --- a/eek/eek-gtk-keyboard.h +++ b/eek/eek-gtk-keyboard.h @@ -30,6 +30,8 @@ #include "eek/eek-renderer.h" #include "eek/eek-types.h" +#include "src/main.h" +#include "src/popover.h" struct submission; struct squeek_layout_state; @@ -48,7 +50,7 @@ struct _EekGtkKeyboardClass gpointer pdummy[24]; }; -GtkWidget *eek_gtk_keyboard_new (EekboardContextService *eekservice, struct submission *submission, struct squeek_layout_state *layout); +GtkWidget *eek_gtk_keyboard_new (EekboardContextService *eekservice, struct submission *submission, struct squeek_state_manager *state_manager, struct squeek_popover *popover); void eek_gtk_keyboard_emit_feedback (EekGtkKeyboard *self); G_END_DECLS diff --git a/eekboard/eekboard-context-service.c b/eekboard/eekboard-context-service.c index ec1ac005..92114a4e 100644 --- a/eekboard/eekboard-context-service.c +++ b/eekboard/eekboard-context-service.c @@ -55,7 +55,7 @@ static guint signals[LAST_SIGNAL] = { 0, }; */ struct _EekboardContextService { GObject parent; - struct squeek_layout_state *layout; // Unowned + struct squeek_state_manager *state_manager; // shared reference LevelKeyboard *keyboard; // currently used keyboard GSettings *settings; // Owned reference @@ -126,24 +126,7 @@ settings_get_layout(GSettings *settings, char **type, char **layout) g_variant_unref(inputs); } -void -eekboard_context_service_use_layout(EekboardContextService *context, struct squeek_layout_state *state, uint32_t timestamp) { - gchar *layout_name = state->layout_name; - gchar *overlay_name = state->overlay_name; - - // try to get the best keyboard layout - if (layout_name == NULL) { - layout_name = "us"; - } - - // overlay is "Normal" for most layouts, we will only look for "terminal" in rust code. - // for now just avoid passing a null pointer - if (overlay_name == NULL) { - overlay_name = ""; // fallback to Normal - } - - // generic part follows - struct squeek_layout *layout = squeek_load_layout(layout_name, state->arrangement, state->purpose, overlay_name); +void eekboard_context_service_set_layout(EekboardContextService *context, struct squeek_layout *layout, uint32_t timestamp) { LevelKeyboard *keyboard = level_keyboard_new(layout); // set as current LevelKeyboard *previous_keyboard = context->keyboard; @@ -169,17 +152,7 @@ static void eekboard_context_service_update_settings_layout(EekboardContextServi settings_get_layout(context->settings, &keyboard_type, &keyboard_layout); - if (g_strcmp0(context->layout->layout_name, keyboard_layout) != 0 || context->layout->overlay_name) { - g_free(context->layout->overlay_name); - context->layout->overlay_name = NULL; - if (keyboard_layout) { - g_free(context->layout->layout_name); - context->layout->layout_name = g_strdup(keyboard_layout); - } - // This must actually update the UI. - uint32_t time = gdk_event_get_time(NULL); - eekboard_context_service_use_layout(context, context->layout, time); - } + squeek_state_send_layout_set(context->state_manager, keyboard_layout, keyboard_type, gdk_event_get_time(NULL)); } static gboolean @@ -297,47 +270,17 @@ eekboard_context_service_get_keyboard (EekboardContextService *context) return context->keyboard; } -// Used from Rust. -// TODO: move hint management to Rust entirely -void eekboard_context_service_set_hint_purpose(EekboardContextService *context, - uint32_t hint, uint32_t purpose) -{ - if (context->layout->hint != hint || context->layout->purpose != purpose) { - context->layout->hint = hint; - context->layout->purpose = purpose; - uint32_t time = gdk_event_get_time(NULL); - eekboard_context_service_use_layout(context, context->layout, time); - } -} - -void -eekboard_context_service_set_overlay(EekboardContextService *context, const char* name) { - if (g_strcmp0(context->layout->overlay_name, name)) { - g_free(context->layout->overlay_name); - context->layout->overlay_name = g_strdup(name); - uint32_t time = gdk_event_get_time(NULL); - eekboard_context_service_use_layout(context, context->layout, time); - } -} - -const char* -eekboard_context_service_get_overlay(EekboardContextService *context) { - return context->layout->overlay_name; -} - -EekboardContextService *eekboard_context_service_new(struct squeek_layout_state *state) +EekboardContextService *eekboard_context_service_new(struct squeek_state_manager *state_manager) { EekboardContextService *context = g_object_new (EEKBOARD_TYPE_CONTEXT_SERVICE, NULL); - context->layout = state; + context->state_manager = state_manager; eekboard_context_service_update_settings_layout(context); - uint32_t time = gdk_event_get_time(NULL); - eekboard_context_service_use_layout(context, context->layout, time); return context; } void eekboard_context_service_set_submission(EekboardContextService *context, struct submission *submission) { context->submission = submission; - if (context->submission) { + if (context->submission && context->keyboard) { uint32_t time = gdk_event_get_time(NULL); submission_use_layout(context->submission, context->keyboard->layout, time); } diff --git a/eekboard/eekboard-context-service.h b/eekboard/eekboard-context-service.h index 4b2e706a..50525d9f 100644 --- a/eekboard/eekboard-context-service.h +++ b/eekboard/eekboard-context-service.h @@ -24,6 +24,7 @@ #include "src/submission.h" #include "src/layout.h" +#include "src/main.h" #include "virtual-keyboard-unstable-v1-client-protocol.h" #include "text-input-unstable-v3-client-protocol.h" @@ -37,7 +38,7 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE(EekboardContextService, eekboard_context_service, EEKBOARD, CONTEXT_SERVICE, GObject) -EekboardContextService *eekboard_context_service_new(struct squeek_layout_state *state); +EekboardContextService *eekboard_context_service_new(struct squeek_state_manager *state_manager); void eekboard_context_service_set_submission(EekboardContextService *context, struct submission *submission); void eekboard_context_service_destroy (EekboardContextService *context); LevelKeyboard *eekboard_context_service_get_keyboard(EekboardContextService *context); @@ -45,7 +46,5 @@ LevelKeyboard *eekboard_context_service_get_keyboard(EekboardContextService *con void eekboard_context_service_set_keymap(EekboardContextService *context, const LevelKeyboard *keyboard); -void -eekboard_context_service_use_layout(EekboardContextService *context, struct squeek_layout_state *layout, uint32_t timestamp); G_END_DECLS #endif /* EEKBOARD_CONTEXT_SERVICE_H */ diff --git a/src/actors/mod.rs b/src/actors/mod.rs new file mode 100644 index 00000000..378da9eb --- /dev/null +++ b/src/actors/mod.rs @@ -0,0 +1,23 @@ +/* Copyright (C) 2022 Purism SPC + * SPDX-License-Identifier: GPL-3.0+ + */ + +/*! Actors are parts of Squeekboard containing state independent from the main application state. + +Because main application state is meant to be immutable, +it cannot be referenced directly by pieces of logic +interacting with the environment. + +Such impure logic is split away (actor's logic) +and combined with relevant pieces of state (actor state), +thus preserving the purity (and sometimes simplicity) of the main state. + +Actors can communicate with the main state by sending it messages, +and by receiving updates from it. +*/ + +// TODO: move crate::panel into crate::actors::panel. +// Panel contains state and logic to protect the main state from getting flooded +// with low-level wayland and gtk sizing events. + +pub mod popover; \ No newline at end of file diff --git a/src/actors/popover.rs b/src/actors/popover.rs new file mode 100644 index 00000000..e3020c2a --- /dev/null +++ b/src/actors/popover.rs @@ -0,0 +1,40 @@ +/* Copyright (C) 2022 Purism SPC + * SPDX-License-Identifier: GPL-3.0+ + */ + +/*! The popover is opened directly by the GTK surface, +without bouncing click events off the main state. +Then it must accurately show which layout has been selected. +It can get the system layout directly from gsettings on open, +but it cannot get the user-selected overlay, because it's stored in state. + +To solve this, overlay will be cached in the popover actor, +and updated by main state every time it changes. +*/ + +pub mod c { + use super::*; + use crate::util::c::Wrapped; + /// The mutable instance of state + pub type Actor = Wrapped; +} + +#[derive(Clone)] +pub struct State { + pub overlay: Option, +} + +impl State { + pub fn new() -> Self { + Self { overlay: None } + } +} + +pub fn set_overlay( + actor: &c::Actor, + overlay: Option, +) { + let actor = actor.clone_ref(); + let mut actor = actor.borrow_mut(); + actor.overlay = overlay; +} \ No newline at end of file diff --git a/src/animation.rs b/src/animation.rs index 2ca9b06f..f1c53fd0 100644 --- a/src/animation.rs +++ b/src/animation.rs @@ -6,18 +6,30 @@ use std::time::Duration; +use crate::imservice::ContentPurpose; +use crate::layout::ArrangementKind; use crate::outputs::OutputId; use crate::panel::PixelSize; /// The keyboard should hide after this has elapsed to prevent flickering. pub const HIDING_TIMEOUT: Duration = Duration::from_millis(200); +/// Description of parameters which influence panel contents +#[derive(PartialEq, Clone, Debug)] +pub struct Contents { + pub name: String, + pub kind: ArrangementKind, + pub overlay_name: Option, + pub purpose: ContentPurpose, +} + /// The outwardly visible state of visibility #[derive(PartialEq, Debug, Clone)] pub enum Outcome { Visible { output: OutputId, height: PixelSize, + contents: Contents, }, Hidden, } diff --git a/src/data/loading.rs b/src/data/loading.rs index 20360f43..a00acae4 100644 --- a/src/data/loading.rs +++ b/src/data/loading.rs @@ -7,64 +7,16 @@ use std::env; use std::fmt; use std::path::PathBuf; -use std::convert::TryFrom; use super::{ Error, LoadError }; use super::parsing; -use ::layout::ArrangementKind; -use ::logging; -use ::util::c::as_str; -use ::xdg; -use ::imservice::ContentPurpose; +use crate::layout; +use crate::layout::ArrangementKind; +use crate::logging; +use crate::xdg; +use crate::imservice::ContentPurpose; -// traits, derives -use ::logging::Warn; - - -/// Gathers stuff defined in C or called by C -pub mod c { - use super::*; - use std::os::raw::c_char; - - #[no_mangle] - pub extern "C" - fn squeek_load_layout( - name: *const c_char, // name of the keyboard - type_: u32, // type like Wide - variant: u32, // purpose variant like numeric, terminal... - overlay: *const c_char, // the overlay (looking for "terminal") - ) -> *mut ::layout::Layout { - let type_ = match type_ { - 0 => ArrangementKind::Base, - 1 => ArrangementKind::Wide, - _ => panic!("Bad enum value"), - }; - - let name = as_str(&name) - .expect("Bad layout name") - .expect("Empty layout name"); - - let variant = ContentPurpose::try_from(variant) - .or_print( - logging::Problem::Warning, - "Received invalid purpose value", - ) - .unwrap_or(ContentPurpose::Normal); - - let overlay_str = as_str(&overlay) - .expect("Bad overlay name") - .expect("Empty overlay name"); - let overlay_str = match overlay_str { - "" => None, - other => Some(other), - }; - - let (kind, layout) = load_layout_data_with_fallback(&name, type_, variant, overlay_str); - let layout = ::layout::Layout::new(layout, kind, variant); - Box::into_raw(Box::new(layout)) - } -} const FALLBACK_LAYOUT_NAME: &str = "us"; @@ -265,7 +217,7 @@ fn load_layout_data_with_fallback( kind: ArrangementKind, purpose: ContentPurpose, overlay: Option<&str>, -) -> (ArrangementKind, ::layout::LayoutData) { +) -> (ArrangementKind, layout::LayoutData) { // Build the path to the right keyboard layout subdirectory let path = env::var_os("SQUEEKBOARD_KEYBOARDSDIR") @@ -300,6 +252,17 @@ fn load_layout_data_with_fallback( panic!("No useful layout found!"); } +pub fn load_layout( + name: String, + kind: ArrangementKind, + variant: ContentPurpose, + overlay: Option, +) -> layout::Layout { + let overlay = overlay.as_ref().map(String::as_str); + let (found_kind, layout) + = load_layout_data_with_fallback(&name, kind, variant, overlay); + layout::Layout::new(layout, found_kind, variant) +} #[cfg(test)] mod tests { diff --git a/src/data/mod.rs b/src/data/mod.rs index 4bf66bd6..9ee1be7c 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -4,7 +4,7 @@ /*! Combined module for dealing with layout files */ -mod loading; +pub mod loading; pub mod parsing; use std::io; diff --git a/src/event_loop/driver.rs b/src/event_loop/driver.rs index c60a14bc..dcc22da5 100644 --- a/src/event_loop/driver.rs +++ b/src/event_loop/driver.rs @@ -39,7 +39,9 @@ type UISender = glib::Sender; /// It sends outcomes to the glib main loop using a channel. /// The outcomes are applied by the UI end of the channel in the `main` module. // This could still be reasonably tested, -// by creating a glib::Sender and checking what messages it receives. +/// by creating a glib::Sender and checking what messages it receives. +// This can/should be abstracted over Event and Commands, +// so that the C call-ins can be thrown away from here and defined near events. #[derive(Clone)] pub struct Threaded { thread: Sender, @@ -108,8 +110,11 @@ mod c { use super::*; use crate::state::Presence; + use crate::state::LayoutChoice; use crate::state::visibility; + use crate::util; use crate::util::c::Wrapped; + use std::os::raw::c_char; #[no_mangle] pub extern "C" @@ -140,4 +145,28 @@ mod c { sender.send(Event::PhysicalKeyboard(state)) .or_warn(&mut logging::Print, logging::Problem::Warning, "Can't send to state manager"); } + + #[no_mangle] + pub extern "C" + fn squeek_state_send_layout_set( + sender: Wrapped, + name: *const c_char, + source: *const c_char, + // TODO: use when synthetic events are needed + _timestamp: u32, + ) { + let sender = sender.clone_ref(); + let sender = sender.borrow(); + let string_or_empty = |v| String::from( + util::c::as_str(v) + .unwrap_or(Some("")) + .unwrap_or("") + ); + sender + .send(Event::LayoutChoice(LayoutChoice { + name: string_or_empty(&name), + source: string_or_empty(&source).into(), + })) + .or_warn(&mut logging::Print, logging::Problem::Warning, "Can't send to state manager"); + } } diff --git a/src/imservice.rs b/src/imservice.rs index 14cbca16..3c52a7e7 100644 --- a/src/imservice.rs +++ b/src/imservice.rs @@ -226,7 +226,7 @@ bitflags!{ /// use rs::imservice::ContentPurpose; /// assert_eq!(ContentPurpose::Alpha as u32, 1); /// ``` -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq)] pub enum ContentPurpose { Normal = 0, Alpha = 1, diff --git a/src/layout.h b/src/layout.h index e50eac8f..1d77876a 100644 --- a/src/layout.h +++ b/src/layout.h @@ -7,6 +7,8 @@ #include "eek/eek-gtk-keyboard.h" #include "eek/eek-renderer.h" #include "eek/eek-types.h" +#include "src/main.h" +#include "src/popover.h" #include "src/submission.h" #include "virtual-keyboard-unstable-v1-client-protocol.h" #include "text-input-unstable-v3-client-protocol.h" @@ -40,7 +42,8 @@ void squeek_layout_release(struct squeek_layout *layout, struct submission *submission, struct transformation widget_to_layout, uint32_t timestamp, - EekboardContextService *manager, + struct squeek_popover *popover, + struct squeek_state_manager *state, EekGtkKeyboard *ui_keyboard); void squeek_layout_release_all_only(struct squeek_layout *layout, struct submission *submission, @@ -54,7 +57,8 @@ void squeek_layout_drag(struct squeek_layout *layout, struct submission *submission, double x_widget, double y_widget, struct transformation widget_to_layout, - uint32_t timestamp, EekboardContextService *manager, + uint32_t timestamp, struct squeek_popover *popover, + struct squeek_state_manager *state, EekGtkKeyboard *ui_keyboard); void squeek_layout_draw_all_changed(struct squeek_layout *layout, EekRenderer* renderer, cairo_t *cr, struct submission *submission); void squeek_draw_layout_base_view(struct squeek_layout *layout, EekRenderer* renderer, cairo_t *cr); diff --git a/src/layout.rs b/src/layout.rs index bb94d4dc..b472c373 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -25,31 +25,36 @@ use std::fmt; use std::rc::Rc; use std::vec::Vec; -use ::action::Action; -use ::drawing; -use ::float_ord::FloatOrd; -use ::keyboard::KeyState; -use ::logging; -use ::manager; -use ::submission::{ Submission, SubmitData, Timestamp }; -use ::util::find_max_double; +use crate::action::Action; +use crate::actors; +use crate::drawing; +use crate::float_ord::FloatOrd; +use crate::keyboard::KeyState; +use crate::logging; +use crate::popover; +use crate::receiver; +use crate::submission::{ Submission, SubmitData, Timestamp }; +use crate::util::find_max_double; -use ::imservice::ContentPurpose; +use crate::imservice::ContentPurpose; // Traits use std::borrow::Borrow; -use ::logging::Warn; +use crate::logging::Warn; /// Gathers stuff defined in C or called by C pub mod c { use super::*; - - use gtk_sys; - use std::os::raw::c_void; + + use crate::receiver; use crate::submission::c::Submission as CSubmission; + use gtk_sys; use std::ops::{ Add, Sub }; - + use std::os::raw::c_void; + + use crate::util::CloneOwned; + // The following defined in C #[repr(transparent)] #[derive(Copy, Clone)] @@ -215,13 +220,17 @@ pub mod c { submission: CSubmission, widget_to_layout: Transformation, time: u32, - manager: manager::c::Manager, + popover: actors::popover::c::Actor, + app_state: receiver::c::State, ui_keyboard: EekGtkKeyboard, ) { let time = Timestamp(time); let layout = unsafe { &mut *layout }; let submission = submission.clone_ref(); let mut submission = submission.borrow_mut(); + let app_state = app_state.clone_owned(); + let popover_state = popover.clone_owned(); + let ui_backend = UIBackend { widget_to_layout, keyboard: ui_keyboard, @@ -236,7 +245,7 @@ pub mod c { &mut submission, Some(&ui_backend), time, - Some(manager), + Some((&popover_state, app_state.clone())), key, ); } @@ -315,13 +324,18 @@ pub mod c { x_widget: f64, y_widget: f64, widget_to_layout: Transformation, time: u32, - manager: manager::c::Manager, + popover: actors::popover::c::Actor, + app_state: receiver::c::State, ui_keyboard: EekGtkKeyboard, ) { let time = Timestamp(time); let layout = unsafe { &mut *layout }; let submission = submission.clone_ref(); let mut submission = submission.borrow_mut(); + // We only need to query state here, not update. + // A copy is enough. + let popover_state = popover.clone_owned(); + let app_state = app_state.clone_owned(); let ui_backend = UIBackend { widget_to_layout, keyboard: ui_keyboard, @@ -352,7 +366,7 @@ pub mod c { &mut submission, Some(&ui_backend), time, - Some(manager), + Some((&popover_state, app_state.clone())), key, ); } @@ -377,7 +391,7 @@ pub mod c { &mut submission, Some(&ui_backend), time, - Some(manager), + Some((&popover_state, app_state.clone())), key, ); } @@ -1035,7 +1049,11 @@ mod seat { submission: &mut Submission, ui: Option<&UIBackend>, time: Timestamp, - manager: Option, + // TODO: intermediate measure: + // passing state conditionally because it's only used for popover. + // Eventually, it should be used for sumitting button events, + // and passed always. + manager: Option<(&actors::popover::State, receiver::State)>, rckey: &Rc>, ) { let key: KeyState = { @@ -1070,7 +1088,7 @@ mod seat { // only show when UI is present Action::ShowPreferences => if let Some(ui) = &ui { // only show when layout manager is available - if let Some(manager) = manager { + if let Some((manager, app_state)) = manager { let view = layout.get_current_view(); let places = ::layout::procedures::find_key_places( view, &rckey, @@ -1085,10 +1103,11 @@ mod seat { width: button.size.width, height: button.size.height, }; - ::popover::show( + popover::show( ui.keyboard, ui.widget_to_layout.reverse_bounds(bounds), manager, + app_state, ); } } diff --git a/src/lib.rs b/src/lib.rs index 38b7944c..d508d051 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,6 +23,7 @@ mod assert_matches; mod logging; mod action; +mod actors; mod animation; pub mod data; mod debug; @@ -34,10 +35,10 @@ mod keyboard; mod layout; mod locale; mod main; -mod manager; mod outputs; mod panel; mod popover; +mod receiver; mod resources; mod state; mod style; diff --git a/src/main.h b/src/main.h index 9b1c14d1..799962ce 100644 --- a/src/main.h +++ b/src/main.h @@ -9,6 +9,7 @@ #include "eek/eek-types.h" #include "dbus.h" #include "panel.h" +#include "src/popover.h" struct receiver; @@ -23,9 +24,10 @@ struct rsobjects { struct squeek_state_manager *state_manager; struct submission *submission; struct squeek_wayland *wayland; + struct squeek_popover *popover; }; -void register_ui_loop_handler(struct receiver *receiver, struct panel_manager *panel, EekboardContextService *hint_manager, DBusHandler *dbus_handler); +void register_ui_loop_handler(struct receiver *receiver, struct panel_manager *panel, struct squeek_popover *popover, EekboardContextService *hint_manager, DBusHandler *dbus_handler); struct rsobjects squeek_init(void); @@ -33,3 +35,4 @@ void squeek_state_send_force_visible(struct squeek_state_manager *state); void squeek_state_send_force_hidden(struct squeek_state_manager *state); void squeek_state_send_keyboard_present(struct squeek_state_manager *state, uint32_t keyboard_present); +void squeek_state_send_layout_set(struct squeek_state_manager *state, char *name, char *layout, uint32_t timestamp); diff --git a/src/main.rs b/src/main.rs index 47ac7982..13c1766c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,9 +3,11 @@ */ /*! Glue for the main loop. */ -use crate::panel; +use crate::actors; +use crate::animation; use crate::debug; -use crate::state; +use crate::data::loading; +use crate::panel; use glib::{Continue, MainContext, PRIORITY_DEFAULT, Receiver}; @@ -19,6 +21,7 @@ mod c { use crate::event_loop::driver; use crate::imservice::IMService; use crate::imservice::c::InputMethod; + use crate::layout; use crate::outputs::Outputs; use crate::state; use crate::submission::Submission; @@ -46,6 +49,7 @@ mod c { submission: Wrapped, /// Not wrapped, because C needs to access this. wayland: *mut Wayland, + popover: actors::popover::c::Actor, } /// Corresponds to wayland.h::squeek_wayland. @@ -78,7 +82,8 @@ mod c { extern "C" { #[allow(improper_ctypes)] fn init_wayland(wayland: *mut Wayland); - fn eekboard_context_service_set_hint_purpose(service: HintManager, hint: u32, purpose: u32); + #[allow(improper_ctypes)] + fn eekboard_context_service_set_layout(service: HintManager, layout: *const layout::Layout, timestamp: u32); // This should probably only get called from the gtk main loop, // given that dbus handler is using glib. fn dbus_handler_set_visible(dbus: *const DBusHandler, visible: u8); @@ -116,6 +121,7 @@ mod c { state_manager: Wrapped::new(state_manager), receiver: Wrapped::new(receiver), wayland: Box::into_raw(wayland), + popover: Wrapped::new(actors::popover::State::new()), } } @@ -125,6 +131,7 @@ mod c { fn register_ui_loop_handler( receiver: Wrapped>, panel_manager: panel::c::PanelManager, + popover: actors::popover::c::Actor, hint_manager: HintManager, dbus_handler: *const DBusHandler, ) { @@ -137,7 +144,13 @@ mod c { receiver.attach( Some(&ctx), move |msg| { - main_loop_handle_message(msg, panel_manager.clone(), hint_manager, dbus_handler); + main_loop_handle_message( + msg, + panel_manager.clone(), + &popover, + hint_manager, + dbus_handler, + ); Continue(true) }, ); @@ -152,6 +165,7 @@ mod c { fn main_loop_handle_message( msg: Commands, panel_manager: Wrapped, + popover: &actors::popover::c::Actor, hint_manager: HintManager, dbus_handler: *const DBusHandler, ) { @@ -164,24 +178,37 @@ mod c { unsafe { dbus_handler_set_visible(dbus_handler, visible as u8) }; } } - - if let Some(hints) = msg.layout_hint_set { + + if let Some(commands::SetLayout { description }) = msg.layout_selection { + let animation::Contents { + name, + kind, + overlay_name, + purpose, + } = description; + actors::popover::set_overlay(popover, overlay_name.clone()); + let layout = loading::load_layout(name, kind, purpose, overlay_name); + let layout = Box::into_raw(Box::new(layout)); unsafe { - eekboard_context_service_set_hint_purpose( - hint_manager, - hints.hint.bits(), - hints.purpose.clone() as u32, - ) - }; + eekboard_context_service_set_layout(hint_manager, layout, 0); + } } } } +pub mod commands { + use crate::animation; + #[derive(Clone, Debug)] + pub struct SetLayout { + pub description: animation::Contents, + } +} + /// The commands consumed by the main loop, /// to be sent out to external components. #[derive(Clone)] pub struct Commands { pub panel_visibility: Option, - pub layout_hint_set: Option, pub dbus_visible_set: Option, + pub layout_selection: Option, } diff --git a/src/manager.rs b/src/manager.rs deleted file mode 100644 index 55fc561a..00000000 --- a/src/manager.rs +++ /dev/null @@ -1,33 +0,0 @@ -/*! Procedures relating to the management of the switching of layouts */ -use ::util; - -pub mod c { - use std::os::raw::{c_char, c_void}; - - /// EekboardContextService* - #[repr(transparent)] - #[derive(Clone, Copy)] - pub struct Manager(*const c_void); - - extern "C" { - pub fn eekboard_context_service_set_overlay( - manager: Manager, - name: *const c_char, - ); - - pub fn eekboard_context_service_get_overlay( - manager: Manager, - ) -> *const c_char; - } -} - -/// Returns the overlay name. -/// The result lifetime is "as long as the C copy lives" -pub fn get_overlay(manager: c::Manager) -> Option { - let raw_str = unsafe { - c::eekboard_context_service_get_overlay(manager) - }; - // this string is generated from Rust, should never be invalid - util::c::as_str(&raw_str).unwrap() - .map(String::from) -} diff --git a/src/panel.c b/src/panel.c index 6dfb69c9..82640dfe 100644 --- a/src/panel.c +++ b/src/panel.c @@ -52,7 +52,7 @@ make_widget (struct panel_manager *self) if (self->widget) { g_error("Widget already present"); } - self->widget = eek_gtk_keyboard_new (self->state, self->submission, self->layout); + self->widget = eek_gtk_keyboard_new (self->state, self->submission, self->state_manager, self->popover); gtk_widget_set_has_tooltip (self->widget, TRUE); gtk_container_add (GTK_CONTAINER(self->window), self->widget); @@ -116,15 +116,16 @@ panel_manager_resize (struct panel_manager *self, uint32_t height) } -struct panel_manager panel_manager_new(EekboardContextService *state, struct submission *submission, struct squeek_layout_state *layout) +struct panel_manager panel_manager_new(EekboardContextService *state, struct submission *submission, struct squeek_state_manager *state_manager, struct squeek_popover *popover) { struct panel_manager mgr = { .state = state, .submission = submission, - .layout = layout, .window = NULL, .widget = NULL, .current_output = NULL, + .state_manager = state_manager, + .popover = popover, }; return mgr; } diff --git a/src/panel.h b/src/panel.h index 5d0ae615..00aa7f91 100644 --- a/src/panel.h +++ b/src/panel.h @@ -2,14 +2,16 @@ #include "eek/layersurface.h" #include "src/layout.h" +#include "src/main.h" #include "src/submission.h" // Stores the objects that the panel and its widget will refer to struct panel_manager { EekboardContextService *state; // unowned /// Needed for instantiating the widget + struct squeek_state_manager *state_manager; // shared reference + struct squeek_popover *popover; // shared reference struct submission *submission; // unowned - struct squeek_layout_state *layout; PhoshLayerSurface *window; GtkWidget *widget; // nullable @@ -18,4 +20,4 @@ struct panel_manager { struct wl_output *current_output; }; -struct panel_manager panel_manager_new(EekboardContextService *state, struct submission *submission, struct squeek_layout_state *layout); +struct panel_manager panel_manager_new(EekboardContextService *state, struct submission *submission, struct squeek_state_manager *state_manager, struct squeek_popover *popover); diff --git a/src/popover.h b/src/popover.h new file mode 100644 index 00000000..cc653922 --- /dev/null +++ b/src/popover.h @@ -0,0 +1,5 @@ +#pragma once +/// Popover state. +/// Wrapped +struct squeek_popover; + diff --git a/src/popover.rs b/src/popover.rs index 1e9ad259..5aba7e99 100644 --- a/src/popover.rs +++ b/src/popover.rs @@ -4,11 +4,13 @@ use gio; use gtk; use std::ffi::CString; use std::cmp::Ordering; -use ::layout::c::{ Bounds, EekGtkKeyboard }; -use ::locale::{ OwnedTranslation, compare_current_locale }; -use ::logging; -use ::manager; -use ::resources; +use crate::actors; +use crate::layout::c::{ Bounds, EekGtkKeyboard }; +use crate::locale::{ OwnedTranslation, compare_current_locale }; +use crate::logging; +use crate::receiver; +use crate::resources; +use crate::state; // Traits use gio::prelude::ActionMapExt; @@ -16,7 +18,7 @@ use gio::prelude::SettingsExt; use glib::translate::FromGlibPtrNone; use glib::variant::ToVariant; use gtk::prelude::*; -use ::logging::Warn; +use crate::logging::Warn; mod c { use std::os::raw::c_char; @@ -127,9 +129,11 @@ fn get_settings(schema_name: &str) -> Option { .map(|_sschema| gio::Settings::new(schema_name)) } -fn set_layout(kind: String, name: String) { +fn set_layout(kind: &str, name: &str) { let settings = get_settings("org.gnome.desktop.input-sources"); if let Some(settings) = settings { + let kind = String::from(kind); + let name = String::from(name); #[cfg(feature = "glib_v0_14")] let inputs = settings.value("sources"); #[cfg(not(feature = "glib_v0_14"))] @@ -150,7 +154,7 @@ fn set_layout(kind: String, name: String) { /// A reference to what the user wants to see #[derive(PartialEq, Clone, Debug)] -enum LayoutId { +pub enum LayoutId { /// Affects the layout in system settings System { kind: String, @@ -170,40 +174,23 @@ impl LayoutId { } fn set_visible_layout( - manager: manager::c::Manager, - layout_id: LayoutId, + layout_id: &LayoutId, ) { match layout_id { LayoutId::System { kind, name } => { - unsafe { - use std::ptr; - manager::c::eekboard_context_service_set_overlay( - manager, - ptr::null(), - ); - } set_layout(kind, name); - } - LayoutId::Local(name) => { - let name = CString::new(name.as_str()).unwrap(); - let name_ptr = name.as_ptr(); - unsafe { - manager::c::eekboard_context_service_set_overlay( - manager, - name_ptr, - ) - } }, + _ => {}, } } /// Takes into account first any overlays, then system layouts from the list fn get_current_layout( - manager: manager::c::Manager, + popover: &actors::popover::State, system_layouts: &Vec, ) -> Option { - match manager::get_overlay(manager) { - Some(name) => Some(LayoutId::Local(name)), + match &popover.overlay { + Some(name) => Some(LayoutId::Local(name.into())), None => system_layouts.get(0).map(LayoutId::clone), } } @@ -247,7 +234,8 @@ fn translate_layout_names(layouts: &Vec) -> Vec { pub fn show( window: EekGtkKeyboard, position: Bounds, - manager: manager::c::Manager, + popover: &actors::popover::State, + app_state: receiver::State, ) { unsafe { gtk::set_initialized() }; let window = unsafe { gtk::Widget::from_glib_none(window.0) }; @@ -327,7 +315,7 @@ pub fn show( let action_group = gio::SimpleActionGroup::new(); - if let Some(current_layout) = get_current_layout(manager, &system_layouts) { + if let Some(current_layout) = get_current_layout(popover, &system_layouts) { let current_layout_name = all_layouts.iter() .find( |l| l.get_name() == current_layout.get_name() @@ -356,10 +344,13 @@ pub fn show( .find( |choices| state == choices.get_name() ).unwrap(); - set_visible_layout( - manager, - layout.clone(), - ) + app_state + .send(state::Event::OverlayChanged(layout.clone())) + .or_print( + logging::Problem::Bug, + &format!("Can't send to state"), + ); + set_visible_layout(layout) }); }, None => log_print!( diff --git a/src/receiver.rs b/src/receiver.rs new file mode 100644 index 00000000..de0e95d9 --- /dev/null +++ b/src/receiver.rs @@ -0,0 +1,15 @@ +/*! Defines the application-wide message bus for updating state.*/ + +use crate::event_loop::driver::Threaded; + +pub mod c { + use super::*; + use crate::util::c::Wrapped; + pub type State = Wrapped; +} + +// The state receiver is an endpoint of a channel, so it's safely cloneable. +// There's no need to keep it in a Rc. +// The C version uses Wrapped with an underlying Rc, +// because Wrapped is well-tested already. +pub type State = Threaded; \ No newline at end of file diff --git a/src/server-main.c b/src/server-main.c index 4807053c..ccbce2e7 100644 --- a/src/server-main.c +++ b/src/server-main.c @@ -56,8 +56,6 @@ struct squeekboard { /// Gsettings hook for visibility. TODO: this does not belong in gsettings. ServerContextService *settings_handler; struct panel_manager panel_manager; // Controls the shape of the panel. - /// Currently wanted layout. TODO: merge into state::Application - struct squeek_layout_state layout_choice; }; @@ -400,7 +398,7 @@ main (int argc, char **argv) // Also initializes wayland struct rsobjects rsobjects = squeek_init(); - instance.settings_context = eekboard_context_service_new(&instance.layout_choice); + instance.settings_context = eekboard_context_service_new(rsobjects.state_manager); // set up dbus @@ -450,9 +448,10 @@ main (int argc, char **argv) instance.panel_manager = panel_manager_new(instance.settings_context, rsobjects.submission, - &instance.layout_choice); + rsobjects.state_manager, + rsobjects.popover); - register_ui_loop_handler(rsobjects.receiver, &instance.panel_manager, instance.settings_context, instance.dbus_handler); + register_ui_loop_handler(rsobjects.receiver, &instance.panel_manager, rsobjects.popover, instance.settings_context, instance.dbus_handler); session_register(); diff --git a/src/state.rs b/src/state.rs index a9b7cca4..ba141d3c 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Purism SPC +/* Copyright (C) 2021,2022 Purism SPC * SPDX-License-Identifier: GPL-3.0+ */ @@ -8,11 +8,14 @@ use crate::animation; use crate::debug; use crate::imservice::{ ContentHint, ContentPurpose }; +use crate::layout::ArrangementKind; +use crate::main; use crate::main::Commands; use crate::outputs; use crate::outputs::{Millimeter, OutputId, OutputState}; use crate::panel; use crate::panel::PixelSize; +use crate::popover; use crate::util::Rational; use std::cmp; use std::collections::HashMap; @@ -37,6 +40,29 @@ pub enum InputMethod { InactiveSince(Instant), } +#[derive(Clone, Debug)] +pub enum LayoutSource { + Xkb, + Other(String), +} + +impl From for LayoutSource { + fn from(v: String) -> Self { + if v.as_str() == "xkb" { + LayoutSource::Xkb + } else { + LayoutSource::Other(v) + } + } +} + +/// The user's preferred system layout +#[derive(Clone, Debug)] +pub struct LayoutChoice { + pub name: String, + pub source: LayoutSource, +} + /// Incoming events. /// This contains events that cause a change to the internal state. #[derive(Clone, Debug)] @@ -45,6 +71,8 @@ pub enum Event { Visibility(visibility::Event), PhysicalKeyboard(Presence), Output(outputs::Event), + LayoutChoice(LayoutChoice), + OverlayChanged(popover::LayoutId), Debug(debug::Event), /// Event triggered because a moment in time passed. /// Use to animate state transitions. @@ -87,7 +115,7 @@ pub mod visibility { /// The outwardly visible state. #[derive(Clone, Debug)] pub struct Outcome { - pub visibility: animation::Outcome, + pub panel: animation::Outcome, pub im: InputMethod, } @@ -98,36 +126,40 @@ impl Outcome { /// The receivers of the commands bear the burden /// of checking if the commands end up being no-ops. pub fn get_commands_to_reach(&self, new_state: &Self) -> Commands { - let layout_hint_set = match new_state { - Outcome { - visibility: animation::Outcome::Visible{..}, - im: InputMethod::Active(hints), - } => Some(hints.clone()), - - Outcome { - visibility: animation::Outcome::Visible{..}, - im: InputMethod::InactiveSince(_), - } => Some(InputMethodDetails { - hint: ContentHint::NONE, - purpose: ContentPurpose::Normal, - }), - - Outcome { - visibility: animation::Outcome::Hidden, - .. - } => None, - }; // FIXME: handle switching outputs - let (dbus_visible_set, panel_visibility) = match new_state.visibility { - animation::Outcome::Visible{output, height} + let (dbus_visible_set, panel_visibility) = match new_state.panel { + animation::Outcome::Visible{output, height, ..} => (Some(true), Some(panel::Command::Show{output, height})), animation::Outcome::Hidden => (Some(false), Some(panel::Command::Hide)), }; + // Compare the old and new states as not to flood with updates, + // which may look up in the file system. + use animation::Outcome::*; + let layout_selection = match &new_state.panel { + Visible{ contents: new_contents, ..} => { + let same + = if let Visible { contents, .. } = &self.panel { + contents == new_contents + } else { + false + }; + + if !same { + Some(main::commands::SetLayout { + description: new_contents.clone() + }) + } else { + None + } + }, + animation::Outcome::Hidden => None, + }; + Commands { panel_visibility, - layout_hint_set, dbus_visible_set, + layout_selection, } } } @@ -156,6 +188,13 @@ pub struct Application { /// but not sure about being allowed on non-touch displays. pub preferred_output: Option, pub outputs: HashMap, + /// We presume that the system always has some preference, + /// even though we receive the preference after init, + /// and we might not receive one at all (gsettings missing). + /// Then a default is used. + pub layout_choice: LayoutChoice, + /// Manual override of the system layout + pub overlay_layout: Option, } impl Application { @@ -173,6 +212,11 @@ impl Application { debug_mode_enabled: false, preferred_output: None, outputs: Default::default(), + layout_choice: LayoutChoice { + name: String::from("us"), + source: LayoutSource::Xkb, + }, + overlay_layout: None, } } @@ -257,7 +301,18 @@ impl Application { im: InputMethod::InactiveSince(old), ..self }, - } + }, + + Event::LayoutChoice(layout_choice) => Self { + layout_choice, + overlay_layout: None, + ..self + }, + + Event::OverlayChanged(overlay_layout) => Self { + overlay_layout: Some(overlay_layout), + ..self + }, }; if state.debug_mode_enabled { @@ -273,7 +328,9 @@ Outcome: state } - fn get_preferred_height(output: &OutputState) -> Option { + fn get_preferred_height_and_arrangement(output: &OutputState) + -> Option<(PixelSize, ArrangementKind)> + { output.get_pixel_size() .map(|px_size| { // Assume isotropy. @@ -301,61 +358,95 @@ Outcome: // TODO: calculate based on selected layout const ROW_COUNT: u32 = 4; - let height = { - let ideal_height = IDEAL_TARGET_SIZE * ROW_COUNT as i32; - let ideal_height_px = (ideal_height * density).ceil().0 as u32; + let ideal_height = IDEAL_TARGET_SIZE * ROW_COUNT as i32; + let ideal_height_px = (ideal_height * density).ceil().0 as u32; - // Reduce height to match what the layout can fill. - // For this, we need to guess if normal or wide will be picked up. - // This must match `eek_gtk_keyboard.c::get_type`. - // TODO: query layout database and choose one directly - let abstract_width - = PixelSize { - scale_factor: output.scale as u32, - pixels: px_size.width, - } - .as_scaled_ceiling(); + // Reduce height to match what the layout can fill. + // For this, we need to guess if normal or wide will be picked up. + // This must match `eek_gtk_keyboard.c::get_type`. + // TODO: query layout database and choose one directly + let abstract_width + = PixelSize { + scale_factor: output.scale as u32, + pixels: px_size.width, + } + .as_scaled_ceiling(); - let height_as_widths = { - if abstract_width < 540 { - // Normal - Rational { - numerator: 210, - denominator: 360, - } - } else { - // Wide - Rational { - numerator: 172, - denominator: 540, - } + let (arrangement, height_as_widths) = { + if abstract_width < 540 {( + ArrangementKind::Base, + Rational { + numerator: 210, + denominator: 360, + }, + )} else {( + ArrangementKind::Wide, + Rational { + numerator: 172, + denominator: 540, } - }; - cmp::min( + )} + }; + + let height + = cmp::min( ideal_height_px, (height_as_widths * px_size.width as i32).ceil() as u32, - ) - }; - PixelSize { - scale_factor: output.scale as u32, - pixels: cmp::min(height, px_size.height / 2), - } + ); + + ( + PixelSize { + scale_factor: output.scale as u32, + pixels: cmp::min(height, px_size.height / 2), + }, + arrangement, + ) }) } + + /// Returns layout name, overlay name + fn get_layout_names(&self) -> (String, Option) { + ( + String::from(match &self.overlay_layout { + Some(popover::LayoutId::System { name, .. }) => name, + _ => &self.layout_choice.name, + }), + match &self.overlay_layout { + Some(popover::LayoutId::Local(name)) => Some(name.clone()), + _ => None, + }, + ) + } pub fn get_outcome(&self, now: Instant) -> Outcome { // FIXME: include physical keyboard presence Outcome { - visibility: match self.preferred_output { + panel: match self.preferred_output { None => animation::Outcome::Hidden, Some(output) => { - // Hoping that this will get optimized out on branches not using `visible`. - let height = Self::get_preferred_height(self.outputs.get(&output).unwrap()) - .unwrap_or(PixelSize{pixels: 0, scale_factor: 1}); + let (height, arrangement) = Self::get_preferred_height_and_arrangement(self.outputs.get(&output).unwrap()) + .unwrap_or(( + PixelSize{pixels: 0, scale_factor: 1}, + ArrangementKind::Base, + )); + let (layout_name, overlay) = self.get_layout_names(); + // TODO: Instead of setting size to 0 when the output is invalid, // simply go invisible. - let visible = animation::Outcome::Visible{ output, height }; - + let visible = animation::Outcome::Visible{ + output, + height, + contents: animation::Contents { + kind: arrangement, + name: layout_name, + overlay_name: overlay, + purpose: match self.im { + InputMethod::Active(InputMethodDetails { purpose, .. }) => purpose, + InputMethod::InactiveSince(_) => ContentPurpose::Normal, + }, + } + }; + match (self.physical_keyboard, self.visibility_override) { (_, visibility::State::ForcedHidden) => animation::Outcome::Hidden, (_, visibility::State::ForcedVisible) => visible, @@ -446,7 +537,7 @@ pub mod test { for _i in 0..100 { now += Duration::from_millis(1); assert_matches!( - state.get_outcome(now).visibility, + state.get_outcome(now).panel, animation::Outcome::Visible{..}, "Hidden when it should remain visible: {:?}", now.saturating_duration_since(start), @@ -455,7 +546,10 @@ pub mod test { let state = state.apply_event(Event::InputMethod(InputMethod::Active(imdetails_new())), now); - assert_matches!(state.get_outcome(now).visibility, animation::Outcome::Visible{..}); + assert_matches!( + state.get_outcome(now).panel, + animation::Outcome::Visible{..} + ); } /// Make sure that hiding works when input method goes away @@ -472,7 +566,7 @@ pub mod test { let state = state.apply_event(Event::InputMethod(InputMethod::InactiveSince(now)), now); - while let animation::Outcome::Visible{..} = state.get_outcome(now).visibility { + while let animation::Outcome::Visible{..} = state.get_outcome(now).panel { now += Duration::from_millis(1); assert!( now < start + Duration::from_millis(250), @@ -502,7 +596,7 @@ pub mod test { let state = state.apply_event(Event::InputMethod(InputMethod::Active(imdetails_new())), now); let state = state.apply_event(Event::InputMethod(InputMethod::InactiveSince(now)), now); - while let animation::Outcome::Visible{..} = state.get_outcome(now).visibility { + while let animation::Outcome::Visible{..} = state.get_outcome(now).panel { now += Duration::from_millis(1); assert!( now < start + Duration::from_millis(250), @@ -515,7 +609,7 @@ pub mod test { for _i in 0..1000 { now += Duration::from_millis(1); assert_eq!( - state.get_outcome(now).visibility, + state.get_outcome(now).panel, animation::Outcome::Hidden, "Appeared unnecessarily: {:?}", now.saturating_duration_since(start), @@ -537,7 +631,7 @@ pub mod test { let state = state.apply_event(Event::Visibility(visibility::Event::ForceVisible), now); assert_matches!( - state.get_outcome(now).visibility, + state.get_outcome(now).panel, animation::Outcome::Visible{..}, "Failed to show: {:?}", now.saturating_duration_since(start), @@ -550,7 +644,7 @@ pub mod test { now += Duration::from_secs(1); assert_eq!( - state.get_outcome(now).visibility, + state.get_outcome(now).panel, animation::Outcome::Hidden, "Failed to release forced visibility: {:?}", now.saturating_duration_since(start), @@ -571,7 +665,7 @@ pub mod test { let state = state.apply_event(Event::PhysicalKeyboard(Presence::Present), now); assert_eq!( - state.get_outcome(now).visibility, + state.get_outcome(now).panel, animation::Outcome::Hidden, "Failed to hide: {:?}", now.saturating_duration_since(start), @@ -583,7 +677,7 @@ pub mod test { let state = state.apply_event(Event::InputMethod(InputMethod::Active(imdetails_new())), now); assert_eq!( - state.get_outcome(now).visibility, + state.get_outcome(now).panel, animation::Outcome::Hidden, "Failed to remain hidden: {:?}", now.saturating_duration_since(start), @@ -593,7 +687,7 @@ pub mod test { let state = state.apply_event(Event::PhysicalKeyboard(Presence::Missing), now); assert_matches!( - state.get_outcome(now).visibility, + state.get_outcome(now).panel, animation::Outcome::Visible{..}, "Failed to appear: {:?}", now.saturating_duration_since(start), @@ -605,7 +699,7 @@ pub mod test { fn size_l5() { use crate::outputs::{Mode, Geometry, c, Size}; assert_eq!( - Application::get_preferred_height(&OutputState { + Application::get_preferred_height_and_arrangement(&OutputState { current_mode: Some(Mode { width: 720, height: 1440, @@ -619,10 +713,13 @@ pub mod test { }), scale: 2, }), - Some(PixelSize { - scale_factor: 2, - pixels: 420, - }), + Some(( + PixelSize { + scale_factor: 2, + pixels: 420, + }, + ArrangementKind::Base, + )), ); } }