Commit f9fbd3fb authored by Dorota Czaplejewicz's avatar Dorota Czaplejewicz
Browse files

rendering: Simplify Cairo context usage, remove unneeded calls.

Moved Cairo context usage to Rust, and rearranged ctx setup (position) to happen in one place.

Removed render calls that were overwritten on each draw call anyway.
parent 6e32a2ef
......@@ -324,6 +324,9 @@ name = "rs"
version = "0.1.0"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
......
......@@ -10,6 +10,15 @@ serde = { version = "1.0.*", features = ["derive"] }
serde_yaml = "0.8.*"
xkbcommon = { version = "0.4.*", features = ["wayland"] }
[dependencies.cairo-rs]
version = "0.5.*"
[dependencies.cairo-sys-rs]
version = ""
[dependencies.gdk]
version = ""
[dependencies.gio]
version = ""
features = ["v2_44"]
......@@ -22,7 +31,6 @@ features = ["v2_44"]
version = ""
features = ["v2_44"]
[dependencies.gtk]
version = "0.5.*"
features = ["v3_22"]
......
......@@ -85,7 +85,7 @@ eek_gtk_keyboard_real_draw (GtkWidget *self,
cairo_t *cr)
{
EekGtkKeyboardPrivate *priv =
eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
GtkAllocation allocation;
gtk_widget_get_allocation (self, &allocation);
......@@ -101,10 +101,7 @@ 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);
// render only a few remaining changes
squeek_layout_draw_all_changed(priv->keyboard->layout, EEK_GTK_KEYBOARD(self));
return FALSE;
}
......@@ -113,7 +110,7 @@ eek_gtk_keyboard_real_size_allocate (GtkWidget *self,
GtkAllocation *allocation)
{
EekGtkKeyboardPrivate *priv =
eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
if (priv->renderer)
eek_renderer_set_allocation_size (priv->renderer,
......@@ -231,7 +228,7 @@ static void
eek_gtk_keyboard_real_unmap (GtkWidget *self)
{
EekGtkKeyboardPrivate *priv =
eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
if (priv->keyboard) {
squeek_layout_release_all_only(
......@@ -322,25 +319,9 @@ eek_gtk_keyboard_new (LevelKeyboard *keyboard)
return GTK_WIDGET(ret);
}
static void
render_pressed_button (GtkWidget *widget,
struct button_place *place)
{
EekGtkKeyboard *self = EEK_GTK_KEYBOARD (widget);
EekRenderer *eek_gtk_keyboard_get_renderer(EekGtkKeyboard *self) {
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
GdkWindow *window = gtk_widget_get_window (widget);
cairo_region_t *region = gdk_window_get_clip_region (window);
GdkDrawingContext *context = gdk_window_begin_draw_frame (window, region);
cairo_t *cr = gdk_drawing_context_get_cairo_context (context);
eek_renderer_render_button (priv->renderer, cr, place, 1.0, TRUE, FALSE);
/*
eek_renderer_render_key (priv->renderer, cr, key, 1.5, TRUE);
*/
gdk_window_end_draw_frame (window, context);
cairo_region_destroy (region);
return priv->renderer;
}
void
......@@ -381,31 +362,6 @@ render_released_button (GtkWidget *widget,
cairo_region_destroy (region);
}
void
eek_gtk_on_button_pressed (struct button_place place,
EekGtkKeyboard *self)
{
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
/* renderer may have not been set yet if the widget is a popup */
if (!priv->renderer)
return;
if (!place.row) {
return;
}
render_pressed_button (GTK_WIDGET(self), &place);
gtk_widget_queue_draw (GTK_WIDGET(self));
#if HAVE_LIBCANBERRA
ca_gtk_play_for_widget (widget, 0,
CA_PROP_EVENT_ID, "button-pressed",
CA_PROP_EVENT_DESCRIPTION, "virtual key pressed",
CA_PROP_APPLICATION_ID, "org.fedorahosted.Eekboard",
NULL);
#endif
}
void
eek_gtk_on_button_released (const struct squeek_button *button,
struct squeek_view *view,
......
......@@ -63,7 +63,7 @@ static void eek_renderer_render_button_label (EekRenderer *self, cairo_t *cr, Gt
const struct squeek_button *button);
static void invalidate (EekRenderer *renderer);
static void render_button (EekRenderer *self,
void eek_render_button (EekRenderer *self,
cairo_t *cr, const struct squeek_button *button,
gboolean pressed, gboolean locked);
......@@ -92,7 +92,7 @@ create_keyboard_surface_button_callback (struct squeek_button *button,
bounds.height);
cairo_clip (data->cr);
render_button (data->renderer, data->cr, button, FALSE, FALSE);
eek_render_button (data->renderer, data->cr, button, FALSE, FALSE);
cairo_restore (data->cr);
}
......@@ -147,8 +147,6 @@ render_keyboard_surface (EekRenderer *renderer, struct squeek_view *view)
cairo_save (data.cr);
cairo_scale (data.cr, priv->scale, priv->scale);
EekBounds bounds = squeek_view_get_bounds (view);
cairo_translate (data.cr, bounds.x, bounds.y);
......@@ -186,12 +184,10 @@ render_outline (cairo_t *cr,
}
static void render_button_in_context(EekRenderer *self,
gdouble scale,
gint scale_factor,
cairo_t *cr,
GtkStyleContext *ctx,
const struct squeek_button *button,
gboolean active) {
const struct squeek_button *button) {
/* blank background */
cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.0);
cairo_paint (cr);
......@@ -234,8 +230,8 @@ static void render_button_in_context(EekRenderer *self,
eek_renderer_render_button_label (self, cr, ctx, button);
}
static void
render_button (EekRenderer *self,
void
eek_render_button (EekRenderer *self,
cairo_t *cr,
const struct squeek_button *button,
gboolean pressed,
......@@ -263,7 +259,7 @@ render_button (EekRenderer *self,
}
gtk_style_context_add_class(ctx, outline_name);
render_button_in_context(self, priv->scale, priv->scale_factor, cr, ctx, button, pressed);
render_button_in_context(self, priv->scale_factor, cr, ctx, button);
// Save and restore functions don't work if gtk_render_* was used in between
gtk_style_context_set_state(ctx, GTK_STATE_FLAG_NORMAL);
......@@ -417,7 +413,7 @@ eek_renderer_render_button (EekRenderer *self,
cairo_translate (cr, bounds.x, bounds.y);
eek_renderer_apply_transformation_for_button (cr, place, scale);
render_button (
eek_render_button (
self, cr, place->button,
is_pressed,
is_locked
......@@ -425,21 +421,19 @@ eek_renderer_render_button (EekRenderer *self,
cairo_restore (cr);
}
static void
eek_renderer_real_render_keyboard (EekRenderer *self,
void
eek_renderer_render_keyboard (EekRenderer *self,
cairo_t *cr)
{
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
cairo_pattern_t *source;
g_return_if_fail (priv->keyboard);
g_return_if_fail (priv->allocation_width > 0.0);
g_return_if_fail (priv->allocation_height > 0.0);
cairo_save (cr);
cairo_save(cr);
cairo_translate (cr, priv->origin_x, priv->origin_y);
cairo_scale (cr, priv->scale, priv->scale);
if (priv->keyboard_surface)
cairo_surface_destroy (priv->keyboard_surface);
......@@ -450,10 +444,11 @@ eek_renderer_real_render_keyboard (EekRenderer *self,
render_keyboard_surface (self, squeek_layout_get_current_view(priv->keyboard->layout));
cairo_set_source_surface (cr, priv->keyboard_surface, 0.0, 0.0);
source = cairo_get_source (cr);
cairo_pattern_t *source = cairo_get_source (cr);
cairo_pattern_set_extend (source, CAIRO_EXTEND_PAD);
cairo_paint (cr);
squeek_layout_draw_all_changed(priv->keyboard->layout, self, cr);
cairo_restore (cr);
}
......@@ -529,8 +524,6 @@ eek_renderer_class_init (EekRendererClass *klass)
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GParamSpec *pspec;
klass->render_keyboard = eek_renderer_real_render_keyboard;
gobject_class->set_property = eek_renderer_set_property;
gobject_class->get_property = eek_renderer_get_property;
gobject_class->dispose = eek_renderer_dispose;
......@@ -783,14 +776,6 @@ eek_renderer_get_icon_surface (const gchar *icon_name,
return surface;
}
void
eek_renderer_render_keyboard (EekRenderer *renderer,
cairo_t *cr)
{
g_return_if_fail (EEK_IS_RENDERER(renderer));
EEK_RENDERER_GET_CLASS(renderer)->render_keyboard (renderer, cr);
}
static gboolean
sign (EekPoint *p1, EekPoint *p2, EekPoint *p3)
{
......
......@@ -25,7 +25,6 @@
#include <pango/pangocairo.h>
#include "eek-types.h"
#include "src/layout.h"
G_BEGIN_DECLS
......@@ -36,9 +35,6 @@ struct _EekRendererClass
{
GObjectClass parent_class;
void (* render_keyboard) (EekRenderer *self,
cairo_t *cr);
cairo_surface_t *(* get_icon_surface) (EekRenderer *self,
const gchar *icon_name,
gint size,
......
......@@ -88,5 +88,14 @@ struct transformation {
gdouble origin_y;
gdouble scale;
};
struct squeek_button;
struct squeek_row;
/// Represents the path to the button within a view
struct button_place {
const struct squeek_row *row;
const struct squeek_button *button;
};
G_END_DECLS
#endif /* EEK_TYPES_H */
/*! Drawing the UI */
use cairo;
use std::cell::RefCell;
use ::keyboard;
use ::layout;
use ::layout::{ Button, Layout };
use ::layout::c::{ EekGtkKeyboard, Point };
use gdk::{ WindowExt, DrawingContextExt };
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,
);
pub fn eek_gtk_keyboard_get_renderer(
keyboard: EekGtkKeyboard,
) -> EekRenderer;
pub fn eek_renderer_get_transformation(
renderer: EekRenderer,
) -> layout::c::Transformation;
}
#[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,
);
}
}
}
}
}
/// 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();
}
/// Renders a single frame
/// Opens a frame on `keyboard`'s `GdkWindow`, attempt to get a drawing context,
/// calls `f`, closes the frame.
/// If the drawing context was successfully retrieved, returns `f` call result.
pub fn render_as_frame<F, T>(keyboard: EekGtkKeyboard, mut f: F) -> Option<T>
where F: FnMut(c::EekRenderer, &cairo::Context) -> T
{
let renderer = unsafe { c::eek_gtk_keyboard_get_renderer(keyboard) };
let widget = unsafe { gtk::Widget::from_glib_none(keyboard.0) };
widget.get_window()
.and_then(|window| {
// Need to split the `.and_then` chain here
// because `window` needs to be in scope
// for the references deeper inside.
window.get_clip_region()
// contrary to the docs, `Region` gets destroyed automatically
.and_then(|region| window.begin_draw_frame(&region))
.and_then(|drawctx| {
let ret: Option<T> = drawctx.get_cairo_context()
.map(|cr| {
let transformation = unsafe {
c::eek_renderer_get_transformation(renderer)
};
cr.translate(
transformation.origin_x,
transformation.origin_y,
);
cr.scale(
transformation.scale,
transformation.scale,
);
queue_redraw(keyboard);
f(renderer, &cr) // finally!
});
// This must always happen after `begin_draw_frame`,
// enven if `get_cairo_context` fails.
window.end_draw_frame(&drawctx);
ret
})
})
}
......@@ -11,7 +11,7 @@ use ::action::Action;
use std::io::Write;
use std::iter::{ FromIterator, IntoIterator };
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PressType {
Released = 0,
Pressed = 1,
......
......@@ -5,6 +5,7 @@
#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 "virtual-keyboard-unstable-v1-client-protocol.h"
......@@ -13,17 +14,9 @@ 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*);
......@@ -71,5 +64,5 @@ 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);
#endif
......@@ -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,10 +35,12 @@ 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
......@@ -55,6 +58,23 @@ 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)]
......@@ -66,6 +86,51 @@ pub mod c {
pub height: f64
}
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,
}
}
}
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);
......@@ -212,7 +277,7 @@ pub mod c {
use super::*;
use ::submission::c::ZwpVirtualKeyboardV1;
#[repr(C)]
#[derive(PartialEq, Debug)]
pub struct CButtonPlace {
......@@ -228,42 +293,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,