diff --git a/eek/eek-gtk-keyboard.c b/eek/eek-gtk-keyboard.c index 25c9f49b..4d07b7c0 100644 --- a/eek/eek-gtk-keyboard.c +++ b/eek/eek-gtk-keyboard.c @@ -44,11 +44,11 @@ typedef struct _EekGtkKeyboardPrivate { - EekRenderer *renderer; + EekRenderer *renderer; // owned, nullable EekboardContextService *eekboard_context; // unowned reference struct submission *submission; // unowned reference - struct squeek_layout_state *layout; + struct squeek_layout_state *layout; // unowned LevelKeyboard *keyboard; // unowned reference; it's kept in server-context GdkEventSequence *sequence; // unowned reference @@ -92,18 +92,21 @@ eek_gtk_keyboard_real_draw (GtkWidget *self, pcontext); eek_renderer_set_allocation_size (priv->renderer, + priv->keyboard->layout, allocation.width, allocation.height); eek_renderer_set_scale_factor (priv->renderer, gtk_widget_get_scale_factor (self)); } - eek_renderer_render_keyboard (priv->renderer, priv->submission, cr); + + eek_renderer_render_keyboard (priv->renderer, priv->submission, cr, priv->keyboard); return FALSE; } +// Units of pixel size static enum squeek_arrangement_kind get_type(uint32_t width, uint32_t height) { (void)height; - if (width < 540) { + if (width < 1080) { return ARRANGEMENT_KIND_BASE; } return ARRANGEMENT_KIND_WIDE; @@ -115,11 +118,11 @@ eek_gtk_keyboard_real_size_allocate (GtkWidget *self, { EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self)); - + uint32_t scale = (uint32_t)gtk_widget_get_scale_factor(self); // 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)); + (uint32_t)(allocation->width - allocation->x) * scale, + (uint32_t)(allocation->height - allocation->y) * scale); if (priv->layout->arrangement != new_type) { priv->layout->arrangement = new_type; @@ -128,6 +131,7 @@ eek_gtk_keyboard_real_size_allocate (GtkWidget *self, if (priv->renderer) eek_renderer_set_allocation_size (priv->renderer, + priv->keyboard->layout, allocation->width, allocation->height); @@ -286,7 +290,7 @@ eek_gtk_keyboard_dispose (GObject *object) EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self); if (priv->renderer) { - g_object_unref (priv->renderer); + eek_renderer_free(priv->renderer); priv->renderer = NULL; priv->renderer = NULL; } @@ -341,7 +345,7 @@ on_notify_keyboard (GObject *object, EekGtkKeyboardPrivate *priv = (EekGtkKeyboardPrivate*)eek_gtk_keyboard_get_instance_private (self); priv->keyboard = eekboard_context_service_get_keyboard(EEKBOARD_CONTEXT_SERVICE(object)); if (priv->renderer) { - g_object_unref(priv->renderer); + eek_renderer_free(priv->renderer); } priv->renderer = NULL; gtk_widget_queue_draw(GTK_WIDGET(self)); diff --git a/eek/eek-gtk-keyboard.h b/eek/eek-gtk-keyboard.h index ca127011..7570ac05 100644 --- a/eek/eek-gtk-keyboard.h +++ b/eek/eek-gtk-keyboard.h @@ -32,7 +32,6 @@ struct submission; struct squeek_layout_state; -typedef struct _LevelKeyboard LevelKeyboard; // including causes weird bugs G_BEGIN_DECLS #define EEK_TYPE_GTK_KEYBOARD (eek_gtk_keyboard_get_type()) diff --git a/eek/eek-keyboard.c b/eek/eek-keyboard.c index 24ef537b..292a0879 100644 --- a/eek/eek-keyboard.c +++ b/eek/eek-keyboard.c @@ -38,10 +38,8 @@ void level_keyboard_free(LevelKeyboard *self) { } LevelKeyboard* -level_keyboard_new (const gchar *keyboard_type, - enum squeek_arrangement_kind t) +level_keyboard_new (struct squeek_layout *layout) { - struct squeek_layout *layout = squeek_load_layout(keyboard_type, t); LevelKeyboard *keyboard = g_new0(LevelKeyboard, 1); if (!keyboard) { diff --git a/eek/eek-keyboard.h b/eek/eek-keyboard.h index fab549e1..b1377c47 100644 --- a/eek/eek-keyboard.h +++ b/eek/eek-keyboard.h @@ -47,8 +47,7 @@ gchar * eek_keyboard_get_keymap (LevelKeyboard *keyboard); LevelKeyboard* -level_keyboard_new (const gchar *keyboard_type, - enum squeek_arrangement_kind t); +level_keyboard_new (struct squeek_layout *layout); void level_keyboard_free(LevelKeyboard *self); G_END_DECLS diff --git a/eek/eek-renderer.c b/eek/eek-renderer.c index c8d03e48..ceeeb02d 100644 --- a/eek/eek-renderer.c +++ b/eek/eek-renderer.c @@ -28,27 +28,6 @@ #include "eek-renderer.h" #include "src/style.h" -enum { - PROP_0, - PROP_PCONTEXT, - PROP_LAST -}; - -typedef struct _EekRendererPrivate -{ - LevelKeyboard *keyboard; // unowned - PangoContext *pcontext; // owned - GtkCssProvider *css_provider; // owned - GtkStyleContext *view_context; // owned - GtkStyleContext *button_context; // TODO: maybe move a copy to each button - - gdouble allocation_width; - gdouble allocation_height; - gint scale_factor; /* the outputs scale factor */ - struct transformation widget_to_layout; -} EekRendererPrivate; - -G_DEFINE_TYPE_WITH_PRIVATE (EekRenderer, eek_renderer, G_TYPE_OBJECT) /* eek-keyboard-drawing.c */ static void render_button_label (cairo_t *cr, GtkStyleContext *ctx, @@ -138,9 +117,7 @@ eek_render_button (EekRenderer *self, gboolean pressed, gboolean locked) { - EekRendererPrivate *priv = eek_renderer_get_instance_private (self); - - GtkStyleContext *ctx = priv->button_context; + GtkStyleContext *ctx = self->button_context; /* Set the name of the button on the widget path, using the name obtained from the button's symbol. */ g_autoptr (GtkWidgetPath) path = NULL; @@ -160,7 +137,7 @@ eek_render_button (EekRenderer *self, } gtk_style_context_add_class(ctx, outline_name); - render_button_in_context(priv->scale_factor, cr, ctx, button); + render_button_in_context(self->scale_factor, cr, ctx, button); // Save and restore functions don't work if gtk_render_* was used in between gtk_style_context_set_state(ctx, GTK_STATE_FLAG_NORMAL); @@ -218,116 +195,42 @@ render_button_label (cairo_t *cr, void eek_renderer_render_keyboard (EekRenderer *self, struct submission *submission, - cairo_t *cr) + cairo_t *cr, + LevelKeyboard *keyboard) { - EekRendererPrivate *priv = eek_renderer_get_instance_private (self); - - g_return_if_fail (priv->keyboard); - g_return_if_fail (priv->allocation_width > 0.0); - g_return_if_fail (priv->allocation_height > 0.0); + g_return_if_fail (self->allocation_width > 0.0); + g_return_if_fail (self->allocation_height > 0.0); /* Paint the background covering the entire widget area */ - gtk_render_background (priv->view_context, + gtk_render_background (self->view_context, cr, 0, 0, - priv->allocation_width, priv->allocation_height); + self->allocation_width, self->allocation_height); cairo_save(cr); - cairo_translate (cr, priv->widget_to_layout.origin_x, priv->widget_to_layout.origin_y); - cairo_scale (cr, priv->widget_to_layout.scale, priv->widget_to_layout.scale); + cairo_translate (cr, self->widget_to_layout.origin_x, self->widget_to_layout.origin_y); + cairo_scale (cr, self->widget_to_layout.scale, self->widget_to_layout.scale); - squeek_draw_layout_base_view(priv->keyboard->layout, self, cr); - squeek_layout_draw_all_changed(priv->keyboard->layout, self, cr, submission); + squeek_draw_layout_base_view(keyboard->layout, self, cr); + squeek_layout_draw_all_changed(keyboard->layout, self, cr, submission); cairo_restore (cr); } -static void -eek_renderer_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) +void +eek_renderer_free (EekRenderer *self) { - EekRendererPrivate *priv = eek_renderer_get_instance_private ( - EEK_RENDERER(object)); - - switch (prop_id) { - case PROP_PCONTEXT: - priv->pcontext = g_value_get_object (value); - g_object_ref (priv->pcontext); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; + if (self->pcontext) { + g_object_unref (self->pcontext); + self->pcontext = NULL; } -} - -static void -eek_renderer_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - (void)value; - switch (prop_id) { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -eek_renderer_dispose (GObject *object) -{ - EekRenderer *self = EEK_RENDERER (object); - EekRendererPrivate *priv = eek_renderer_get_instance_private (self); - - if (priv->keyboard) { - priv->keyboard = NULL; - } - if (priv->pcontext) { - g_object_unref (priv->pcontext); - priv->pcontext = NULL; - } - + g_object_unref(self->css_provider); + g_object_unref(self->view_context); + g_object_unref(self->button_context); // this is where renderer-specific surfaces would be released - G_OBJECT_CLASS (eek_renderer_parent_class)->dispose (object); + free(self); } -static void -eek_renderer_finalize (GObject *object) -{ - EekRenderer *self = EEK_RENDERER(object); - EekRendererPrivate *priv = eek_renderer_get_instance_private (self); - - g_object_unref(priv->css_provider); - g_object_unref(priv->view_context); - g_object_unref(priv->button_context); - G_OBJECT_CLASS (eek_renderer_parent_class)->finalize (object); -} - -static void -eek_renderer_class_init (EekRendererClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GParamSpec *pspec; - - gobject_class->set_property = eek_renderer_set_property; - gobject_class->get_property = eek_renderer_get_property; - gobject_class->dispose = eek_renderer_dispose; - gobject_class->finalize = eek_renderer_finalize; - - pspec = g_param_spec_object ("pango-context", - "Pango Context", - "Pango Context", - PANGO_TYPE_CONTEXT, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE); - g_object_class_install_property (gobject_class, - PROP_PCONTEXT, - pspec); -} - - static GType new_type(char *name) { GTypeInfo info = {0}; info.class_size = sizeof(GtkWidgetClass); @@ -355,81 +258,75 @@ static GType button_type() { } static void -eek_renderer_init (EekRenderer *self) +renderer_init (EekRenderer *self) { - EekRendererPrivate *priv = eek_renderer_get_instance_private (self); - - priv->keyboard = NULL; - priv->pcontext = NULL; - priv->allocation_width = 0.0; - priv->allocation_height = 0.0; - priv->scale_factor = 1; + self->pcontext = NULL; + self->allocation_width = 0.0; + self->allocation_height = 0.0; + self->scale_factor = 1; GtkIconTheme *theme = gtk_icon_theme_get_default (); gtk_icon_theme_add_resource_path (theme, "/sm/puri/squeekboard/icons"); - priv->css_provider = squeek_load_style(); + self->css_provider = squeek_load_style(); } EekRenderer * eek_renderer_new (LevelKeyboard *keyboard, PangoContext *pcontext) { - EekRenderer *renderer = g_object_new (EEK_TYPE_RENDERER, - "pango-context", pcontext, - NULL); - EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer); - priv->keyboard = keyboard; + EekRenderer *renderer = calloc(1, sizeof(EekRenderer)); + renderer_init(renderer); + renderer->pcontext = pcontext; + g_object_ref (renderer->pcontext); /* Create a style context for the layout */ GtkWidgetPath *path = gtk_widget_path_new(); gtk_widget_path_append_type(path, view_type()); - priv->view_context = gtk_style_context_new(); - gtk_style_context_set_path(priv->view_context, path); + renderer->view_context = gtk_style_context_new(); + gtk_style_context_set_path(renderer->view_context, path); gtk_widget_path_unref(path); - if (squeek_layout_get_kind(priv->keyboard->layout) == ARRANGEMENT_KIND_WIDE) { - gtk_style_context_add_class(priv->view_context, "wide"); + if (squeek_layout_get_kind(keyboard->layout) == ARRANGEMENT_KIND_WIDE) { + gtk_style_context_add_class(renderer->view_context, "wide"); } - gtk_style_context_add_provider (priv->view_context, - GTK_STYLE_PROVIDER(priv->css_provider), + gtk_style_context_add_provider (renderer->view_context, + GTK_STYLE_PROVIDER(renderer->css_provider), GTK_STYLE_PROVIDER_PRIORITY_USER); /* Create a style context for the buttons */ path = gtk_widget_path_new(); gtk_widget_path_append_type(path, view_type()); - if (squeek_layout_get_kind(priv->keyboard->layout) == ARRANGEMENT_KIND_WIDE) { + if (squeek_layout_get_kind(keyboard->layout) == ARRANGEMENT_KIND_WIDE) { gtk_widget_path_iter_add_class(path, -1, "wide"); } gtk_widget_path_append_type(path, button_type()); - priv->button_context = gtk_style_context_new (); - gtk_style_context_set_path(priv->button_context, path); + renderer->button_context = gtk_style_context_new (); + gtk_style_context_set_path(renderer->button_context, path); gtk_widget_path_unref(path); - gtk_style_context_set_parent(priv->button_context, priv->view_context); - gtk_style_context_set_state (priv->button_context, GTK_STATE_FLAG_NORMAL); - gtk_style_context_add_provider (priv->button_context, - GTK_STYLE_PROVIDER(priv->css_provider), + gtk_style_context_set_parent(renderer->button_context, renderer->view_context); + gtk_style_context_set_state (renderer->button_context, GTK_STATE_FLAG_NORMAL); + gtk_style_context_add_provider (renderer->button_context, + GTK_STYLE_PROVIDER(renderer->css_provider), GTK_STYLE_PROVIDER_PRIORITY_USER); return renderer; } void eek_renderer_set_allocation_size (EekRenderer *renderer, + struct squeek_layout *layout, gdouble width, gdouble height) { - g_return_if_fail (EEK_IS_RENDERER(renderer)); g_return_if_fail (width > 0.0 && height > 0.0); - EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer); + renderer->allocation_width = width; + renderer->allocation_height = height; - priv->allocation_width = width; - priv->allocation_height = height; - - priv->widget_to_layout = squeek_layout_calculate_transformation( - priv->keyboard->layout, - priv->allocation_width, priv->allocation_height); + renderer->widget_to_layout = squeek_layout_calculate_transformation( + layout, + renderer->allocation_width, renderer->allocation_height); // This is where size-dependent surfaces would be released } @@ -437,10 +334,7 @@ eek_renderer_set_allocation_size (EekRenderer *renderer, void eek_renderer_set_scale_factor (EekRenderer *renderer, gint scale) { - g_return_if_fail (EEK_IS_RENDERER(renderer)); - - EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer); - priv->scale_factor = scale; + renderer->scale_factor = scale; } cairo_surface_t * @@ -469,9 +363,5 @@ eek_renderer_get_icon_surface (const gchar *icon_name, struct transformation eek_renderer_get_transformation (EekRenderer *renderer) { - struct transformation failed = {0}; - g_return_val_if_fail (EEK_IS_RENDERER(renderer), failed); - - EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer); - return priv->widget_to_layout; + return renderer->widget_to_layout; } diff --git a/eek/eek-renderer.h b/eek/eek-renderer.h index 954ad171..f1823456 100644 --- a/eek/eek-renderer.h +++ b/eek/eek-renderer.h @@ -27,30 +27,34 @@ #include "eek-types.h" #include "src/submission.h" -G_BEGIN_DECLS +struct squeek_layout; -#define EEK_TYPE_RENDERER (eek_renderer_get_type()) -G_DECLARE_DERIVABLE_TYPE (EekRenderer, eek_renderer, EEK, RENDERER, GObject) - -struct _EekRendererClass +/// Renders LevelKayboards +/// It cannot adjust styles at runtime. +typedef struct EekRenderer { - GObjectClass parent_class; + PangoContext *pcontext; // owned + GtkCssProvider *css_provider; // owned + GtkStyleContext *view_context; // owned + GtkStyleContext *button_context; // TODO: maybe move a copy to each button + /// Style class for rendering the view and button CSS. + gchar *extra_style; // owned - cairo_surface_t *(* get_icon_surface) (EekRenderer *self, - const gchar *icon_name, - gint size, - gint scale); + // Mutable state + /// Background extents + gdouble allocation_width; + gdouble allocation_height; + gint scale_factor; /* the outputs scale factor */ + /// Coords transformation + struct transformation widget_to_layout; +} EekRenderer; - /*< private >*/ - /* padding */ - gpointer pdummy[23]; -}; GType eek_renderer_get_type (void) G_GNUC_CONST; EekRenderer *eek_renderer_new (LevelKeyboard *keyboard, PangoContext *pcontext); void eek_renderer_set_allocation_size - (EekRenderer *renderer, + (EekRenderer *renderer, struct squeek_layout *layout, gdouble width, gdouble height); void eek_renderer_set_scale_factor (EekRenderer *renderer, @@ -61,7 +65,9 @@ cairo_surface_t *eek_renderer_get_icon_surface(const gchar *icon_name, gint scale); void eek_renderer_render_keyboard (EekRenderer *renderer, struct submission *submission, - cairo_t *cr); + cairo_t *cr, LevelKeyboard *keyboard); +void +eek_renderer_free (EekRenderer *self); struct transformation eek_renderer_get_transformation (EekRenderer *renderer); diff --git a/eekboard/eekboard-context-service.c b/eekboard/eekboard-context-service.c index 8189c88f..a97f1231 100644 --- a/eekboard/eekboard-context-service.c +++ b/eekboard/eekboard-context-service.c @@ -47,7 +47,6 @@ static guint signals[LAST_SIGNAL] = { 0, }; struct _EekboardContextServicePrivate { LevelKeyboard *keyboard; // currently used keyboard - GHashTable *keyboard_hash; // a table of available keyboards, per layout GSettings *settings; // Owned reference // Maybe TODO: it's used only for fetching layout type. @@ -94,13 +93,6 @@ eekboard_context_service_get_property (GObject *object, static void eekboard_context_service_dispose (GObject *object) { - EekboardContextService *context = EEKBOARD_CONTEXT_SERVICE(object); - - if (context->priv->keyboard_hash) { - g_hash_table_destroy (context->priv->keyboard_hash); - context->priv->keyboard_hash = NULL; - } - G_OBJECT_CLASS (eekboard_context_service_parent_class)-> dispose (object); } @@ -148,7 +140,8 @@ eekboard_context_service_use_layout(EekboardContextService *context, struct sque } // generic part follows - LevelKeyboard *keyboard = level_keyboard_new(layout_name, state->arrangement); + struct squeek_layout *layout = squeek_load_layout(layout_name, state->arrangement); + LevelKeyboard *keyboard = level_keyboard_new(layout); // set as current LevelKeyboard *previous_keyboard = context->priv->keyboard; context->priv->keyboard = keyboard; @@ -248,12 +241,6 @@ static void eekboard_context_service_init (EekboardContextService *self) { self->priv = EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE(self); - - self->priv->keyboard_hash = - g_hash_table_new_full (g_direct_hash, - g_direct_equal, - NULL, - (GDestroyNotify)g_object_unref); const char *schema_name = "org.gnome.desktop.input-sources"; GSettingsSchemaSource *ssrc = g_settings_schema_source_get_default(); if (ssrc) { diff --git a/eekboard/eekboard-context-service.h b/eekboard/eekboard-context-service.h index b5f17988..8547e3e7 100644 --- a/eekboard/eekboard-context-service.h +++ b/eekboard/eekboard-context-service.h @@ -72,9 +72,6 @@ struct _EekboardContextServiceClass { GObjectClass parent_class; /*< public >*/ - struct squeek_view *(*create_keyboard) (EekboardContextService *self, - const gchar *keyboard_type); - /* signals */ void (*destroyed) (EekboardContextService *self); diff --git a/src/lib.rs b/src/lib.rs index ba71686b..287b5286 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,5 +35,6 @@ mod style; mod submission; pub mod tests; pub mod util; +mod ui_manager; mod vkeyboard; mod xdg; diff --git a/src/outputs.h b/src/outputs.h index 38a207f6..e018d037 100644 --- a/src/outputs.h +++ b/src/outputs.h @@ -4,10 +4,14 @@ #include "wayland-client-protocol.h" struct squeek_outputs; +struct squeek_output_handle { + struct wl_output *output; + struct squeek_outputs *outputs; +}; struct squeek_outputs *squeek_outputs_new(); void squeek_outputs_free(struct squeek_outputs*); void squeek_outputs_register(struct squeek_outputs*, struct wl_output *output); -struct wl_output *squeek_outputs_get_current(struct squeek_outputs*); +struct squeek_output_handle squeek_outputs_get_current(struct squeek_outputs*); int32_t squeek_outputs_get_perceptual_width(struct squeek_outputs*, struct wl_output *output); #endif diff --git a/src/outputs.rs b/src/outputs.rs index 08ac590d..29f4e78c 100644 --- a/src/outputs.rs +++ b/src/outputs.rs @@ -17,7 +17,7 @@ pub mod c { // Defined in C #[repr(transparent)] - #[derive(Clone, PartialEq)] + #[derive(Clone, PartialEq, Copy)] pub struct WlOutput(*const c_void); #[repr(C)] @@ -105,6 +105,24 @@ pub mod c { type COutputs = ::util::c::Wrapped; + /// A stable reference to an output. + #[derive(Clone)] + #[repr(C)] + pub struct OutputHandle { + wl_output: WlOutput, + outputs: COutputs, + } + + impl OutputHandle { + // Cannot return an Output reference + // because COutputs is too deeply wrapped + pub fn get_state(&self) -> Option { + let outputs = self.outputs.clone_ref(); + let outputs = outputs.borrow(); + find_output(&outputs, self.wl_output.clone()).map(|o| o.current.clone()) + } + } + // Defined in Rust extern fn outputs_handle_geometry( @@ -240,46 +258,15 @@ pub mod c { #[no_mangle] pub extern "C" - fn squeek_outputs_get_current(raw_collection: COutputs) -> WlOutput { + fn squeek_outputs_get_current(raw_collection: COutputs) -> OutputHandle { let collection = raw_collection.clone_ref(); let collection = collection.borrow(); - collection.outputs[0].output.clone() - } - - #[no_mangle] - pub extern "C" - fn squeek_outputs_get_perceptual_width( - raw_collection: COutputs, - wl_output: WlOutput, - ) -> i32 { - let collection = raw_collection.clone_ref(); - let collection = collection.borrow(); - - let output_state = find_output(&collection, wl_output) - .map(|o| &o.current); - match output_state { - Some(OutputState { - current_mode: Some(super::Mode { width, height } ), - transform: Some(transform), - scale, - }) => { - match transform { - Transform::Normal - | Transform::Rotated180 - | Transform::Flipped - | Transform::FlippedRotated180 => width / scale, - _ => height / scale, - } - }, - _ => { - log_print!( - logging::Level::Surprise, - "Not enough info received on output", - ); - 0 - }, + OutputHandle { + wl_output: collection.outputs[0].output.clone(), + outputs: raw_collection.clone(), } } + // TODO: handle unregistration fn find_output( @@ -305,6 +292,14 @@ pub mod c { } } +/// Generic size +#[derive(Clone)] +pub struct Size { + pub width: u32, + pub height: u32, +} + +/// wl_output mode #[derive(Clone)] struct Mode { width: i32, @@ -315,10 +310,16 @@ struct Mode { pub struct OutputState { current_mode: Option, transform: Option, - scale: i32, + pub scale: i32, } impl OutputState { + // More properly, this would have been a builder kind of struct, + // with wl_output gradually adding properties to it + // before it reached a fully initialized state, + // when it would transform into a struct without all (some?) of the Options. + // However, it's not clear which state is fully initialized, + // and whether it would make things easier at all anyway. fn uninitialized() -> OutputState { OutputState { current_mode: None, @@ -326,6 +327,32 @@ impl OutputState { scale: 1, } } + + pub fn get_pixel_size(&self) -> Option { + use self::c::Transform; + match self { + OutputState { + current_mode: Some(Mode { width, height } ), + transform: Some(transform), + scale: _, + } => Some( + match transform { + Transform::Normal + | Transform::Rotated180 + | Transform::Flipped + | Transform::FlippedRotated180 => Size { + width: *width as u32, + height: *height as u32, + }, + _ => Size { + width: *height as u32, + height: *width as u32, + }, + } + ), + _ => None, + } + } } pub struct Output { diff --git a/src/server-context-service.c b/src/server-context-service.c index 3e1e7f11..46811ce4 100644 --- a/src/server-context-service.c +++ b/src/server-context-service.c @@ -43,6 +43,7 @@ struct _ServerContextService { /// Needed for instantiating the widget struct submission *submission; // unowned struct squeek_layout_state *layout; + struct ui_manager *manager; // unowned gboolean visible; PhoshLayerSurface *window; @@ -86,18 +87,6 @@ on_notify_unmap (GObject *object, g_object_set (context, "visible", FALSE, NULL); } -static uint32_t -calculate_height(int32_t width) -{ - uint32_t height = 180; - if (width < 360 && width > 0) { - height = ((unsigned)width * 7 / 12); // to match 360×210 - } else if (width < 540) { - height = 180 + (540 - (unsigned)width) * 30 / 180; // smooth transition - } - return height; -} - static void on_surface_configure(PhoshLayerSurface *surface, ServerContextService *context) { @@ -108,7 +97,7 @@ on_surface_configure(PhoshLayerSurface *surface, ServerContextService *context) "configured-height", &height, NULL); - guint desired_height = calculate_height(width); + guint desired_height = squeek_uiman_get_perceptual_height(context->manager); guint configured_height = (guint)height; // if height was already requested once but a different one was given // (for the same set of surrounding properties), @@ -131,14 +120,14 @@ make_window (ServerContextService *context) if (context->window) g_error("Window already present"); - struct wl_output *output = squeek_outputs_get_current(squeek_wayland->outputs); - int32_t width = squeek_outputs_get_perceptual_width(squeek_wayland->outputs, output); - uint32_t height = calculate_height(width); + struct squeek_output_handle output = squeek_outputs_get_current(squeek_wayland->outputs); + squeek_uiman_set_output(context->manager, output); + uint32_t height = squeek_uiman_get_perceptual_height(context->manager); context->window = g_object_new ( PHOSH_TYPE_LAYER_SURFACE, "layer-shell", squeek_wayland->layer_shell, - "wl-output", output, + "wl-output", output.output, "height", height, "anchor", ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT @@ -322,11 +311,12 @@ server_context_service_init (ServerContextService *state) { } ServerContextService * -server_context_service_new (EekboardContextService *state, struct submission *submission, struct squeek_layout_state *layout) +server_context_service_new (EekboardContextService *state, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman) { ServerContextService *ui = g_object_new (SERVER_TYPE_CONTEXT_SERVICE, NULL); ui->submission = submission; ui->state = state; ui->layout = layout; + ui->manager = uiman; return ui; } diff --git a/src/server-context-service.h b/src/server-context-service.h index 2c88a8bc..a69f85ae 100644 --- a/src/server-context-service.h +++ b/src/server-context-service.h @@ -20,6 +20,7 @@ #include "src/layout.h" #include "src/submission.h" +#include "ui_manager.h" G_BEGIN_DECLS @@ -36,7 +37,7 @@ typedef struct _ServerContextService ServerContextService; GType server_context_service_get_type (void) G_GNUC_CONST; -ServerContextService *server_context_service_new(EekboardContextService *state, struct submission *submission, struct squeek_layout_state *layout); +ServerContextService *server_context_service_new(EekboardContextService *state, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman); enum squeek_arrangement_kind server_context_service_get_layout_type(ServerContextService *); void server_context_service_show_keyboard (ServerContextService *context); void server_context_service_hide_keyboard (ServerContextService *context); diff --git a/src/server-main.c b/src/server-main.c index f0b9a896..f3f5edb8 100644 --- a/src/server-main.c +++ b/src/server-main.c @@ -32,6 +32,7 @@ #include "outputs.h" #include "submission.h" #include "server-context-service.h" +#include "ui_manager.h" #include "wayland.h" #include @@ -45,6 +46,7 @@ struct squeekboard { ServerContextService *ui_context; // mess, includes the entire UI struct submission *submission; // Wayland text input handling. struct squeek_layout_state layout_choice; // Currently wanted layout. + struct ui_manager *ui_manager; // UI shape tracker/chooser. TODO: merge with layuot choice }; @@ -201,6 +203,8 @@ main (int argc, char **argv) g_warning("Wayland input method interface not available"); } + instance.ui_manager = squeek_uiman_new(); + instance.settings_context = eekboard_context_service_new(&instance.layout_choice); // set up dbus @@ -282,7 +286,8 @@ main (int argc, char **argv) ServerContextService *ui_context = server_context_service_new( instance.settings_context, instance.submission, - &instance.layout_choice); + &instance.layout_choice, + instance.ui_manager); if (!ui_context) { g_error("Could not initialize GUI"); exit(1); diff --git a/src/ui_manager.h b/src/ui_manager.h new file mode 100644 index 00000000..57d3cc70 --- /dev/null +++ b/src/ui_manager.h @@ -0,0 +1,14 @@ +#ifndef UI_MANAGER__ +#define UI_MANAGER__ + +#include + +#include "outputs.h" + +struct ui_manager; + +struct ui_manager *squeek_uiman_new(); +void squeek_uiman_set_output(struct ui_manager *uiman, struct squeek_output_handle output); +uint32_t squeek_uiman_get_perceptual_height(struct ui_manager *uiman); + +#endif diff --git a/src/ui_manager.rs b/src/ui_manager.rs new file mode 100644 index 00000000..c7af6521 --- /dev/null +++ b/src/ui_manager.rs @@ -0,0 +1,81 @@ +/* Copyright (C) 2020 Purism SPC + * SPDX-License-Identifier: GPL-3.0+ + */ + +/*! Centrally manages the shape of the UI widgets, and the choice of layout. + * + * Coordinates this based on information collated from all possible sources. + */ + +use std::cmp::min; +use ::outputs::c::OutputHandle; + +mod c { + use super::*; + use ::util::c::Wrapped; + + #[no_mangle] + pub extern "C" + fn squeek_uiman_new() -> Wrapped { + Wrapped::new(Manager { output: None }) + } + + /// Used to size the layer surface containing all the OSK widgets. + #[no_mangle] + pub extern "C" + fn squeek_uiman_get_perceptual_height( + uiman: Wrapped, + ) -> u32 { + let uiman = uiman.clone_ref(); + let uiman = uiman.borrow(); + // TODO: what to do when there's no output? + uiman.get_perceptual_height().unwrap_or(0) + } + + #[no_mangle] + pub extern "C" + fn squeek_uiman_set_output( + uiman: Wrapped, + output: OutputHandle, + ) { + let uiman = uiman.clone_ref(); + let mut uiman = uiman.borrow_mut(); + uiman.output = Some(output); + } +} + +/// Stores current state of all things influencing what the UI should look like. +pub struct Manager { + /// Shared output handle, current state updated whenever it's needed. + // TODO: Stop assuming that the output never changes. + // (There's no way for the output manager to update the ui manager.) + // FIXME: Turn into an OutputState and apply relevant connections elsewhere. + // Otherwise testability and predictablity is low. + output: Option, + //// Pixel size of the surface. Needs explicit updating. + //surface_size: Option, +} + +impl Manager { + fn get_perceptual_height(&self) -> Option { + let output_info = (&self.output).as_ref() + .and_then(|o| o.get_state()) + .map(|os| (os.scale as u32, os.get_pixel_size())); + match output_info { + Some((scale, Some(px_size))) => Some({ + let height = if (px_size.width < 720) & (px_size.width > 0) { + px_size.width * 7 / 12 // to match 360×210 + } else if px_size.width < 1080 { + 360 + (1080 - px_size.width) * 60 / 360 // smooth transition + } else { + 360 + }; + + // Don't exceed half the display size + min(height, px_size.height / 2) / scale + }), + Some((scale, None)) => Some(360 / scale), + None => None, + } + } +} diff --git a/src/util.rs b/src/util.rs index 9dafb34e..767c32ea 100644 --- a/src/util.rs +++ b/src/util.rs @@ -98,7 +98,8 @@ pub mod c { Rc::from_raw(self.0) } - /// Creates a new Rc reference to the same data + /// Creates a new Rc reference to the same data. + /// Use for accessing the underlying data as a reference. pub fn clone_ref(&self) -> Rc> { // A bit dangerous: the Rc may be in use elsewhere let used_rc = unsafe { Rc::from_raw(self.0) }; @@ -130,6 +131,7 @@ pub mod c { impl COpaquePtr for Wrapped {} } +/// Clones the underlying data structure, like ToOwned. pub trait CloneOwned { type Owned; fn clone_owned(&self) -> Self::Owned;