Merge branch 'layouts' into 'master'
state: Select the layout See merge request World/Phosh/squeekboard!553
This commit is contained in:
@ -35,6 +35,7 @@
|
|||||||
|
|
||||||
#include "eekboard/eekboard-context-service.h"
|
#include "eekboard/eekboard-context-service.h"
|
||||||
#include "src/layout.h"
|
#include "src/layout.h"
|
||||||
|
#include "src/popover.h"
|
||||||
#include "src/submission.h"
|
#include "src/submission.h"
|
||||||
|
|
||||||
#define LIBFEEDBACK_USE_UNSTABLE_API
|
#define LIBFEEDBACK_USE_UNSTABLE_API
|
||||||
@ -48,6 +49,8 @@ typedef struct _EekGtkKeyboardPrivate
|
|||||||
struct render_geometry render_geometry; // mutable
|
struct render_geometry render_geometry; // mutable
|
||||||
|
|
||||||
EekboardContextService *eekboard_context; // unowned reference
|
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 submission *submission; // unowned reference
|
||||||
|
|
||||||
struct squeek_layout_state *layout; // unowned
|
struct squeek_layout_state *layout; // unowned
|
||||||
@ -118,15 +121,6 @@ eek_gtk_keyboard_real_draw (GtkWidget *self,
|
|||||||
return FALSE;
|
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
|
static void
|
||||||
eek_gtk_keyboard_real_size_allocate (GtkWidget *self,
|
eek_gtk_keyboard_real_size_allocate (GtkWidget *self,
|
||||||
GtkAllocation *allocation)
|
GtkAllocation *allocation)
|
||||||
@ -134,15 +128,6 @@ eek_gtk_keyboard_real_size_allocate (GtkWidget *self,
|
|||||||
EekGtkKeyboard *keyboard = EEK_GTK_KEYBOARD (self);
|
EekGtkKeyboard *keyboard = EEK_GTK_KEYBOARD (self);
|
||||||
EekGtkKeyboardPrivate *priv =
|
EekGtkKeyboardPrivate *priv =
|
||||||
eek_gtk_keyboard_get_instance_private (keyboard);
|
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) {
|
if (priv->renderer) {
|
||||||
set_allocation_size (keyboard, priv->keyboard->layout,
|
set_allocation_size (keyboard, priv->keyboard->layout,
|
||||||
@ -158,6 +143,7 @@ on_event_triggered (LfbEvent *event,
|
|||||||
GAsyncResult *res,
|
GAsyncResult *res,
|
||||||
gpointer unused)
|
gpointer unused)
|
||||||
{
|
{
|
||||||
|
(void)unused;
|
||||||
g_autoptr (GError) err = NULL;
|
g_autoptr (GError) err = NULL;
|
||||||
|
|
||||||
if (!lfb_event_trigger_feedback_finish (event, res, &err)) {
|
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,
|
squeek_layout_drag(eekboard_context_service_get_keyboard(priv->eekboard_context)->layout,
|
||||||
priv->submission,
|
priv->submission,
|
||||||
x, y, priv->render_geometry.widget_to_layout, time,
|
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)
|
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,
|
squeek_layout_release(eekboard_context_service_get_keyboard(priv->eekboard_context)->layout,
|
||||||
priv->submission, priv->render_geometry.widget_to_layout, time,
|
priv->submission, priv->render_geometry.widget_to_layout, time,
|
||||||
priv->eekboard_context, self);
|
priv->popover, priv->state_manager, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
@ -406,13 +392,15 @@ on_notify_keyboard (GObject *object,
|
|||||||
GtkWidget *
|
GtkWidget *
|
||||||
eek_gtk_keyboard_new (EekboardContextService *eekservice,
|
eek_gtk_keyboard_new (EekboardContextService *eekservice,
|
||||||
struct submission *submission,
|
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));
|
EekGtkKeyboard *ret = EEK_GTK_KEYBOARD(g_object_new (EEK_TYPE_GTK_KEYBOARD, NULL));
|
||||||
EekGtkKeyboardPrivate *priv = (EekGtkKeyboardPrivate*)eek_gtk_keyboard_get_instance_private (ret);
|
EekGtkKeyboardPrivate *priv = (EekGtkKeyboardPrivate*)eek_gtk_keyboard_get_instance_private (ret);
|
||||||
|
priv->popover = popover;
|
||||||
priv->eekboard_context = eekservice;
|
priv->eekboard_context = eekservice;
|
||||||
priv->submission = submission;
|
priv->submission = submission;
|
||||||
priv->layout = layout;
|
priv->state_manager = state_manager;
|
||||||
priv->renderer = NULL;
|
priv->renderer = NULL;
|
||||||
// This should really be done on initialization.
|
// This should really be done on initialization.
|
||||||
// Before the widget is allocated,
|
// Before the widget is allocated,
|
||||||
|
|||||||
@ -30,6 +30,8 @@
|
|||||||
|
|
||||||
#include "eek/eek-renderer.h"
|
#include "eek/eek-renderer.h"
|
||||||
#include "eek/eek-types.h"
|
#include "eek/eek-types.h"
|
||||||
|
#include "src/main.h"
|
||||||
|
#include "src/popover.h"
|
||||||
|
|
||||||
struct submission;
|
struct submission;
|
||||||
struct squeek_layout_state;
|
struct squeek_layout_state;
|
||||||
@ -48,7 +50,7 @@ struct _EekGtkKeyboardClass
|
|||||||
gpointer pdummy[24];
|
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);
|
void eek_gtk_keyboard_emit_feedback (EekGtkKeyboard *self);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|||||||
@ -55,7 +55,7 @@ static guint signals[LAST_SIGNAL] = { 0, };
|
|||||||
*/
|
*/
|
||||||
struct _EekboardContextService {
|
struct _EekboardContextService {
|
||||||
GObject parent;
|
GObject parent;
|
||||||
struct squeek_layout_state *layout; // Unowned
|
struct squeek_state_manager *state_manager; // shared reference
|
||||||
|
|
||||||
LevelKeyboard *keyboard; // currently used keyboard
|
LevelKeyboard *keyboard; // currently used keyboard
|
||||||
GSettings *settings; // Owned reference
|
GSettings *settings; // Owned reference
|
||||||
@ -126,24 +126,7 @@ settings_get_layout(GSettings *settings, char **type, char **layout)
|
|||||||
g_variant_unref(inputs);
|
g_variant_unref(inputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void eekboard_context_service_set_layout(EekboardContextService *context, struct squeek_layout *layout, uint32_t timestamp) {
|
||||||
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);
|
|
||||||
LevelKeyboard *keyboard = level_keyboard_new(layout);
|
LevelKeyboard *keyboard = level_keyboard_new(layout);
|
||||||
// set as current
|
// set as current
|
||||||
LevelKeyboard *previous_keyboard = context->keyboard;
|
LevelKeyboard *previous_keyboard = context->keyboard;
|
||||||
@ -169,17 +152,7 @@ static void eekboard_context_service_update_settings_layout(EekboardContextServi
|
|||||||
settings_get_layout(context->settings,
|
settings_get_layout(context->settings,
|
||||||
&keyboard_type, &keyboard_layout);
|
&keyboard_type, &keyboard_layout);
|
||||||
|
|
||||||
if (g_strcmp0(context->layout->layout_name, keyboard_layout) != 0 || context->layout->overlay_name) {
|
squeek_state_send_layout_set(context->state_manager, keyboard_layout, keyboard_type, gdk_event_get_time(NULL));
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
@ -297,47 +270,17 @@ eekboard_context_service_get_keyboard (EekboardContextService *context)
|
|||||||
return context->keyboard;
|
return context->keyboard;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used from Rust.
|
EekboardContextService *eekboard_context_service_new(struct squeek_state_manager *state_manager)
|
||||||
// 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 *context = g_object_new (EEKBOARD_TYPE_CONTEXT_SERVICE, NULL);
|
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);
|
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;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
void eekboard_context_service_set_submission(EekboardContextService *context, struct submission *submission) {
|
void eekboard_context_service_set_submission(EekboardContextService *context, struct submission *submission) {
|
||||||
context->submission = submission;
|
context->submission = submission;
|
||||||
if (context->submission) {
|
if (context->submission && context->keyboard) {
|
||||||
uint32_t time = gdk_event_get_time(NULL);
|
uint32_t time = gdk_event_get_time(NULL);
|
||||||
submission_use_layout(context->submission, context->keyboard->layout, time);
|
submission_use_layout(context->submission, context->keyboard->layout, time);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#include "src/submission.h"
|
#include "src/submission.h"
|
||||||
#include "src/layout.h"
|
#include "src/layout.h"
|
||||||
|
#include "src/main.h"
|
||||||
|
|
||||||
#include "virtual-keyboard-unstable-v1-client-protocol.h"
|
#include "virtual-keyboard-unstable-v1-client-protocol.h"
|
||||||
#include "text-input-unstable-v3-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)
|
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_set_submission(EekboardContextService *context, struct submission *submission);
|
||||||
void eekboard_context_service_destroy (EekboardContextService *context);
|
void eekboard_context_service_destroy (EekboardContextService *context);
|
||||||
LevelKeyboard *eekboard_context_service_get_keyboard(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,
|
void eekboard_context_service_set_keymap(EekboardContextService *context,
|
||||||
const LevelKeyboard *keyboard);
|
const LevelKeyboard *keyboard);
|
||||||
|
|
||||||
void
|
|
||||||
eekboard_context_service_use_layout(EekboardContextService *context, struct squeek_layout_state *layout, uint32_t timestamp);
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
#endif /* EEKBOARD_CONTEXT_SERVICE_H */
|
#endif /* EEKBOARD_CONTEXT_SERVICE_H */
|
||||||
|
|||||||
23
src/actors/mod.rs
Normal file
23
src/actors/mod.rs
Normal file
@ -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;
|
||||||
40
src/actors/popover.rs
Normal file
40
src/actors/popover.rs
Normal file
@ -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<State>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct State {
|
||||||
|
pub overlay: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { overlay: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_overlay(
|
||||||
|
actor: &c::Actor,
|
||||||
|
overlay: Option<String>,
|
||||||
|
) {
|
||||||
|
let actor = actor.clone_ref();
|
||||||
|
let mut actor = actor.borrow_mut();
|
||||||
|
actor.overlay = overlay;
|
||||||
|
}
|
||||||
@ -6,18 +6,30 @@
|
|||||||
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use crate::imservice::ContentPurpose;
|
||||||
|
use crate::layout::ArrangementKind;
|
||||||
use crate::outputs::OutputId;
|
use crate::outputs::OutputId;
|
||||||
use crate::panel::PixelSize;
|
use crate::panel::PixelSize;
|
||||||
|
|
||||||
/// The keyboard should hide after this has elapsed to prevent flickering.
|
/// The keyboard should hide after this has elapsed to prevent flickering.
|
||||||
pub const HIDING_TIMEOUT: Duration = Duration::from_millis(200);
|
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<String>,
|
||||||
|
pub purpose: ContentPurpose,
|
||||||
|
}
|
||||||
|
|
||||||
/// The outwardly visible state of visibility
|
/// The outwardly visible state of visibility
|
||||||
#[derive(PartialEq, Debug, Clone)]
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
pub enum Outcome {
|
pub enum Outcome {
|
||||||
Visible {
|
Visible {
|
||||||
output: OutputId,
|
output: OutputId,
|
||||||
height: PixelSize,
|
height: PixelSize,
|
||||||
|
contents: Contents,
|
||||||
},
|
},
|
||||||
Hidden,
|
Hidden,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,64 +7,16 @@
|
|||||||
use std::env;
|
use std::env;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::convert::TryFrom;
|
|
||||||
|
|
||||||
use super::{ Error, LoadError };
|
use super::{ Error, LoadError };
|
||||||
use super::parsing;
|
use super::parsing;
|
||||||
|
|
||||||
use ::layout::ArrangementKind;
|
use crate::layout;
|
||||||
use ::logging;
|
use crate::layout::ArrangementKind;
|
||||||
use ::util::c::as_str;
|
use crate::logging;
|
||||||
use ::xdg;
|
use crate::xdg;
|
||||||
use ::imservice::ContentPurpose;
|
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";
|
const FALLBACK_LAYOUT_NAME: &str = "us";
|
||||||
|
|
||||||
@ -265,7 +217,7 @@ fn load_layout_data_with_fallback(
|
|||||||
kind: ArrangementKind,
|
kind: ArrangementKind,
|
||||||
purpose: ContentPurpose,
|
purpose: ContentPurpose,
|
||||||
overlay: Option<&str>,
|
overlay: Option<&str>,
|
||||||
) -> (ArrangementKind, ::layout::LayoutData) {
|
) -> (ArrangementKind, layout::LayoutData) {
|
||||||
|
|
||||||
// Build the path to the right keyboard layout subdirectory
|
// Build the path to the right keyboard layout subdirectory
|
||||||
let path = env::var_os("SQUEEKBOARD_KEYBOARDSDIR")
|
let path = env::var_os("SQUEEKBOARD_KEYBOARDSDIR")
|
||||||
@ -300,6 +252,17 @@ fn load_layout_data_with_fallback(
|
|||||||
panic!("No useful layout found!");
|
panic!("No useful layout found!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load_layout(
|
||||||
|
name: String,
|
||||||
|
kind: ArrangementKind,
|
||||||
|
variant: ContentPurpose,
|
||||||
|
overlay: Option<String>,
|
||||||
|
) -> 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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
/*! Combined module for dealing with layout files */
|
/*! Combined module for dealing with layout files */
|
||||||
|
|
||||||
mod loading;
|
pub mod loading;
|
||||||
pub mod parsing;
|
pub mod parsing;
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|||||||
@ -39,7 +39,9 @@ type UISender = glib::Sender<Commands>;
|
|||||||
/// It sends outcomes to the glib main loop using a channel.
|
/// 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.
|
/// The outcomes are applied by the UI end of the channel in the `main` module.
|
||||||
// This could still be reasonably tested,
|
// 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)]
|
#[derive(Clone)]
|
||||||
pub struct Threaded {
|
pub struct Threaded {
|
||||||
thread: Sender,
|
thread: Sender,
|
||||||
@ -108,8 +110,11 @@ mod c {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use crate::state::Presence;
|
use crate::state::Presence;
|
||||||
|
use crate::state::LayoutChoice;
|
||||||
use crate::state::visibility;
|
use crate::state::visibility;
|
||||||
|
use crate::util;
|
||||||
use crate::util::c::Wrapped;
|
use crate::util::c::Wrapped;
|
||||||
|
use std::os::raw::c_char;
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
@ -140,4 +145,28 @@ mod c {
|
|||||||
sender.send(Event::PhysicalKeyboard(state))
|
sender.send(Event::PhysicalKeyboard(state))
|
||||||
.or_warn(&mut logging::Print, logging::Problem::Warning, "Can't send to state manager");
|
.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<Threaded>,
|
||||||
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -226,7 +226,7 @@ bitflags!{
|
|||||||
/// use rs::imservice::ContentPurpose;
|
/// use rs::imservice::ContentPurpose;
|
||||||
/// assert_eq!(ContentPurpose::Alpha as u32, 1);
|
/// assert_eq!(ContentPurpose::Alpha as u32, 1);
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
pub enum ContentPurpose {
|
pub enum ContentPurpose {
|
||||||
Normal = 0,
|
Normal = 0,
|
||||||
Alpha = 1,
|
Alpha = 1,
|
||||||
|
|||||||
@ -7,6 +7,8 @@
|
|||||||
#include "eek/eek-gtk-keyboard.h"
|
#include "eek/eek-gtk-keyboard.h"
|
||||||
#include "eek/eek-renderer.h"
|
#include "eek/eek-renderer.h"
|
||||||
#include "eek/eek-types.h"
|
#include "eek/eek-types.h"
|
||||||
|
#include "src/main.h"
|
||||||
|
#include "src/popover.h"
|
||||||
#include "src/submission.h"
|
#include "src/submission.h"
|
||||||
#include "virtual-keyboard-unstable-v1-client-protocol.h"
|
#include "virtual-keyboard-unstable-v1-client-protocol.h"
|
||||||
#include "text-input-unstable-v3-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 submission *submission,
|
||||||
struct transformation widget_to_layout,
|
struct transformation widget_to_layout,
|
||||||
uint32_t timestamp,
|
uint32_t timestamp,
|
||||||
EekboardContextService *manager,
|
struct squeek_popover *popover,
|
||||||
|
struct squeek_state_manager *state,
|
||||||
EekGtkKeyboard *ui_keyboard);
|
EekGtkKeyboard *ui_keyboard);
|
||||||
void squeek_layout_release_all_only(struct squeek_layout *layout,
|
void squeek_layout_release_all_only(struct squeek_layout *layout,
|
||||||
struct submission *submission,
|
struct submission *submission,
|
||||||
@ -54,7 +57,8 @@ void squeek_layout_drag(struct squeek_layout *layout,
|
|||||||
struct submission *submission,
|
struct submission *submission,
|
||||||
double x_widget, double y_widget,
|
double x_widget, double y_widget,
|
||||||
struct transformation widget_to_layout,
|
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);
|
EekGtkKeyboard *ui_keyboard);
|
||||||
void squeek_layout_draw_all_changed(struct squeek_layout *layout, EekRenderer* renderer, cairo_t *cr, struct submission *submission);
|
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);
|
void squeek_draw_layout_base_view(struct squeek_layout *layout, EekRenderer* renderer, cairo_t *cr);
|
||||||
|
|||||||
@ -25,30 +25,35 @@ use std::fmt;
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
use ::action::Action;
|
use crate::action::Action;
|
||||||
use ::drawing;
|
use crate::actors;
|
||||||
use ::float_ord::FloatOrd;
|
use crate::drawing;
|
||||||
use ::keyboard::KeyState;
|
use crate::float_ord::FloatOrd;
|
||||||
use ::logging;
|
use crate::keyboard::KeyState;
|
||||||
use ::manager;
|
use crate::logging;
|
||||||
use ::submission::{ Submission, SubmitData, Timestamp };
|
use crate::popover;
|
||||||
use ::util::find_max_double;
|
use crate::receiver;
|
||||||
|
use crate::submission::{ Submission, SubmitData, Timestamp };
|
||||||
|
use crate::util::find_max_double;
|
||||||
|
|
||||||
use ::imservice::ContentPurpose;
|
use crate::imservice::ContentPurpose;
|
||||||
|
|
||||||
// Traits
|
// Traits
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use ::logging::Warn;
|
use crate::logging::Warn;
|
||||||
|
|
||||||
/// Gathers stuff defined in C or called by C
|
/// Gathers stuff defined in C or called by C
|
||||||
pub mod c {
|
pub mod c {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use gtk_sys;
|
use crate::receiver;
|
||||||
use std::os::raw::c_void;
|
|
||||||
use crate::submission::c::Submission as CSubmission;
|
use crate::submission::c::Submission as CSubmission;
|
||||||
|
|
||||||
|
use gtk_sys;
|
||||||
use std::ops::{ Add, Sub };
|
use std::ops::{ Add, Sub };
|
||||||
|
use std::os::raw::c_void;
|
||||||
|
|
||||||
|
use crate::util::CloneOwned;
|
||||||
|
|
||||||
// The following defined in C
|
// The following defined in C
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
@ -215,13 +220,17 @@ pub mod c {
|
|||||||
submission: CSubmission,
|
submission: CSubmission,
|
||||||
widget_to_layout: Transformation,
|
widget_to_layout: Transformation,
|
||||||
time: u32,
|
time: u32,
|
||||||
manager: manager::c::Manager,
|
popover: actors::popover::c::Actor,
|
||||||
|
app_state: receiver::c::State,
|
||||||
ui_keyboard: EekGtkKeyboard,
|
ui_keyboard: EekGtkKeyboard,
|
||||||
) {
|
) {
|
||||||
let time = Timestamp(time);
|
let time = Timestamp(time);
|
||||||
let layout = unsafe { &mut *layout };
|
let layout = unsafe { &mut *layout };
|
||||||
let submission = submission.clone_ref();
|
let submission = submission.clone_ref();
|
||||||
let mut submission = submission.borrow_mut();
|
let mut submission = submission.borrow_mut();
|
||||||
|
let app_state = app_state.clone_owned();
|
||||||
|
let popover_state = popover.clone_owned();
|
||||||
|
|
||||||
let ui_backend = UIBackend {
|
let ui_backend = UIBackend {
|
||||||
widget_to_layout,
|
widget_to_layout,
|
||||||
keyboard: ui_keyboard,
|
keyboard: ui_keyboard,
|
||||||
@ -236,7 +245,7 @@ pub mod c {
|
|||||||
&mut submission,
|
&mut submission,
|
||||||
Some(&ui_backend),
|
Some(&ui_backend),
|
||||||
time,
|
time,
|
||||||
Some(manager),
|
Some((&popover_state, app_state.clone())),
|
||||||
key,
|
key,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -315,13 +324,18 @@ pub mod c {
|
|||||||
x_widget: f64, y_widget: f64,
|
x_widget: f64, y_widget: f64,
|
||||||
widget_to_layout: Transformation,
|
widget_to_layout: Transformation,
|
||||||
time: u32,
|
time: u32,
|
||||||
manager: manager::c::Manager,
|
popover: actors::popover::c::Actor,
|
||||||
|
app_state: receiver::c::State,
|
||||||
ui_keyboard: EekGtkKeyboard,
|
ui_keyboard: EekGtkKeyboard,
|
||||||
) {
|
) {
|
||||||
let time = Timestamp(time);
|
let time = Timestamp(time);
|
||||||
let layout = unsafe { &mut *layout };
|
let layout = unsafe { &mut *layout };
|
||||||
let submission = submission.clone_ref();
|
let submission = submission.clone_ref();
|
||||||
let mut submission = submission.borrow_mut();
|
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 {
|
let ui_backend = UIBackend {
|
||||||
widget_to_layout,
|
widget_to_layout,
|
||||||
keyboard: ui_keyboard,
|
keyboard: ui_keyboard,
|
||||||
@ -352,7 +366,7 @@ pub mod c {
|
|||||||
&mut submission,
|
&mut submission,
|
||||||
Some(&ui_backend),
|
Some(&ui_backend),
|
||||||
time,
|
time,
|
||||||
Some(manager),
|
Some((&popover_state, app_state.clone())),
|
||||||
key,
|
key,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -377,7 +391,7 @@ pub mod c {
|
|||||||
&mut submission,
|
&mut submission,
|
||||||
Some(&ui_backend),
|
Some(&ui_backend),
|
||||||
time,
|
time,
|
||||||
Some(manager),
|
Some((&popover_state, app_state.clone())),
|
||||||
key,
|
key,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1035,7 +1049,11 @@ mod seat {
|
|||||||
submission: &mut Submission,
|
submission: &mut Submission,
|
||||||
ui: Option<&UIBackend>,
|
ui: Option<&UIBackend>,
|
||||||
time: Timestamp,
|
time: Timestamp,
|
||||||
manager: Option<manager::c::Manager>,
|
// 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<RefCell<KeyState>>,
|
rckey: &Rc<RefCell<KeyState>>,
|
||||||
) {
|
) {
|
||||||
let key: KeyState = {
|
let key: KeyState = {
|
||||||
@ -1070,7 +1088,7 @@ mod seat {
|
|||||||
// only show when UI is present
|
// only show when UI is present
|
||||||
Action::ShowPreferences => if let Some(ui) = &ui {
|
Action::ShowPreferences => if let Some(ui) = &ui {
|
||||||
// only show when layout manager is available
|
// 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 view = layout.get_current_view();
|
||||||
let places = ::layout::procedures::find_key_places(
|
let places = ::layout::procedures::find_key_places(
|
||||||
view, &rckey,
|
view, &rckey,
|
||||||
@ -1085,10 +1103,11 @@ mod seat {
|
|||||||
width: button.size.width,
|
width: button.size.width,
|
||||||
height: button.size.height,
|
height: button.size.height,
|
||||||
};
|
};
|
||||||
::popover::show(
|
popover::show(
|
||||||
ui.keyboard,
|
ui.keyboard,
|
||||||
ui.widget_to_layout.reverse_bounds(bounds),
|
ui.widget_to_layout.reverse_bounds(bounds),
|
||||||
manager,
|
manager,
|
||||||
|
app_state,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,7 @@ mod assert_matches;
|
|||||||
mod logging;
|
mod logging;
|
||||||
|
|
||||||
mod action;
|
mod action;
|
||||||
|
mod actors;
|
||||||
mod animation;
|
mod animation;
|
||||||
pub mod data;
|
pub mod data;
|
||||||
mod debug;
|
mod debug;
|
||||||
@ -34,10 +35,10 @@ mod keyboard;
|
|||||||
mod layout;
|
mod layout;
|
||||||
mod locale;
|
mod locale;
|
||||||
mod main;
|
mod main;
|
||||||
mod manager;
|
|
||||||
mod outputs;
|
mod outputs;
|
||||||
mod panel;
|
mod panel;
|
||||||
mod popover;
|
mod popover;
|
||||||
|
mod receiver;
|
||||||
mod resources;
|
mod resources;
|
||||||
mod state;
|
mod state;
|
||||||
mod style;
|
mod style;
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
#include "eek/eek-types.h"
|
#include "eek/eek-types.h"
|
||||||
#include "dbus.h"
|
#include "dbus.h"
|
||||||
#include "panel.h"
|
#include "panel.h"
|
||||||
|
#include "src/popover.h"
|
||||||
|
|
||||||
|
|
||||||
struct receiver;
|
struct receiver;
|
||||||
@ -23,9 +24,10 @@ struct rsobjects {
|
|||||||
struct squeek_state_manager *state_manager;
|
struct squeek_state_manager *state_manager;
|
||||||
struct submission *submission;
|
struct submission *submission;
|
||||||
struct squeek_wayland *wayland;
|
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);
|
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_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_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);
|
||||||
|
|||||||
51
src/main.rs
51
src/main.rs
@ -3,9 +3,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*! Glue for the main loop. */
|
/*! Glue for the main loop. */
|
||||||
use crate::panel;
|
use crate::actors;
|
||||||
|
use crate::animation;
|
||||||
use crate::debug;
|
use crate::debug;
|
||||||
use crate::state;
|
use crate::data::loading;
|
||||||
|
use crate::panel;
|
||||||
use glib::{Continue, MainContext, PRIORITY_DEFAULT, Receiver};
|
use glib::{Continue, MainContext, PRIORITY_DEFAULT, Receiver};
|
||||||
|
|
||||||
|
|
||||||
@ -19,6 +21,7 @@ mod c {
|
|||||||
use crate::event_loop::driver;
|
use crate::event_loop::driver;
|
||||||
use crate::imservice::IMService;
|
use crate::imservice::IMService;
|
||||||
use crate::imservice::c::InputMethod;
|
use crate::imservice::c::InputMethod;
|
||||||
|
use crate::layout;
|
||||||
use crate::outputs::Outputs;
|
use crate::outputs::Outputs;
|
||||||
use crate::state;
|
use crate::state;
|
||||||
use crate::submission::Submission;
|
use crate::submission::Submission;
|
||||||
@ -46,6 +49,7 @@ mod c {
|
|||||||
submission: Wrapped<Submission>,
|
submission: Wrapped<Submission>,
|
||||||
/// Not wrapped, because C needs to access this.
|
/// Not wrapped, because C needs to access this.
|
||||||
wayland: *mut Wayland,
|
wayland: *mut Wayland,
|
||||||
|
popover: actors::popover::c::Actor,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Corresponds to wayland.h::squeek_wayland.
|
/// Corresponds to wayland.h::squeek_wayland.
|
||||||
@ -78,7 +82,8 @@ mod c {
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#[allow(improper_ctypes)]
|
#[allow(improper_ctypes)]
|
||||||
fn init_wayland(wayland: *mut Wayland);
|
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,
|
// This should probably only get called from the gtk main loop,
|
||||||
// given that dbus handler is using glib.
|
// given that dbus handler is using glib.
|
||||||
fn dbus_handler_set_visible(dbus: *const DBusHandler, visible: u8);
|
fn dbus_handler_set_visible(dbus: *const DBusHandler, visible: u8);
|
||||||
@ -116,6 +121,7 @@ mod c {
|
|||||||
state_manager: Wrapped::new(state_manager),
|
state_manager: Wrapped::new(state_manager),
|
||||||
receiver: Wrapped::new(receiver),
|
receiver: Wrapped::new(receiver),
|
||||||
wayland: Box::into_raw(wayland),
|
wayland: Box::into_raw(wayland),
|
||||||
|
popover: Wrapped::new(actors::popover::State::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,6 +131,7 @@ mod c {
|
|||||||
fn register_ui_loop_handler(
|
fn register_ui_loop_handler(
|
||||||
receiver: Wrapped<Receiver<Commands>>,
|
receiver: Wrapped<Receiver<Commands>>,
|
||||||
panel_manager: panel::c::PanelManager,
|
panel_manager: panel::c::PanelManager,
|
||||||
|
popover: actors::popover::c::Actor,
|
||||||
hint_manager: HintManager,
|
hint_manager: HintManager,
|
||||||
dbus_handler: *const DBusHandler,
|
dbus_handler: *const DBusHandler,
|
||||||
) {
|
) {
|
||||||
@ -137,7 +144,13 @@ mod c {
|
|||||||
receiver.attach(
|
receiver.attach(
|
||||||
Some(&ctx),
|
Some(&ctx),
|
||||||
move |msg| {
|
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)
|
Continue(true)
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -152,6 +165,7 @@ mod c {
|
|||||||
fn main_loop_handle_message(
|
fn main_loop_handle_message(
|
||||||
msg: Commands,
|
msg: Commands,
|
||||||
panel_manager: Wrapped<panel::Manager>,
|
panel_manager: Wrapped<panel::Manager>,
|
||||||
|
popover: &actors::popover::c::Actor,
|
||||||
hint_manager: HintManager,
|
hint_manager: HintManager,
|
||||||
dbus_handler: *const DBusHandler,
|
dbus_handler: *const DBusHandler,
|
||||||
) {
|
) {
|
||||||
@ -165,23 +179,36 @@ mod c {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
unsafe {
|
||||||
eekboard_context_service_set_hint_purpose(
|
eekboard_context_service_set_layout(hint_manager, layout, 0);
|
||||||
hint_manager,
|
|
||||||
hints.hint.bits(),
|
|
||||||
hints.purpose.clone() as u32,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod commands {
|
||||||
|
use crate::animation;
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct SetLayout {
|
||||||
|
pub description: animation::Contents,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The commands consumed by the main loop,
|
/// The commands consumed by the main loop,
|
||||||
/// to be sent out to external components.
|
/// to be sent out to external components.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Commands {
|
pub struct Commands {
|
||||||
pub panel_visibility: Option<panel::Command>,
|
pub panel_visibility: Option<panel::Command>,
|
||||||
pub layout_hint_set: Option<state::InputMethodDetails>,
|
|
||||||
pub dbus_visible_set: Option<bool>,
|
pub dbus_visible_set: Option<bool>,
|
||||||
|
pub layout_selection: Option<commands::SetLayout>,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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<String> {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
@ -52,7 +52,7 @@ make_widget (struct panel_manager *self)
|
|||||||
if (self->widget) {
|
if (self->widget) {
|
||||||
g_error("Widget already present");
|
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_widget_set_has_tooltip (self->widget, TRUE);
|
||||||
gtk_container_add (GTK_CONTAINER(self->window), self->widget);
|
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 = {
|
struct panel_manager mgr = {
|
||||||
.state = state,
|
.state = state,
|
||||||
.submission = submission,
|
.submission = submission,
|
||||||
.layout = layout,
|
|
||||||
.window = NULL,
|
.window = NULL,
|
||||||
.widget = NULL,
|
.widget = NULL,
|
||||||
.current_output = NULL,
|
.current_output = NULL,
|
||||||
|
.state_manager = state_manager,
|
||||||
|
.popover = popover,
|
||||||
};
|
};
|
||||||
return mgr;
|
return mgr;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,14 +2,16 @@
|
|||||||
|
|
||||||
#include "eek/layersurface.h"
|
#include "eek/layersurface.h"
|
||||||
#include "src/layout.h"
|
#include "src/layout.h"
|
||||||
|
#include "src/main.h"
|
||||||
#include "src/submission.h"
|
#include "src/submission.h"
|
||||||
|
|
||||||
// Stores the objects that the panel and its widget will refer to
|
// Stores the objects that the panel and its widget will refer to
|
||||||
struct panel_manager {
|
struct panel_manager {
|
||||||
EekboardContextService *state; // unowned
|
EekboardContextService *state; // unowned
|
||||||
/// Needed for instantiating the widget
|
/// Needed for instantiating the widget
|
||||||
|
struct squeek_state_manager *state_manager; // shared reference
|
||||||
|
struct squeek_popover *popover; // shared reference
|
||||||
struct submission *submission; // unowned
|
struct submission *submission; // unowned
|
||||||
struct squeek_layout_state *layout;
|
|
||||||
|
|
||||||
PhoshLayerSurface *window;
|
PhoshLayerSurface *window;
|
||||||
GtkWidget *widget; // nullable
|
GtkWidget *widget; // nullable
|
||||||
@ -18,4 +20,4 @@ struct panel_manager {
|
|||||||
struct wl_output *current_output;
|
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);
|
||||||
|
|||||||
5
src/popover.h
Normal file
5
src/popover.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
/// Popover state.
|
||||||
|
/// Wrapped<actors::popover::State>
|
||||||
|
struct squeek_popover;
|
||||||
|
|
||||||
@ -4,11 +4,13 @@ use gio;
|
|||||||
use gtk;
|
use gtk;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use ::layout::c::{ Bounds, EekGtkKeyboard };
|
use crate::actors;
|
||||||
use ::locale::{ OwnedTranslation, compare_current_locale };
|
use crate::layout::c::{ Bounds, EekGtkKeyboard };
|
||||||
use ::logging;
|
use crate::locale::{ OwnedTranslation, compare_current_locale };
|
||||||
use ::manager;
|
use crate::logging;
|
||||||
use ::resources;
|
use crate::receiver;
|
||||||
|
use crate::resources;
|
||||||
|
use crate::state;
|
||||||
|
|
||||||
// Traits
|
// Traits
|
||||||
use gio::prelude::ActionMapExt;
|
use gio::prelude::ActionMapExt;
|
||||||
@ -16,7 +18,7 @@ use gio::prelude::SettingsExt;
|
|||||||
use glib::translate::FromGlibPtrNone;
|
use glib::translate::FromGlibPtrNone;
|
||||||
use glib::variant::ToVariant;
|
use glib::variant::ToVariant;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use ::logging::Warn;
|
use crate::logging::Warn;
|
||||||
|
|
||||||
mod c {
|
mod c {
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
@ -127,9 +129,11 @@ fn get_settings(schema_name: &str) -> Option<gio::Settings> {
|
|||||||
.map(|_sschema| gio::Settings::new(schema_name))
|
.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");
|
let settings = get_settings("org.gnome.desktop.input-sources");
|
||||||
if let Some(settings) = settings {
|
if let Some(settings) = settings {
|
||||||
|
let kind = String::from(kind);
|
||||||
|
let name = String::from(name);
|
||||||
#[cfg(feature = "glib_v0_14")]
|
#[cfg(feature = "glib_v0_14")]
|
||||||
let inputs = settings.value("sources");
|
let inputs = settings.value("sources");
|
||||||
#[cfg(not(feature = "glib_v0_14"))]
|
#[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
|
/// A reference to what the user wants to see
|
||||||
#[derive(PartialEq, Clone, Debug)]
|
#[derive(PartialEq, Clone, Debug)]
|
||||||
enum LayoutId {
|
pub enum LayoutId {
|
||||||
/// Affects the layout in system settings
|
/// Affects the layout in system settings
|
||||||
System {
|
System {
|
||||||
kind: String,
|
kind: String,
|
||||||
@ -170,40 +174,23 @@ impl LayoutId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn set_visible_layout(
|
fn set_visible_layout(
|
||||||
manager: manager::c::Manager,
|
layout_id: &LayoutId,
|
||||||
layout_id: LayoutId,
|
|
||||||
) {
|
) {
|
||||||
match layout_id {
|
match layout_id {
|
||||||
LayoutId::System { kind, name } => {
|
LayoutId::System { kind, name } => {
|
||||||
unsafe {
|
|
||||||
use std::ptr;
|
|
||||||
manager::c::eekboard_context_service_set_overlay(
|
|
||||||
manager,
|
|
||||||
ptr::null(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
set_layout(kind, name);
|
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
|
/// Takes into account first any overlays, then system layouts from the list
|
||||||
fn get_current_layout(
|
fn get_current_layout(
|
||||||
manager: manager::c::Manager,
|
popover: &actors::popover::State,
|
||||||
system_layouts: &Vec<LayoutId>,
|
system_layouts: &Vec<LayoutId>,
|
||||||
) -> Option<LayoutId> {
|
) -> Option<LayoutId> {
|
||||||
match manager::get_overlay(manager) {
|
match &popover.overlay {
|
||||||
Some(name) => Some(LayoutId::Local(name)),
|
Some(name) => Some(LayoutId::Local(name.into())),
|
||||||
None => system_layouts.get(0).map(LayoutId::clone),
|
None => system_layouts.get(0).map(LayoutId::clone),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -247,7 +234,8 @@ fn translate_layout_names(layouts: &Vec<LayoutId>) -> Vec<OwnedTranslation> {
|
|||||||
pub fn show(
|
pub fn show(
|
||||||
window: EekGtkKeyboard,
|
window: EekGtkKeyboard,
|
||||||
position: Bounds,
|
position: Bounds,
|
||||||
manager: manager::c::Manager,
|
popover: &actors::popover::State,
|
||||||
|
app_state: receiver::State,
|
||||||
) {
|
) {
|
||||||
unsafe { gtk::set_initialized() };
|
unsafe { gtk::set_initialized() };
|
||||||
let window = unsafe { gtk::Widget::from_glib_none(window.0) };
|
let window = unsafe { gtk::Widget::from_glib_none(window.0) };
|
||||||
@ -327,7 +315,7 @@ pub fn show(
|
|||||||
|
|
||||||
let action_group = gio::SimpleActionGroup::new();
|
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()
|
let current_layout_name = all_layouts.iter()
|
||||||
.find(
|
.find(
|
||||||
|l| l.get_name() == current_layout.get_name()
|
|l| l.get_name() == current_layout.get_name()
|
||||||
@ -356,10 +344,13 @@ pub fn show(
|
|||||||
.find(
|
.find(
|
||||||
|choices| state == choices.get_name()
|
|choices| state == choices.get_name()
|
||||||
).unwrap();
|
).unwrap();
|
||||||
set_visible_layout(
|
app_state
|
||||||
manager,
|
.send(state::Event::OverlayChanged(layout.clone()))
|
||||||
layout.clone(),
|
.or_print(
|
||||||
)
|
logging::Problem::Bug,
|
||||||
|
&format!("Can't send to state"),
|
||||||
|
);
|
||||||
|
set_visible_layout(layout)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
None => log_print!(
|
None => log_print!(
|
||||||
|
|||||||
15
src/receiver.rs
Normal file
15
src/receiver.rs
Normal file
@ -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<Threaded>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
@ -56,8 +56,6 @@ struct squeekboard {
|
|||||||
/// Gsettings hook for visibility. TODO: this does not belong in gsettings.
|
/// Gsettings hook for visibility. TODO: this does not belong in gsettings.
|
||||||
ServerContextService *settings_handler;
|
ServerContextService *settings_handler;
|
||||||
struct panel_manager panel_manager; // Controls the shape of the panel.
|
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
|
// Also initializes wayland
|
||||||
struct rsobjects rsobjects = squeek_init();
|
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
|
// set up dbus
|
||||||
|
|
||||||
@ -450,9 +448,10 @@ main (int argc, char **argv)
|
|||||||
|
|
||||||
instance.panel_manager = panel_manager_new(instance.settings_context,
|
instance.panel_manager = panel_manager_new(instance.settings_context,
|
||||||
rsobjects.submission,
|
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();
|
session_register();
|
||||||
|
|
||||||
|
|||||||
209
src/state.rs
209
src/state.rs
@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2021 Purism SPC
|
/* Copyright (C) 2021,2022 Purism SPC
|
||||||
* SPDX-License-Identifier: GPL-3.0+
|
* SPDX-License-Identifier: GPL-3.0+
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -8,11 +8,14 @@
|
|||||||
use crate::animation;
|
use crate::animation;
|
||||||
use crate::debug;
|
use crate::debug;
|
||||||
use crate::imservice::{ ContentHint, ContentPurpose };
|
use crate::imservice::{ ContentHint, ContentPurpose };
|
||||||
|
use crate::layout::ArrangementKind;
|
||||||
|
use crate::main;
|
||||||
use crate::main::Commands;
|
use crate::main::Commands;
|
||||||
use crate::outputs;
|
use crate::outputs;
|
||||||
use crate::outputs::{Millimeter, OutputId, OutputState};
|
use crate::outputs::{Millimeter, OutputId, OutputState};
|
||||||
use crate::panel;
|
use crate::panel;
|
||||||
use crate::panel::PixelSize;
|
use crate::panel::PixelSize;
|
||||||
|
use crate::popover;
|
||||||
use crate::util::Rational;
|
use crate::util::Rational;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@ -37,6 +40,29 @@ pub enum InputMethod {
|
|||||||
InactiveSince(Instant),
|
InactiveSince(Instant),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum LayoutSource {
|
||||||
|
Xkb,
|
||||||
|
Other(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> 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.
|
/// Incoming events.
|
||||||
/// This contains events that cause a change to the internal state.
|
/// This contains events that cause a change to the internal state.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -45,6 +71,8 @@ pub enum Event {
|
|||||||
Visibility(visibility::Event),
|
Visibility(visibility::Event),
|
||||||
PhysicalKeyboard(Presence),
|
PhysicalKeyboard(Presence),
|
||||||
Output(outputs::Event),
|
Output(outputs::Event),
|
||||||
|
LayoutChoice(LayoutChoice),
|
||||||
|
OverlayChanged(popover::LayoutId),
|
||||||
Debug(debug::Event),
|
Debug(debug::Event),
|
||||||
/// Event triggered because a moment in time passed.
|
/// Event triggered because a moment in time passed.
|
||||||
/// Use to animate state transitions.
|
/// Use to animate state transitions.
|
||||||
@ -87,7 +115,7 @@ pub mod visibility {
|
|||||||
/// The outwardly visible state.
|
/// The outwardly visible state.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Outcome {
|
pub struct Outcome {
|
||||||
pub visibility: animation::Outcome,
|
pub panel: animation::Outcome,
|
||||||
pub im: InputMethod,
|
pub im: InputMethod,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,36 +126,40 @@ impl Outcome {
|
|||||||
/// The receivers of the commands bear the burden
|
/// The receivers of the commands bear the burden
|
||||||
/// of checking if the commands end up being no-ops.
|
/// of checking if the commands end up being no-ops.
|
||||||
pub fn get_commands_to_reach(&self, new_state: &Self) -> Commands {
|
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
|
// FIXME: handle switching outputs
|
||||||
let (dbus_visible_set, panel_visibility) = match new_state.visibility {
|
let (dbus_visible_set, panel_visibility) = match new_state.panel {
|
||||||
animation::Outcome::Visible{output, height}
|
animation::Outcome::Visible{output, height, ..}
|
||||||
=> (Some(true), Some(panel::Command::Show{output, height})),
|
=> (Some(true), Some(panel::Command::Show{output, height})),
|
||||||
animation::Outcome::Hidden => (Some(false), Some(panel::Command::Hide)),
|
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 {
|
Commands {
|
||||||
panel_visibility,
|
panel_visibility,
|
||||||
layout_hint_set,
|
|
||||||
dbus_visible_set,
|
dbus_visible_set,
|
||||||
|
layout_selection,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -156,6 +188,13 @@ pub struct Application {
|
|||||||
/// but not sure about being allowed on non-touch displays.
|
/// but not sure about being allowed on non-touch displays.
|
||||||
pub preferred_output: Option<OutputId>,
|
pub preferred_output: Option<OutputId>,
|
||||||
pub outputs: HashMap<OutputId, OutputState>,
|
pub outputs: HashMap<OutputId, OutputState>,
|
||||||
|
/// 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<popover::LayoutId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application {
|
impl Application {
|
||||||
@ -173,6 +212,11 @@ impl Application {
|
|||||||
debug_mode_enabled: false,
|
debug_mode_enabled: false,
|
||||||
preferred_output: None,
|
preferred_output: None,
|
||||||
outputs: Default::default(),
|
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),
|
im: InputMethod::InactiveSince(old),
|
||||||
..self
|
..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 {
|
if state.debug_mode_enabled {
|
||||||
@ -273,7 +328,9 @@ Outcome:
|
|||||||
state
|
state
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_preferred_height(output: &OutputState) -> Option<PixelSize> {
|
fn get_preferred_height_and_arrangement(output: &OutputState)
|
||||||
|
-> Option<(PixelSize, ArrangementKind)>
|
||||||
|
{
|
||||||
output.get_pixel_size()
|
output.get_pixel_size()
|
||||||
.map(|px_size| {
|
.map(|px_size| {
|
||||||
// Assume isotropy.
|
// Assume isotropy.
|
||||||
@ -301,7 +358,6 @@ Outcome:
|
|||||||
// TODO: calculate based on selected layout
|
// TODO: calculate based on selected layout
|
||||||
const ROW_COUNT: u32 = 4;
|
const ROW_COUNT: u32 = 4;
|
||||||
|
|
||||||
let height = {
|
|
||||||
let ideal_height = IDEAL_TARGET_SIZE * ROW_COUNT as i32;
|
let ideal_height = IDEAL_TARGET_SIZE * ROW_COUNT as i32;
|
||||||
let ideal_height_px = (ideal_height * density).ceil().0 as u32;
|
let ideal_height_px = (ideal_height * density).ceil().0 as u32;
|
||||||
|
|
||||||
@ -316,45 +372,80 @@ Outcome:
|
|||||||
}
|
}
|
||||||
.as_scaled_ceiling();
|
.as_scaled_ceiling();
|
||||||
|
|
||||||
let height_as_widths = {
|
let (arrangement, height_as_widths) = {
|
||||||
if abstract_width < 540 {
|
if abstract_width < 540 {(
|
||||||
// Normal
|
ArrangementKind::Base,
|
||||||
Rational {
|
Rational {
|
||||||
numerator: 210,
|
numerator: 210,
|
||||||
denominator: 360,
|
denominator: 360,
|
||||||
}
|
},
|
||||||
} else {
|
)} else {(
|
||||||
// Wide
|
ArrangementKind::Wide,
|
||||||
Rational {
|
Rational {
|
||||||
numerator: 172,
|
numerator: 172,
|
||||||
denominator: 540,
|
denominator: 540,
|
||||||
}
|
}
|
||||||
}
|
)}
|
||||||
};
|
};
|
||||||
cmp::min(
|
|
||||||
|
let height
|
||||||
|
= cmp::min(
|
||||||
ideal_height_px,
|
ideal_height_px,
|
||||||
(height_as_widths * px_size.width as i32).ceil() as u32,
|
(height_as_widths * px_size.width as i32).ceil() as u32,
|
||||||
)
|
);
|
||||||
};
|
|
||||||
|
(
|
||||||
PixelSize {
|
PixelSize {
|
||||||
scale_factor: output.scale as u32,
|
scale_factor: output.scale as u32,
|
||||||
pixels: cmp::min(height, px_size.height / 2),
|
pixels: cmp::min(height, px_size.height / 2),
|
||||||
}
|
},
|
||||||
|
arrangement,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns layout name, overlay name
|
||||||
|
fn get_layout_names(&self) -> (String, Option<String>) {
|
||||||
|
(
|
||||||
|
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 {
|
pub fn get_outcome(&self, now: Instant) -> Outcome {
|
||||||
// FIXME: include physical keyboard presence
|
// FIXME: include physical keyboard presence
|
||||||
Outcome {
|
Outcome {
|
||||||
visibility: match self.preferred_output {
|
panel: match self.preferred_output {
|
||||||
None => animation::Outcome::Hidden,
|
None => animation::Outcome::Hidden,
|
||||||
Some(output) => {
|
Some(output) => {
|
||||||
// Hoping that this will get optimized out on branches not using `visible`.
|
let (height, arrangement) = Self::get_preferred_height_and_arrangement(self.outputs.get(&output).unwrap())
|
||||||
let height = Self::get_preferred_height(self.outputs.get(&output).unwrap())
|
.unwrap_or((
|
||||||
.unwrap_or(PixelSize{pixels: 0, scale_factor: 1});
|
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,
|
// TODO: Instead of setting size to 0 when the output is invalid,
|
||||||
// simply go invisible.
|
// 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) {
|
match (self.physical_keyboard, self.visibility_override) {
|
||||||
(_, visibility::State::ForcedHidden) => animation::Outcome::Hidden,
|
(_, visibility::State::ForcedHidden) => animation::Outcome::Hidden,
|
||||||
@ -446,7 +537,7 @@ pub mod test {
|
|||||||
for _i in 0..100 {
|
for _i in 0..100 {
|
||||||
now += Duration::from_millis(1);
|
now += Duration::from_millis(1);
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
state.get_outcome(now).visibility,
|
state.get_outcome(now).panel,
|
||||||
animation::Outcome::Visible{..},
|
animation::Outcome::Visible{..},
|
||||||
"Hidden when it should remain visible: {:?}",
|
"Hidden when it should remain visible: {:?}",
|
||||||
now.saturating_duration_since(start),
|
now.saturating_duration_since(start),
|
||||||
@ -455,7 +546,10 @@ pub mod test {
|
|||||||
|
|
||||||
let state = state.apply_event(Event::InputMethod(InputMethod::Active(imdetails_new())), now);
|
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
|
/// 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);
|
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);
|
now += Duration::from_millis(1);
|
||||||
assert!(
|
assert!(
|
||||||
now < start + Duration::from_millis(250),
|
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::Active(imdetails_new())), now);
|
||||||
let state = state.apply_event(Event::InputMethod(InputMethod::InactiveSince(now)), 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);
|
now += Duration::from_millis(1);
|
||||||
assert!(
|
assert!(
|
||||||
now < start + Duration::from_millis(250),
|
now < start + Duration::from_millis(250),
|
||||||
@ -515,7 +609,7 @@ pub mod test {
|
|||||||
for _i in 0..1000 {
|
for _i in 0..1000 {
|
||||||
now += Duration::from_millis(1);
|
now += Duration::from_millis(1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
state.get_outcome(now).visibility,
|
state.get_outcome(now).panel,
|
||||||
animation::Outcome::Hidden,
|
animation::Outcome::Hidden,
|
||||||
"Appeared unnecessarily: {:?}",
|
"Appeared unnecessarily: {:?}",
|
||||||
now.saturating_duration_since(start),
|
now.saturating_duration_since(start),
|
||||||
@ -537,7 +631,7 @@ pub mod test {
|
|||||||
|
|
||||||
let state = state.apply_event(Event::Visibility(visibility::Event::ForceVisible), now);
|
let state = state.apply_event(Event::Visibility(visibility::Event::ForceVisible), now);
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
state.get_outcome(now).visibility,
|
state.get_outcome(now).panel,
|
||||||
animation::Outcome::Visible{..},
|
animation::Outcome::Visible{..},
|
||||||
"Failed to show: {:?}",
|
"Failed to show: {:?}",
|
||||||
now.saturating_duration_since(start),
|
now.saturating_duration_since(start),
|
||||||
@ -550,7 +644,7 @@ pub mod test {
|
|||||||
now += Duration::from_secs(1);
|
now += Duration::from_secs(1);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
state.get_outcome(now).visibility,
|
state.get_outcome(now).panel,
|
||||||
animation::Outcome::Hidden,
|
animation::Outcome::Hidden,
|
||||||
"Failed to release forced visibility: {:?}",
|
"Failed to release forced visibility: {:?}",
|
||||||
now.saturating_duration_since(start),
|
now.saturating_duration_since(start),
|
||||||
@ -571,7 +665,7 @@ pub mod test {
|
|||||||
|
|
||||||
let state = state.apply_event(Event::PhysicalKeyboard(Presence::Present), now);
|
let state = state.apply_event(Event::PhysicalKeyboard(Presence::Present), now);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
state.get_outcome(now).visibility,
|
state.get_outcome(now).panel,
|
||||||
animation::Outcome::Hidden,
|
animation::Outcome::Hidden,
|
||||||
"Failed to hide: {:?}",
|
"Failed to hide: {:?}",
|
||||||
now.saturating_duration_since(start),
|
now.saturating_duration_since(start),
|
||||||
@ -583,7 +677,7 @@ pub mod test {
|
|||||||
let state = state.apply_event(Event::InputMethod(InputMethod::Active(imdetails_new())), now);
|
let state = state.apply_event(Event::InputMethod(InputMethod::Active(imdetails_new())), now);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
state.get_outcome(now).visibility,
|
state.get_outcome(now).panel,
|
||||||
animation::Outcome::Hidden,
|
animation::Outcome::Hidden,
|
||||||
"Failed to remain hidden: {:?}",
|
"Failed to remain hidden: {:?}",
|
||||||
now.saturating_duration_since(start),
|
now.saturating_duration_since(start),
|
||||||
@ -593,7 +687,7 @@ pub mod test {
|
|||||||
let state = state.apply_event(Event::PhysicalKeyboard(Presence::Missing), now);
|
let state = state.apply_event(Event::PhysicalKeyboard(Presence::Missing), now);
|
||||||
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
state.get_outcome(now).visibility,
|
state.get_outcome(now).panel,
|
||||||
animation::Outcome::Visible{..},
|
animation::Outcome::Visible{..},
|
||||||
"Failed to appear: {:?}",
|
"Failed to appear: {:?}",
|
||||||
now.saturating_duration_since(start),
|
now.saturating_duration_since(start),
|
||||||
@ -605,7 +699,7 @@ pub mod test {
|
|||||||
fn size_l5() {
|
fn size_l5() {
|
||||||
use crate::outputs::{Mode, Geometry, c, Size};
|
use crate::outputs::{Mode, Geometry, c, Size};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Application::get_preferred_height(&OutputState {
|
Application::get_preferred_height_and_arrangement(&OutputState {
|
||||||
current_mode: Some(Mode {
|
current_mode: Some(Mode {
|
||||||
width: 720,
|
width: 720,
|
||||||
height: 1440,
|
height: 1440,
|
||||||
@ -619,10 +713,13 @@ pub mod test {
|
|||||||
}),
|
}),
|
||||||
scale: 2,
|
scale: 2,
|
||||||
}),
|
}),
|
||||||
Some(PixelSize {
|
Some((
|
||||||
|
PixelSize {
|
||||||
scale_factor: 2,
|
scale_factor: 2,
|
||||||
pixels: 420,
|
pixels: 420,
|
||||||
}),
|
},
|
||||||
|
ArrangementKind::Base,
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user