From c99efc430ccc35e46affa729d5b089bb26309748 Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz Date: Thu, 17 Oct 2019 13:33:23 +0000 Subject: [PATCH] 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. --- eek/eek-gtk-keyboard.c | 150 ++------- eek/eek-gtk-keyboard.h | 2 +- eek/eek-keyboard.c | 59 ---- eek/eek-keyboard.h | 14 +- eek/eek-renderer.c | 36 +- eek/eek-renderer.h | 8 +- eek/eek-types.h | 5 + src/data.rs | 6 +- src/keyboard.h | 9 - src/keyboard.rs | 84 +---- src/layout.h | 37 ++- src/layout.rs | 733 +++++++++++++++++++++++++---------------- src/lib.rs | 3 +- src/submission.rs | 68 ++++ src/util.rs | 23 +- 15 files changed, 626 insertions(+), 611 deletions(-) create mode 100644 src/submission.rs diff --git a/eek/eek-gtk-keyboard.c b/eek/eek-gtk-keyboard.c index 7407bfde..0674f103 100644 --- a/eek/eek-gtk-keyboard.c +++ b/eek/eek-gtk-keyboard.c @@ -37,6 +37,9 @@ #include "eek-gtk-keyboard.h" +#include "eekboard/eekboard-context-service.h" +#include "src/layout.h" + enum { PROP_0, PROP_LAST @@ -58,14 +61,7 @@ typedef struct _EekGtkKeyboardPrivate 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_locked_button (GtkWidget *widget, - struct button_place *place); static void render_released_button (GtkWidget *widget, const struct squeek_button *button); @@ -105,30 +101,10 @@ eek_gtk_keyboard_real_draw (GtkWidget *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); - - struct squeek_view *view = squeek_layout_get_current_view(priv->keyboard->layout); - - /* 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); - } - + // render only a few remaining changes + squeek_layout_draw_all_changed(priv->keyboard->layout, EEK_GTK_KEYBOARD(self)); return FALSE; } @@ -152,84 +128,24 @@ static void depress(EekGtkKeyboard *self, gdouble x, gdouble y, guint32 time) { 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) { - eek_keyboard_press_key( - priv->keyboard, - squeek_button_get_key(button), - time - ); - on_button_pressed(button, view, self); - } + squeek_layout_depress(priv->keyboard->layout, priv->keyboard->manager->virtual_keyboard, + x, y, eek_renderer_get_transformation(priv->renderer), time, self); } static void drag(EekGtkKeyboard *self, gdouble x, gdouble y, guint32 time) { 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); - 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); - } + squeek_layout_drag(priv->keyboard->layout, priv->keyboard->manager->virtual_keyboard, + x, y, eek_renderer_get_transformation(priv->renderer), time, self); } static void release(EekGtkKeyboard *self, guint32 time) { EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self); - struct squeek_view *view = level_keyboard_current(priv->keyboard); - - 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); + squeek_layout_release(priv->keyboard->layout, priv->keyboard->manager->virtual_keyboard, time, self); } static gboolean @@ -304,15 +220,9 @@ eek_gtk_keyboard_real_unmap (GtkWidget *self) eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self)); if (priv->keyboard) { - GList *head; - - for (head = priv->keyboard->pressed_keys; head; head = g_list_next (head)) { - /* 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)); - } + squeek_layout_release_all_only( + priv->keyboard->layout, priv->keyboard->manager->virtual_keyboard, + gdk_event_get_time(NULL)); } GTK_WIDGET_CLASS (eek_gtk_keyboard_parent_class)->unmap (self); @@ -344,13 +254,9 @@ eek_gtk_keyboard_dispose (GObject *object) } if (priv->keyboard) { - GList *head; - - for (head = priv->keyboard->pressed_keys; head; head = g_list_next (head)) { - eek_keyboard_release_key(priv->keyboard, head->data, - gdk_event_get_time(NULL)); - } - + squeek_layout_release_all_only( + priv->keyboard->layout, priv->keyboard->manager->virtual_keyboard, + gdk_event_get_time(NULL)); priv->keyboard = NULL; } @@ -420,18 +326,17 @@ render_pressed_button (GtkWidget *widget, cairo_region_destroy (region); } -static void -render_locked_button (GtkWidget *widget, struct button_place *place) +void +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); - 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); GdkDrawingContext *context = gdk_window_begin_draw_frame (window, region); cairo_t *cr = gdk_drawing_context_get_cairo_context (context); - eek_renderer_render_button (priv->renderer, cr, place, 1.0, TRUE); + eek_renderer_render_button (priv->renderer, cr, &place, 1.0, TRUE); gdk_window_end_draw_frame (window, context); @@ -459,9 +364,8 @@ render_released_button (GtkWidget *widget, cairo_region_destroy (region); } -static void -on_button_pressed (struct squeek_button *button, - struct squeek_view *view, +void +eek_gtk_on_button_pressed (struct button_place place, EekGtkKeyboard *self) { EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self); @@ -470,10 +374,6 @@ on_button_pressed (struct squeek_button *button, if (!priv->renderer) return; - struct button_place place = { - .button = button, - .row = squeek_view_get_row(view, button), - }; if (!place.row) { return; } @@ -489,8 +389,8 @@ on_button_pressed (struct squeek_button *button, #endif } -static void -on_button_released (const struct squeek_button *button, +void +eek_gtk_on_button_released (const struct squeek_button *button, struct squeek_view *view, EekGtkKeyboard *self) { diff --git a/eek/eek-gtk-keyboard.h b/eek/eek-gtk-keyboard.h index 051dc652..35876b51 100644 --- a/eek/eek-gtk-keyboard.h +++ b/eek/eek-gtk-keyboard.h @@ -28,7 +28,7 @@ #include #include -#include "eek-keyboard.h" +typedef struct _LevelKeyboard LevelKeyboard; // including causes weird bugs G_BEGIN_DECLS #define EEK_TYPE_GTK_KEYBOARD (eek_gtk_keyboard_get_type()) diff --git a/eek/eek-keyboard.c b/eek/eek-keyboard.c index 66a0f9a2..40e86fcc 100644 --- a/eek/eek-keyboard.c +++ b/eek/eek-keyboard.c @@ -38,65 +38,6 @@ #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) { squeek_layout_free(self->layout); } diff --git a/eek/eek-keyboard.h b/eek/eek-keyboard.h index bf8bb1f7..60dbf3f3 100644 --- a/eek/eek-keyboard.h +++ b/eek/eek-keyboard.h @@ -35,29 +35,17 @@ G_BEGIN_DECLS /// Keyboard state holder struct _LevelKeyboard { - struct squeek_layout *layout; + struct squeek_layout *layout; // owned struct xkb_keymap *keymap; int keymap_fd; // keymap formatted as XKB string 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 EekboardContextService *manager; // unowned reference }; 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 (LevelKeyboard *keyboard); diff --git a/eek/eek-renderer.c b/eek/eek-renderer.c index 93a32dcf..96960863 100644 --- a/eek/eek-renderer.c +++ b/eek/eek-renderer.c @@ -24,6 +24,7 @@ #include #include +#include "eek-keyboard.h" #include "eek-renderer.h" enum { @@ -454,8 +455,8 @@ eek_renderer_real_render_button (EekRenderer *self, struct squeek_key *key = squeek_button_get_key(place->button); render_button ( self, cr, place, - squeek_key_is_pressed(key), - squeek_key_is_locked (key) + squeek_key_is_pressed(key) != 0, + squeek_key_is_locked (key) != 0 ); cairo_restore (cr); } @@ -959,29 +960,16 @@ eek_are_bounds_inside (EekBounds bounds, EekPoint point, EekPoint origin, int32_ return 0; } -/** - * eek_renderer_find_key_by_position: - * @renderer: The renderer normally used to render the key - * @x: The horizontal widget coordinate of the position to test for a key - * @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); +struct transformation +eek_renderer_get_transformation (EekRenderer *renderer) { + struct transformation failed = {0}; + g_return_val_if_fail (EEK_IS_RENDERER(renderer), failed); EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer); - - /* Transform from widget coordinates to keyboard coordinates */ - EekPoint point = { - .x = (x - priv->origin_x)/priv->scale, - .y = (y - priv->origin_y)/priv->scale, + struct transformation ret = { + .origin_x = priv->origin_x, + .origin_y = priv->origin_y, + .scale = priv->scale, }; - return squeek_view_find_button_by_position(view, point); + return ret; } diff --git a/eek/eek-renderer.h b/eek/eek-renderer.h index 76d151f5..6b903758 100644 --- a/eek/eek-renderer.h +++ b/eek/eek-renderer.h @@ -24,8 +24,8 @@ #include #include -#include "eek-keyboard.h" #include "eek-types.h" +#include "src/layout.h" G_BEGIN_DECLS @@ -102,14 +102,14 @@ void eek_renderer_get_foreground_color EekColor *color); void eek_renderer_set_border_width (EekRenderer *renderer, 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 (EekRenderer *renderer, cairo_t *cr, struct button_place *place, gdouble scale, gboolean rotate); +struct transformation +eek_renderer_get_transformation (EekRenderer *renderer); + G_END_DECLS #endif /* EEK_RENDERER_H */ diff --git a/eek/eek-types.h b/eek/eek-types.h index db951831..1da762f8 100644 --- a/eek/eek-types.h +++ b/eek/eek-types.h @@ -112,5 +112,10 @@ EekColor *eek_color_new (gdouble red, EekColor *eek_color_copy (const EekColor *color); void eek_color_free (EekColor *color); +struct transformation { + gdouble origin_x; + gdouble origin_y; + gdouble scale; +}; G_END_DECLS #endif /* EEK_TYPES_H */ diff --git a/src/data.rs b/src/data.rs index de73712d..525cfc83 100644 --- a/src/data.rs +++ b/src/data.rs @@ -14,7 +14,7 @@ use std::vec::Vec; use xkbcommon::xkb; use ::keyboard::{ - KeyState, + KeyState, PressType, generate_keymap, generate_keycodes, FormattingError }; use ::resources; @@ -346,7 +346,7 @@ impl Layout { ( name.into(), KeyState { - pressed: false, + pressed: PressType::Released, locked: false, keycodes, action, @@ -407,6 +407,8 @@ impl Layout { CString::new(keymap_str) .expect("Invalid keymap string generated") }, + locked_keys: HashSet::new(), + pressed_keys: HashSet::new(), }) } } diff --git a/src/keyboard.h b/src/keyboard.h index 6baeeb8c..554fb382 100644 --- a/src/keyboard.h +++ b/src/keyboard.h @@ -9,13 +9,4 @@ struct squeek_key; uint32_t squeek_key_is_pressed(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 diff --git a/src/keyboard.rs b/src/keyboard.rs index cce713a5..a0c5c8ab 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -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::fmt; use std::io; -use std::rc::Rc; use std::string::FromUtf8Error; use ::action::Action; @@ -17,34 +17,10 @@ pub mod c { use super::*; use ::util::c; - use std::os::raw::c_void; - pub type CKeyState = c::Wrapped; - #[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 - /// 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] pub extern "C" fn squeek_key_is_pressed(key: CKeyState) -> u32 { @@ -57,59 +33,17 @@ pub mod c { fn squeek_key_is_locked(key: CKeyState) -> u32 { return key.clone_owned().locked as u32; } - - #[no_mangle] - pub extern "C" - fn squeek_key_set_locked(key: CKeyState, locked: u32) { - let key = key.clone_ref(); - 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, Copy)] +pub enum PressType { + Released = 0, + Pressed = 1, } #[derive(Debug, Clone)] pub struct KeyState { - pub pressed: bool, + pub pressed: PressType, pub locked: bool, /// A cache of raw keycodes derived from Action::Sumbit given a keymap pub keycodes: Vec, @@ -245,7 +179,7 @@ mod tests { }, keycodes: vec!(9, 10), locked: false, - pressed: false, + pressed: PressType::Released, }, }).unwrap(); diff --git a/src/layout.h b/src/layout.h index 70c87665..d820d229 100644 --- a/src/layout.h +++ b/src/layout.h @@ -4,30 +4,31 @@ #include #include #include "eek/eek-element.h" +#include "eek/eek-gtk-keyboard.h" +#include "eek/eek-types.h" #include "src/keyboard.h" +#include "virtual-keyboard-unstable-v1-client-protocol.h" struct squeek_button; struct squeek_row; struct squeek_view; struct squeek_layout; +/// Represents the path to the button within a view +struct button_place { + const struct squeek_row *row; + const struct squeek_button *button; +}; + int32_t squeek_row_get_angle(const struct squeek_row*); EekBounds squeek_row_get_bounds(const struct squeek_row*); -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); void squeek_row_foreach(struct squeek_row*, ButtonCallback callback, 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*); const char *squeek_button_get_label(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, 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 squeek_layout_place_contents(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); const char *squeek_layout_get_keymap(const 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 diff --git a/src/layout.rs b/src/layout.rs index 7958c0c8..8989744d 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -18,16 +18,17 @@ */ use std::cell::RefCell; -use std::collections::HashMap; +use std::collections::{ HashMap, HashSet }; use std::ffi::CString; use std::rc::Rc; use std::vec::Vec; use ::action::Action; 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 pub mod c { @@ -42,6 +43,10 @@ pub mod c { #[repr(transparent)] pub struct UserData(*const c_void); + #[repr(transparent)] + #[derive(Copy, Clone)] + pub struct EekGtkKeyboard(*const c_void); + /// Defined in eek-types.h #[repr(C)] #[derive(Clone, Debug)] @@ -101,14 +106,6 @@ pub mod c { None => panic!("Row doesn't have any bounds yet"), } } - - /// 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] pub extern "C" @@ -123,12 +120,6 @@ pub mod c { unsafe { callback(button, data) }; } } - - #[no_mangle] - pub extern "C" - fn squeek_row_free(row: *mut ::layout::Row) { - unsafe { Box::from_raw(row) }; // gets dropped - } #[no_mangle] pub extern "C" @@ -187,18 +178,6 @@ pub mod c { let button = unsafe { &*button }; 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] pub extern "C" @@ -229,19 +208,48 @@ pub mod c { fn squeek_layout_free(layout: *mut Layout) { unsafe { Box::from_raw(layout) }; } - - + /// Entry points for more complex procedures and algoithms which span multiple modules pub mod procedures { use super::*; + use ::submission::c::ZwpVirtualKeyboardV1; + #[repr(C)] #[derive(PartialEq, Debug)] - pub struct ButtonPlace { + pub struct CButtonPlace { row: *const Row, button: *const Button, } + + impl<'a> From> 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)] pub struct LevelKeyboard(*const c_void); @@ -254,55 +262,34 @@ pub mod c { origin: Point, angle: i32 ) -> 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)] - pub fn eek_keyboard_set_key_locked( - keyboard: *mut LevelKeyboard, - key: ::keyboard::c::CKeyState, + pub fn eek_gtk_on_button_released( + button: *const Button, + view: *const View, + keyboard: EekGtkKeyboard, ); - } - - #[no_mangle] - pub extern "C" - fn squeek_layout_set_state_from_press( - layout: *mut Layout, - keyboard: *mut LevelKeyboard, - key: ::keyboard::c::CKeyState, - ) { - let layout = unsafe { &mut *layout }; - let view_name = match key.clone_owned().action { - Action::SetLevel(name) => { - Some(name.clone()) - }, - Action::LockLevel { lock, unlock } => { - let locked = { - let key = key.clone_ref(); - 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) - }; - }; + // Button and View inside CButtonPlace are safe to pass to C + // as long as they don't outlive the call + // and nothing dereferences them + #[allow(improper_ctypes)] + pub fn eek_gtk_on_button_pressed( + place: CButtonPlace, + keyboard: EekGtkKeyboard, + ); + + // Button and View inside CButtonPlace are safe to pass to C + // as long as they don't outlive the call + // and nothing dereferences them + #[allow(improper_ctypes)] + pub fn eek_gtk_render_locked_button( + keyboard: EekGtkKeyboard, + place: CButtonPlace, + ); } /// Places each button in order, starting from 0 on the left, @@ -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] pub extern "C" - fn squeek_view_get_row( - view: *mut View, - needle: *const ::layout::Button, - ) -> *mut Row { - let view = unsafe { &mut *view }; - let result = view.rows.iter_mut().find(|row| { - squeek_row_contains(row, needle) - }); - match result { - Some(row) => row.as_mut() as *mut Row, - None => ptr::null_mut(), + fn squeek_layout_release( + layout: *mut Layout, + virtual_keyboard: ZwpVirtualKeyboardV1, // TODO: receive a reference to the backend + time: u32, + ui_keyboard: EekGtkKeyboard, + ) { + 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> = 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, + ); + } + } + + /// Release all buittons but don't redraw + #[no_mangle] + pub extern "C" + 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> = key.borrow(); + layout.release_key( + &virtual_keyboard, + &mut key.clone(), + Timestamp(time) + ); } } #[no_mangle] pub extern "C" - fn squeek_view_find_key( - view: *const View, - needle: ::keyboard::c::CKeyState, - ) -> ButtonPlace { - let view = unsafe { &*view }; - let state = needle.clone_ref(); + 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 paths = ::layout::procedures::find_key_paths(view, &state); + let place = view.find_button_by_position(point); + // the immutable reference to `layout` through `view` + // must be dropped + // before `layout.press_key` borrows it mutably again + let state_place = place.map(|place| {( + place.button.state.clone(), + place.into(), + )}); + + if let Some((mut state, c_place)) = state_place { + layout.press_key( + &VirtualKeyboard(virtual_keyboard), + &mut state, + Timestamp(time), + ); - // 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 } + unsafe { eek_gtk_on_button_pressed(c_place, ui_keyboard) }; + } } + // FIXME: this will work funny + // when 2 touch points are on buttons and moving one after another + // Solution is to have separate pressed lists for each point + #[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> = 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> = 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, + ); + } + } + } #[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 mut row = Row::new(0); - row.buttons.push(button); - 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] - fn view_has_button() { - let state = make_state(); - let state_clone = ::keyboard::c::CKeyState::wrap(state.clone()); - - let button = make_button_with_state("1".into(), state); - let button_ptr = button.as_ref() as *const Button; - - let row = Box::new(Row { - buttons: vec!(button), - angle: 0, - bounds: None - }); - let row_ptr = row.as_ref() as *const Row; - - let view = View { - bounds: Bounds { - x: 0f64, y: 0f64, - width: 0f64, height: 0f64 - }, - rows: vec!(row), - }; - - assert_eq!( - squeek_view_find_key( - &view as *const View, - state_clone.clone(), - ), - ButtonPlace { - row: row_ptr, - button: button_ptr, + fn squeek_layout_draw_all_changed( + layout: *mut Layout, + ui_keyboard: EekGtkKeyboard, + ) { + let layout = unsafe { &mut *layout }; + + for row in &layout.get_current_view().rows { + for button in &row.buttons { + let c_place = CButtonPlace::from( + ButtonPlace { row, button } + ); + let state = RefCell::borrow(&button.state); + match (state.pressed, state.locked) { + (PressType::Released, false) => {} + (PressType::Pressed, _) => unsafe { + eek_gtk_on_button_pressed(c_place, ui_keyboard) + }, + (_, true) => unsafe { + eek_gtk_render_locked_button(ui_keyboard, c_place) + }, } - ); - - let view = View { - bounds: Bounds { - x: 0f64, y: 0f64, - width: 0f64, height: 0f64 - }, - rows: Vec::new(), - }; - assert_eq!( - squeek_view_find_key( - &view as *const View, - state_clone.clone() - ), - ButtonPlace { - row: ptr::null(), - button: ptr::null(), - } - ); + } } } } - - #[cfg(test)] - mod test { - use super::*; - - use ::keyboard::c::CKeyState; +} - pub fn make_state() -> Rc> { - 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>, - ) -> Box