From 9522d4e302c83f666b0a806149570ccbb8ff29c2 Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz Date: Fri, 4 Dec 2020 14:07:02 +0000 Subject: [PATCH] renderer: Bring button drawing closer to Rust --- eek/eek-renderer.c | 46 ++++++++++-------- src/drawing.rs | 115 ++++++++++++++++++++++++++++++++++++++++----- src/layout.h | 6 --- src/layout.rs | 62 ++++-------------------- 4 files changed, 138 insertions(+), 91 deletions(-) diff --git a/eek/eek-renderer.c b/eek/eek-renderer.c index 143c553e..d7dcdf43 100644 --- a/eek/eek-renderer.c +++ b/eek/eek-renderer.c @@ -18,8 +18,6 @@ * 02110-1301 USA */ -#include "config.h" - #include #include #include @@ -60,21 +58,21 @@ render_outline (cairo_t *cr, position.x, position.y, position.width, position.height); } -static void render_button_in_context(gint scale_factor, +/// Rust interface +void eek_render_button_in_context(uint32_t scale_factor, cairo_t *cr, GtkStyleContext *ctx, - const struct squeek_button *button) { + EekBounds bounds, + const char *icon_name, + const gchar *label) { /* blank background */ cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.0); cairo_paint (cr); - EekBounds bounds = squeek_button_get_bounds(button); render_outline (cr, ctx, bounds); cairo_paint (cr); /* render icon (if any) */ - const char *icon_name = squeek_button_get_icon_name(button); - if (icon_name) { cairo_surface_t *icon_surface = eek_renderer_get_icon_surface (icon_name, 16, scale_factor); @@ -104,25 +102,27 @@ static void render_button_in_context(gint scale_factor, } } - const gchar *label = squeek_button_get_label(button); if (label) { - render_button_label (cr, ctx, label, squeek_button_get_bounds(button)); + render_button_label (cr, ctx, label, bounds); } } -void -eek_render_button (EekRenderer *self, - cairo_t *cr, - const struct squeek_button *button, - gboolean pressed, - gboolean locked) +/// Prepare context for drawing the button. +/// The context MUST be released using the corresponing "put" procedure +/// before drawing the next button. +/// Interface for Rust. +GtkStyleContext * +eek_get_style_context_for_button (EekRenderer *self, + const char *name, + const char *outline_name, + uint64_t pressed, + uint64_t locked) { GtkStyleContext *ctx = self->button_context; /* Set the name of the button on the widget path, using the name obtained from the button's symbol. */ g_autoptr (GtkWidgetPath) path = NULL; path = gtk_widget_path_copy (gtk_style_context_get_path (ctx)); - const char *name = squeek_button_get_name(button); gtk_widget_path_iter_set_name (path, -1, name); /* Update the style context with the updated widget path. */ @@ -131,14 +131,17 @@ eek_render_button (EekRenderer *self, (pressed) or normal. */ gtk_style_context_set_state(ctx, pressed ? GTK_STATE_FLAG_ACTIVE : GTK_STATE_FLAG_NORMAL); - const char *outline_name = squeek_button_get_outline_name(button); if (locked) { gtk_style_context_add_class(ctx, "locked"); } gtk_style_context_add_class(ctx, outline_name); + return ctx; +} - render_button_in_context(self->scale_factor, cr, ctx, button); - +/// Interface for Rust. +void eek_put_style_context_for_button(GtkStyleContext *ctx, + const char *outline_name, + uint64_t locked) { // Save and restore functions don't work if gtk_render_* was used in between gtk_style_context_set_state(ctx, GTK_STATE_FLAG_NORMAL); gtk_style_context_remove_class(ctx, outline_name); @@ -333,6 +336,11 @@ eek_renderer_set_scale_factor (EekRenderer *renderer, gint scale) renderer->scale_factor = scale; } +/// Rust interface. +uint32_t eek_renderer_get_scale_factor(EekRenderer *renderer) { + return renderer->scale_factor; +} + cairo_surface_t * eek_renderer_get_icon_surface (const gchar *icon_name, gint size, diff --git a/src/drawing.rs b/src/drawing.rs index 5a0f91ad..432ff938 100644 --- a/src/drawing.rs +++ b/src/drawing.rs @@ -5,18 +5,21 @@ use std::cell::RefCell; use ::action::Action; use ::keyboard; -use ::layout::{ Button, Layout }; -use ::layout::c::{ EekGtkKeyboard, Point }; +use ::layout::{ Button, Label, Layout }; +use ::layout::c::{ Bounds, EekGtkKeyboard, Point }; use ::submission::Submission; use glib::translate::FromGlibPtrNone; use gtk::WidgetExt; +use std::ffi::CStr; +use std::ptr; + mod c { use super::*; use cairo_sys; - use std::os::raw::c_void; + use std::os::raw::{ c_char, c_void }; // This is constructed only in C, no need for warnings #[allow(dead_code)] @@ -24,18 +27,45 @@ mod c { #[derive(Clone, Copy)] pub struct EekRenderer(*const c_void); + // This is constructed only in C, no need for warnings + /// Just don't clone this for no reason. + #[allow(dead_code)] + #[repr(transparent)] + #[derive(Clone, Copy)] + pub struct GtkStyleContext(*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( + pub fn eek_renderer_get_scale_factor( renderer: EekRenderer, + ) -> u32; + + #[allow(improper_ctypes)] + pub fn eek_render_button_in_context( + scale_factor: u32, cr: *mut cairo_sys::cairo_t, - button: *const Button, + ctx: GtkStyleContext, + bounds: Bounds, + icon_name: *const c_char, + label: *const c_char, + ); + + #[allow(improper_ctypes)] + pub fn eek_get_style_context_for_button( + renderer: EekRenderer, + name: *const c_char, + outline_name: *const c_char, pressed: u64, locked: u64, + ) -> GtkStyleContext; + + #[allow(improper_ctypes)] + pub fn eek_put_style_context_for_button( + ctx: GtkStyleContext, + outline_name: *const c_char, + locked: u64, ); } @@ -109,16 +139,75 @@ pub fn render_button_at_position( button.size.width, button.size.height ); cr.clip(); - unsafe { - c::eek_render_button( + + let scale_factor = unsafe { + c::eek_renderer_get_scale_factor(renderer) + }; + let bounds = button.get_bounds(); + let (label_c, icon_name_c) = match &button.label { + Label::Text(text) => (text.as_ptr(), ptr::null()), + Label::IconName(name) => { + let l = unsafe { + // CStr doesn't allocate anything, so it only points to + // the 'static str, avoiding a memory leak + CStr::from_bytes_with_nul_unchecked(b"icon\0") + }; + (l.as_ptr(), name.as_ptr()) + }, + }; + + with_button_context( + renderer, + button, + pressed, + locked, + |ctx| unsafe { + // TODO: split into separate procedures: + // draw outline, draw label, draw icon. + c::eek_render_button_in_context( + scale_factor, + cairo::Context::to_raw_none(&cr), + *ctx, + bounds, + icon_name_c, + label_c, + ) + } + ); + + cr.restore(); +} + +fn with_button_context R>( + renderer: c::EekRenderer, + button: &Button, + pressed: keyboard::PressType, + locked: bool, + operation: F, +) -> R { + let outline_name_c = button.outline_name.as_ptr(); + + let ctx = unsafe { + c::eek_get_style_context_for_button( renderer, - cairo::Context::to_raw_none(&cr), - button as *const Button, + button.name.as_ptr(), + outline_name_c, pressed as u64, locked as u64, ) }; - cr.restore(); + + let r = operation(&ctx); + + unsafe { + c::eek_put_style_context_for_button( + ctx, + outline_name_c, + locked as u64, + ) + }; + + r } pub fn queue_redraw(keyboard: EekGtkKeyboard) { diff --git a/src/layout.h b/src/layout.h index 53318bc4..086b4097 100644 --- a/src/layout.h +++ b/src/layout.h @@ -26,12 +26,6 @@ struct squeek_layout_state { struct squeek_layout; -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*); - void squeek_button_print(const struct squeek_button* button); struct transformation squeek_layout_calculate_transformation( diff --git a/src/layout.rs b/src/layout.rs index aa05de77..aa2f4198 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -41,9 +41,7 @@ pub mod c { use super::*; use gtk_sys; - use std::ffi::CStr; use std::os::raw::{ c_char, c_void }; - use std::ptr; use std::ops::{ Add, Sub }; @@ -162,57 +160,6 @@ pub mod c { pub struct LevelKeyboard(*const c_void); // 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_button_get_bounds(button: *const ::layout::Button) -> Bounds { - let button = unsafe { &*button }; - Bounds { - x: 0.0, y: 0.0, - width: button.size.width, height: button.size.height - } - } - - #[no_mangle] - pub extern "C" - fn squeek_button_get_label( - button: *const ::layout::Button - ) -> *const c_char { - let button = unsafe { &*button }; - match &button.label { - Label::Text(text) => text.as_ptr(), - // returning static strings to C is a bit cumbersome - Label::IconName(_) => unsafe { - // CStr doesn't allocate anything, so it only points to - // the 'static str, avoiding a memory leak - CStr::from_bytes_with_nul_unchecked(b"icon\0") - }.as_ptr(), - } - } - - #[no_mangle] - pub extern "C" - fn squeek_button_get_icon_name(button: *const Button) -> *const c_char { - let button = unsafe { &*button }; - match &button.label { - Label::Text(_) => ptr::null(), - Label::IconName(name) => name.as_ptr(), - } - } - - #[no_mangle] - pub extern "C" - fn squeek_button_get_name(button: *const Button) -> *const c_char { - let button = unsafe { &*button }; - button.name.as_ptr() - } - - #[no_mangle] - pub extern "C" - fn squeek_button_get_outline_name(button: *const Button) -> *const c_char { - let button = unsafe { &*button }; - button.outline_name.as_ptr() - } #[no_mangle] pub extern "C" @@ -485,6 +432,15 @@ pub struct Button { pub state: Rc>, } +impl Button { + pub fn get_bounds(&self) -> c::Bounds { + c::Bounds { + x: 0.0, y: 0.0, + width: self.size.width, height: self.size.height, + } + } +} + /// The graphical representation of a row of buttons #[derive(Clone, Debug)] pub struct Row {