presses: Move press handling to Rust
This fixes some rendering things which would happen with multiple state-sharing buttons. It also removes some interfaces exposing rows, views, layouts, and buttons, bringing the code closer to removing them from the FFI entirely.
This commit is contained in:
		@ -37,6 +37,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "eek-gtk-keyboard.h"
 | 
					#include "eek-gtk-keyboard.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "eekboard/eekboard-context-service.h"
 | 
				
			||||||
 | 
					#include "src/layout.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum {
 | 
					enum {
 | 
				
			||||||
    PROP_0,
 | 
					    PROP_0,
 | 
				
			||||||
    PROP_LAST
 | 
					    PROP_LAST
 | 
				
			||||||
@ -58,14 +61,7 @@ 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       on_button_pressed          (struct squeek_button *button, struct squeek_view *view,
 | 
					 | 
				
			||||||
                                              EekGtkKeyboard *self);
 | 
					 | 
				
			||||||
static void       on_button_released         (const struct squeek_button *button,
 | 
					 | 
				
			||||||
                                              struct squeek_view *view,
 | 
					 | 
				
			||||||
                                              EekGtkKeyboard *self);
 | 
					 | 
				
			||||||
static void       render_pressed_button      (GtkWidget *widget, struct button_place *place);
 | 
					static void       render_pressed_button      (GtkWidget *widget, struct button_place *place);
 | 
				
			||||||
static void       render_locked_button       (GtkWidget *widget,
 | 
					 | 
				
			||||||
                                              struct button_place *place);
 | 
					 | 
				
			||||||
static void       render_released_button     (GtkWidget *widget,
 | 
					static void       render_released_button     (GtkWidget *widget,
 | 
				
			||||||
                                              const struct squeek_button *button);
 | 
					                                              const struct squeek_button *button);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -105,30 +101,10 @@ 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
 | 
				
			||||||
    struct squeek_view *view = squeek_layout_get_current_view(priv->keyboard->layout);
 | 
					    squeek_layout_draw_all_changed(priv->keyboard->layout, EEK_GTK_KEYBOARD(self));
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* redraw pressed key */
 | 
					 | 
				
			||||||
    const GList *list = priv->keyboard->pressed_keys;
 | 
					 | 
				
			||||||
    for (const GList *head = list; head; head = g_list_next (head)) {
 | 
					 | 
				
			||||||
        struct button_place place = squeek_view_find_key(
 | 
					 | 
				
			||||||
            view, head->data
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        if (place.button)
 | 
					 | 
				
			||||||
            render_pressed_button (self, &place);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* redraw locked key */
 | 
					 | 
				
			||||||
    list = priv->keyboard->locked_keys;
 | 
					 | 
				
			||||||
    for (const GList *head = list; head; head = g_list_next (head)) {
 | 
					 | 
				
			||||||
        struct button_place place = squeek_view_find_key(
 | 
					 | 
				
			||||||
            view, (struct squeek_key *)head->data
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        if (place.button)
 | 
					 | 
				
			||||||
            render_locked_button (self, &place);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return FALSE;
 | 
					    return FALSE;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -152,84 +128,24 @@ static void depress(EekGtkKeyboard *self,
 | 
				
			|||||||
                    gdouble x, gdouble y, guint32 time)
 | 
					                    gdouble x, gdouble y, guint32 time)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
 | 
					    EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
 | 
				
			||||||
    struct squeek_view *view = level_keyboard_current(priv->keyboard);
 | 
					 | 
				
			||||||
    struct squeek_button *button = eek_renderer_find_button_by_position (priv->renderer, view, x, y);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (button) {
 | 
					    squeek_layout_depress(priv->keyboard->layout, priv->keyboard->manager->virtual_keyboard,
 | 
				
			||||||
        eek_keyboard_press_key(
 | 
					                          x, y, eek_renderer_get_transformation(priv->renderer), time, self);
 | 
				
			||||||
            priv->keyboard,
 | 
					 | 
				
			||||||
            squeek_button_get_key(button),
 | 
					 | 
				
			||||||
            time
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        on_button_pressed(button, view, self);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void drag(EekGtkKeyboard *self,
 | 
					static void drag(EekGtkKeyboard *self,
 | 
				
			||||||
                 gdouble x, gdouble y, guint32 time)
 | 
					                 gdouble x, gdouble y, guint32 time)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
 | 
					    EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
 | 
				
			||||||
    struct squeek_view *view = level_keyboard_current(priv->keyboard);
 | 
					    squeek_layout_drag(priv->keyboard->layout, priv->keyboard->manager->virtual_keyboard,
 | 
				
			||||||
    struct squeek_button *button = eek_renderer_find_button_by_position (priv->renderer, view, x, y);
 | 
					                       x, y, eek_renderer_get_transformation(priv->renderer), time, self);
 | 
				
			||||||
    GList *list, *head;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    list = g_list_copy(priv->keyboard->pressed_keys);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (button) {
 | 
					 | 
				
			||||||
        gboolean found = FALSE;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (head = list; head; head = g_list_next (head)) {
 | 
					 | 
				
			||||||
            struct squeek_key *key = head->data;
 | 
					 | 
				
			||||||
            if (squeek_button_has_key(button, key)) {
 | 
					 | 
				
			||||||
                found = TRUE;
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                eek_keyboard_release_key(priv->keyboard, key, time);
 | 
					 | 
				
			||||||
                // The released handler proceeds to ignore this info...
 | 
					 | 
				
			||||||
                // let's do this for consistency nevertheless
 | 
					 | 
				
			||||||
                struct button_place place = squeek_view_find_key(view, key);
 | 
					 | 
				
			||||||
                on_button_released(place.button, view, self);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        g_list_free (list);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (!found) {
 | 
					 | 
				
			||||||
            eek_keyboard_press_key(
 | 
					 | 
				
			||||||
                priv->keyboard,
 | 
					 | 
				
			||||||
                squeek_button_get_key(button),
 | 
					 | 
				
			||||||
                time
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            on_button_pressed(button, view, self);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        for (head = list; head; head = g_list_next (head)) {
 | 
					 | 
				
			||||||
            struct squeek_key *key = head->data;
 | 
					 | 
				
			||||||
            eek_keyboard_release_key(priv->keyboard, key, time);
 | 
					 | 
				
			||||||
            // The released handler proceeds to ignore this info...
 | 
					 | 
				
			||||||
            // let's do this for consistency nevertheless
 | 
					 | 
				
			||||||
            struct button_place place = squeek_view_find_key(view, key);
 | 
					 | 
				
			||||||
            on_button_released(place.button, view, self);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        g_list_free (list);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void release(EekGtkKeyboard *self, guint32 time)
 | 
					static void release(EekGtkKeyboard *self, guint32 time)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
 | 
					    EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct squeek_view *view = level_keyboard_current(priv->keyboard);
 | 
					    squeek_layout_release(priv->keyboard->layout, priv->keyboard->manager->virtual_keyboard, time, self);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    GList *list = g_list_copy(priv->keyboard->pressed_keys);
 | 
					 | 
				
			||||||
    for (GList *head = list; head; head = g_list_next (head)) {
 | 
					 | 
				
			||||||
        struct squeek_key *key = head->data;
 | 
					 | 
				
			||||||
        eek_keyboard_release_key(priv->keyboard, key, time);
 | 
					 | 
				
			||||||
        // The released handler proceeds to ignore this info...
 | 
					 | 
				
			||||||
        // let's do this for consistency nevertheless
 | 
					 | 
				
			||||||
        struct button_place place = squeek_view_find_key(view, key);
 | 
					 | 
				
			||||||
        on_button_released(place.button, view, self);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    g_list_free (list);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static gboolean
 | 
					static gboolean
 | 
				
			||||||
@ -304,15 +220,9 @@ eek_gtk_keyboard_real_unmap (GtkWidget *self)
 | 
				
			|||||||
	    eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
 | 
						    eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (priv->keyboard) {
 | 
					    if (priv->keyboard) {
 | 
				
			||||||
        GList *head;
 | 
					        squeek_layout_release_all_only(
 | 
				
			||||||
 | 
					            priv->keyboard->layout, priv->keyboard->manager->virtual_keyboard,
 | 
				
			||||||
        for (head = priv->keyboard->pressed_keys; head; head = g_list_next (head)) {
 | 
					            gdk_event_get_time(NULL));
 | 
				
			||||||
            /* Unlike other places where we call this, we don't call
 | 
					 | 
				
			||||||
               on_button_released afterwards since we don't want to queue a
 | 
					 | 
				
			||||||
               redraw. */
 | 
					 | 
				
			||||||
            eek_keyboard_release_key(priv->keyboard, head->data,
 | 
					 | 
				
			||||||
                                        gdk_event_get_time(NULL));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    GTK_WIDGET_CLASS (eek_gtk_keyboard_parent_class)->unmap (self);
 | 
					    GTK_WIDGET_CLASS (eek_gtk_keyboard_parent_class)->unmap (self);
 | 
				
			||||||
@ -344,13 +254,9 @@ eek_gtk_keyboard_dispose (GObject *object)
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (priv->keyboard) {
 | 
					    if (priv->keyboard) {
 | 
				
			||||||
        GList *head;
 | 
					        squeek_layout_release_all_only(
 | 
				
			||||||
 | 
					            priv->keyboard->layout, priv->keyboard->manager->virtual_keyboard,
 | 
				
			||||||
        for (head = priv->keyboard->pressed_keys; head; head = g_list_next (head)) {
 | 
					            gdk_event_get_time(NULL));
 | 
				
			||||||
            eek_keyboard_release_key(priv->keyboard, head->data,
 | 
					 | 
				
			||||||
                                        gdk_event_get_time(NULL));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        priv->keyboard = NULL;
 | 
					        priv->keyboard = NULL;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -420,18 +326,17 @@ render_pressed_button (GtkWidget *widget,
 | 
				
			|||||||
    cairo_region_destroy (region);
 | 
					    cairo_region_destroy (region);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					void
 | 
				
			||||||
render_locked_button (GtkWidget *widget, struct button_place *place)
 | 
					eek_gtk_render_locked_button (EekGtkKeyboard *self, 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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    GdkWindow         *window  = gtk_widget_get_window (widget);
 | 
					    GdkWindow         *window  = gtk_widget_get_window (GTK_WIDGET(self));
 | 
				
			||||||
    cairo_region_t    *region  = gdk_window_get_clip_region (window);
 | 
					    cairo_region_t    *region  = gdk_window_get_clip_region (window);
 | 
				
			||||||
    GdkDrawingContext *context = gdk_window_begin_draw_frame (window, region);
 | 
					    GdkDrawingContext *context = gdk_window_begin_draw_frame (window, region);
 | 
				
			||||||
    cairo_t           *cr      = gdk_drawing_context_get_cairo_context (context);
 | 
					    cairo_t           *cr      = gdk_drawing_context_get_cairo_context (context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    eek_renderer_render_button (priv->renderer, cr, place, 1.0, TRUE);
 | 
					    eek_renderer_render_button (priv->renderer, cr, &place, 1.0, TRUE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    gdk_window_end_draw_frame (window, context);
 | 
					    gdk_window_end_draw_frame (window, context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -459,9 +364,8 @@ render_released_button (GtkWidget *widget,
 | 
				
			|||||||
    cairo_region_destroy (region);
 | 
					    cairo_region_destroy (region);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					void
 | 
				
			||||||
on_button_pressed (struct squeek_button *button,
 | 
					eek_gtk_on_button_pressed (struct button_place place,
 | 
				
			||||||
                   struct squeek_view *view,
 | 
					 | 
				
			||||||
                   EekGtkKeyboard *self)
 | 
					                   EekGtkKeyboard *self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
 | 
					    EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
 | 
				
			||||||
@ -470,10 +374,6 @@ on_button_pressed (struct squeek_button *button,
 | 
				
			|||||||
    if (!priv->renderer)
 | 
					    if (!priv->renderer)
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct button_place place = {
 | 
					 | 
				
			||||||
        .button = button,
 | 
					 | 
				
			||||||
        .row = squeek_view_get_row(view, button),
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    if (!place.row) {
 | 
					    if (!place.row) {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -489,8 +389,8 @@ on_button_pressed (struct squeek_button *button,
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					void
 | 
				
			||||||
on_button_released (const struct squeek_button *button,
 | 
					eek_gtk_on_button_released (const struct squeek_button *button,
 | 
				
			||||||
                    struct squeek_view *view,
 | 
					                    struct squeek_view *view,
 | 
				
			||||||
                    EekGtkKeyboard *self)
 | 
					                    EekGtkKeyboard *self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
				
			|||||||
@ -28,7 +28,7 @@
 | 
				
			|||||||
#include <glib.h>
 | 
					#include <glib.h>
 | 
				
			||||||
#include <gtk/gtk.h>
 | 
					#include <gtk/gtk.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "eek-keyboard.h"
 | 
					typedef struct _LevelKeyboard LevelKeyboard; // including causes weird bugs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
G_BEGIN_DECLS
 | 
					G_BEGIN_DECLS
 | 
				
			||||||
#define EEK_TYPE_GTK_KEYBOARD (eek_gtk_keyboard_get_type())
 | 
					#define EEK_TYPE_GTK_KEYBOARD (eek_gtk_keyboard_get_type())
 | 
				
			||||||
 | 
				
			|||||||
@ -38,65 +38,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "eek-keyboard.h"
 | 
					#include "eek-keyboard.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void
 | 
					 | 
				
			||||||
eek_keyboard_set_key_locked (LevelKeyboard    *keyboard,
 | 
					 | 
				
			||||||
                            struct squeek_key *key)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    keyboard->locked_keys =
 | 
					 | 
				
			||||||
            g_list_prepend (keyboard->locked_keys, key);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Unlock all locked keys.
 | 
					 | 
				
			||||||
/// All locked keys will unlock at the next keypress (should be called "stuck")
 | 
					 | 
				
			||||||
/// Returns the number of handled keys
 | 
					 | 
				
			||||||
/// TODO: may need to check key type in order to chain locks
 | 
					 | 
				
			||||||
/// before pressing an "emitting" key
 | 
					 | 
				
			||||||
static int unlock_keys(LevelKeyboard *keyboard) {
 | 
					 | 
				
			||||||
    int handled = 0;
 | 
					 | 
				
			||||||
    for (GList *head = keyboard->locked_keys; head; ) {
 | 
					 | 
				
			||||||
        struct squeek_key *key = head->data;
 | 
					 | 
				
			||||||
        GList *next = g_list_next (head);
 | 
					 | 
				
			||||||
        keyboard->locked_keys =
 | 
					 | 
				
			||||||
                g_list_remove_link (keyboard->locked_keys, head);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        squeek_layout_set_state_from_press(keyboard->layout, keyboard, key);
 | 
					 | 
				
			||||||
        g_list_free1 (head);
 | 
					 | 
				
			||||||
        head = next;
 | 
					 | 
				
			||||||
        handled++;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return handled;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
set_level_from_press (LevelKeyboard *keyboard, struct squeek_key *key)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    // If the currently locked key was already handled in the unlock phase,
 | 
					 | 
				
			||||||
    // then skip
 | 
					 | 
				
			||||||
    if (unlock_keys(keyboard) == 0) {
 | 
					 | 
				
			||||||
        squeek_layout_set_state_from_press(keyboard->layout, keyboard, key);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void eek_keyboard_press_key(LevelKeyboard *keyboard, struct squeek_key *key, guint32 timestamp) {
 | 
					 | 
				
			||||||
    keyboard->pressed_keys = g_list_prepend (keyboard->pressed_keys, key);
 | 
					 | 
				
			||||||
    squeek_key_press(key, keyboard->manager->virtual_keyboard, KEY_PRESS, timestamp);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void eek_keyboard_release_key(LevelKeyboard *keyboard,
 | 
					 | 
				
			||||||
                              struct squeek_key *key,
 | 
					 | 
				
			||||||
                              guint32 timestamp) {
 | 
					 | 
				
			||||||
    for (GList *head = keyboard->pressed_keys; head; head = g_list_next (head)) {
 | 
					 | 
				
			||||||
        if (squeek_key_equal(head->data, key)) {
 | 
					 | 
				
			||||||
            keyboard->pressed_keys = g_list_remove_link (keyboard->pressed_keys, head);
 | 
					 | 
				
			||||||
            g_list_free1 (head);
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    set_level_from_press (keyboard, key);
 | 
					 | 
				
			||||||
    squeek_key_press(key, keyboard->manager->virtual_keyboard, KEY_RELEASE, timestamp);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void level_keyboard_deinit(LevelKeyboard *self) {
 | 
					void level_keyboard_deinit(LevelKeyboard *self) {
 | 
				
			||||||
    squeek_layout_free(self->layout);
 | 
					    squeek_layout_free(self->layout);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -35,29 +35,17 @@ G_BEGIN_DECLS
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/// Keyboard state holder
 | 
					/// Keyboard state holder
 | 
				
			||||||
struct _LevelKeyboard {
 | 
					struct _LevelKeyboard {
 | 
				
			||||||
    struct squeek_layout *layout;
 | 
					    struct squeek_layout *layout; // owned
 | 
				
			||||||
    struct xkb_keymap *keymap;
 | 
					    struct xkb_keymap *keymap;
 | 
				
			||||||
    int keymap_fd; // keymap formatted as XKB string
 | 
					    int keymap_fd; // keymap formatted as XKB string
 | 
				
			||||||
    size_t keymap_len; // length of the data inside keymap_fd
 | 
					    size_t keymap_len; // length of the data inside keymap_fd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    GList *pressed_keys; // struct squeek_key*
 | 
					 | 
				
			||||||
    GList *locked_keys; // struct squeek_key*
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    guint id; // as a key to layout choices
 | 
					    guint id; // as a key to layout choices
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    EekboardContextService *manager; // unowned reference
 | 
					    EekboardContextService *manager; // unowned reference
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
typedef struct _LevelKeyboard LevelKeyboard;
 | 
					typedef struct _LevelKeyboard LevelKeyboard;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Represents the path to the button within a view
 | 
					 | 
				
			||||||
struct button_place {
 | 
					 | 
				
			||||||
    const struct squeek_row *row;
 | 
					 | 
				
			||||||
    const struct squeek_button *button;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void eek_keyboard_press_key(LevelKeyboard *keyboard, struct squeek_key *key, guint32 timestamp);
 | 
					 | 
				
			||||||
void eek_keyboard_release_key(LevelKeyboard *keyboard, struct squeek_key *key, guint32 timestamp);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
gchar *             eek_keyboard_get_keymap
 | 
					gchar *             eek_keyboard_get_keymap
 | 
				
			||||||
                                     (LevelKeyboard *keyboard);
 | 
					                                     (LevelKeyboard *keyboard);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -24,6 +24,7 @@
 | 
				
			|||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
#include <gdk-pixbuf/gdk-pixbuf.h>
 | 
					#include <gdk-pixbuf/gdk-pixbuf.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "eek-keyboard.h"
 | 
				
			||||||
#include "eek-renderer.h"
 | 
					#include "eek-renderer.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum {
 | 
					enum {
 | 
				
			||||||
@ -454,8 +455,8 @@ eek_renderer_real_render_button (EekRenderer *self,
 | 
				
			|||||||
    struct squeek_key *key = squeek_button_get_key(place->button);
 | 
					    struct squeek_key *key = squeek_button_get_key(place->button);
 | 
				
			||||||
    render_button (
 | 
					    render_button (
 | 
				
			||||||
                self, cr, place,
 | 
					                self, cr, place,
 | 
				
			||||||
                squeek_key_is_pressed(key),
 | 
					                squeek_key_is_pressed(key) != 0,
 | 
				
			||||||
                squeek_key_is_locked (key)
 | 
					                squeek_key_is_locked (key) != 0
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    cairo_restore (cr);
 | 
					    cairo_restore (cr);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -959,29 +960,16 @@ eek_are_bounds_inside (EekBounds bounds, EekPoint point, EekPoint origin, int32_
 | 
				
			|||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					struct transformation
 | 
				
			||||||
 * eek_renderer_find_key_by_position:
 | 
					eek_renderer_get_transformation (EekRenderer *renderer) {
 | 
				
			||||||
 * @renderer: The renderer normally used to render the key
 | 
					    struct transformation failed = {0};
 | 
				
			||||||
 * @x: The horizontal widget coordinate of the position to test for a key
 | 
					    g_return_val_if_fail (EEK_IS_RENDERER(renderer), failed);
 | 
				
			||||||
 * @y: The vertical widget coordinate of the position to test for a key
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Return value: the key located at the position x, y in widget coordinates, or
 | 
					 | 
				
			||||||
 *   NULL if no key can be found at that location
 | 
					 | 
				
			||||||
 **/
 | 
					 | 
				
			||||||
struct squeek_button *
 | 
					 | 
				
			||||||
eek_renderer_find_button_by_position (EekRenderer *renderer,
 | 
					 | 
				
			||||||
                                      struct squeek_view *view,
 | 
					 | 
				
			||||||
                                      gdouble      x,
 | 
					 | 
				
			||||||
                                      gdouble      y)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    g_return_val_if_fail (EEK_IS_RENDERER(renderer), NULL);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
 | 
					    EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
 | 
				
			||||||
 | 
					    struct transformation ret = {
 | 
				
			||||||
    /* Transform from widget coordinates to keyboard coordinates */
 | 
					        .origin_x = priv->origin_x,
 | 
				
			||||||
    EekPoint point = {
 | 
					        .origin_y = priv->origin_y,
 | 
				
			||||||
        .x = (x - priv->origin_x)/priv->scale,
 | 
					        .scale = priv->scale,
 | 
				
			||||||
        .y = (y - priv->origin_y)/priv->scale,
 | 
					 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    return squeek_view_find_button_by_position(view, point);
 | 
					    return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -24,8 +24,8 @@
 | 
				
			|||||||
#include <gtk/gtk.h>
 | 
					#include <gtk/gtk.h>
 | 
				
			||||||
#include <pango/pangocairo.h>
 | 
					#include <pango/pangocairo.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "eek-keyboard.h"
 | 
					 | 
				
			||||||
#include "eek-types.h"
 | 
					#include "eek-types.h"
 | 
				
			||||||
 | 
					#include "src/layout.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
G_BEGIN_DECLS
 | 
					G_BEGIN_DECLS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -102,14 +102,14 @@ void             eek_renderer_get_foreground_color
 | 
				
			|||||||
                                                EekColor        *color);
 | 
					                                                EekColor        *color);
 | 
				
			||||||
void             eek_renderer_set_border_width (EekRenderer     *renderer,
 | 
					void             eek_renderer_set_border_width (EekRenderer     *renderer,
 | 
				
			||||||
                                                gdouble          border_width);
 | 
					                                                gdouble          border_width);
 | 
				
			||||||
struct squeek_button *eek_renderer_find_button_by_position(EekRenderer     *renderer, struct squeek_view *view,
 | 
					 | 
				
			||||||
                                                gdouble          x,
 | 
					 | 
				
			||||||
                                                gdouble          y);
 | 
					 | 
				
			||||||
void             eek_renderer_apply_transformation_for_button
 | 
					void             eek_renderer_apply_transformation_for_button
 | 
				
			||||||
                                               (EekRenderer     *renderer,
 | 
					                                               (EekRenderer     *renderer,
 | 
				
			||||||
                                                cairo_t         *cr, struct button_place *place,
 | 
					                                                cairo_t         *cr, struct button_place *place,
 | 
				
			||||||
                                                gdouble          scale,
 | 
					                                                gdouble          scale,
 | 
				
			||||||
                                                gboolean         rotate);
 | 
					                                                gboolean         rotate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct transformation
 | 
				
			||||||
 | 
					eek_renderer_get_transformation (EekRenderer *renderer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
G_END_DECLS
 | 
					G_END_DECLS
 | 
				
			||||||
#endif  /* EEK_RENDERER_H */
 | 
					#endif  /* EEK_RENDERER_H */
 | 
				
			||||||
 | 
				
			|||||||
@ -112,5 +112,10 @@ EekColor *eek_color_new      (gdouble         red,
 | 
				
			|||||||
EekColor *eek_color_copy     (const EekColor *color);
 | 
					EekColor *eek_color_copy     (const EekColor *color);
 | 
				
			||||||
void      eek_color_free     (EekColor       *color);
 | 
					void      eek_color_free     (EekColor       *color);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct transformation {
 | 
				
			||||||
 | 
					    gdouble origin_x;
 | 
				
			||||||
 | 
					    gdouble origin_y;
 | 
				
			||||||
 | 
					    gdouble scale;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
G_END_DECLS
 | 
					G_END_DECLS
 | 
				
			||||||
#endif  /* EEK_TYPES_H */
 | 
					#endif  /* EEK_TYPES_H */
 | 
				
			||||||
 | 
				
			|||||||
@ -14,7 +14,7 @@ use std::vec::Vec;
 | 
				
			|||||||
use xkbcommon::xkb;
 | 
					use xkbcommon::xkb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use ::keyboard::{
 | 
					use ::keyboard::{
 | 
				
			||||||
    KeyState,
 | 
					    KeyState, PressType,
 | 
				
			||||||
    generate_keymap, generate_keycodes, FormattingError
 | 
					    generate_keymap, generate_keycodes, FormattingError
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use ::resources;
 | 
					use ::resources;
 | 
				
			||||||
@ -346,7 +346,7 @@ impl Layout {
 | 
				
			|||||||
            (
 | 
					            (
 | 
				
			||||||
                name.into(),
 | 
					                name.into(),
 | 
				
			||||||
                KeyState {
 | 
					                KeyState {
 | 
				
			||||||
                    pressed: false,
 | 
					                    pressed: PressType::Released,
 | 
				
			||||||
                    locked: false,
 | 
					                    locked: false,
 | 
				
			||||||
                    keycodes,
 | 
					                    keycodes,
 | 
				
			||||||
                    action,
 | 
					                    action,
 | 
				
			||||||
@ -407,6 +407,8 @@ impl Layout {
 | 
				
			|||||||
                CString::new(keymap_str)
 | 
					                CString::new(keymap_str)
 | 
				
			||||||
                    .expect("Invalid keymap string generated")
 | 
					                    .expect("Invalid keymap string generated")
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
 | 
					            locked_keys: HashSet::new(),
 | 
				
			||||||
 | 
					            pressed_keys: HashSet::new(),
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -9,13 +9,4 @@ struct squeek_key;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
uint32_t squeek_key_is_pressed(struct squeek_key *key);
 | 
					uint32_t squeek_key_is_pressed(struct squeek_key *key);
 | 
				
			||||||
uint32_t squeek_key_is_locked(struct squeek_key *key);
 | 
					uint32_t squeek_key_is_locked(struct squeek_key *key);
 | 
				
			||||||
void squeek_key_set_locked(struct squeek_key *key, uint32_t pressed);
 | 
					 | 
				
			||||||
uint32_t squeek_key_equal(struct squeek_key* key, struct squeek_key* key1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
enum key_press {
 | 
					 | 
				
			||||||
    KEY_RELEASE = 0,
 | 
					 | 
				
			||||||
    KEY_PRESS = 1,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void squeek_key_press(struct squeek_key *key, struct zwp_virtual_keyboard_v1*, enum key_press press, uint32_t timestamp);
 | 
					 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,9 @@
 | 
				
			|||||||
/*! State of the emulated keyboard and keys */
 | 
					/*! State of the emulated keyboard and keys.
 | 
				
			||||||
 | 
					 * Regards the keyboard as if it was composed of switches. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::collections::HashMap;
 | 
					use std::collections::HashMap;
 | 
				
			||||||
use std::fmt;
 | 
					use std::fmt;
 | 
				
			||||||
use std::io;
 | 
					use std::io;
 | 
				
			||||||
use std::rc::Rc;
 | 
					 | 
				
			||||||
use std::string::FromUtf8Error;
 | 
					use std::string::FromUtf8Error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use ::action::Action;
 | 
					use ::action::Action;
 | 
				
			||||||
@ -17,34 +17,10 @@ pub mod c {
 | 
				
			|||||||
    use super::*;
 | 
					    use super::*;
 | 
				
			||||||
    use ::util::c;
 | 
					    use ::util::c;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    use std::os::raw::c_void;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub type CKeyState = c::Wrapped<KeyState>;
 | 
					    pub type CKeyState = c::Wrapped<KeyState>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[repr(transparent)]
 | 
					 | 
				
			||||||
    pub struct ZwpVirtualKeyboardV1(*const c_void);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[no_mangle]
 | 
					 | 
				
			||||||
    extern "C" {
 | 
					 | 
				
			||||||
        /// Checks if point falls within bounds,
 | 
					 | 
				
			||||||
        /// which are relative to origin and rotated by angle (I think)
 | 
					 | 
				
			||||||
        pub fn eek_virtual_keyboard_v1_key(
 | 
					 | 
				
			||||||
            virtual_keyboard: *mut ZwpVirtualKeyboardV1,
 | 
					 | 
				
			||||||
            timestamp: u32,
 | 
					 | 
				
			||||||
            keycode: u32,
 | 
					 | 
				
			||||||
            press: u32,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
 | 
					    // The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Compares pointers to the data
 | 
					 | 
				
			||||||
    #[no_mangle]
 | 
					 | 
				
			||||||
    pub extern "C"
 | 
					 | 
				
			||||||
    fn squeek_key_equal(key: CKeyState, key2: CKeyState) -> u32 {
 | 
					 | 
				
			||||||
        return Rc::ptr_eq(&key.clone_ref(), &key2.clone_ref()) as u32
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    #[no_mangle]
 | 
					    #[no_mangle]
 | 
				
			||||||
    pub extern "C"
 | 
					    pub extern "C"
 | 
				
			||||||
    fn squeek_key_is_pressed(key: CKeyState) -> u32 {
 | 
					    fn squeek_key_is_pressed(key: CKeyState) -> u32 {
 | 
				
			||||||
@ -57,59 +33,17 @@ pub mod c {
 | 
				
			|||||||
    fn squeek_key_is_locked(key: CKeyState) -> u32 {
 | 
					    fn squeek_key_is_locked(key: CKeyState) -> u32 {
 | 
				
			||||||
        return key.clone_owned().locked as u32;
 | 
					        return key.clone_owned().locked as u32;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[no_mangle]
 | 
					#[derive(Debug, Clone, Copy)]
 | 
				
			||||||
    pub extern "C"
 | 
					pub enum PressType {
 | 
				
			||||||
    fn squeek_key_set_locked(key: CKeyState, locked: u32) {
 | 
					    Released = 0,
 | 
				
			||||||
        let key = key.clone_ref();
 | 
					    Pressed = 1,
 | 
				
			||||||
        let mut key = key.borrow_mut();
 | 
					 | 
				
			||||||
        key.locked = locked != 0;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[no_mangle]
 | 
					 | 
				
			||||||
    pub extern "C"
 | 
					 | 
				
			||||||
    fn squeek_key_press(
 | 
					 | 
				
			||||||
        key: CKeyState,
 | 
					 | 
				
			||||||
        virtual_keyboard: *mut ZwpVirtualKeyboardV1,
 | 
					 | 
				
			||||||
        press: u32,
 | 
					 | 
				
			||||||
        timestamp: u32,
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
        let key = key.clone_ref();
 | 
					 | 
				
			||||||
        let mut key = key.borrow_mut();
 | 
					 | 
				
			||||||
        key.pressed = press != 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let keycodes_count = key.keycodes.len();
 | 
					 | 
				
			||||||
        for keycode in key.keycodes.iter() {
 | 
					 | 
				
			||||||
            let keycode = keycode - 8;
 | 
					 | 
				
			||||||
            match (key.pressed, keycodes_count) {
 | 
					 | 
				
			||||||
                // Pressing a key made out of a single keycode is simple:
 | 
					 | 
				
			||||||
                // press on press, release on release.
 | 
					 | 
				
			||||||
                (_, 1) => unsafe {
 | 
					 | 
				
			||||||
                    eek_virtual_keyboard_v1_key(
 | 
					 | 
				
			||||||
                        virtual_keyboard, timestamp, keycode, press
 | 
					 | 
				
			||||||
                    );
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                // A key made of multiple keycodes
 | 
					 | 
				
			||||||
                // has to submit them one after the other
 | 
					 | 
				
			||||||
                (true, _) => unsafe {
 | 
					 | 
				
			||||||
                    eek_virtual_keyboard_v1_key(
 | 
					 | 
				
			||||||
                        virtual_keyboard, timestamp, keycode, 1
 | 
					 | 
				
			||||||
                    );
 | 
					 | 
				
			||||||
                    eek_virtual_keyboard_v1_key(
 | 
					 | 
				
			||||||
                        virtual_keyboard, timestamp, keycode, 0
 | 
					 | 
				
			||||||
                    );
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                // Design choice here: submit multiple all at press time
 | 
					 | 
				
			||||||
                // and do nothing at release time
 | 
					 | 
				
			||||||
                (false, _) => {},
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Clone)]
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
pub struct KeyState {
 | 
					pub struct KeyState {
 | 
				
			||||||
    pub pressed: bool,
 | 
					    pub pressed: PressType,
 | 
				
			||||||
    pub locked: bool,
 | 
					    pub locked: bool,
 | 
				
			||||||
    /// A cache of raw keycodes derived from Action::Sumbit given a keymap
 | 
					    /// A cache of raw keycodes derived from Action::Sumbit given a keymap
 | 
				
			||||||
    pub keycodes: Vec<u32>,
 | 
					    pub keycodes: Vec<u32>,
 | 
				
			||||||
@ -245,7 +179,7 @@ mod tests {
 | 
				
			|||||||
                },
 | 
					                },
 | 
				
			||||||
                keycodes: vec!(9, 10),
 | 
					                keycodes: vec!(9, 10),
 | 
				
			||||||
                locked: false,
 | 
					                locked: false,
 | 
				
			||||||
                pressed: false,
 | 
					                pressed: PressType::Released,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        }).unwrap();
 | 
					        }).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										37
									
								
								src/layout.h
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								src/layout.h
									
									
									
									
									
								
							@ -4,30 +4,31 @@
 | 
				
			|||||||
#include <inttypes.h>
 | 
					#include <inttypes.h>
 | 
				
			||||||
#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-types.h"
 | 
				
			||||||
#include "src/keyboard.h"
 | 
					#include "src/keyboard.h"
 | 
				
			||||||
 | 
					#include "virtual-keyboard-unstable-v1-client-protocol.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct squeek_button;
 | 
					struct squeek_button;
 | 
				
			||||||
struct squeek_row;
 | 
					struct squeek_row;
 | 
				
			||||||
struct squeek_view;
 | 
					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*);
 | 
					int32_t squeek_row_get_angle(const struct squeek_row*);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EekBounds squeek_row_get_bounds(const struct squeek_row*);
 | 
					EekBounds squeek_row_get_bounds(const struct squeek_row*);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
uint32_t squeek_row_contains(struct squeek_row*, struct squeek_button *button);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct button_place squeek_view_find_key(struct squeek_view*, struct squeek_key *state);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef void (*ButtonCallback) (struct squeek_button *button, gpointer user_data);
 | 
					typedef void (*ButtonCallback) (struct squeek_button *button, gpointer user_data);
 | 
				
			||||||
void squeek_row_foreach(struct squeek_row*,
 | 
					void squeek_row_foreach(struct squeek_row*,
 | 
				
			||||||
                            ButtonCallback   callback,
 | 
					                            ButtonCallback   callback,
 | 
				
			||||||
                            gpointer      user_data);
 | 
					                            gpointer      user_data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void squeek_row_free(struct squeek_row*);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
uint32_t squeek_button_get_oref(const struct squeek_button*);
 | 
					 | 
				
			||||||
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*);
 | 
				
			||||||
@ -47,19 +48,23 @@ void squeek_view_foreach(struct squeek_view*,
 | 
				
			|||||||
                            RowCallback   callback,
 | 
					                            RowCallback   callback,
 | 
				
			||||||
                            gpointer      user_data);
 | 
					                            gpointer      user_data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct squeek_row *squeek_view_get_row(struct squeek_view *view,
 | 
					 | 
				
			||||||
                                       struct squeek_button *button);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct squeek_button *squeek_view_find_button_by_position(struct squeek_view *view, EekPoint point);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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_view *squeek_layout_get_current_view(struct squeek_layout*);
 | 
				
			||||||
void squeek_layout_set_state_from_press(struct squeek_layout* layout, LevelKeyboard *keyboard, struct squeek_key* key);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct squeek_layout *squeek_load_layout(const char *type);
 | 
					struct squeek_layout *squeek_load_layout(const char *type);
 | 
				
			||||||
const char *squeek_layout_get_keymap(const struct squeek_layout*);
 | 
					const char *squeek_layout_get_keymap(const struct squeek_layout*);
 | 
				
			||||||
void squeek_layout_free(struct squeek_layout*);
 | 
					void squeek_layout_free(struct squeek_layout*);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void squeek_layout_release(struct squeek_layout *layout, struct zwp_virtual_keyboard_v1 *virtual_keyboard, uint32_t timestamp, EekGtkKeyboard *ui_keyboard);
 | 
				
			||||||
 | 
					void squeek_layout_release_all_only(struct squeek_layout *layout, struct zwp_virtual_keyboard_v1 *virtual_keyboard, uint32_t timestamp);
 | 
				
			||||||
 | 
					void squeek_layout_depress(struct squeek_layout *layout, struct zwp_virtual_keyboard_v1 *virtual_keyboard,
 | 
				
			||||||
 | 
					                           double x_widget, double y_widget,
 | 
				
			||||||
 | 
					                           struct transformation widget_to_layout,
 | 
				
			||||||
 | 
					                           uint32_t timestamp, EekGtkKeyboard *ui_keyboard);
 | 
				
			||||||
 | 
					void squeek_layout_drag(struct squeek_layout *layout, struct zwp_virtual_keyboard_v1 *virtual_keyboard,
 | 
				
			||||||
 | 
					                        double x_widget, double y_widget,
 | 
				
			||||||
 | 
					                        struct transformation widget_to_layout,
 | 
				
			||||||
 | 
					                        uint32_t timestamp, EekGtkKeyboard *ui_keyboard);
 | 
				
			||||||
 | 
					void squeek_layout_draw_all_changed(struct squeek_layout *layout, EekGtkKeyboard *ui_keyboard);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										729
									
								
								src/layout.rs
									
									
									
									
									
								
							
							
						
						
									
										729
									
								
								src/layout.rs
									
									
									
									
									
								
							@ -18,16 +18,17 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::cell::RefCell;
 | 
					use std::cell::RefCell;
 | 
				
			||||||
use std::collections::HashMap;
 | 
					use std::collections::{ HashMap, HashSet };
 | 
				
			||||||
use std::ffi::CString;
 | 
					use std::ffi::CString;
 | 
				
			||||||
use std::rc::Rc;
 | 
					use std::rc::Rc;
 | 
				
			||||||
use std::vec::Vec;
 | 
					use std::vec::Vec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use ::action::Action;
 | 
					use ::action::Action;
 | 
				
			||||||
use ::float_ord::FloatOrd;
 | 
					use ::float_ord::FloatOrd;
 | 
				
			||||||
use ::keyboard::*;
 | 
					use ::keyboard::{ KeyState, PressType };
 | 
				
			||||||
 | 
					use ::submission::{ Timestamp, VirtualKeyboard };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use ::util::CloneOwned;
 | 
					use std::borrow::Borrow;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Gathers stuff defined in C or called by C
 | 
					/// Gathers stuff defined in C or called by C
 | 
				
			||||||
pub mod c {
 | 
					pub mod c {
 | 
				
			||||||
@ -42,6 +43,10 @@ pub mod c {
 | 
				
			|||||||
    #[repr(transparent)]
 | 
					    #[repr(transparent)]
 | 
				
			||||||
    pub struct UserData(*const c_void);
 | 
					    pub struct UserData(*const c_void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[repr(transparent)]
 | 
				
			||||||
 | 
					    #[derive(Copy, Clone)]
 | 
				
			||||||
 | 
					    pub struct EekGtkKeyboard(*const c_void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Defined in eek-types.h
 | 
					    /// Defined in eek-types.h
 | 
				
			||||||
    #[repr(C)]
 | 
					    #[repr(C)]
 | 
				
			||||||
    #[derive(Clone, Debug)]
 | 
					    #[derive(Clone, Debug)]
 | 
				
			||||||
@ -102,14 +107,6 @@ pub mod c {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    /// Set bounds by consuming the value
 | 
					 | 
				
			||||||
    #[no_mangle]
 | 
					 | 
				
			||||||
    pub extern "C"
 | 
					 | 
				
			||||||
    fn squeek_row_set_bounds(row: *mut ::layout::Row, bounds: Bounds) {
 | 
					 | 
				
			||||||
        let row = unsafe { &mut *row };
 | 
					 | 
				
			||||||
        row.bounds = Some(bounds);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    #[no_mangle]
 | 
					    #[no_mangle]
 | 
				
			||||||
    pub extern "C"
 | 
					    pub extern "C"
 | 
				
			||||||
    fn squeek_row_foreach(
 | 
					    fn squeek_row_foreach(
 | 
				
			||||||
@ -124,12 +121,6 @@ pub mod c {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[no_mangle]
 | 
					 | 
				
			||||||
    pub extern "C"
 | 
					 | 
				
			||||||
    fn squeek_row_free(row: *mut ::layout::Row) {
 | 
					 | 
				
			||||||
        unsafe { Box::from_raw(row) }; // gets dropped
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[no_mangle]
 | 
					    #[no_mangle]
 | 
				
			||||||
    pub extern "C"
 | 
					    pub extern "C"
 | 
				
			||||||
    fn squeek_button_get_bounds(button: *const ::layout::Button) -> Bounds {
 | 
					    fn squeek_button_get_bounds(button: *const ::layout::Button) -> Bounds {
 | 
				
			||||||
@ -188,18 +179,6 @@ pub mod c {
 | 
				
			|||||||
        button.outline_name.as_ptr()
 | 
					        button.outline_name.as_ptr()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    #[no_mangle]
 | 
					 | 
				
			||||||
    pub extern "C"
 | 
					 | 
				
			||||||
    fn squeek_button_has_key(
 | 
					 | 
				
			||||||
        button: *const ::layout::Button,
 | 
					 | 
				
			||||||
        state: ::keyboard::c::CKeyState,
 | 
					 | 
				
			||||||
    ) -> u32 {
 | 
					 | 
				
			||||||
        let button = unsafe { &*button };
 | 
					 | 
				
			||||||
        let state = state.clone_ref();
 | 
					 | 
				
			||||||
        let equal = Rc::ptr_eq(&button.state, &state);
 | 
					 | 
				
			||||||
        equal as u32
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    #[no_mangle]
 | 
					    #[no_mangle]
 | 
				
			||||||
    pub extern "C"
 | 
					    pub extern "C"
 | 
				
			||||||
    fn squeek_button_print(button: *const ::layout::Button) {
 | 
					    fn squeek_button_print(button: *const ::layout::Button) {
 | 
				
			||||||
@ -230,18 +209,47 @@ pub mod c {
 | 
				
			|||||||
        unsafe { Box::from_raw(layout) };
 | 
					        unsafe { Box::from_raw(layout) };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    /// Entry points for more complex procedures and algoithms which span multiple modules
 | 
					    /// Entry points for more complex procedures and algoithms which span multiple modules
 | 
				
			||||||
    pub mod procedures {
 | 
					    pub mod procedures {
 | 
				
			||||||
        use super::*;
 | 
					        use super::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        use ::submission::c::ZwpVirtualKeyboardV1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #[repr(C)]
 | 
					        #[repr(C)]
 | 
				
			||||||
        #[derive(PartialEq, Debug)]
 | 
					        #[derive(PartialEq, Debug)]
 | 
				
			||||||
        pub struct ButtonPlace {
 | 
					        pub struct CButtonPlace {
 | 
				
			||||||
            row: *const Row,
 | 
					            row: *const Row,
 | 
				
			||||||
            button: *const Button,
 | 
					            button: *const Button,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        impl<'a> From<ButtonPlace<'a>> for CButtonPlace {
 | 
				
			||||||
 | 
					            fn from(value: ButtonPlace<'a>) -> CButtonPlace {
 | 
				
			||||||
 | 
					                CButtonPlace {
 | 
				
			||||||
 | 
					                    row: value.row as *const Row,
 | 
				
			||||||
 | 
					                    button: value.button as *const Button,
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// 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,
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // This is constructed only in C, no need for warnings
 | 
				
			||||||
 | 
					        #[allow(dead_code)]
 | 
				
			||||||
        #[repr(transparent)]
 | 
					        #[repr(transparent)]
 | 
				
			||||||
        pub struct LevelKeyboard(*const c_void);
 | 
					        pub struct LevelKeyboard(*const c_void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -255,54 +263,33 @@ pub mod c {
 | 
				
			|||||||
                angle: i32
 | 
					                angle: i32
 | 
				
			||||||
            ) -> u32;
 | 
					            ) -> u32;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // CKeyState is safe to pass to C as long as nothing dereferences it
 | 
					            // 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)]
 | 
					            #[allow(improper_ctypes)]
 | 
				
			||||||
            pub fn eek_keyboard_set_key_locked(
 | 
					            pub fn eek_gtk_on_button_released(
 | 
				
			||||||
                keyboard: *mut LevelKeyboard,
 | 
					                button: *const Button,
 | 
				
			||||||
                key: ::keyboard::c::CKeyState,
 | 
					                view: *const View,
 | 
				
			||||||
 | 
					                keyboard: EekGtkKeyboard,
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #[no_mangle]
 | 
					            // Button and View inside CButtonPlace are safe to pass to C
 | 
				
			||||||
        pub extern "C"
 | 
					            // as long as they don't outlive the call
 | 
				
			||||||
        fn squeek_layout_set_state_from_press(
 | 
					            // and nothing dereferences them
 | 
				
			||||||
            layout: *mut Layout,
 | 
					            #[allow(improper_ctypes)]
 | 
				
			||||||
            keyboard: *mut LevelKeyboard,
 | 
					            pub fn eek_gtk_on_button_pressed(
 | 
				
			||||||
            key: ::keyboard::c::CKeyState,
 | 
					                place: CButtonPlace,
 | 
				
			||||||
        ) {
 | 
					                keyboard: EekGtkKeyboard,
 | 
				
			||||||
            let layout = unsafe { &mut *layout };
 | 
					            );
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            let view_name = match key.clone_owned().action {
 | 
					            // Button and View inside CButtonPlace are safe to pass to C
 | 
				
			||||||
                Action::SetLevel(name) => {
 | 
					            // as long as they don't outlive the call
 | 
				
			||||||
                    Some(name.clone())
 | 
					            // and nothing dereferences them
 | 
				
			||||||
                },
 | 
					            #[allow(improper_ctypes)]
 | 
				
			||||||
                Action::LockLevel { lock, unlock } => {
 | 
					            pub fn eek_gtk_render_locked_button(
 | 
				
			||||||
                    let locked = {
 | 
					                keyboard: EekGtkKeyboard,
 | 
				
			||||||
                        let key = key.clone_ref();
 | 
					                place: CButtonPlace,
 | 
				
			||||||
                        let mut key = key.borrow_mut();
 | 
					            );
 | 
				
			||||||
                        key.locked ^= true;
 | 
					 | 
				
			||||||
                        key.locked
 | 
					 | 
				
			||||||
                    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if locked {
 | 
					 | 
				
			||||||
                        unsafe {
 | 
					 | 
				
			||||||
                            eek_keyboard_set_key_locked(
 | 
					 | 
				
			||||||
                                keyboard,
 | 
					 | 
				
			||||||
                                key
 | 
					 | 
				
			||||||
                            )
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    Some(if locked { lock } else { unlock }.clone())
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                _ => None,
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if let Some(view_name) = view_name {
 | 
					 | 
				
			||||||
                if let Err(_e) = layout.set_view(view_name.clone()) {
 | 
					 | 
				
			||||||
                    eprintln!("No such view: {}, ignoring switch", view_name)
 | 
					 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// Places each button in order, starting from 0 on the left,
 | 
					        /// Places each button in order, starting from 0 on the left,
 | 
				
			||||||
@ -325,209 +312,194 @@ pub mod c {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        fn squeek_row_contains(row: &Row, needle: *const Button) -> bool {
 | 
					 | 
				
			||||||
            row.buttons.iter().position(
 | 
					 | 
				
			||||||
                // TODO: wrap Button properly in Rc; this comparison is unreliable
 | 
					 | 
				
			||||||
                |button| button.as_ref() as *const ::layout::Button == needle
 | 
					 | 
				
			||||||
            ).is_some()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #[no_mangle]
 | 
					        #[no_mangle]
 | 
				
			||||||
        pub extern "C"
 | 
					        pub extern "C"
 | 
				
			||||||
        fn squeek_view_get_row(
 | 
					        fn squeek_layout_release(
 | 
				
			||||||
            view: *mut View,
 | 
					            layout: *mut Layout,
 | 
				
			||||||
            needle: *const ::layout::Button,
 | 
					            virtual_keyboard: ZwpVirtualKeyboardV1, // TODO: receive a reference to the backend
 | 
				
			||||||
        ) -> *mut Row {
 | 
					            time: u32,
 | 
				
			||||||
            let view = unsafe { &mut *view };
 | 
					            ui_keyboard: EekGtkKeyboard,
 | 
				
			||||||
            let result = view.rows.iter_mut().find(|row| {
 | 
					        ) {
 | 
				
			||||||
                squeek_row_contains(row, needle)
 | 
					            let layout = unsafe { &mut *layout };
 | 
				
			||||||
            });
 | 
					            let virtual_keyboard = VirtualKeyboard(virtual_keyboard);
 | 
				
			||||||
            match result {
 | 
					            // The list must be copied,
 | 
				
			||||||
                Some(row) => row.as_mut() as *mut Row,
 | 
					            // because it will be mutated in the loop
 | 
				
			||||||
                None => ptr::null_mut(),
 | 
					            for key in layout.pressed_keys.clone() {
 | 
				
			||||||
            }
 | 
					                let key: &Rc<RefCell<KeyState>> = key.borrow();
 | 
				
			||||||
        }
 | 
					                layout.release_key(
 | 
				
			||||||
 | 
					                    &virtual_keyboard,
 | 
				
			||||||
        #[no_mangle]
 | 
					                    &mut key.clone(),
 | 
				
			||||||
        pub extern "C"
 | 
					                    Timestamp(time)
 | 
				
			||||||
        fn squeek_view_find_key(
 | 
					 | 
				
			||||||
            view: *const View,
 | 
					 | 
				
			||||||
            needle: ::keyboard::c::CKeyState,
 | 
					 | 
				
			||||||
        ) -> ButtonPlace {
 | 
					 | 
				
			||||||
            let view = unsafe { &*view };
 | 
					 | 
				
			||||||
            let state = needle.clone_ref();
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            let paths = ::layout::procedures::find_key_paths(view, &state);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Can only return 1 entry back to C
 | 
					 | 
				
			||||||
            let (row, button) = match paths.get(0) {
 | 
					 | 
				
			||||||
                Some((row, button)) => (
 | 
					 | 
				
			||||||
                    row.as_ref() as *const Row,
 | 
					 | 
				
			||||||
                    button.as_ref() as *const Button,
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                None => ( ptr::null(), ptr::null() ),
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
            ButtonPlace { row, button }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #[no_mangle]
 | 
					 | 
				
			||||||
        pub extern "C"
 | 
					 | 
				
			||||||
        fn squeek_view_find_button_by_position(
 | 
					 | 
				
			||||||
            view: *mut View, point: Point
 | 
					 | 
				
			||||||
        ) -> *mut Button {
 | 
					 | 
				
			||||||
            let view = unsafe { &mut *view };
 | 
					 | 
				
			||||||
            let result = view.find_button_by_position(point);
 | 
					 | 
				
			||||||
            match result {
 | 
					 | 
				
			||||||
                Some(button) => button.as_mut() as *mut Button,
 | 
					 | 
				
			||||||
                None => ptr::null_mut(),
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #[cfg(test)]
 | 
					 | 
				
			||||||
        mod test {
 | 
					 | 
				
			||||||
            use super::*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            use super::super::test::*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            #[test]
 | 
					 | 
				
			||||||
            fn row_has_button() {
 | 
					 | 
				
			||||||
                let state = make_state();
 | 
					 | 
				
			||||||
                let button = make_button_with_state(
 | 
					 | 
				
			||||||
                    "test".into(),
 | 
					 | 
				
			||||||
                    state.clone()
 | 
					 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
                let button_ptr = button_as_raw(&button);
 | 
					                let view = layout.get_current_view();
 | 
				
			||||||
                let mut row = Row::new(0);
 | 
					                ::layout::procedures::release_ui_buttons(
 | 
				
			||||||
                row.buttons.push(button);
 | 
					                    &view, key, ui_keyboard,
 | 
				
			||||||
                assert_eq!(squeek_row_contains(&row, button_ptr), true);
 | 
					 | 
				
			||||||
                let shared_button = make_button_with_state(
 | 
					 | 
				
			||||||
                    "test2".into(),
 | 
					 | 
				
			||||||
                    state
 | 
					 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
                let shared_button_ptr = button_as_raw(&shared_button);
 | 
					 | 
				
			||||||
                row.buttons.push(shared_button);
 | 
					 | 
				
			||||||
                assert_eq!(squeek_row_contains(&row, shared_button_ptr), true);
 | 
					 | 
				
			||||||
                let row = Row::new(0);
 | 
					 | 
				
			||||||
                assert_eq!(squeek_row_contains(&row, button_ptr), false);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            #[test]
 | 
					        /// Release all buittons but don't redraw
 | 
				
			||||||
            fn view_has_button() {
 | 
					        #[no_mangle]
 | 
				
			||||||
                let state = make_state();
 | 
					        pub extern "C"
 | 
				
			||||||
                let state_clone = ::keyboard::c::CKeyState::wrap(state.clone());
 | 
					        fn squeek_layout_release_all_only(
 | 
				
			||||||
 | 
					            layout: *mut Layout,
 | 
				
			||||||
 | 
					            virtual_keyboard: ZwpVirtualKeyboardV1, // TODO: receive a reference to the backend
 | 
				
			||||||
 | 
					            time: u32,
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					            let layout = unsafe { &mut *layout };
 | 
				
			||||||
 | 
					            let virtual_keyboard = VirtualKeyboard(virtual_keyboard);
 | 
				
			||||||
 | 
					            // The list must be copied,
 | 
				
			||||||
 | 
					            // because it will be mutated in the loop
 | 
				
			||||||
 | 
					            for key in layout.pressed_keys.clone() {
 | 
				
			||||||
 | 
					                let key: &Rc<RefCell<KeyState>> = key.borrow();
 | 
				
			||||||
 | 
					                layout.release_key(
 | 
				
			||||||
 | 
					                    &virtual_keyboard,
 | 
				
			||||||
 | 
					                    &mut key.clone(),
 | 
				
			||||||
 | 
					                    Timestamp(time)
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let button = make_button_with_state("1".into(), state);
 | 
					        #[no_mangle]
 | 
				
			||||||
                let button_ptr = button.as_ref() as *const Button;
 | 
					        pub extern "C"
 | 
				
			||||||
 | 
					        fn squeek_layout_depress(
 | 
				
			||||||
 | 
					            layout: *mut Layout,
 | 
				
			||||||
 | 
					            virtual_keyboard: ZwpVirtualKeyboardV1, // TODO: receive a reference to the backend
 | 
				
			||||||
 | 
					            x_widget: f64, y_widget: f64,
 | 
				
			||||||
 | 
					            widget_to_layout: Transformation,
 | 
				
			||||||
 | 
					            time: u32,
 | 
				
			||||||
 | 
					            ui_keyboard: EekGtkKeyboard,
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					            let layout = unsafe { &mut *layout };
 | 
				
			||||||
 | 
					            let view = layout.get_current_view();
 | 
				
			||||||
 | 
					            let point = widget_to_layout.forward(
 | 
				
			||||||
 | 
					                Point { x: x_widget, y: y_widget }
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
                let row = Box::new(Row {
 | 
					            let place = view.find_button_by_position(point);
 | 
				
			||||||
                    buttons: vec!(button),
 | 
					            // the immutable reference to `layout` through `view`
 | 
				
			||||||
                    angle: 0,
 | 
					            // must be dropped
 | 
				
			||||||
                    bounds: None
 | 
					            // before `layout.press_key` borrows it mutably again
 | 
				
			||||||
                });
 | 
					            let state_place = place.map(|place| {(
 | 
				
			||||||
                let row_ptr = row.as_ref() as *const Row;
 | 
					                place.button.state.clone(),
 | 
				
			||||||
 | 
					                place.into(),
 | 
				
			||||||
 | 
					            )});
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
                let view = View {
 | 
					            if let Some((mut state, c_place)) = state_place {
 | 
				
			||||||
                    bounds: Bounds {
 | 
					                layout.press_key(
 | 
				
			||||||
                        x: 0f64, y: 0f64,
 | 
					                    &VirtualKeyboard(virtual_keyboard),
 | 
				
			||||||
                        width: 0f64, height: 0f64
 | 
					                    &mut state,
 | 
				
			||||||
                    },
 | 
					                    Timestamp(time),
 | 
				
			||||||
                    rows: vec!(row),
 | 
					                );
 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                assert_eq!(
 | 
					                unsafe { eek_gtk_on_button_pressed(c_place, ui_keyboard) };
 | 
				
			||||||
                    squeek_view_find_key(
 | 
					            }
 | 
				
			||||||
                        &view as *const View,
 | 
					        }
 | 
				
			||||||
                        state_clone.clone(),
 | 
					
 | 
				
			||||||
                    ),
 | 
					        // FIXME: this will work funny
 | 
				
			||||||
                    ButtonPlace {
 | 
					        // when 2 touch points are on buttons and moving one after another
 | 
				
			||||||
                        row: row_ptr,
 | 
					        // Solution is to have separate pressed lists for each point
 | 
				
			||||||
                        button: button_ptr,
 | 
					        #[no_mangle]
 | 
				
			||||||
 | 
					        pub extern "C"
 | 
				
			||||||
 | 
					        fn squeek_layout_drag(
 | 
				
			||||||
 | 
					            layout: *mut Layout,
 | 
				
			||||||
 | 
					            virtual_keyboard: ZwpVirtualKeyboardV1, // TODO: receive a reference to the backend
 | 
				
			||||||
 | 
					            x_widget: f64, y_widget: f64,
 | 
				
			||||||
 | 
					            widget_to_layout: Transformation,
 | 
				
			||||||
 | 
					            time: u32,
 | 
				
			||||||
 | 
					            ui_keyboard: EekGtkKeyboard,
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					            let layout = unsafe { &mut *layout };
 | 
				
			||||||
 | 
					            let virtual_keyboard = VirtualKeyboard(virtual_keyboard);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let view = layout.get_current_view();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            let point = widget_to_layout.forward(
 | 
				
			||||||
 | 
					                Point { x: x_widget, y: y_widget }
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            let pressed = layout.pressed_keys.clone();
 | 
				
			||||||
 | 
					            let place = view.find_button_by_position(point);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let state_place = place.map(|place| {(
 | 
				
			||||||
 | 
					                place.button.state.clone(),
 | 
				
			||||||
 | 
					                place.into(),
 | 
				
			||||||
 | 
					            )});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if let Some((mut state, c_place)) = state_place {
 | 
				
			||||||
 | 
					                let mut found = false;
 | 
				
			||||||
 | 
					                for wrapped_key in pressed {
 | 
				
			||||||
 | 
					                    let key: &Rc<RefCell<KeyState>> = wrapped_key.borrow();
 | 
				
			||||||
 | 
					                    if Rc::ptr_eq(&state, &wrapped_key.0) {
 | 
				
			||||||
 | 
					                        found = true;
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        layout.release_key(
 | 
				
			||||||
 | 
					                            &virtual_keyboard,
 | 
				
			||||||
 | 
					                            &mut key.clone(), 
 | 
				
			||||||
 | 
					                            Timestamp(time),
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
 | 
					                        let view = layout.get_current_view();
 | 
				
			||||||
 | 
					                        ::layout::procedures::release_ui_buttons(
 | 
				
			||||||
 | 
					                            &view, key, ui_keyboard,
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                );
 | 
					                }
 | 
				
			||||||
 | 
					                if !found {
 | 
				
			||||||
 | 
					                    layout.press_key(
 | 
				
			||||||
 | 
					                        &virtual_keyboard,
 | 
				
			||||||
 | 
					                        &mut state,
 | 
				
			||||||
 | 
					                        Timestamp(time),
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                    unsafe { eek_gtk_on_button_pressed(c_place, ui_keyboard) };
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                for wrapped_key in pressed {
 | 
				
			||||||
 | 
					                    let key: &Rc<RefCell<KeyState>> = wrapped_key.borrow();
 | 
				
			||||||
 | 
					                    layout.release_key(
 | 
				
			||||||
 | 
					                        &virtual_keyboard,
 | 
				
			||||||
 | 
					                        &mut key.clone(), 
 | 
				
			||||||
 | 
					                        Timestamp(time),
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                    let view = layout.get_current_view();
 | 
				
			||||||
 | 
					                    ::layout::procedures::release_ui_buttons(
 | 
				
			||||||
 | 
					                        &view, key, ui_keyboard,
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let view = View {
 | 
					        #[no_mangle]
 | 
				
			||||||
                    bounds: Bounds {
 | 
					        pub extern "C"
 | 
				
			||||||
                        x: 0f64, y: 0f64,
 | 
					        fn squeek_layout_draw_all_changed(
 | 
				
			||||||
                        width: 0f64, height: 0f64
 | 
					            layout: *mut Layout,
 | 
				
			||||||
                    },
 | 
					            ui_keyboard: EekGtkKeyboard,
 | 
				
			||||||
                    rows: Vec::new(),
 | 
					        ) {
 | 
				
			||||||
                };
 | 
					            let layout = unsafe { &mut *layout };
 | 
				
			||||||
                assert_eq!(
 | 
					            
 | 
				
			||||||
                    squeek_view_find_key(
 | 
					            for row in &layout.get_current_view().rows {
 | 
				
			||||||
                        &view as *const View,
 | 
					                for button in &row.buttons {
 | 
				
			||||||
                        state_clone.clone()
 | 
					                    let c_place = CButtonPlace::from(
 | 
				
			||||||
                    ),
 | 
					                        ButtonPlace { row, button }
 | 
				
			||||||
                    ButtonPlace {
 | 
					                    );
 | 
				
			||||||
                        row: ptr::null(),
 | 
					                    let state = RefCell::borrow(&button.state);
 | 
				
			||||||
                        button: ptr::null(),
 | 
					                    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)]
 | 
					pub struct ButtonPlace<'a> {
 | 
				
			||||||
    mod test {
 | 
					    button: &'a Button,
 | 
				
			||||||
        use super::*;
 | 
					    row: &'a Row,
 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        use ::keyboard::c::CKeyState;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        pub fn make_state() -> Rc<RefCell<::keyboard::KeyState>> {
 | 
					 | 
				
			||||||
            Rc::new(RefCell::new(::keyboard::KeyState {
 | 
					 | 
				
			||||||
                pressed: false,
 | 
					 | 
				
			||||||
                locked: false,
 | 
					 | 
				
			||||||
                keycodes: Vec::new(),
 | 
					 | 
				
			||||||
                action: Action::SetLevel("default".into()),
 | 
					 | 
				
			||||||
            }))
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        pub fn make_button_with_state(
 | 
					 | 
				
			||||||
            name: String,
 | 
					 | 
				
			||||||
            state: Rc<RefCell<::keyboard::KeyState>>,
 | 
					 | 
				
			||||||
        ) -> Box<Button> {
 | 
					 | 
				
			||||||
            Box::new(Button {
 | 
					 | 
				
			||||||
                name: CString::new(name.clone()).unwrap(),
 | 
					 | 
				
			||||||
                bounds: c::Bounds {
 | 
					 | 
				
			||||||
                    x: 0f64, y: 0f64, width: 0f64, height: 0f64
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                outline_name: CString::new("test").unwrap(),
 | 
					 | 
				
			||||||
                label: Label::Text(CString::new(name).unwrap()),
 | 
					 | 
				
			||||||
                state: state,
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        pub fn button_as_raw(button: &Box<Button>) -> *const Button {
 | 
					 | 
				
			||||||
            button.as_ref() as *const Button
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #[test]
 | 
					 | 
				
			||||||
        fn button_has_key() {
 | 
					 | 
				
			||||||
            let state = make_state();
 | 
					 | 
				
			||||||
            let button = make_button_with_state("1".into(), state.clone());
 | 
					 | 
				
			||||||
            assert_eq!(
 | 
					 | 
				
			||||||
                squeek_button_has_key(
 | 
					 | 
				
			||||||
                    button_as_raw(&button),
 | 
					 | 
				
			||||||
                    CKeyState::wrap(state.clone())
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                1
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
            let other_state = make_state();
 | 
					 | 
				
			||||||
            let other_button = make_button_with_state("1".into(), other_state);
 | 
					 | 
				
			||||||
            assert_eq!(
 | 
					 | 
				
			||||||
                squeek_button_has_key(
 | 
					 | 
				
			||||||
                    button_as_raw(&other_button),
 | 
					 | 
				
			||||||
                    CKeyState::wrap(state.clone())
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                0
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
            let orphan_state = CKeyState::wrap(make_state());
 | 
					 | 
				
			||||||
            assert_eq!(
 | 
					 | 
				
			||||||
                squeek_button_has_key(button_as_raw(&button), orphan_state),
 | 
					 | 
				
			||||||
                0
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug)]
 | 
				
			||||||
@ -570,14 +542,6 @@ pub struct Row {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Row {
 | 
					impl Row {
 | 
				
			||||||
    fn new(angle: i32) -> Row {
 | 
					 | 
				
			||||||
        Row {
 | 
					 | 
				
			||||||
            buttons: Vec::new(),
 | 
					 | 
				
			||||||
            angle: angle,
 | 
					 | 
				
			||||||
            bounds: None,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    fn last(positions: &Vec<c::Bounds>) -> Option<&c::Bounds> {
 | 
					    fn last(positions: &Vec<c::Bounds>) -> Option<&c::Bounds> {
 | 
				
			||||||
        let len = positions.len();
 | 
					        let len = positions.len();
 | 
				
			||||||
        match len {
 | 
					        match len {
 | 
				
			||||||
@ -615,8 +579,8 @@ impl Row {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /// Finds the first button that covers the specified point
 | 
					    /// Finds the first button that covers the specified point
 | 
				
			||||||
    /// relative to row's position's origin
 | 
					    /// relative to row's position's origin
 | 
				
			||||||
    fn find_button_by_position(&mut self, point: c::Point)
 | 
					    fn find_button_by_position(&self, point: c::Point)
 | 
				
			||||||
        -> Option<&mut Box<Button>>
 | 
					        -> Option<&Box<Button>>
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        let row_bounds = self.bounds.as_ref().expect("Missing bounds on row");
 | 
					        let row_bounds = self.bounds.as_ref().expect("Missing bounds on row");
 | 
				
			||||||
        let origin = c::Point {
 | 
					        let origin = c::Point {
 | 
				
			||||||
@ -624,7 +588,7 @@ impl Row {
 | 
				
			|||||||
            y: row_bounds.y,
 | 
					            y: row_bounds.y,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        let angle = self.angle;
 | 
					        let angle = self.angle;
 | 
				
			||||||
        self.buttons.iter_mut().find(|button| {
 | 
					        self.buttons.iter().find(|button| {
 | 
				
			||||||
            let bounds = button.bounds.clone();
 | 
					            let bounds = button.bounds.clone();
 | 
				
			||||||
            let point = point.clone();
 | 
					            let point = point.clone();
 | 
				
			||||||
            let origin = origin.clone();
 | 
					            let origin = origin.clone();
 | 
				
			||||||
@ -704,8 +668,8 @@ impl View {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /// Finds the first button that covers the specified point
 | 
					    /// Finds the first button that covers the specified point
 | 
				
			||||||
    /// relative to view's position's origin
 | 
					    /// relative to view's position's origin
 | 
				
			||||||
    fn find_button_by_position(&mut self, point: c::Point)
 | 
					    fn find_button_by_position(&self, point: c::Point)
 | 
				
			||||||
        -> Option<&mut Box<Button>>
 | 
					        -> Option<ButtonPlace>
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // make point relative to the inside of the view,
 | 
					        // make point relative to the inside of the view,
 | 
				
			||||||
        // which is the origin of all rows
 | 
					        // which is the origin of all rows
 | 
				
			||||||
@ -714,22 +678,43 @@ impl View {
 | 
				
			|||||||
            y: point.y - self.bounds.y,
 | 
					            y: point.y - self.bounds.y,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.rows.iter_mut().find_map(
 | 
					        self.rows.iter().find_map(|row| {
 | 
				
			||||||
            |row| row.find_button_by_position(point.clone())
 | 
					            row.find_button_by_position(point.clone())
 | 
				
			||||||
        )
 | 
					                .map(|button| ButtonPlace {row, button})
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: split into sth like
 | 
				
			||||||
 | 
					// Arrangement (views) + details (keymap) + State (keys)
 | 
				
			||||||
 | 
					/// State of the UI, contains the backend as well
 | 
				
			||||||
pub struct Layout {
 | 
					pub struct Layout {
 | 
				
			||||||
    pub current_view: String,
 | 
					    pub current_view: String,
 | 
				
			||||||
 | 
					    // Views own the actual buttons which have state
 | 
				
			||||||
 | 
					    // Maybe they should own UI only,
 | 
				
			||||||
 | 
					    // and keys should be owned by a dedicated non-UI-State?
 | 
				
			||||||
    pub views: HashMap<String, Box<View>>,
 | 
					    pub views: HashMap<String, Box<View>>,
 | 
				
			||||||
    // TODO: move to ::keyboard::Keyboard
 | 
					
 | 
				
			||||||
 | 
					    // Non-UI stuff
 | 
				
			||||||
 | 
					    /// xkb keymap applicable to the contained keys. Unchangeable
 | 
				
			||||||
    pub keymap_str: CString,
 | 
					    pub keymap_str: CString,
 | 
				
			||||||
 | 
					    // Changeable state
 | 
				
			||||||
 | 
					    // a Vec would be enough, but who cares, this will be small & fast enough
 | 
				
			||||||
 | 
					    pub pressed_keys: HashSet<::util::Pointer<RefCell<KeyState>>>,
 | 
				
			||||||
 | 
					    pub locked_keys: HashSet<::util::Pointer<RefCell<KeyState>>>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct NoSuchView;
 | 
					struct NoSuchView;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Unfortunately, changes are not atomic due to mutability :(
 | 
				
			||||||
 | 
					// An error will not be recoverable
 | 
				
			||||||
 | 
					// The usage of &mut on Rc<RefCell<KeyState>> doesn't mean anything special.
 | 
				
			||||||
 | 
					// Cloning could also be used.
 | 
				
			||||||
impl Layout {
 | 
					impl Layout {
 | 
				
			||||||
 | 
					    fn get_current_view(&self) -> &Box<View> {
 | 
				
			||||||
 | 
					        self.views.get(&self.current_view).expect("Selected nonexistent view")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn set_view(&mut self, view: String) -> Result<(), NoSuchView> {
 | 
					    fn set_view(&mut self, view: String) -> Result<(), NoSuchView> {
 | 
				
			||||||
        if self.views.contains_key(&view) {
 | 
					        if self.views.contains_key(&view) {
 | 
				
			||||||
            self.current_view = view;
 | 
					            self.current_view = view;
 | 
				
			||||||
@ -738,6 +723,86 @@ impl Layout {
 | 
				
			|||||||
            Err(NoSuchView)
 | 
					            Err(NoSuchView)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn release_key(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        virtual_keyboard: &VirtualKeyboard,
 | 
				
			||||||
 | 
					        mut key: &mut Rc<RefCell<KeyState>>,
 | 
				
			||||||
 | 
					        time: Timestamp,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        if !self.pressed_keys.remove(&::util::Pointer(key.clone())) {
 | 
				
			||||||
 | 
					            eprintln!("Warning: key {:?} was not pressed", key);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        virtual_keyboard.switch(
 | 
				
			||||||
 | 
					            &mut key.borrow_mut(),
 | 
				
			||||||
 | 
					            PressType::Released,
 | 
				
			||||||
 | 
					            time,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        self.set_level_from_press(&mut key);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    fn press_key(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        virtual_keyboard: &VirtualKeyboard,
 | 
				
			||||||
 | 
					        key: &mut Rc<RefCell<KeyState>>,
 | 
				
			||||||
 | 
					        time: Timestamp,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        if !self.pressed_keys.insert(::util::Pointer(key.clone())) {
 | 
				
			||||||
 | 
					            eprintln!("Warning: key {:?} was already pressed", key);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        virtual_keyboard.switch(
 | 
				
			||||||
 | 
					            &mut key.borrow_mut(),
 | 
				
			||||||
 | 
					            PressType::Pressed,
 | 
				
			||||||
 | 
					            time,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn set_level_from_press(&mut self, key: &Rc<RefCell<KeyState>>) {
 | 
				
			||||||
 | 
					        let keys = self.locked_keys.clone();
 | 
				
			||||||
 | 
					        for key in &keys {
 | 
				
			||||||
 | 
					            self.locked_keys.remove(key);
 | 
				
			||||||
 | 
					            self.set_state_from_press(key.borrow());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Don't handle the same key twice, but handle it at least once,
 | 
				
			||||||
 | 
					        // because its press is the reason we're here
 | 
				
			||||||
 | 
					        if !keys.contains(&::util::Pointer(key.clone())) {
 | 
				
			||||||
 | 
					            self.set_state_from_press(key);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn set_state_from_press(&mut self, key: &Rc<RefCell<KeyState>>) {
 | 
				
			||||||
 | 
					        // Action should not hold a reference to key,
 | 
				
			||||||
 | 
					        // because key is later borrowed for mutation. So action is cloned.
 | 
				
			||||||
 | 
					        // RefCell::borrow() is covered up by (dyn Borrow)::borrow()
 | 
				
			||||||
 | 
					        // if used like key.borrow() :(
 | 
				
			||||||
 | 
					        let action = RefCell::borrow(key).action.clone();
 | 
				
			||||||
 | 
					        let view_name = match action {
 | 
				
			||||||
 | 
					            Action::SetLevel(name) => {
 | 
				
			||||||
 | 
					                Some(name.clone())
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            Action::LockLevel { lock, unlock } => {
 | 
				
			||||||
 | 
					                let locked = {
 | 
				
			||||||
 | 
					                    let mut key = key.borrow_mut();
 | 
				
			||||||
 | 
					                    key.locked ^= true;
 | 
				
			||||||
 | 
					                    key.locked
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if locked {
 | 
				
			||||||
 | 
					                    self.locked_keys.insert(::util::Pointer(key.clone()));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                Some(if locked { lock } else { unlock }.clone())
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            _ => None,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if let Some(view_name) = view_name {
 | 
				
			||||||
 | 
					            if let Err(_e) = self.set_view(view_name.clone()) {
 | 
				
			||||||
 | 
					                eprintln!("No such view: {}, ignoring switch", view_name)
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod procedures {
 | 
					mod procedures {
 | 
				
			||||||
@ -774,4 +839,112 @@ mod procedures {
 | 
				
			|||||||
            c::procedures::eek_are_bounds_inside(bounds, point, origin, angle)
 | 
					            c::procedures::eek_are_bounds_inside(bounds, point, origin, angle)
 | 
				
			||||||
        }) == 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)]
 | 
				
			||||||
 | 
					    mod test {
 | 
				
			||||||
 | 
					        use super::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        use ::layout::test::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// Checks whether the path points to the same boxed instances.
 | 
				
			||||||
 | 
					        /// The instance constraint will be droppable
 | 
				
			||||||
 | 
					        /// when C stops holding references to the data
 | 
				
			||||||
 | 
					        #[test]
 | 
				
			||||||
 | 
					        fn view_has_button() {
 | 
				
			||||||
 | 
					            fn as_ptr<T>(v: &Box<T>) -> *const T {
 | 
				
			||||||
 | 
					                v.as_ref() as *const T
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let state = make_state();
 | 
				
			||||||
 | 
					            let state_clone = state.clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let button = make_button_with_state("1".into(), state);
 | 
				
			||||||
 | 
					            let button_ptr = as_ptr(&button);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            let row = Box::new(Row {
 | 
				
			||||||
 | 
					                buttons: vec!(button),
 | 
				
			||||||
 | 
					                angle: 0,
 | 
				
			||||||
 | 
					                bounds: None
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            let row_ptr = as_ptr(&row);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let view = View {
 | 
				
			||||||
 | 
					                bounds: c::Bounds {
 | 
				
			||||||
 | 
					                    x: 0f64, y: 0f64,
 | 
				
			||||||
 | 
					                    width: 0f64, height: 0f64
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                rows: vec!(row),
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            assert_eq!(
 | 
				
			||||||
 | 
					                find_key_paths(&view, &state_clone.clone()).iter()
 | 
				
			||||||
 | 
					                    .map(|(row, button)| { (as_ptr(row), as_ptr(button)) })
 | 
				
			||||||
 | 
					                    .collect::<Vec<_>>(),
 | 
				
			||||||
 | 
					                vec!(
 | 
				
			||||||
 | 
					                    (row_ptr, button_ptr)
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let view = View {
 | 
				
			||||||
 | 
					                bounds: c::Bounds {
 | 
				
			||||||
 | 
					                    x: 0f64, y: 0f64,
 | 
				
			||||||
 | 
					                    width: 0f64, height: 0f64
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                rows: Vec::new(),
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            assert_eq!(
 | 
				
			||||||
 | 
					                find_key_paths(&view, &state_clone.clone()).is_empty(),
 | 
				
			||||||
 | 
					                true
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod test {
 | 
				
			||||||
 | 
					    use super::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    use std::ffi::CString;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn make_state() -> Rc<RefCell<::keyboard::KeyState>> {
 | 
				
			||||||
 | 
					        Rc::new(RefCell::new(::keyboard::KeyState {
 | 
				
			||||||
 | 
					            pressed: PressType::Released,
 | 
				
			||||||
 | 
					            locked: false,
 | 
				
			||||||
 | 
					            keycodes: Vec::new(),
 | 
				
			||||||
 | 
					            action: Action::SetLevel("default".into()),
 | 
				
			||||||
 | 
					        }))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn make_button_with_state(
 | 
				
			||||||
 | 
					        name: String,
 | 
				
			||||||
 | 
					        state: Rc<RefCell<::keyboard::KeyState>>,
 | 
				
			||||||
 | 
					    ) -> Box<Button> {
 | 
				
			||||||
 | 
					        Box::new(Button {
 | 
				
			||||||
 | 
					            name: CString::new(name.clone()).unwrap(),
 | 
				
			||||||
 | 
					            bounds: c::Bounds {
 | 
				
			||||||
 | 
					                x: 0f64, y: 0f64, width: 0f64, height: 0f64
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            outline_name: CString::new("test").unwrap(),
 | 
				
			||||||
 | 
					            label: Label::Text(CString::new(name).unwrap()),
 | 
				
			||||||
 | 
					            state: state,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -6,12 +6,13 @@ extern crate maplit;
 | 
				
			|||||||
extern crate serde;
 | 
					extern crate serde;
 | 
				
			||||||
extern crate xkbcommon;
 | 
					extern crate xkbcommon;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mod action;
 | 
				
			||||||
pub mod data;
 | 
					pub mod data;
 | 
				
			||||||
pub mod float_ord;
 | 
					pub mod float_ord;
 | 
				
			||||||
pub mod imservice;
 | 
					pub mod imservice;
 | 
				
			||||||
mod keyboard;
 | 
					mod keyboard;
 | 
				
			||||||
mod layout;
 | 
					mod layout;
 | 
				
			||||||
mod resources;
 | 
					mod resources;
 | 
				
			||||||
mod action;
 | 
					mod submission;
 | 
				
			||||||
mod util;
 | 
					mod util;
 | 
				
			||||||
mod xdg;
 | 
					mod xdg;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										68
									
								
								src/submission.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								src/submission.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,68 @@
 | 
				
			|||||||
 | 
					/*! Managing the events belonging to virtual-keyboard interface. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use ::keyboard::{ KeyState, PressType };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Gathers stuff defined in C or called by C
 | 
				
			||||||
 | 
					pub mod c {
 | 
				
			||||||
 | 
					    use std::os::raw::c_void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[repr(transparent)]
 | 
				
			||||||
 | 
					    #[derive(Clone, Copy)]
 | 
				
			||||||
 | 
					    pub struct ZwpVirtualKeyboardV1(*const c_void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[no_mangle]
 | 
				
			||||||
 | 
					    extern "C" {
 | 
				
			||||||
 | 
					        /// Checks if point falls within bounds,
 | 
				
			||||||
 | 
					        /// which are relative to origin and rotated by angle (I think)
 | 
				
			||||||
 | 
					        pub fn eek_virtual_keyboard_v1_key(
 | 
				
			||||||
 | 
					            virtual_keyboard: ZwpVirtualKeyboardV1,
 | 
				
			||||||
 | 
					            timestamp: u32,
 | 
				
			||||||
 | 
					            keycode: u32,
 | 
				
			||||||
 | 
					            press: u32,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct Timestamp(pub u32);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Layout-independent backend. TODO: Have one instance per program or seat
 | 
				
			||||||
 | 
					pub struct VirtualKeyboard(pub c::ZwpVirtualKeyboardV1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl VirtualKeyboard {
 | 
				
			||||||
 | 
					    // TODO: split out keyboard state management
 | 
				
			||||||
 | 
					    pub fn switch(
 | 
				
			||||||
 | 
					        &self,
 | 
				
			||||||
 | 
					        key: &mut KeyState,
 | 
				
			||||||
 | 
					        action: PressType,
 | 
				
			||||||
 | 
					        timestamp: Timestamp,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        key.pressed = action.clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let keycodes_count = key.keycodes.len();
 | 
				
			||||||
 | 
					        for keycode in key.keycodes.iter() {
 | 
				
			||||||
 | 
					            let keycode = keycode - 8;
 | 
				
			||||||
 | 
					            match (&key.pressed, keycodes_count) {
 | 
				
			||||||
 | 
					                // Pressing a key made out of a single keycode is simple:
 | 
				
			||||||
 | 
					                // press on press, release on release.
 | 
				
			||||||
 | 
					                (_, 1) => unsafe {
 | 
				
			||||||
 | 
					                    c::eek_virtual_keyboard_v1_key(
 | 
				
			||||||
 | 
					                        self.0, timestamp.0, keycode, action.clone() as u32
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                // A key made of multiple keycodes
 | 
				
			||||||
 | 
					                // has to submit them one after the other
 | 
				
			||||||
 | 
					                (PressType::Pressed, _) => unsafe {
 | 
				
			||||||
 | 
					                    c::eek_virtual_keyboard_v1_key(
 | 
				
			||||||
 | 
					                        self.0, timestamp.0, keycode, PressType::Pressed as u32
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                    c::eek_virtual_keyboard_v1_key(
 | 
				
			||||||
 | 
					                        self.0, timestamp.0, keycode, PressType::Released as u32
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                // Design choice here: submit multiple all at press time
 | 
				
			||||||
 | 
					                // and do nothing at release time
 | 
				
			||||||
 | 
					                (PressType::Released, _) => {},
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										23
									
								
								src/util.rs
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								src/util.rs
									
									
									
									
									
								
							@ -2,6 +2,7 @@
 | 
				
			|||||||
use std::collections::HashMap;
 | 
					use std::collections::HashMap;
 | 
				
			||||||
use std::rc::Rc;
 | 
					use std::rc::Rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use std::borrow::Borrow;
 | 
				
			||||||
use std::hash::{ Hash, Hasher };
 | 
					use std::hash::{ Hash, Hasher };
 | 
				
			||||||
use std::iter::FromIterator;
 | 
					use std::iter::FromIterator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -111,7 +112,7 @@ pub mod c {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        fn clone_owned(&self) -> T {
 | 
					        fn clone_owned(&self) -> T {
 | 
				
			||||||
            let rc = self.clone_ref();
 | 
					            let rc = self.clone_ref();
 | 
				
			||||||
            let r = rc.borrow();
 | 
					            let r = RefCell::borrow(&rc);
 | 
				
			||||||
            r.to_owned()
 | 
					            r.to_owned()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -133,7 +134,13 @@ pub fn hash_map_map<K, V, F, K1, V1>(map: HashMap<K, V>, mut f: F)
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Compares pointers but not internal values of Rc
 | 
					/// Compares pointers but not internal values of Rc
 | 
				
			||||||
pub struct Pointer<T>(Rc<T>);
 | 
					pub struct Pointer<T>(pub Rc<T>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<T> Pointer<T> {
 | 
				
			||||||
 | 
					    pub fn new(value: T) -> Self {
 | 
				
			||||||
 | 
					        Pointer(Rc::new(value))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<T> Hash for Pointer<T> {
 | 
					impl<T> Hash for Pointer<T> {
 | 
				
			||||||
    fn hash<H: Hasher>(&self, state: &mut H) {
 | 
					    fn hash<H: Hasher>(&self, state: &mut H) {
 | 
				
			||||||
@ -149,6 +156,18 @@ impl<T> PartialEq for Pointer<T> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
impl<T> Eq for Pointer<T> {}
 | 
					impl<T> Eq for Pointer<T> {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<T> Clone for Pointer<T> {
 | 
				
			||||||
 | 
					    fn clone(&self) -> Self {
 | 
				
			||||||
 | 
					        Pointer(self.0.clone())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<T> Borrow<Rc<T>> for Pointer<T> {
 | 
				
			||||||
 | 
					    fn borrow(&self) -> &Rc<T> {
 | 
				
			||||||
 | 
					        &self.0
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(test)]
 | 
					#[cfg(test)]
 | 
				
			||||||
mod tests {
 | 
					mod tests {
 | 
				
			||||||
    use super::*;
 | 
					    use super::*;
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user