Merge branch 'drop_key' into 'master'

Simplify renderer

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

View File

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

126
src/drawing.rs Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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