Merge branch 'output_fix' into 'master'

Panel handling cleanup

See merge request World/Phosh/squeekboard!529
This commit is contained in:
dcz
2022-04-21 07:28:38 +00:00
18 changed files with 486 additions and 275 deletions

View File

@ -55,6 +55,8 @@ typedef struct _EekGtkKeyboardPrivate
GdkEventSequence *sequence; // unowned reference
LfbEvent *event;
gulong kb_signal;
} EekGtkKeyboardPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (EekGtkKeyboard, eek_gtk_keyboard, GTK_TYPE_DRAWING_AREA)
@ -307,12 +309,19 @@ eek_gtk_keyboard_set_property (GObject *object,
}
}
// This may actually get called multiple times in a row
// if both a parent object and its parent get destroyed
static void
eek_gtk_keyboard_dispose (GObject *object)
{
EekGtkKeyboard *self = EEK_GTK_KEYBOARD (object);
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
if (priv->kb_signal != 0) {
g_signal_handler_disconnect(priv->eekboard_context, priv->kb_signal);
priv->kb_signal = 0;
}
if (priv->renderer) {
eek_renderer_free(priv->renderer);
priv->renderer = NULL;
@ -424,7 +433,7 @@ eek_gtk_keyboard_new (EekboardContextService *eekservice,
};
priv->render_geometry = initial_geometry;
g_signal_connect (eekservice,
priv->kb_signal = g_signal_connect (eekservice,
"notify::keyboard",
G_CALLBACK(on_notify_keyboard),
ret);

View File

@ -60,10 +60,10 @@ struct _EekboardContextService {
LevelKeyboard *keyboard; // currently used keyboard
GSettings *settings; // Owned reference
// Maybe TODO: it's used only for fetching layout type.
// Maybe let UI push the type to this structure?
ServerContextService *ui; // unowned reference
/// Needed for keymap changes after keyboard updates
/// Needed for keymap changes after keyboard updates.
// TODO: can the main loop access submission to change the key maps instead?
// This should probably land together with passing buttons through state,
// to avoid race conditions between setting buttons and key maps.
struct submission *submission; // unowned
};
@ -297,6 +297,8 @@ 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)
{
@ -340,7 +342,3 @@ void eekboard_context_service_set_submission(EekboardContextService *context, st
submission_use_layout(context->submission, context->keyboard->layout, time);
}
}
void eekboard_context_service_set_ui(EekboardContextService *context, ServerContextService *ui) {
context->ui = ui;
}

View File

@ -39,16 +39,12 @@ G_DECLARE_FINAL_TYPE(EekboardContextService, eekboard_context_service, EEKBOARD,
EekboardContextService *eekboard_context_service_new(struct squeek_layout_state *state);
void eekboard_context_service_set_submission(EekboardContextService *context, struct submission *submission);
void eekboard_context_service_set_ui(EekboardContextService *context, ServerContextService *ui);
void eekboard_context_service_destroy (EekboardContextService *context);
LevelKeyboard *eekboard_context_service_get_keyboard(EekboardContextService *context);
void eekboard_context_service_set_keymap(EekboardContextService *context,
const LevelKeyboard *keyboard);
void eekboard_context_service_set_hint_purpose(EekboardContextService *context,
uint32_t hint,
uint32_t purpose);
void
eekboard_context_service_use_layout(EekboardContextService *context, struct squeek_layout_state *layout, uint32_t timestamp);
G_END_DECLS

View File

@ -6,8 +6,8 @@
use std::time::Duration;
use crate::main::PixelSize;
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);

View File

@ -151,7 +151,7 @@ mod test {
use super::*;
use crate::animation;
use crate::imservice::{ ContentHint, ContentPurpose };
use crate::main::PanelCommand;
use crate::panel;
use crate::state::{ Application, InputMethod, InputMethodDetails, Presence, visibility };
use crate::state::test::application_with_fake_output;
@ -176,13 +176,13 @@ mod test {
let l = State::new(state, now);
let (l, commands) = handle_event(l, InputMethod::InactiveSince(now).into(), now);
assert_matches!(commands.panel_visibility, Some(PanelCommand::Show{..}));
assert_matches!(commands.panel_visibility, Some(panel::Command::Show{..}));
assert_eq!(l.scheduled_wakeup, Some(now + animation::HIDING_TIMEOUT));
now += animation::HIDING_TIMEOUT;
let (l, commands) = handle_event(l, Event::TimeoutReached(now), now);
assert_eq!(commands.panel_visibility, Some(PanelCommand::Hide));
assert_eq!(commands.panel_visibility, Some(panel::Command::Hide));
assert_eq!(l.scheduled_wakeup, None);
}
}

View File

@ -1,6 +1,7 @@
#include "submission.h"
#include "input-method-unstable-v2-client-protocol.h"
#include "virtual-keyboard-unstable-v1-client-protocol.h"
#include <glib.h>
#include "submission.h"
struct imservice;

View File

@ -36,6 +36,7 @@ mod locale;
mod main;
mod manager;
mod outputs;
mod panel;
mod popover;
mod resources;
mod state;

View File

@ -8,6 +8,7 @@
#include "eek/eek-types.h"
#include "dbus.h"
#include "panel.h"
struct receiver;
@ -24,7 +25,7 @@ struct rsobjects {
struct squeek_wayland *wayland;
};
void register_ui_loop_handler(struct receiver *receiver, ServerContextService *ui, DBusHandler *dbus_handler);
void register_ui_loop_handler(struct receiver *receiver, struct panel_manager *panel, EekboardContextService *hint_manager, DBusHandler *dbus_handler);
struct rsobjects squeek_init(void);

View File

@ -3,7 +3,7 @@
*/
/*! Glue for the main loop. */
use crate::outputs::OutputId;
use crate::panel;
use crate::debug;
use crate::state;
use glib::{Continue, MainContext, PRIORITY_DEFAULT, Receiver};
@ -20,19 +20,21 @@ mod c {
use crate::imservice::IMService;
use crate::imservice::c::InputMethod;
use crate::outputs::Outputs;
use crate::outputs::c::WlOutput;
use crate::state;
use crate::submission::Submission;
use crate::util::c::Wrapped;
use crate::vkeyboard::c::ZwpVirtualKeyboardV1;
/// ServerContextService*
#[repr(transparent)]
pub struct UIManager(*const c_void);
/// DbusHandler*
#[repr(transparent)]
pub struct DBusHandler(*const c_void);
/// EekboardContextService* in the role of a hint receiver
// The clone/copy is a concession to C style of programming.
// It would be hard to get rid of it.
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct HintManager(*const c_void);
/// Holds the Rust structures that are interesting from C.
#[repr(C)]
@ -76,9 +78,7 @@ mod c {
extern "C" {
#[allow(improper_ctypes)]
fn init_wayland(wayland: *mut Wayland);
fn server_context_service_update_keyboard(service: *const UIManager, output: WlOutput, scaled_height: u32);
fn server_context_service_real_hide_keyboard(service: *const UIManager);
fn server_context_service_set_hint_purpose(service: *const UIManager, hint: u32, purpose: u32);
fn eekboard_context_service_set_hint_purpose(service: HintManager, hint: u32, purpose: 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);
@ -124,18 +124,20 @@ mod c {
pub extern "C"
fn register_ui_loop_handler(
receiver: Wrapped<Receiver<Commands>>,
ui_manager: *const UIManager,
panel_manager: panel::c::PanelManager,
hint_manager: HintManager,
dbus_handler: *const DBusHandler,
) {
let receiver = unsafe { receiver.unwrap() };
let receiver = Rc::try_unwrap(receiver).expect("References still present");
let receiver = receiver.into_inner();
let panel_manager = Wrapped::new(panel::Manager::new(panel_manager));
let ctx = MainContext::default();
let _acqu = ctx.acquire();
receiver.attach(
Some(&ctx),
move |msg| {
main_loop_handle_message(msg, ui_manager, dbus_handler);
main_loop_handle_message(msg, panel_manager.clone(), hint_manager, dbus_handler);
Continue(true)
},
);
@ -149,18 +151,13 @@ mod c {
/// and doesn't lend itself to testing other than integration.
fn main_loop_handle_message(
msg: Commands,
ui_manager: *const UIManager,
panel_manager: Wrapped<panel::Manager>,
hint_manager: HintManager,
dbus_handler: *const DBusHandler,
) {
match msg.panel_visibility {
Some(PanelCommand::Show { output, height }) => unsafe {
server_context_service_update_keyboard(ui_manager, output.0, height.as_scaled_ceiling());
},
Some(PanelCommand::Hide) => unsafe {
server_context_service_real_hide_keyboard(ui_manager);
},
None => {},
};
if let Some(visibility) = msg.panel_visibility {
panel::Manager::update(panel_manager, visibility);
}
if let Some(visible) = msg.dbus_visible_set {
if dbus_handler != std::ptr::null() {
@ -170,8 +167,8 @@ mod c {
if let Some(hints) = msg.layout_hint_set {
unsafe {
server_context_service_set_hint_purpose(
ui_manager,
eekboard_context_service_set_hint_purpose(
hint_manager,
hints.hint.bits(),
hints.purpose.clone() as u32,
)
@ -180,42 +177,11 @@ mod c {
}
}
/// Size in pixels that is aware of scaling
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct PixelSize {
pub pixels: u32,
pub scale_factor: u32,
}
fn div_ceil(a: u32, b: u32) -> u32 {
// Given that it's for pixels on a screen, an overflow is unlikely.
(a + b - 1) / b
}
impl PixelSize {
pub fn as_scaled_floor(&self) -> u32 {
self.pixels / self.scale_factor
}
pub fn as_scaled_ceiling(&self) -> u32 {
div_ceil(self.pixels, self.scale_factor)
}
}
#[derive(Clone, PartialEq, Debug)]
pub enum PanelCommand {
Show {
output: OutputId,
height: PixelSize,
},
Hide,
}
/// The commands consumed by the main loop,
/// to be sent out to external components.
#[derive(Clone)]
pub struct Commands {
pub panel_visibility: Option<PanelCommand>,
pub panel_visibility: Option<panel::Command>,
pub layout_hint_set: Option<state::InputMethodDetails>,
pub dbus_visible_set: Option<bool>,
}

View File

@ -14,6 +14,7 @@ sources = [
config_h,
'dbus.c',
'imservice.c',
'panel.c',
'popover.c',
'server-context-service.c',
'wayland.c',

130
src/panel.c Normal file
View File

@ -0,0 +1,130 @@
#include "eekboard/eekboard-context-service.h"
#include "wayland.h"
#include "panel.h"
// Called from rust
/// Destroys the widget
void
panel_manager_hide(struct panel_manager *self)
{
if (self->window) {
gtk_widget_destroy (GTK_WIDGET (self->window));
}
if (self->widget) {
gtk_widget_destroy (GTK_WIDGET (self->widget));
}
self->window = NULL;
self->widget = NULL;
}
static void
on_destroy (struct panel_manager *self, GtkWidget *widget)
{
g_assert (widget == GTK_WIDGET(self->window));
panel_manager_hide(self);
}
/// panel::Manager. Only needed for this callback
struct squeek_panel_manager;
/// Calls back into Rust
void squeek_panel_manager_configured(struct squeek_panel_manager *mgr, uint32_t width, uint32_t height);
static void
on_surface_configure(struct squeek_panel_manager *self, PhoshLayerSurface *surface)
{
gint width;
gint height;
g_return_if_fail (PHOSH_IS_LAYER_SURFACE (surface));
g_object_get(G_OBJECT(surface),
"configured-width", &width,
"configured-height", &height,
NULL);
squeek_panel_manager_configured(self, width, height);
}
static void
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);
gtk_widget_set_has_tooltip (self->widget, TRUE);
gtk_container_add (GTK_CONTAINER(self->window), self->widget);
gtk_widget_show_all(self->widget);
}
// Called from rust
/// Creates a new panel widget
void
panel_manager_request_widget (struct panel_manager *self, struct wl_output *output, uint32_t height, struct squeek_panel_manager *mgr)
{
if (self->window) {
g_error("Window already present");
}
self->window = g_object_new (
PHOSH_TYPE_LAYER_SURFACE,
"layer-shell", squeek_wayland->layer_shell,
"wl-output", output,
"height", height,
"anchor", ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM
| ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
| ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT,
"layer", ZWLR_LAYER_SHELL_V1_LAYER_TOP,
"kbd-interactivity", FALSE,
"exclusive-zone", height,
"namespace", "osk",
NULL
);
g_object_connect (self->window,
"swapped-signal::destroy", G_CALLBACK(on_destroy), self,
"swapped-signal::configured", G_CALLBACK(on_surface_configure), mgr,
NULL);
// The properties below are just to make hacking easier.
// The way we use layer-shell overrides some,
// and there's no space in the protocol for others.
// Those may still be useful in the future,
// or for hacks with regular windows.
gtk_widget_set_can_focus (GTK_WIDGET(self->window), FALSE);
g_object_set (G_OBJECT(self->window), "accept_focus", FALSE, NULL);
gtk_window_set_title (GTK_WINDOW(self->window), "Squeekboard");
gtk_window_set_icon_name (GTK_WINDOW(self->window), "squeekboard");
gtk_window_set_keep_above (GTK_WINDOW(self->window), TRUE);
make_widget(self);
gtk_widget_show (GTK_WIDGET(self->window));
}
// Called from rust
/// Updates the size
void
panel_manager_resize (struct panel_manager *self, uint32_t height)
{
phosh_layer_surface_set_size(self->window, 0, height);
phosh_layer_surface_set_exclusive_zone(self->window, height);
phosh_layer_surface_wl_surface_commit(self->window);
}
struct panel_manager panel_manager_new(EekboardContextService *state, struct submission *submission, struct squeek_layout_state *layout)
{
struct panel_manager mgr = {
.state = state,
.submission = submission,
.layout = layout,
.window = NULL,
.widget = NULL,
.current_output = NULL,
};
return mgr;
}

21
src/panel.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include "eek/layersurface.h"
#include "src/layout.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 submission *submission; // unowned
struct squeek_layout_state *layout;
PhoshLayerSurface *window;
GtkWidget *widget; // nullable
// Those should be held in Rust
struct wl_output *current_output;
};
struct panel_manager panel_manager_new(EekboardContextService *state, struct submission *submission, struct squeek_layout_state *layout);

248
src/panel.rs Normal file
View File

@ -0,0 +1,248 @@
/* Copyright (C) 2022 Purism SPC
* SPDX-License-Identifier: GPL-3.0+
*/
/*! Panel state management.
*
* This is effectively a mirror of the previous C code,
* with an explicit state machine managing the panel size.
*
* It still relies on a callback from Wayland to accept the panel size,
* which makes this code somewhat prone to mistakes.
*
* An alternative to the callback would be
* to send a message all the way to `state::State`
* every time the allocated size changes.
* That would allow for a more holistic view
* of interactions of different pieces of state.
*
* However, `state::State` already has the potential to become a ball of mud,
* tightly coupling different functionality and making it difficult to see independent units.
*
* For this reason, I'm taking a light touch approach with the panel manager,
* and moving it just a bit closer to `state::State`.
* Hopefully ths still allows us to expose assumptions that were not stated yet
* (e.g. can the output disappear between size request andallocation?).
*
* Tight coupling, e.g. a future one between presented hints and layout size,
* will have to be taken into account later.
*/
use crate::logging;
use crate::outputs::OutputId;
use crate::util::c::Wrapped;
pub mod c {
use super::*;
use glib;
use gtk::Continue;
use std::os::raw::c_void;
use crate::outputs::c::WlOutput;
/// struct panel_manager*
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct PanelManager(*const c_void);
extern "C" {
#[allow(improper_ctypes)]
pub fn panel_manager_request_widget(
service: PanelManager,
output: WlOutput,
height: u32,
// for callbacks
panel: Wrapped<Manager>,
);
pub fn panel_manager_resize(service: PanelManager, height: u32);
pub fn panel_manager_hide(service: PanelManager);
}
#[no_mangle]
pub extern "C"
fn squeek_panel_manager_configured(panel: Wrapped<Manager>, width: u32, height: u32) {
// This is why this needs to be moved into state::State:
// it's getting too coupled to glib.
glib::idle_add_local(move || {
let panel = panel.clone_ref();
panel.borrow_mut().set_configured(Size{width, height});
Continue(false)
});
}
}
/// Size in pixels that is aware of scaling
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct PixelSize {
pub pixels: u32,
pub scale_factor: u32,
}
fn div_ceil(a: u32, b: u32) -> u32 {
// Given that it's for pixels on a screen, an overflow is unlikely.
(a + b - 1) / b
}
impl PixelSize {
pub fn as_scaled_floor(&self) -> u32 {
self.pixels / self.scale_factor
}
pub fn as_scaled_ceiling(&self) -> u32 {
div_ceil(self.pixels, self.scale_factor)
}
}
#[derive(Clone, Debug)]
struct Size {
width: u32,
height: u32,
}
/// This state requests the Wayland layer shell protocol synchronization:
/// the application asks for some size,
/// and then receives a size that the compositor thought appropriate.
/// Stores raw values passed to Wayland, i.e. scaled dimensions.
#[derive(Clone, Debug)]
enum State {
Hidden,
SizeRequested {
output: OutputId,
height: u32,
//width: u32,
},
SizeAllocated {
output: OutputId,
wanted_height: u32,
allocated: Size,
},
}
#[derive(Clone, PartialEq, Debug)]
pub enum Command {
Show {
output: OutputId,
height: PixelSize,
},
Hide,
}
/// Tries to contain all the panel sizing duties.
pub struct Manager {
panel: c::PanelManager,
state: State,
}
impl Manager {
pub fn new(panel: c::PanelManager) -> Self {
Self {
panel,
state: State::Hidden,
}
}
// TODO: mabe send the allocated size back to state::State,
// to perform layout adjustments
fn set_configured(&mut self, size: Size) {
self.state = match self.state.clone() {
State::Hidden => {
// This may happen if a hide is scheduled immediately after a show.
log_print!(
logging::Level::Surprise,
"Panel has been configured, but no request is pending. Ignoring",
);
State::Hidden
},
State::SizeAllocated{output, wanted_height, ..} => {
log_print!(
logging::Level::Surprise,
"Panel received new configuration without asking",
);
State::SizeAllocated{output, wanted_height, allocated: size}
},
State::SizeRequested{output, height} => State::SizeAllocated {
output,
wanted_height: height,
allocated: size,
},
};
}
pub fn update(mgr: Wrapped<Manager>, cmd: Command) {
let copied = mgr.clone();
let mgr = mgr.clone_ref();
let mut mgr = mgr.borrow_mut();
(*mgr).state = match (cmd, mgr.state.clone()) {
(Command::Hide, State::Hidden) => State::Hidden,
(Command::Hide, State::SizeAllocated{..}) => {
unsafe { c::panel_manager_hide(mgr.panel); }
State::Hidden
},
(Command::Hide, State::SizeRequested{..}) => {
unsafe { c::panel_manager_hide(mgr.panel); }
State::Hidden
},
(Command::Show{output, height}, State::Hidden) => {
let height = height.as_scaled_ceiling();
unsafe { c::panel_manager_request_widget(mgr.panel, output.0, height, copied); }
State::SizeRequested{output, height}
},
(
Command::Show{output, height},
State::SizeRequested{output: req_output, height: req_height},
) => {
let height = height.as_scaled_ceiling();
if output == req_output && height == req_height {
State::SizeRequested{output: req_output, height: req_height}
} else if output == req_output {
// I'm not sure about that.
// This could cause a busy loop,
// when two requests are being processed at the same time:
// one message in the compositor to allocate size A,
// causing the state to update to height A'
// the other from the state wanting height B',
// causing the compositor to change size to B.
// So better cut this short here, despite artifacts.
// Out of simplicty, just ignore the new request.
// If that causes problems, the request in flight could be stored
// for the purpose of handling it better somehow.
State::SizeRequested{output: req_output, height: req_height}
} else {
// This looks weird, but should be safe.
// The stack seems to handle
// configure events on a dead surface.
unsafe {
c::panel_manager_hide(mgr.panel);
c::panel_manager_request_widget(mgr.panel, output.0, height, copied);
}
State::SizeRequested{output, height}
}
},
(
Command::Show{output, height},
State::SizeAllocated{output: alloc_output, allocated, wanted_height},
) => {
let height = height.as_scaled_ceiling();
if output == alloc_output && height == wanted_height {
State::SizeAllocated{output: alloc_output, wanted_height, allocated}
} else if output == alloc_output && height == allocated.height {
State::SizeAllocated{output: alloc_output, wanted_height: height, allocated}
} else if output == alloc_output {
// Should *all* other heights cause a resize?
// What about those between wanted and allocated?
unsafe { c::panel_manager_resize(mgr.panel, height); }
State::SizeRequested{output, height}
} else {
unsafe {
c::panel_manager_hide(mgr.panel);
c::panel_manager_request_widget(mgr.panel, output.0, height, copied);
}
State::SizeRequested{output, height}
}
},
}
}
}

View File

@ -20,14 +20,7 @@
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include "eek/eek.h"
#include "eek/eek-gtk-keyboard.h"
#include "eek/layersurface.h"
#include "eekboard/eekboard-context-service.h"
#include "submission.h"
#include "wayland.h"
#include "server-context-service.h"
#include "wayland-client-protocol.h"
enum {
PROP_0,
@ -37,149 +30,13 @@ enum {
struct _ServerContextService {
GObject parent;
EekboardContextService *state; // unowned
/// Needed for instantiating the widget
struct submission *submission; // unowned
struct squeek_layout_state *layout;
struct squeek_state_manager *state_manager; // shared reference
PhoshLayerSurface *window;
GtkWidget *widget; // nullable
struct wl_output *current_output;
guint last_requested_height;
};
G_DEFINE_TYPE(ServerContextService, server_context_service, G_TYPE_OBJECT);
static void
on_destroy (ServerContextService *self, GtkWidget *widget)
{
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE (self));
g_assert (widget == GTK_WIDGET(self->window));
self->window = NULL;
self->widget = NULL;
//eekboard_context_service_destroy (EEKBOARD_CONTEXT_SERVICE (context));
}
static void
make_window (ServerContextService *self, struct wl_output *output, uint32_t height)
{
if (self->window) {
g_error("Window already present");
}
self->window = g_object_new (
PHOSH_TYPE_LAYER_SURFACE,
"layer-shell", squeek_wayland->layer_shell,
"wl-output", output,
"height", height,
"anchor", ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM
| ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
| ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT,
"layer", ZWLR_LAYER_SHELL_V1_LAYER_TOP,
"kbd-interactivity", FALSE,
"exclusive-zone", height,
"namespace", "osk",
NULL
);
g_object_connect (self->window,
"swapped-signal::destroy", G_CALLBACK(on_destroy), self,
//"swapped-signal::configured", G_CALLBACK(on_surface_configure), self,
NULL);
// The properties below are just to make hacking easier.
// The way we use layer-shell overrides some,
// and there's no space in the protocol for others.
// Those may still be useful in the future,
// or for hacks with regular windows.
gtk_widget_set_can_focus (GTK_WIDGET(self->window), FALSE);
g_object_set (G_OBJECT(self->window), "accept_focus", FALSE, NULL);
gtk_window_set_title (GTK_WINDOW(self->window), "Squeekboard");
gtk_window_set_icon_name (GTK_WINDOW(self->window), "squeekboard");
gtk_window_set_keep_above (GTK_WINDOW(self->window), TRUE);
}
static void
destroy_window (ServerContextService *self)
{
gtk_widget_destroy (GTK_WIDGET (self->window));
self->window = NULL;
}
static void
make_widget (ServerContextService *self)
{
if (self->widget) {
gtk_widget_destroy(self->widget);
self->widget = NULL;
}
self->widget = eek_gtk_keyboard_new (self->state, self->submission, self->layout);
gtk_widget_set_has_tooltip (self->widget, TRUE);
gtk_container_add (GTK_CONTAINER(self->window), self->widget);
gtk_widget_show_all(self->widget);
}
// Called from rust
/// Updates the type of hiddenness
void
server_context_service_real_hide_keyboard (ServerContextService *self)
{
//self->desired_height = 0;
self->current_output = NULL;
if (self->window) {
gtk_widget_hide (GTK_WIDGET(self->window));
}
}
// Called from rust
/// Updates the type of visibility.
/// Height is in scaled units.
void
server_context_service_update_keyboard (ServerContextService *self, struct wl_output *output, uint32_t scaled_height)
{
if (output != self->current_output) {
// Recreate on a new output
server_context_service_real_hide_keyboard(self);
} else {
gint h;
PhoshLayerSurface *surface = self->window;
g_object_get(G_OBJECT(surface),
"configured-height", &h,
NULL);
if ((uint32_t)h != scaled_height) {
//TODO: make sure that redrawing happens in the correct place (it doesn't now).
phosh_layer_surface_set_size(self->window, 0, scaled_height);
phosh_layer_surface_set_exclusive_zone(self->window, scaled_height);
phosh_layer_surface_wl_surface_commit(self->window);
self->current_output = output;
return;
}
}
self->current_output = output;
if (!self->window) {
make_window (self, output, scaled_height);
}
if (!self->widget) {
make_widget (self);
}
gtk_widget_show (GTK_WIDGET(self->window));
}
static void
server_context_service_set_property (GObject *object,
guint prop_id,
const GValue *value,
@ -210,17 +67,6 @@ server_context_service_get_property (GObject *object,
}
}
static void
server_context_service_dispose (GObject *object)
{
ServerContextService *self = SERVER_CONTEXT_SERVICE(object);
destroy_window (self);
self->widget = NULL;
G_OBJECT_CLASS (server_context_service_parent_class)->dispose (object);
}
static void
server_context_service_class_init (ServerContextServiceClass *klass)
{
@ -229,7 +75,6 @@ server_context_service_class_init (ServerContextServiceClass *klass)
gobject_class->set_property = server_context_service_set_property;
gobject_class->get_property = server_context_service_get_property;
gobject_class->dispose = server_context_service_dispose;
/**
* ServerContextServie:keyboard:
@ -250,41 +95,29 @@ server_context_service_class_init (ServerContextServiceClass *klass)
static void
server_context_service_init (ServerContextService *self) {}
static void
init (ServerContextService *self) {
ServerContextService *
server_context_service_new (struct squeek_state_manager *state_manager)
{
ServerContextService *holder = g_object_new (SERVER_TYPE_CONTEXT_SERVICE, NULL);
holder->state_manager = state_manager;
const char *schema_name = "org.gnome.desktop.a11y.applications";
GSettingsSchemaSource *ssrc = g_settings_schema_source_get_default();
g_autoptr(GSettingsSchema) schema = NULL;
if (!ssrc) {
g_warning("No gsettings schemas installed.");
return;
return NULL;
}
schema = g_settings_schema_source_lookup(ssrc, schema_name, TRUE);
if (schema) {
g_autoptr(GSettings) settings = g_settings_new (schema_name);
g_settings_bind (settings, "screen-keyboard-enabled",
self, "enabled", G_SETTINGS_BIND_GET);
holder, "enabled", G_SETTINGS_BIND_GET);
} else {
g_warning("Gsettings schema %s is not installed on the system. "
"Enabling by default.", schema_name);
}
}
ServerContextService *
server_context_service_new (EekboardContextService *self, struct submission *submission, struct squeek_layout_state *layout, struct squeek_state_manager *state_manager)
{
ServerContextService *ui = g_object_new (SERVER_TYPE_CONTEXT_SERVICE, NULL);
ui->submission = submission;
ui->state = self;
ui->layout = layout;
ui->state_manager = state_manager;
init(ui);
return ui;
}
// Used from Rust
void server_context_service_set_hint_purpose(ServerContextService *self, uint32_t hint,
uint32_t purpose) {
eekboard_context_service_set_hint_purpose(self->state, hint, purpose);
return holder;
}

View File

@ -17,9 +17,9 @@
*/
#ifndef SERVER_CONTEXT_SERVICE_H
#define SERVER_CONTEXT_SERVICE_H 1
#include <gtk/gtk.h>
#include "src/layout.h"
#include "src/submission.h"
#include "main.h"
G_BEGIN_DECLS
@ -28,8 +28,8 @@ G_BEGIN_DECLS
/** Manages the lifecycle of the window displaying layouts. */
G_DECLARE_FINAL_TYPE (ServerContextService, server_context_service, SERVER, CONTEXT_SERVICE, GObject)
ServerContextService *server_context_service_new(EekboardContextService *self, struct submission *submission, struct squeek_layout_state *layout, struct squeek_state_manager *state_manager);
enum squeek_arrangement_kind server_context_service_get_layout_type(ServerContextService *);
ServerContextService *server_context_service_new(struct squeek_state_manager *state_manager);
G_END_DECLS
#endif /* SERVER_CONTEXT_SERVICE_H */

View File

@ -31,6 +31,7 @@
#include "layout.h"
#include "main.h"
#include "outputs.h"
#include "panel.h"
#include "submission.h"
#include "server-context-service.h"
#include "wayland.h"
@ -51,8 +52,10 @@ typedef enum _SqueekboardDebugFlags {
struct squeekboard {
struct squeek_wayland wayland; // Just hooks.
DBusHandler *dbus_handler; // Controls visibility of the OSK.
EekboardContextService *settings_context; // Gsettings hooks.
ServerContextService *ui_context; // mess, includes the entire UI
EekboardContextService *settings_context; // Gsettings hooks for layouts.
/// 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;
};
@ -435,20 +438,21 @@ main (int argc, char **argv)
}
}
eekboard_context_service_set_submission(instance.settings_context, rsobjects.submission);
ServerContextService *ui_context = server_context_service_new(
instance.settings_context,
rsobjects.submission,
&instance.layout_choice,
ServerContextService *setting_listener = server_context_service_new(
rsobjects.state_manager);
if (!ui_context) {
g_error("Could not initialize GUI");
exit(1);
if (!setting_listener) {
g_warning ("could not connect to gsettings");
}
instance.ui_context = ui_context;
register_ui_loop_handler(rsobjects.receiver, instance.ui_context, instance.dbus_handler);
instance.settings_handler = setting_listener;
eekboard_context_service_set_submission(instance.settings_context, rsobjects.submission);
instance.panel_manager = panel_manager_new(instance.settings_context,
rsobjects.submission,
&instance.layout_choice);
register_ui_loop_handler(rsobjects.receiver, &instance.panel_manager, instance.settings_context, instance.dbus_handler);
session_register();

View File

@ -8,9 +8,11 @@
use crate::animation;
use crate::debug;
use crate::imservice::{ ContentHint, ContentPurpose };
use crate::main::{ Commands, PanelCommand, PixelSize };
use crate::main::Commands;
use crate::outputs;
use crate::outputs::{Millimeter, OutputId, OutputState};
use crate::panel;
use crate::panel::PixelSize;
use crate::util::Rational;
use std::cmp;
use std::collections::HashMap;
@ -118,8 +120,8 @@ impl Outcome {
// FIXME: handle switching outputs
let (dbus_visible_set, panel_visibility) = match new_state.visibility {
animation::Outcome::Visible{output, height}
=> (Some(true), Some(PanelCommand::Show{output, height})),
animation::Outcome::Hidden => (Some(false), Some(PanelCommand::Hide)),
=> (Some(true), Some(panel::Command::Show{output, height})),
animation::Outcome::Hidden => (Some(false), Some(panel::Command::Hide)),
};
Commands {

View File

@ -1,12 +1,12 @@
#ifndef __SUBMISSION_H
#define __SUBMISSION_H
#include "input-method-unstable-v2-client-protocol.h"
#include "virtual-keyboard-unstable-v1-client-protocol.h"
#include "inttypes.h"
#include "eek/eek-types.h"
#include "main.h"
struct squeek_layout;
struct submission;
// Defined in Rust
uint8_t submission_hint_available(struct submission *self);