Merge branch 'drop_key' into 'master'

Simplify renderer

See merge request Librem5/squeekboard!272
This commit is contained in:
Dorota Czaplejewicz
2019-12-15 16:38:31 +00:00
16 changed files with 346 additions and 849 deletions

3
Cargo.lock generated
View File

@ -324,6 +324,9 @@ name = "rs"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "gio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -11,6 +11,15 @@ serde = { version = "1.0.*", features = ["derive"] }
serde_yaml = "0.8.*" serde_yaml = "0.8.*"
xkbcommon = { version = "0.4.*", features = ["wayland"] } xkbcommon = { version = "0.4.*", features = ["wayland"] }
[dependencies.cairo-rs]
version = "0.5.*"
[dependencies.cairo-sys-rs]
version = ""
[dependencies.gdk]
version = ""
[dependencies.gio] [dependencies.gio]
version = "" version = ""
features = ["v2_44"] features = ["v2_44"]
@ -23,7 +32,6 @@ features = ["v2_44"]
version = "" version = ""
features = ["v2_44"] features = ["v2_44"]
[dependencies.gtk] [dependencies.gtk]
version = "0.5.*" version = "0.5.*"
features = ["v3_22"] features = ["v3_22"]

View File

@ -61,10 +61,6 @@ typedef struct _EekGtkKeyboardPrivate
G_DEFINE_TYPE_WITH_PRIVATE (EekGtkKeyboard, eek_gtk_keyboard, GTK_TYPE_DRAWING_AREA) G_DEFINE_TYPE_WITH_PRIVATE (EekGtkKeyboard, eek_gtk_keyboard, GTK_TYPE_DRAWING_AREA)
static void render_pressed_button (GtkWidget *widget, struct button_place *place);
static void render_released_button (GtkWidget *widget,
const struct squeek_button *button);
static void static void
eek_gtk_keyboard_real_realize (GtkWidget *self) eek_gtk_keyboard_real_realize (GtkWidget *self)
{ {
@ -101,10 +97,7 @@ eek_gtk_keyboard_real_draw (GtkWidget *self,
gtk_widget_get_scale_factor (self)); gtk_widget_get_scale_factor (self));
} }
// render the keyboard without any key activity (TODO: from a cached buffer)
eek_renderer_render_keyboard (priv->renderer, cr); eek_renderer_render_keyboard (priv->renderer, cr);
// render only a few remaining changes
squeek_layout_draw_all_changed(priv->keyboard->layout, EEK_GTK_KEYBOARD(self));
return FALSE; return FALSE;
} }
@ -158,8 +151,6 @@ eek_gtk_keyboard_real_button_press_event (GtkWidget *self,
return TRUE; return TRUE;
} }
// TODO: this belongs more in gtk_keyboard, with a way to find out which key to re-render // TODO: this belongs more in gtk_keyboard, with a way to find out which key to re-render
static gboolean static gboolean
eek_gtk_keyboard_real_button_release_event (GtkWidget *self, eek_gtk_keyboard_real_button_release_event (GtkWidget *self,
@ -248,6 +239,7 @@ eek_gtk_keyboard_set_property (GObject *object,
const GValue *value, const GValue *value,
GParamSpec *pspec) GParamSpec *pspec)
{ {
(void)value;
switch (prop_id) { switch (prop_id) {
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@ -304,7 +296,9 @@ eek_gtk_keyboard_class_init (EekGtkKeyboardClass *klass)
static void static void
eek_gtk_keyboard_init (EekGtkKeyboard *self) eek_gtk_keyboard_init (EekGtkKeyboard *self)
{} {
(void)self;
}
/** /**
* eek_gtk_keyboard_new: * eek_gtk_keyboard_new:
@ -322,110 +316,7 @@ eek_gtk_keyboard_new (LevelKeyboard *keyboard)
return GTK_WIDGET(ret); return GTK_WIDGET(ret);
} }
static void EekRenderer *eek_gtk_keyboard_get_renderer(EekGtkKeyboard *self) {
render_pressed_button (GtkWidget *widget,
struct button_place *place)
{
EekGtkKeyboard *self = EEK_GTK_KEYBOARD (widget);
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self); EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
return priv->renderer;
GdkWindow *window = gtk_widget_get_window (widget);
cairo_region_t *region = gdk_window_get_clip_region (window);
GdkDrawingContext *context = gdk_window_begin_draw_frame (window, region);
cairo_t *cr = gdk_drawing_context_get_cairo_context (context);
eek_renderer_render_button (priv->renderer, cr, place, 1.0, TRUE);
/*
eek_renderer_render_key (priv->renderer, cr, key, 1.5, TRUE);
*/
gdk_window_end_draw_frame (window, context);
cairo_region_destroy (region);
}
void
eek_gtk_render_locked_button (EekGtkKeyboard *self, struct button_place place)
{
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
GdkWindow *window = gtk_widget_get_window (GTK_WIDGET(self));
cairo_region_t *region = gdk_window_get_clip_region (window);
GdkDrawingContext *context = gdk_window_begin_draw_frame (window, region);
cairo_t *cr = gdk_drawing_context_get_cairo_context (context);
eek_renderer_render_button (priv->renderer, cr, &place, 1.0, TRUE);
gdk_window_end_draw_frame (window, context);
cairo_region_destroy (region);
}
// TODO: does it really redraw the entire keyboard?
static void
render_released_button (GtkWidget *widget,
const struct squeek_button *button)
{
(void)button;
EekGtkKeyboard *self = EEK_GTK_KEYBOARD (widget);
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
GdkWindow *window = gtk_widget_get_window (widget);
cairo_region_t *region = gdk_window_get_clip_region (window);
GdkDrawingContext *context = gdk_window_begin_draw_frame (window, region);
cairo_t *cr = gdk_drawing_context_get_cairo_context (context);
eek_renderer_render_keyboard (priv->renderer, cr);
gdk_window_end_draw_frame (window, context);
cairo_region_destroy (region);
}
void
eek_gtk_on_button_pressed (struct button_place place,
EekGtkKeyboard *self)
{
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
/* renderer may have not been set yet if the widget is a popup */
if (!priv->renderer)
return;
if (!place.row) {
return;
}
render_pressed_button (GTK_WIDGET(self), &place);
gtk_widget_queue_draw (GTK_WIDGET(self));
#if HAVE_LIBCANBERRA
ca_gtk_play_for_widget (widget, 0,
CA_PROP_EVENT_ID, "button-pressed",
CA_PROP_EVENT_DESCRIPTION, "virtual key pressed",
CA_PROP_APPLICATION_ID, "org.fedorahosted.Eekboard",
NULL);
#endif
}
void
eek_gtk_on_button_released (const struct squeek_button *button,
struct squeek_view *view,
EekGtkKeyboard *self)
{
(void)view;
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
/* renderer may have not been set yet if the widget is a popup */
if (!priv->renderer)
return;
render_released_button (GTK_WIDGET(self), button);
gtk_widget_queue_draw (GTK_WIDGET(self));
#if HAVE_LIBCANBERRA
ca_gtk_play_for_widget (widget, 0,
CA_PROP_EVENT_ID, "button-released",
CA_PROP_EVENT_DESCRIPTION, "virtual key pressed",
CA_PROP_APPLICATION_ID, "org.fedorahosted.Eekboard",
NULL);
#endif
} }

View File

@ -34,8 +34,6 @@
#include "eekboard/eekboard-context-service.h" #include "eekboard/eekboard-context-service.h"
#include "eekboard/key-emitter.h" #include "eekboard/key-emitter.h"
#include "keymap.h" #include "keymap.h"
#include "src/keyboard.h"
#include "eek-keyboard.h" #include "eek-keyboard.h"
void level_keyboard_deinit(LevelKeyboard *self) { void level_keyboard_deinit(LevelKeyboard *self) {
@ -59,8 +57,3 @@ LevelKeyboard *level_keyboard_new(EekboardContextService *manager, struct squeek
keyboard->manager = manager; keyboard->manager = manager;
return keyboard; return keyboard;
} }
struct squeek_view *level_keyboard_current(LevelKeyboard *keyboard)
{
return squeek_layout_get_current_view(keyboard->layout);
}

View File

@ -49,7 +49,6 @@ typedef struct _LevelKeyboard LevelKeyboard;
gchar * eek_keyboard_get_keymap gchar * eek_keyboard_get_keymap
(LevelKeyboard *keyboard); (LevelKeyboard *keyboard);
struct squeek_view *level_keyboard_current(LevelKeyboard *keyboard);
LevelKeyboard *level_keyboard_new(EekboardContextService *manager, struct squeek_layout *layout); LevelKeyboard *level_keyboard_new(EekboardContextService *manager, struct squeek_layout *layout);
void level_keyboard_deinit(LevelKeyboard *self); void level_keyboard_deinit(LevelKeyboard *self);
void level_keyboard_free(LevelKeyboard *self); void level_keyboard_free(LevelKeyboard *self);

View File

@ -42,17 +42,14 @@ typedef struct _EekRendererPrivate
GtkStyleContext *view_context; // owned GtkStyleContext *view_context; // owned
GtkStyleContext *button_context; // TODO: maybe move a copy to each button GtkStyleContext *button_context; // TODO: maybe move a copy to each button
gdouble border_width; gdouble border_width; // FIXME: border of what?
gdouble allocation_width; gdouble allocation_width;
gdouble allocation_height; gdouble allocation_height;
gdouble scale;
gint scale_factor; /* the outputs scale factor */ gint scale_factor; /* the outputs scale factor */
gint origin_x; struct transformation widget_to_layout;
gint origin_y;
PangoFontDescription *font; // owned reference PangoFontDescription *font; // owned reference
cairo_surface_t *keyboard_surface;
} EekRendererPrivate; } EekRendererPrivate;
@ -62,110 +59,10 @@ G_DEFINE_TYPE_WITH_PRIVATE (EekRenderer, eek_renderer, G_TYPE_OBJECT)
static void eek_renderer_render_button_label (EekRenderer *self, cairo_t *cr, GtkStyleContext *ctx, static void eek_renderer_render_button_label (EekRenderer *self, cairo_t *cr, GtkStyleContext *ctx,
const struct squeek_button *button); const struct squeek_button *button);
static void invalidate (EekRenderer *renderer); void eek_render_button (EekRenderer *self,
static void render_button (EekRenderer *self, cairo_t *cr, const struct squeek_button *button,
cairo_t *cr, EekBounds view_bounds, struct button_place *place,
gboolean pressed, gboolean locked); gboolean pressed, gboolean locked);
struct _CreateKeyboardSurfaceCallbackData {
cairo_t *cr;
EekRenderer *renderer;
struct squeek_view *view;
struct squeek_row *row;
};
typedef struct _CreateKeyboardSurfaceCallbackData CreateKeyboardSurfaceCallbackData;
static void
create_keyboard_surface_button_callback (struct squeek_button *button,
gpointer user_data)
{
CreateKeyboardSurfaceCallbackData *data = user_data;
EekBounds bounds = squeek_button_get_bounds(button);
cairo_save (data->cr);
cairo_translate (data->cr, bounds.x, bounds.y);
cairo_rectangle (data->cr,
0.0,
0.0,
bounds.width + 100,
bounds.height + 100);
cairo_clip (data->cr);
struct button_place place = {
.row = data->row,
.button = button,
};
render_button (data->renderer, data->cr, squeek_view_get_bounds(data->view), &place, FALSE, FALSE);
cairo_restore (data->cr);
}
static void
create_keyboard_surface_row_callback (struct squeek_row *row,
gpointer user_data)
{
CreateKeyboardSurfaceCallbackData *data = user_data;
EekBounds bounds = squeek_row_get_bounds(row);
cairo_save (data->cr);
cairo_translate (data->cr, bounds.x, bounds.y);
gint angle = squeek_row_get_angle (row);
cairo_rotate (data->cr, angle * G_PI / 180);
data->row = row;
squeek_row_foreach(row, create_keyboard_surface_button_callback, data);
cairo_restore (data->cr);
}
static void
render_keyboard_surface (EekRenderer *renderer, struct squeek_view *view)
{
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
GdkRGBA color = {0};
gtk_style_context_get_color (priv->view_context, GTK_STATE_FLAG_NORMAL, &color);
EekBounds bounds = squeek_view_get_bounds (level_keyboard_current(priv->keyboard));
CreateKeyboardSurfaceCallbackData data = {
.cr = cairo_create (priv->keyboard_surface),
.renderer = renderer,
.view = view,
};
/* Paint the background covering the entire widget area */
gtk_render_background (priv->view_context,
data.cr,
0, 0,
priv->allocation_width, priv->allocation_height);
gtk_render_frame (priv->view_context,
data.cr,
0, 0,
priv->allocation_width, priv->allocation_height);
cairo_save (data.cr);
cairo_scale (data.cr, priv->scale, priv->scale);
cairo_translate (data.cr, bounds.x, bounds.y);
cairo_set_source_rgba (data.cr,
color.red,
color.green,
color.blue,
color.alpha);
/* draw rows */
squeek_view_foreach(level_keyboard_current(priv->keyboard),
create_keyboard_surface_row_callback,
&data);
cairo_restore (data.cr);
cairo_destroy (data.cr);
}
static void static void
render_outline (cairo_t *cr, render_outline (cairo_t *cr,
GtkStyleContext *ctx, GtkStyleContext *ctx,
@ -190,46 +87,20 @@ render_outline (cairo_t *cr,
} }
static void render_button_in_context(EekRenderer *self, static void render_button_in_context(EekRenderer *self,
gdouble scale,
gint scale_factor, gint scale_factor,
cairo_t *cr, cairo_t *cr,
GtkStyleContext *ctx, GtkStyleContext *ctx,
EekBounds view_bounds, const struct squeek_button *button) {
struct button_place *place,
gboolean active) {
cairo_surface_t *outline_surface = NULL;
/* render outline */
EekBounds bounds = squeek_button_get_bounds(place->button);
{
cairo_t *cr;
// Outline will be drawn on the outside of the button, so the
// surface needs to be bigger than the button
outline_surface =
cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
(int)ceil(bounds.width) + 10,
(int)ceil(bounds.height) + 10);
cr = cairo_create (outline_surface);
/* blank background */ /* blank background */
cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.0); cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.0);
cairo_paint (cr); cairo_paint (cr);
cairo_save (cr); EekBounds bounds = squeek_button_get_bounds(button);
eek_renderer_apply_transformation_for_button (cr, view_bounds, place, 1.0, FALSE);
render_outline (cr, ctx, bounds); render_outline (cr, ctx, bounds);
cairo_restore (cr);
cairo_destroy (cr);
}
cairo_set_source_surface (cr, outline_surface, 0.0, 0.0);
cairo_surface_destroy(outline_surface);
cairo_paint (cr); cairo_paint (cr);
/* render icon (if any) */ /* render icon (if any) */
const char *icon_name = squeek_button_get_icon_name(place->button); const char *icon_name = squeek_button_get_icon_name(button);
if (icon_name) { if (icon_name) {
cairo_surface_t *icon_surface = cairo_surface_t *icon_surface =
@ -259,14 +130,13 @@ static void render_button_in_context(EekRenderer *self,
return; return;
} }
} }
eek_renderer_render_button_label (self, cr, ctx, place->button); eek_renderer_render_button_label (self, cr, ctx, button);
} }
static void void
render_button (EekRenderer *self, eek_render_button (EekRenderer *self,
cairo_t *cr, cairo_t *cr,
EekBounds view_bounds, const struct squeek_button *button,
struct button_place *place,
gboolean pressed, gboolean pressed,
gboolean locked) gboolean locked)
{ {
@ -277,7 +147,7 @@ render_button (EekRenderer *self,
from the button's symbol. */ from the button's symbol. */
g_autoptr (GtkWidgetPath) path = NULL; g_autoptr (GtkWidgetPath) path = NULL;
path = gtk_widget_path_copy (gtk_style_context_get_path (ctx)); path = gtk_widget_path_copy (gtk_style_context_get_path (ctx));
const char *name = squeek_button_get_name(place->button); const char *name = squeek_button_get_name(button);
gtk_widget_path_iter_set_name (path, -1, name); gtk_widget_path_iter_set_name (path, -1, name);
/* Update the style context with the updated widget path. */ /* Update the style context with the updated widget path. */
@ -286,13 +156,13 @@ render_button (EekRenderer *self,
(pressed) or normal. */ (pressed) or normal. */
gtk_style_context_set_state(ctx, gtk_style_context_set_state(ctx,
pressed ? GTK_STATE_FLAG_ACTIVE : GTK_STATE_FLAG_NORMAL); pressed ? GTK_STATE_FLAG_ACTIVE : GTK_STATE_FLAG_NORMAL);
const char *outline_name = squeek_button_get_outline_name(place->button); const char *outline_name = squeek_button_get_outline_name(button);
if (locked) { if (locked) {
gtk_style_context_add_class(ctx, "locked"); gtk_style_context_add_class(ctx, "locked");
} }
gtk_style_context_add_class(ctx, outline_name); gtk_style_context_add_class(ctx, outline_name);
render_button_in_context(self, priv->scale, priv->scale_factor, cr, ctx, view_bounds, place, pressed); render_button_in_context(self, priv->scale_factor, cr, ctx, button);
// Save and restore functions don't work if gtk_render_* was used in between // Save and restore functions don't work if gtk_render_* was used in between
gtk_style_context_set_state(ctx, GTK_STATE_FLAG_NORMAL); gtk_style_context_set_state(ctx, GTK_STATE_FLAG_NORMAL);
@ -302,47 +172,6 @@ render_button (EekRenderer *self,
} }
} }
/**
* eek_renderer_apply_transformation_for_key:
* @self: The renderer used to render the key
* @cr: The Cairo rendering context used for rendering
* @key: The key to be transformed
* @scale: The factor used to scale the key bounds before rendering
* @rotate: Whether to rotate the key by the angle defined for the key's
* in its section definition
*
* Applies a transformation, consisting of scaling and rotation, to the
* current rendering context using the bounds for the given key. The scale
* factor is separate to the normal scale factor for the keyboard as a whole
* and is applied cumulatively. It is typically used to render larger than
* normal keys for popups.
*/
void
eek_renderer_apply_transformation_for_button (cairo_t *cr,
EekBounds view_bounds,
struct button_place *place,
gdouble scale,
gboolean rotate)
{
EekBounds bounds, rotated_bounds;
gdouble s;
eek_renderer_get_button_bounds (view_bounds, place, &bounds, FALSE);
eek_renderer_get_button_bounds (view_bounds, place, &rotated_bounds, TRUE);
gint angle = squeek_row_get_angle (place->row);
cairo_scale (cr, scale, scale);
if (rotate) {
s = sin (angle * G_PI / 180);
if (s < 0)
cairo_translate (cr, 0, - bounds.width * s);
else
cairo_translate (cr, bounds.height * s, 0);
cairo_rotate (cr, angle * G_PI / 180);
}
}
static void static void
eek_renderer_render_button_label (EekRenderer *self, eek_renderer_render_button_label (EekRenderer *self,
cairo_t *cr, cairo_t *cr,
@ -415,76 +244,28 @@ eek_renderer_render_button_label (EekRenderer *self,
g_object_unref (layout); g_object_unref (layout);
} }
/* void
* eek_renderer_real_render_key: eek_renderer_render_keyboard (EekRenderer *self,
* @self: The renderer used to render the key
* @cr: The Cairo rendering context used for rendering
* @key: The key to be transformed
* @scale: The factor used to scale the key bounds before rendering
* @rotate: Whether to rotate the key by the angle defined for the key's
* in its section definition
*
* Renders a key separately from the normal keyboard rendering.
*/
static void
eek_renderer_real_render_button (EekRenderer *self,
cairo_t *cr,
struct button_place *place,
gdouble scale,
gboolean rotate)
{
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
EekBounds bounds;
EekBounds view_bounds = squeek_view_get_bounds (level_keyboard_current(priv->keyboard));
eek_renderer_get_button_bounds (view_bounds, place, &bounds, rotate);
cairo_save (cr);
/* Because this function is called separately from the keyboard rendering
function, the transformation for the context needs to be set up */
cairo_translate (cr, priv->origin_x, priv->origin_y);
cairo_scale (cr, priv->scale, priv->scale);
cairo_translate (cr, bounds.x, bounds.y);
eek_renderer_apply_transformation_for_button (cr, view_bounds, place, scale, rotate);
struct squeek_key *key = squeek_button_get_key(place->button);
render_button (
self, cr, view_bounds, place,
squeek_key_is_pressed(key) != 0,
squeek_key_is_locked (key) != 0
);
cairo_restore (cr);
}
static void
eek_renderer_real_render_keyboard (EekRenderer *self,
cairo_t *cr) cairo_t *cr)
{ {
EekRendererPrivate *priv = eek_renderer_get_instance_private (self); EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
cairo_pattern_t *source;
g_return_if_fail (priv->keyboard); g_return_if_fail (priv->keyboard);
g_return_if_fail (priv->allocation_width > 0.0); g_return_if_fail (priv->allocation_width > 0.0);
g_return_if_fail (priv->allocation_height > 0.0); g_return_if_fail (priv->allocation_height > 0.0);
cairo_save (cr); /* Paint the background covering the entire widget area */
gtk_render_background (priv->view_context,
cairo_translate (cr, priv->origin_x, priv->origin_y); cr,
0, 0,
if (priv->keyboard_surface)
cairo_surface_destroy (priv->keyboard_surface);
priv->keyboard_surface = cairo_surface_create_for_rectangle (
cairo_get_target (cr), 0, 0,
priv->allocation_width, priv->allocation_height); priv->allocation_width, priv->allocation_height);
render_keyboard_surface (self, squeek_layout_get_current_view(priv->keyboard->layout)); cairo_save(cr);
cairo_translate (cr, priv->widget_to_layout.origin_x, priv->widget_to_layout.origin_y);
cairo_set_source_surface (cr, priv->keyboard_surface, 0.0, 0.0); cairo_scale (cr, priv->widget_to_layout.scale, priv->widget_to_layout.scale);
source = cairo_get_source (cr);
cairo_pattern_set_extend (source, CAIRO_EXTEND_PAD);
cairo_paint (cr);
squeek_draw_layout_base_view(priv->keyboard->layout, self, cr);
squeek_layout_draw_all_changed(priv->keyboard->layout, self, cr);
cairo_restore (cr); cairo_restore (cr);
} }
@ -514,6 +295,7 @@ eek_renderer_get_property (GObject *object,
GValue *value, GValue *value,
GParamSpec *pspec) GParamSpec *pspec)
{ {
(void)value;
switch (prop_id) { switch (prop_id) {
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@ -535,8 +317,7 @@ eek_renderer_dispose (GObject *object)
priv->pcontext = NULL; priv->pcontext = NULL;
} }
/* this will release all allocated surfaces and font if any */ // this is where renderer-specific surfaces would be released
invalidate (EEK_RENDERER(object));
G_OBJECT_CLASS (eek_renderer_parent_class)->dispose (object); G_OBJECT_CLASS (eek_renderer_parent_class)->dispose (object);
} }
@ -560,9 +341,6 @@ eek_renderer_class_init (EekRendererClass *klass)
GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GParamSpec *pspec; GParamSpec *pspec;
klass->render_button = eek_renderer_real_render_button;
klass->render_keyboard = eek_renderer_real_render_keyboard;
gobject_class->set_property = eek_renderer_set_property; gobject_class->set_property = eek_renderer_set_property;
gobject_class->get_property = eek_renderer_get_property; gobject_class->get_property = eek_renderer_get_property;
gobject_class->dispose = eek_renderer_dispose; gobject_class->dispose = eek_renderer_dispose;
@ -615,10 +393,8 @@ eek_renderer_init (EekRenderer *self)
priv->border_width = 1.0; priv->border_width = 1.0;
priv->allocation_width = 0.0; priv->allocation_width = 0.0;
priv->allocation_height = 0.0; priv->allocation_height = 0.0;
priv->scale = 1.0;
priv->scale_factor = 1; priv->scale_factor = 1;
priv->font = NULL; priv->font = NULL;
priv->keyboard_surface = NULL;
GtkIconTheme *theme = gtk_icon_theme_get_default (); GtkIconTheme *theme = gtk_icon_theme_get_default ();
@ -627,17 +403,6 @@ eek_renderer_init (EekRenderer *self)
priv->css_provider = squeek_load_style(); priv->css_provider = squeek_load_style();
} }
static void
invalidate (EekRenderer *renderer)
{
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
if (priv->keyboard_surface) {
cairo_surface_destroy (priv->keyboard_surface);
priv->keyboard_surface = NULL;
}
}
EekRenderer * EekRenderer *
eek_renderer_new (LevelKeyboard *keyboard, eek_renderer_new (LevelKeyboard *keyboard,
PangoContext *pcontext) PangoContext *pcontext)
@ -685,8 +450,6 @@ eek_renderer_set_allocation_size (EekRenderer *renderer,
gdouble width, gdouble width,
gdouble height) gdouble height)
{ {
gdouble scale;
g_return_if_fail (EEK_IS_RENDERER(renderer)); g_return_if_fail (EEK_IS_RENDERER(renderer));
g_return_if_fail (width > 0.0 && height > 0.0); g_return_if_fail (width > 0.0 && height > 0.0);
@ -695,99 +458,11 @@ eek_renderer_set_allocation_size (EekRenderer *renderer,
priv->allocation_width = width; priv->allocation_width = width;
priv->allocation_height = height; priv->allocation_height = height;
/* Calculate a scale factor to use when rendering the keyboard into the priv->widget_to_layout = squeek_layout_calculate_transformation(
available space. */ priv->keyboard->layout,
EekBounds bounds = squeek_view_get_bounds (level_keyboard_current(priv->keyboard)); priv->allocation_width, priv->allocation_height);
gdouble w = (bounds.x * 2) + bounds.width; // This is where size-dependent surfaces would be released
gdouble h = (bounds.y * 2) + bounds.height;
scale = MIN(width / w, height / h);
priv->scale = scale;
/* Set the rendering offset in widget coordinates to center the keyboard */
priv->origin_x = (gint)floor((width - (scale * w)) / 2);
priv->origin_y = (gint)floor((height - (scale * h)) / 2);
invalidate (renderer);
}
void
eek_renderer_get_size (EekRenderer *renderer,
gdouble *width,
gdouble *height)
{
g_return_if_fail (EEK_IS_RENDERER(renderer));
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
EekBounds bounds = squeek_view_get_bounds (level_keyboard_current(priv->keyboard));
if (width)
*width = bounds.width;
if (height)
*height = bounds.height;
}
void
eek_renderer_get_button_bounds (EekBounds view_bounds,
struct button_place *place,
EekBounds *bounds,
gboolean rotate)
{
gint angle = 0;
EekPoint points[4], min, max;
g_return_if_fail (place);
g_return_if_fail (bounds != NULL);
EekBounds button_bounds = squeek_button_get_bounds(place->button);
EekBounds row_bounds = squeek_row_get_bounds (place->row);
if (!rotate) {
button_bounds.x += view_bounds.x + row_bounds.x;
button_bounds.y += view_bounds.y + row_bounds.y;
*bounds = button_bounds;
return;
}
points[0].x = button_bounds.x;
points[0].y = button_bounds.y;
points[1].x = points[0].x + button_bounds.width;
points[1].y = points[0].y;
points[2].x = points[1].x;
points[2].y = points[1].y + button_bounds.height;
points[3].x = points[0].x;
points[3].y = points[2].y;
if (rotate) {
angle = squeek_row_get_angle (place->row);
}
min = points[2];
max = points[0];
for (unsigned i = 0; i < G_N_ELEMENTS(points); i++) {
eek_point_rotate (&points[i], angle);
if (points[i].x < min.x)
min.x = points[i].x;
if (points[i].x > max.x)
max.x = points[i].x;
if (points[i].y < min.y)
min.y = points[i].y;
if (points[i].y > max.y)
max.y = points[i].y;
}
bounds->x = view_bounds.x + row_bounds.x + min.x;
bounds->y = view_bounds.y + row_bounds.y + min.y;
bounds->width = (max.x - min.x);
bounds->height = (max.y - min.y);
}
gdouble
eek_renderer_get_scale (EekRenderer *renderer)
{
g_return_val_if_fail (EEK_IS_RENDERER(renderer), 0);
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
return priv->scale;
} }
void void
@ -823,29 +498,6 @@ eek_renderer_get_icon_surface (const gchar *icon_name,
return surface; return surface;
} }
void
eek_renderer_render_button (EekRenderer *renderer,
cairo_t *cr,
struct button_place *place,
gdouble scale,
gboolean rotate)
{
g_return_if_fail (EEK_IS_RENDERER(renderer));
g_return_if_fail (place);
g_return_if_fail (scale >= 0.0);
EEK_RENDERER_GET_CLASS(renderer)->
render_button (renderer, cr, place, scale, rotate);
}
void
eek_renderer_render_keyboard (EekRenderer *renderer,
cairo_t *cr)
{
g_return_if_fail (EEK_IS_RENDERER(renderer));
EEK_RENDERER_GET_CLASS(renderer)->render_keyboard (renderer, cr);
}
static gboolean static gboolean
sign (EekPoint *p1, EekPoint *p2, EekPoint *p3) sign (EekPoint *p1, EekPoint *p2, EekPoint *p3)
{ {
@ -899,10 +551,5 @@ eek_renderer_get_transformation (EekRenderer *renderer) {
g_return_val_if_fail (EEK_IS_RENDERER(renderer), failed); g_return_val_if_fail (EEK_IS_RENDERER(renderer), failed);
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer); EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
struct transformation ret = { return priv->widget_to_layout;
.origin_x = priv->origin_x,
.origin_y = priv->origin_y,
.scale = priv->scale,
};
return ret;
} }

View File

@ -25,7 +25,6 @@
#include <pango/pangocairo.h> #include <pango/pangocairo.h>
#include "eek-types.h" #include "eek-types.h"
#include "src/layout.h"
G_BEGIN_DECLS G_BEGIN_DECLS
@ -36,15 +35,6 @@ struct _EekRendererClass
{ {
GObjectClass parent_class; GObjectClass parent_class;
void (* render_button) (EekRenderer *self,
cairo_t *cr,
struct button_place *place,
gdouble scale,
gboolean rotate);
void (* render_keyboard) (EekRenderer *self,
cairo_t *cr);
cairo_surface_t *(* get_icon_surface) (EekRenderer *self, cairo_surface_t *(* get_icon_surface) (EekRenderer *self,
const gchar *icon_name, const gchar *icon_name,
gint size, gint size,
@ -62,38 +52,15 @@ void eek_renderer_set_allocation_size
(EekRenderer *renderer, (EekRenderer *renderer,
gdouble width, gdouble width,
gdouble height); gdouble height);
void eek_renderer_get_size (EekRenderer *renderer,
gdouble *width,
gdouble *height);
void eek_renderer_get_button_bounds (EekBounds view_bounds,
struct button_place *button,
EekBounds *bounds,
gboolean rotate);
gdouble eek_renderer_get_scale (EekRenderer *renderer);
void eek_renderer_set_scale_factor (EekRenderer *renderer, void eek_renderer_set_scale_factor (EekRenderer *renderer,
gint scale); gint scale);
void eek_renderer_render_button (EekRenderer *renderer,
cairo_t *cr,
struct button_place *place,
gdouble scale,
gboolean rotate);
cairo_surface_t *eek_renderer_get_icon_surface(const gchar *icon_name, cairo_surface_t *eek_renderer_get_icon_surface(const gchar *icon_name,
gint size, gint size,
gint scale); gint scale);
void eek_renderer_render_keyboard (EekRenderer *renderer, void eek_renderer_render_keyboard (EekRenderer *renderer,
cairo_t *cr); cairo_t *cr);
void eek_renderer_set_border_width (EekRenderer *renderer,
gdouble border_width);
void eek_renderer_apply_transformation_for_button
(cairo_t *cr,
EekBounds view_bounds,
struct button_place *place,
gdouble scale,
gboolean rotate);
struct transformation struct transformation
eek_renderer_get_transformation (EekRenderer *renderer); eek_renderer_get_transformation (EekRenderer *renderer);

View File

@ -88,5 +88,14 @@ struct transformation {
gdouble origin_y; gdouble origin_y;
gdouble scale; gdouble scale;
}; };
struct squeek_button;
struct squeek_row;
/// Represents the path to the button within a view
struct button_place {
const struct squeek_row *row;
const struct squeek_button *button;
};
G_END_DECLS G_END_DECLS
#endif /* EEK_TYPES_H */ #endif /* EEK_TYPES_H */

View File

@ -24,7 +24,6 @@
#include "config.h" #include "config.h"
#include "eek-keyboard.h" #include "eek-keyboard.h"
#include "src/keyboard.h"
#include "src/layout.h" #include "src/layout.h"
#include "eek-xml-layout.h" #include "eek-xml-layout.h"

View File

@ -405,7 +405,7 @@ impl Layout {
let views = HashMap::from_iter( let views = HashMap::from_iter(
self.views.iter().map(|(name, view)| {( self.views.iter().map(|(name, view)| {(
name.clone(), name.clone(),
Box::new(::layout::View { ::layout::View {
bounds: ::layout::c::Bounds { bounds: ::layout::c::Bounds {
x: self.bounds.x, x: self.bounds.x,
y: self.bounds.y, y: self.bounds.y,
@ -413,7 +413,7 @@ impl Layout {
height: self.bounds.height, height: self.bounds.height,
}, },
rows: view.iter().map(|row| { rows: view.iter().map(|row| {
Box::new(::layout::Row { ::layout::Row {
angle: 0, angle: 0,
bounds: None, bounds: None,
buttons: row.split_ascii_whitespace().map(|name| { buttons: row.split_ascii_whitespace().map(|name| {
@ -427,9 +427,9 @@ impl Layout {
&mut warning_handler, &mut warning_handler,
)) ))
}).collect(), }).collect(),
}) }
}).collect(), }).collect(),
}) }
)}) )})
); );

126
src/drawing.rs Normal file
View File

@ -0,0 +1,126 @@
/*! Drawing the UI */
use cairo;
use std::cell::RefCell;
use ::keyboard;
use ::layout::{ Button, Layout };
use ::layout::c::{ EekGtkKeyboard, Point };
use glib::translate::FromGlibPtrNone;
use gtk::WidgetExt;
mod c {
use super::*;
use cairo_sys;
use std::os::raw::c_void;
// This is constructed only in C, no need for warnings
#[allow(dead_code)]
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct EekRenderer(*const c_void);
#[no_mangle]
extern "C" {
// Button and View inside CButtonPlace are safe to pass to C
// as long as they don't outlive the call
// and nothing dereferences them
#[allow(improper_ctypes)]
pub fn eek_render_button(
renderer: EekRenderer,
cr: *mut cairo_sys::cairo_t,
button: *const Button,
pressed: u64,
locked: u64,
);
}
#[no_mangle]
pub extern "C"
fn squeek_layout_draw_all_changed(
layout: *mut Layout,
renderer: EekRenderer,
cr: *mut cairo_sys::cairo_t,
) {
let layout = unsafe { &mut *layout };
let cr = unsafe { cairo::Context::from_raw_none(cr) };
let view = layout.get_current_view();
let view_position = view.bounds.get_position();
for row in &view.rows {
for button in &row.buttons {
let state = RefCell::borrow(&button.state).clone();
if state.pressed == keyboard::PressType::Pressed || state.locked {
let position = &view_position
+ row.bounds.clone().unwrap().get_position()
+ button.bounds.get_position();
render_button_at_position(
renderer, &cr,
position, button.as_ref(),
state.pressed, state.locked,
);
}
}
}
}
#[no_mangle]
pub extern "C"
fn squeek_draw_layout_base_view(
layout: *mut Layout,
renderer: EekRenderer,
cr: *mut cairo_sys::cairo_t,
) {
let layout = unsafe { &mut *layout };
let cr = unsafe { cairo::Context::from_raw_none(cr) };
let view = layout.get_current_view();
let view_position = view.bounds.get_position();
for row in &view.rows {
for button in &row.buttons {
let position = &view_position
+ row.bounds.clone().unwrap().get_position()
+ button.bounds.get_position();
render_button_at_position(
renderer, &cr,
position, button.as_ref(),
keyboard::PressType::Released, false,
);
}
}
}
}
/// Renders a button at a position (button's own bounds ignored)
pub fn render_button_at_position(
renderer: c::EekRenderer,
cr: &cairo::Context,
position: Point,
button: &Button,
pressed: keyboard::PressType,
locked: bool,
) {
cr.save();
cr.translate(position.x, position.y);
cr.rectangle(
0.0, 0.0,
button.bounds.width, button.bounds.height
);
cr.clip();
unsafe {
c::eek_render_button(
renderer,
cairo::Context::to_raw_none(&cr),
button as *const Button,
pressed as u64,
locked as u64,
)
};
cr.restore();
}
pub fn queue_redraw(keyboard: EekGtkKeyboard) {
let widget = unsafe { gtk::Widget::from_glib_none(keyboard.0) };
widget.queue_draw();
}

View File

@ -1,12 +0,0 @@
#ifndef __KEYBOARD_H
#define __KEYBOARD_H
#include "inttypes.h"
#include "stdbool.h"
#include "virtual-keyboard-unstable-v1-client-protocol.h"
struct squeek_key;
uint32_t squeek_key_is_pressed(struct squeek_key *key);
uint32_t squeek_key_is_locked(struct squeek_key *key);
#endif

View File

@ -10,32 +10,8 @@ use ::action::Action;
use std::io::Write; use std::io::Write;
use std::iter::{ FromIterator, IntoIterator }; use std::iter::{ FromIterator, IntoIterator };
use ::util::CloneOwned;
/// Gathers stuff defined in C or called by C #[derive(Debug, Clone, Copy, PartialEq)]
pub mod c {
use super::*;
use ::util::c;
pub type CKeyState = c::Wrapped<KeyState>;
// The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
#[no_mangle]
pub extern "C"
fn squeek_key_is_pressed(key: CKeyState) -> u32 {
//let key = unsafe { Rc::from_raw(key.0) };
return key.clone_owned().pressed as u32;
}
#[no_mangle]
pub extern "C"
fn squeek_key_is_locked(key: CKeyState) -> u32 {
return key.clone_owned().locked as u32;
}
}
#[derive(Debug, Clone, Copy)]
pub enum PressType { pub enum PressType {
Released = 0, Released = 0,
Pressed = 1, Pressed = 1,

View File

@ -5,8 +5,8 @@
#include <glib.h> #include <glib.h>
#include "eek/eek-element.h" #include "eek/eek-element.h"
#include "eek/eek-gtk-keyboard.h" #include "eek/eek-gtk-keyboard.h"
#include "eek/eek-renderer.h"
#include "eek/eek-types.h" #include "eek/eek-types.h"
#include "src/keyboard.h"
#include "virtual-keyboard-unstable-v1-client-protocol.h" #include "virtual-keyboard-unstable-v1-client-protocol.h"
enum squeek_arrangement_kind { enum squeek_arrangement_kind {
@ -14,48 +14,22 @@ enum squeek_arrangement_kind {
ARRANGEMENT_KIND_WIDE = 1, ARRANGEMENT_KIND_WIDE = 1,
}; };
struct squeek_button;
struct squeek_row;
struct squeek_view;
struct squeek_layout; struct squeek_layout;
/// Represents the path to the button within a view
struct button_place {
const struct squeek_row *row;
const struct squeek_button *button;
};
int32_t squeek_row_get_angle(const struct squeek_row*);
EekBounds squeek_row_get_bounds(const struct squeek_row*);
typedef void (*ButtonCallback) (struct squeek_button *button, gpointer user_data);
void squeek_row_foreach(struct squeek_row*,
ButtonCallback callback,
gpointer user_data);
EekBounds squeek_button_get_bounds(const struct squeek_button*); EekBounds squeek_button_get_bounds(const struct squeek_button*);
const char *squeek_button_get_label(const struct squeek_button*); const char *squeek_button_get_label(const struct squeek_button*);
const char *squeek_button_get_icon_name(const struct squeek_button*); const char *squeek_button_get_icon_name(const struct squeek_button*);
const char *squeek_button_get_name(const struct squeek_button*); const char *squeek_button_get_name(const struct squeek_button*);
const char *squeek_button_get_outline_name(const struct squeek_button*); const char *squeek_button_get_outline_name(const struct squeek_button*);
struct squeek_key *squeek_button_get_key(const struct squeek_button*);
uint32_t *squeek_button_has_key(const struct squeek_button* button,
const struct squeek_key *key);
void squeek_button_print(const struct squeek_button* button); void squeek_button_print(const struct squeek_button* button);
struct transformation squeek_layout_calculate_transformation(
EekBounds squeek_view_get_bounds(const struct squeek_view*); const struct squeek_layout *layout,
double allocation_width, double allocation_size);
typedef void (*RowCallback) (struct squeek_row *row, gpointer user_data);
void squeek_view_foreach(struct squeek_view*,
RowCallback callback,
gpointer user_data);
void void
squeek_layout_place_contents(struct squeek_layout*); squeek_layout_place_contents(struct squeek_layout*);
struct squeek_view *squeek_layout_get_current_view(struct squeek_layout*);
struct squeek_layout *squeek_load_layout(const char *name, uint32_t type); struct squeek_layout *squeek_load_layout(const char *name, uint32_t type);
const char *squeek_layout_get_keymap(const struct squeek_layout*); const char *squeek_layout_get_keymap(const struct squeek_layout*);
@ -75,5 +49,6 @@ void squeek_layout_drag(struct squeek_layout *layout, struct zwp_virtual_keyboar
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, EekGtkKeyboard *ui_keyboard); uint32_t timestamp, EekGtkKeyboard *ui_keyboard);
void squeek_layout_draw_all_changed(struct squeek_layout *layout, EekGtkKeyboard *ui_keyboard); void squeek_layout_draw_all_changed(struct squeek_layout *layout, EekRenderer* renderer, cairo_t *cr);
void squeek_draw_layout_base_view(struct squeek_layout *layout, EekRenderer* renderer, cairo_t *cr);
#endif #endif

View File

@ -24,6 +24,7 @@ use std::rc::Rc;
use std::vec::Vec; use std::vec::Vec;
use ::action::Action; use ::action::Action;
use ::drawing;
use ::float_ord::FloatOrd; use ::float_ord::FloatOrd;
use ::keyboard::{ KeyState, PressType }; use ::keyboard::{ KeyState, PressType };
use ::submission::{ Timestamp, VirtualKeyboard }; use ::submission::{ Timestamp, VirtualKeyboard };
@ -34,16 +35,14 @@ use std::borrow::Borrow;
pub mod c { pub mod c {
use super::*; use super::*;
use gtk_sys;
use std::ffi::CStr; use std::ffi::CStr;
use std::os::raw::{ c_char, c_void }; use std::os::raw::{ c_char, c_void };
use std::ptr; use std::ptr;
use gtk_sys;
use std::ops::Add;
// The following defined in C // The following defined in C
#[repr(transparent)]
pub struct UserData(*const c_void);
#[repr(transparent)] #[repr(transparent)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct EekGtkKeyboard(pub *const gtk_sys::GtkWidget); pub struct EekGtkKeyboard(pub *const gtk_sys::GtkWidget);
@ -56,9 +55,26 @@ pub mod c {
pub y: f64, pub y: f64,
} }
impl Add for Point {
type Output = Self;
fn add(self, other: Self) -> Self {
&self + other
}
}
impl Add<Point> for &Point {
type Output = Point;
fn add(self, other: Point) -> Point {
Point {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
/// Defined in eek-types.h /// Defined in eek-types.h
#[repr(C)] #[repr(C)]
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq)]
pub struct Bounds { pub struct Bounds {
pub x: f64, pub x: f64,
pub y: f64, pub y: f64,
@ -66,61 +82,52 @@ pub mod c {
pub height: f64 pub height: f64
} }
type ButtonCallback = unsafe extern "C" fn(button: *mut ::layout::Button, data: *mut UserData); impl Bounds {
type RowCallback = unsafe extern "C" fn(row: *mut ::layout::Row, data: *mut UserData); pub fn get_position(&self) -> Point {
Point {
// The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers x: self.x,
y: self.y,
#[no_mangle] }
pub extern "C" }
fn squeek_view_get_bounds(view: *const ::layout::View) -> Bounds {
unsafe { &*view }.bounds.clone()
} }
#[no_mangle] /// Scale + translate
pub extern "C" #[repr(C)]
fn squeek_view_foreach( pub struct Transformation {
view: *mut ::layout::View, pub origin_x: f64,
callback: RowCallback, pub origin_y: f64,
data: *mut UserData, pub scale: f64,
) { }
let view = unsafe { &mut *view };
for row in view.rows.iter_mut() { impl Transformation {
let row = row.as_mut() as *mut ::layout::Row; fn forward(&self, p: Point) -> Point {
unsafe { callback(row, data) }; Point {
x: (p.x - self.origin_x) / self.scale,
y: (p.y - self.origin_y) / self.scale,
}
}
fn reverse(&self, p: Point) -> Point {
Point {
x: p.x * self.scale + self.origin_x,
y: p.y * self.scale + self.origin_y,
}
}
pub fn reverse_bounds(&self, b: Bounds) -> Bounds {
let start = self.reverse(Point { x: b.x, y: b.y });
let end = self.reverse(Point {
x: b.x + b.width,
y: b.y + b.height,
});
Bounds {
x: start.x,
y: start.y,
width: end.x - start.x,
height: end.y - start.y,
}
} }
} }
#[no_mangle] // The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
pub extern "C"
fn squeek_row_get_angle(row: *const ::layout::Row) -> i32 {
let row = unsafe { &*row };
row.angle
}
#[no_mangle]
pub extern "C"
fn squeek_row_get_bounds(row: *const ::layout::Row) -> Bounds {
let row = unsafe { &*row };
match &row.bounds {
Some(bounds) => bounds.clone(),
None => panic!("Row doesn't have any bounds yet"),
}
}
#[no_mangle]
pub extern "C"
fn squeek_row_foreach(
row: *mut ::layout::Row,
callback: ButtonCallback,
data: *mut UserData,
) {
let row = unsafe { &mut *row };
for button in row.buttons.iter_mut() {
let button = button.as_mut() as *mut ::layout::Button;
unsafe { callback(button, data) };
}
}
#[no_mangle] #[no_mangle]
pub extern "C" pub extern "C"
@ -129,16 +136,6 @@ pub mod c {
button.bounds.clone() button.bounds.clone()
} }
/// Borrow a new reference to key state. Doesn't need freeing
#[no_mangle]
pub extern "C"
fn squeek_button_get_key(
button: *const ::layout::Button
) -> ::keyboard::c::CKeyState {
let button = unsafe { &*button };
::keyboard::c::CKeyState::wrap(button.state.clone())
}
#[no_mangle] #[no_mangle]
pub extern "C" pub extern "C"
fn squeek_button_get_label( fn squeek_button_get_label(
@ -187,14 +184,24 @@ pub mod c {
println!("{:?}", button); println!("{:?}", button);
} }
/// Positions the layout within the available space
#[no_mangle] #[no_mangle]
pub extern "C" pub extern "C"
fn squeek_layout_get_current_view(layout: *const Layout) -> *const View { fn squeek_layout_calculate_transformation(
layout: *const Layout,
allocation_width: f64,
allocation_height: f64,
) -> Transformation {
let layout = unsafe { &*layout }; let layout = unsafe { &*layout };
let view_name = layout.current_view.clone(); let bounds = &layout.get_current_view().bounds;
layout.views.get(&view_name) let h_scale = allocation_width / bounds.width;
.expect("Current view doesn't exist") let v_scale = allocation_height / bounds.height;
.as_ref() as *const View let scale = if h_scale > v_scale { h_scale } else { v_scale };
Transformation {
origin_x: allocation_width - (scale * bounds.width) / 2.0,
origin_y: allocation_height - (scale * bounds.height) / 2.0,
scale: scale,
}
} }
#[no_mangle] #[no_mangle]
@ -239,42 +246,6 @@ pub mod c {
} }
} }
/// Scale + translate
#[repr(C)]
pub struct Transformation {
origin_x: f64,
origin_y: f64,
scale: f64,
}
impl Transformation {
fn forward(&self, p: Point) -> Point {
Point {
x: (p.x - self.origin_x) / self.scale,
y: (p.y - self.origin_y) / self.scale,
}
}
fn reverse(&self, p: Point) -> Point {
Point {
x: p.x * self.scale + self.origin_x,
y: p.y * self.scale + self.origin_y,
}
}
pub fn reverse_bounds(&self, b: Bounds) -> Bounds {
let start = self.reverse(Point { x: b.x, y: b.y });
let end = self.reverse(Point {
x: b.x + b.width,
y: b.y + b.height,
});
Bounds {
x: start.x,
y: start.y,
width: end.x - start.x,
height: end.y - start.y,
}
}
}
// This is constructed only in C, no need for warnings // This is constructed only in C, no need for warnings
#[allow(dead_code)] #[allow(dead_code)]
#[repr(transparent)] #[repr(transparent)]
@ -289,34 +260,6 @@ pub mod c {
origin: Point, origin: Point,
angle: i32 angle: i32
) -> u32; ) -> u32;
// Button and View are safe to pass to C
// as long as they don't outlive the call
// and nothing dereferences them
#[allow(improper_ctypes)]
pub fn eek_gtk_on_button_released(
button: *const Button,
view: *const View,
keyboard: EekGtkKeyboard,
);
// Button and View inside CButtonPlace are safe to pass to C
// as long as they don't outlive the call
// and nothing dereferences them
#[allow(improper_ctypes)]
pub fn eek_gtk_on_button_pressed(
place: CButtonPlace,
keyboard: EekGtkKeyboard,
);
// Button and View inside CButtonPlace are safe to pass to C
// as long as they don't outlive the call
// and nothing dereferences them
#[allow(improper_ctypes)]
pub fn eek_gtk_render_locked_button(
keyboard: EekGtkKeyboard,
place: CButtonPlace,
);
} }
/// Places each button in order, starting from 0 on the left, /// Places each button in order, starting from 0 on the left,
@ -356,15 +299,16 @@ pub mod c {
// because it will be mutated in the loop // because it will be mutated in the loop
for key in layout.pressed_keys.clone() { for key in layout.pressed_keys.clone() {
let key: &Rc<RefCell<KeyState>> = key.borrow(); let key: &Rc<RefCell<KeyState>> = key.borrow();
ui::release_key( seat::handle_release_key(
layout, layout,
&virtual_keyboard, &virtual_keyboard,
&widget_to_layout, &widget_to_layout,
time, time,
ui_keyboard, ui_keyboard,
key key,
); );
} }
drawing::queue_redraw(ui_keyboard);
} }
/// Release all buittons but don't redraw /// Release all buittons but don't redraw
@ -404,26 +348,15 @@ pub mod c {
Point { x: x_widget, y: y_widget } Point { x: x_widget, y: y_widget }
); );
// the immutable reference to `layout` through `view` if let Some(position) = layout.get_button_at_point(point) {
// must be dropped let mut state = position.button.state.clone();
// before `layout.press_key` borrows it mutably again
let state_place = {
let view = layout.get_current_view();
let place = view.find_button_by_position(point);
place.map(|place| {(
place.button.state.clone(),
place.into(),
)})
};
if let Some((mut state, c_place)) = state_place {
layout.press_key( layout.press_key(
&VirtualKeyboard(virtual_keyboard), &VirtualKeyboard(virtual_keyboard),
&mut state, &mut state,
Timestamp(time), Timestamp(time),
); );
// maybe TODO: draw on the display buffer here
unsafe { eek_gtk_on_button_pressed(c_place, ui_keyboard) }; drawing::queue_redraw(ui_keyboard);
} }
} }
@ -449,23 +382,26 @@ pub mod c {
); );
let pressed = layout.pressed_keys.clone(); let pressed = layout.pressed_keys.clone();
let state_place = { let button_info = {
let view = layout.get_current_view(); let view = layout.get_current_view();
let place = view.find_button_by_position(point); let place = view.find_button_by_position(point);
place.map(|place| {( place.map(|place| {(
place.button.state.clone(), place.button.state.clone(),
place.into(), place.button.clone(),
view.bounds.get_position()
+ place.row.bounds.clone().unwrap().get_position()
+ place.button.bounds.get_position(),
)}) )})
}; };
if let Some((mut state, c_place)) = state_place { if let Some((mut state, _button, _view_position)) = button_info {
let mut found = false; let mut found = false;
for wrapped_key in pressed { for wrapped_key in pressed {
let key: &Rc<RefCell<KeyState>> = wrapped_key.borrow(); let key: &Rc<RefCell<KeyState>> = wrapped_key.borrow();
if Rc::ptr_eq(&state, &wrapped_key.0) { if Rc::ptr_eq(&state, &wrapped_key.0) {
found = true; found = true;
} else { } else {
ui::release_key( seat::handle_release_key(
layout, layout,
&virtual_keyboard, &virtual_keyboard,
&widget_to_layout, &widget_to_layout,
@ -477,12 +413,12 @@ pub mod c {
} }
if !found { if !found {
layout.press_key(&virtual_keyboard, &mut state, time); layout.press_key(&virtual_keyboard, &mut state, time);
unsafe { eek_gtk_on_button_pressed(c_place, ui_keyboard) }; // maybe TODO: draw on the display buffer here
} }
} else { } else {
for wrapped_key in pressed { for wrapped_key in pressed {
let key: &Rc<RefCell<KeyState>> = wrapped_key.borrow(); let key: &Rc<RefCell<KeyState>> = wrapped_key.borrow();
ui::release_key( seat::handle_release_key(
layout, layout,
&virtual_keyboard, &virtual_keyboard,
&widget_to_layout, &widget_to_layout,
@ -492,33 +428,7 @@ pub mod c {
); );
} }
} }
} drawing::queue_redraw(ui_keyboard);
#[no_mangle]
pub extern "C"
fn squeek_layout_draw_all_changed(
layout: *mut Layout,
ui_keyboard: EekGtkKeyboard,
) {
let layout = unsafe { &mut *layout };
for row in &layout.get_current_view().rows {
for button in &row.buttons {
let c_place = CButtonPlace::from(
ButtonPlace { row, button }
);
let state = RefCell::borrow(&button.state);
match (state.pressed, state.locked) {
(PressType::Released, false) => {}
(PressType::Pressed, _) => unsafe {
eek_gtk_on_button_pressed(c_place, ui_keyboard)
},
(_, true) => unsafe {
eek_gtk_render_locked_button(ui_keyboard, c_place)
},
}
}
}
} }
#[cfg(test)] #[cfg(test)]
@ -545,6 +455,12 @@ pub mod c {
} }
} }
/// Relative to `View`
struct ButtonPosition {
view_position: c::Point,
button: Button,
}
pub struct ButtonPlace<'a> { pub struct ButtonPlace<'a> {
button: &'a Button, button: &'a Button,
row: &'a Row, row: &'a Row,
@ -654,7 +570,7 @@ pub struct Spacing {
pub struct View { pub struct View {
/// Position relative to keyboard origin /// Position relative to keyboard origin
pub bounds: c::Bounds, pub bounds: c::Bounds,
pub rows: Vec<Box<Row>>, pub rows: Vec<Row>,
} }
impl View { impl View {
@ -749,7 +665,7 @@ pub struct Layout {
// Views own the actual buttons which have state // Views own the actual buttons which have state
// Maybe they should own UI only, // Maybe they should own UI only,
// and keys should be owned by a dedicated non-UI-State? // and keys should be owned by a dedicated non-UI-State?
pub views: HashMap<String, Box<View>>, pub views: HashMap<String, View>,
// Non-UI stuff // Non-UI stuff
/// xkb keymap applicable to the contained keys. Unchangeable /// xkb keymap applicable to the contained keys. Unchangeable
@ -768,7 +684,7 @@ pub struct Layout {
/// A builder structure for picking up layout data from storage /// A builder structure for picking up layout data from storage
pub struct LayoutData { pub struct LayoutData {
pub views: HashMap<String, Box<View>>, pub views: HashMap<String, View>,
pub keymap_str: CString, pub keymap_str: CString,
} }
@ -789,7 +705,8 @@ impl Layout {
locked_keys: HashSet::new(), locked_keys: HashSet::new(),
} }
} }
fn get_current_view(&self) -> &Box<View> {
pub fn get_current_view(&self) -> &View {
self.views.get(&self.current_view).expect("Selected nonexistent view") self.views.get(&self.current_view).expect("Selected nonexistent view")
} }
@ -881,12 +798,24 @@ impl Layout {
}; };
}; };
} }
fn get_button_at_point(&self, point: c::Point) -> Option<ButtonPosition> {
let view = self.get_current_view();
let place = view.find_button_by_position(point);
place.map(|place| ButtonPosition {
button: place.button.clone(),
// Rows have no business being inside a view
// if they have no valid bounds.
view_position: place.row.bounds.clone().unwrap().get_position()
+ place.button.bounds.get_position(),
})
}
} }
mod procedures { mod procedures {
use super::*; use super::*;
type Path<'v> = (&'v Box<Row>, &'v Box<Button>); type Path<'v> = (&'v Row, &'v Box<Button>);
/// Finds all `(row, button)` paths that refer to the specified key `state` /// Finds all `(row, button)` paths that refer to the specified key `state`
pub fn find_key_paths<'v, 's>( pub fn find_key_paths<'v, 's>(
@ -918,24 +847,6 @@ mod procedures {
}) == 1 }) == 1
} }
/// Switch off all UI buttons associated with the (state) key
pub fn release_ui_buttons(
view: &Box<View>,
key: &Rc<RefCell<KeyState>>,
ui_keyboard: c::EekGtkKeyboard,
) {
let paths = ::layout::procedures::find_key_paths(&view, key);
for (_row, button) in paths {
unsafe {
c::procedures::eek_gtk_on_button_released(
button.as_ref() as *const Button,
view.as_ref() as *const View,
ui_keyboard,
);
};
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
@ -957,12 +868,15 @@ mod procedures {
let button = make_button_with_state("1".into(), state); let button = make_button_with_state("1".into(), state);
let button_ptr = as_ptr(&button); let button_ptr = as_ptr(&button);
let row = Box::new(Row { let row_bounds = Some(c::Bounds {
x: 0.1, y: 2.3,
width: 4.5, height: 6.7,
});
let row = Row {
buttons: vec!(button), buttons: vec!(button),
angle: 0, angle: 0,
bounds: None bounds: row_bounds.clone(),
}); };
let row_ptr = as_ptr(&row);
let view = View { let view = View {
bounds: c::Bounds { bounds: c::Bounds {
@ -974,10 +888,10 @@ mod procedures {
assert_eq!( assert_eq!(
find_key_paths(&view, &state_clone.clone()).iter() find_key_paths(&view, &state_clone.clone()).iter()
.map(|(row, button)| { (as_ptr(row), as_ptr(button)) }) .map(|(row, button)| { (row.bounds.clone(), as_ptr(button)) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
vec!( vec!(
(row_ptr, button_ptr) (row_bounds, button_ptr)
) )
); );
@ -1012,15 +926,15 @@ mod procedures {
} }
} }
/// Top level UI procedures /// Top level procedures, dispatching to everything
mod ui { mod seat {
use super::*; use super::*;
// TODO: turn into release_button // TODO: turn into release_button
pub fn release_key( pub fn handle_release_key(
layout: &mut Layout, layout: &mut Layout,
virtual_keyboard: &VirtualKeyboard, virtual_keyboard: &VirtualKeyboard,
widget_to_layout: &c::procedures::Transformation, widget_to_layout: &c::Transformation,
time: Timestamp, time: Timestamp,
ui_keyboard: c::EekGtkKeyboard, ui_keyboard: c::EekGtkKeyboard,
key: &Rc<RefCell<KeyState>>, key: &Rc<RefCell<KeyState>>,
@ -1049,8 +963,6 @@ mod ui {
); );
} }
} }
procedures::release_ui_buttons(view, key, ui_keyboard);
} }
} }

View File

@ -1,5 +1,8 @@
#[macro_use] #[macro_use]
extern crate bitflags; extern crate bitflags;
extern crate cairo;
extern crate cairo_sys;
extern crate gdk;
extern crate gio; extern crate gio;
extern crate glib; extern crate glib;
extern crate glib_sys; extern crate glib_sys;
@ -14,6 +17,7 @@ extern crate xkbcommon;
mod action; mod action;
pub mod data; pub mod data;
mod drawing;
pub mod float_ord; pub mod float_ord;
pub mod imservice; pub mod imservice;
mod keyboard; mod keyboard;