Skip to content
This repository was archived by the owner on Jun 8, 2021. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions cairo-sys-rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -431,8 +431,8 @@ extern "C" {
pub fn cairo_pattern_get_matrix(pattern: *mut cairo_pattern_t, matrix: *mut Matrix);
pub fn cairo_pattern_get_type(pattern: *mut cairo_pattern_t) -> cairo_pattern_type_t;
pub fn cairo_pattern_get_reference_count(pattern: *mut cairo_pattern_t) -> c_uint;
//pub fn cairo_pattern_set_user_data(pattern: *mut cairo_pattern_t, key: *mut cairo_user_data_key_t, user_data: *mut void, destroy: cairo_destroy_func_t) -> cairo_status_t;
//pub fn cairo_pattern_get_user_data(pattern: *mut cairo_pattern_t, key: *mut cairo_user_data_key_t) -> *mut void;
pub fn cairo_pattern_set_user_data(pattern: *mut cairo_pattern_t, key: *const cairo_user_data_key_t, user_data: *mut c_void, destroy: cairo_destroy_func_t) -> cairo_status_t;
pub fn cairo_pattern_get_user_data(pattern: *mut cairo_pattern_t, key: *const cairo_user_data_key_t) -> *mut c_void;

// CAIRO REGIONS
pub fn cairo_region_create() -> *mut cairo_region_t;
Expand Down Expand Up @@ -519,8 +519,8 @@ extern "C" {
pub fn cairo_font_face_status(font_face: *mut cairo_font_face_t) -> cairo_status_t;
pub fn cairo_font_face_get_type(font_face: *mut cairo_font_face_t) -> cairo_font_type_t;
pub fn cairo_font_face_get_reference_count(font_face: *mut cairo_font_face_t) -> c_uint;
//pub fn cairo_font_face_set_user_data(font_face: *mut cairo_font_face_t, key: *mut cairo_user_data_key_t, user_data: *mut void, destroy: cairo_destroy_func_t) -> cairo_status_t;
//pub fn cairo_font_face_get_user_data(font_face: *mut cairo_font_face_t, key: *mut cairo_user_data_key_t) -> *mut void;
pub fn cairo_font_face_set_user_data(font_face: *mut cairo_font_face_t, key: *const cairo_user_data_key_t, user_data: *mut c_void, destroy: cairo_destroy_func_t) -> cairo_status_t;
pub fn cairo_font_face_get_user_data(font_face: *mut cairo_font_face_t, key: *const cairo_user_data_key_t) -> *mut c_void;

// CAIRO SCALED FONT
pub fn cairo_scaled_font_create(font_face: *mut cairo_font_face_t, font_matrix: *const Matrix, ctm: *const Matrix, options: *const cairo_font_options_t) -> *mut cairo_scaled_font_t;
Expand All @@ -538,8 +538,8 @@ extern "C" {
pub fn cairo_scaled_font_get_scale_matrix(scaled_font: *mut cairo_scaled_font_t, scale_matrix: *mut Matrix);
pub fn cairo_scaled_font_get_type(scaled_font: *mut cairo_scaled_font_t) -> cairo_font_type_t;
pub fn cairo_scaled_font_get_reference_count(font_face: *mut cairo_scaled_font_t) -> c_uint;
//pub fn cairo_scaled_font_set_user_data(scaled_font: *mut cairo_scaled_font_t, key: *mut cairo_user_data_key_t, user_data: *mut void, destroy: cairo_destroy_func_t) -> cairo_status_t;
//pub fn cairo_scaled_font_get_user_data(scaled_font: *mut cairo_scaled_font_t, key: *mut cairo_user_data_key_t) -> *mut void;
pub fn cairo_scaled_font_set_user_data(scaled_font: *mut cairo_scaled_font_t, key: *const cairo_user_data_key_t, user_data: *mut c_void, destroy: cairo_destroy_func_t) -> cairo_status_t;
pub fn cairo_scaled_font_get_user_data(scaled_font: *mut cairo_scaled_font_t, key: *const cairo_user_data_key_t) -> *mut c_void;

// CAIRO FONT OPTIONS
pub fn cairo_font_options_create() -> *mut cairo_font_options_t;
Expand Down Expand Up @@ -580,8 +580,8 @@ extern "C" {
pub fn cairo_surface_status(surface: *mut cairo_surface_t) -> cairo_status_t;
pub fn cairo_surface_get_type(surface: *mut cairo_surface_t) -> cairo_surface_type_t;
pub fn cairo_surface_reference(surface: *mut cairo_surface_t) -> *mut cairo_surface_t;
pub fn cairo_surface_get_user_data(surface: *mut cairo_surface_t, key: *mut cairo_user_data_key_t) -> *mut c_void;
pub fn cairo_surface_set_user_data(surface: *mut cairo_surface_t, key: *mut cairo_user_data_key_t, user_data: *mut c_void, destroy: cairo_destroy_func_t) -> cairo_status_t;
pub fn cairo_surface_get_user_data(surface: *mut cairo_surface_t, key: *const cairo_user_data_key_t) -> *mut c_void;
pub fn cairo_surface_set_user_data(surface: *mut cairo_surface_t, key: *const cairo_user_data_key_t, user_data: *mut c_void, destroy: cairo_destroy_func_t) -> cairo_status_t;
pub fn cairo_surface_get_reference_count(surface: *mut cairo_surface_t) -> c_uint;
pub fn cairo_surface_mark_dirty(surface: *mut cairo_surface_t);
pub fn cairo_surface_create_similar(surface: *mut cairo_surface_t, content: cairo_content_t, width: c_int, height: c_int) -> *mut cairo_surface_t;
Expand Down Expand Up @@ -862,8 +862,8 @@ extern "C" {
pub fn cairo_device_get_type(device: *mut cairo_device_t) -> cairo_device_type_t;
pub fn cairo_device_reference(device: *mut cairo_device_t) -> *mut cairo_device_t;
pub fn cairo_device_get_reference_count(device: *mut cairo_device_t) -> c_uint;
// pub fn cairo_device_set_user_data() -> cairo_status_t;
// pub fn cairo_device_get_user_data() -> *mut c_void;
pub fn cairo_device_set_user_data(device: *mut cairo_device_t, key: *const cairo_user_data_key_t, user_data: *mut c_void, destroy: cairo_destroy_func_t) -> cairo_status_t;
pub fn cairo_device_get_user_data(device: *mut cairo_device_t, key: *const cairo_user_data_key_t) -> *mut c_void;
pub fn cairo_device_acquire(device: *mut cairo_device_t) -> cairo_status_t;
pub fn cairo_device_release(device: *mut cairo_device_t);
pub fn cairo_device_observer_elapsed(device: *mut cairo_device_t) -> c_double;
Expand Down
6 changes: 6 additions & 0 deletions src/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,12 @@ impl Device {
}
}
}

user_data_methods! {
Device::to_raw_none,
ffi::cairo_device_get_user_data,
ffi::cairo_device_set_user_data,
}
}

#[cfg(feature = "use_glib")]
Expand Down
6 changes: 6 additions & 0 deletions src/font/font_face.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@ impl FontFace {
ffi::cairo_ft_font_face_unset_synthesize(self.to_raw_none(), synth_flags.into())
}
}

user_data_methods! {
FontFace::to_raw_none,
ffi::cairo_font_face_get_user_data,
ffi::cairo_font_face_set_user_data,
}
}

#[cfg(not(feature = "use_glib"))]
Expand Down
6 changes: 6 additions & 0 deletions src/font/scaled_font.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,12 @@ impl ScaledFont {

matrix
}

user_data_methods! {
ScaledFont::to_raw_none,
ffi::cairo_scaled_font_get_user_data,
ffi::cairo_scaled_font_set_user_data,
}
}

#[cfg(not(feature = "use_glib"))]
Expand Down
26 changes: 14 additions & 12 deletions src/image_surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use std::ops::{Deref, DerefMut};
use std::rc::Rc;
use std::slice;

#[cfg(feature = "use_glib")]
Expand All @@ -14,7 +15,7 @@ use ::enums::{
};

use BorrowError;
use surface::{Surface, SurfaceExt, SurfacePriv};
use surface::{Surface, SurfaceExt};
use Status;
use std::fmt;

Expand Down Expand Up @@ -48,23 +49,26 @@ impl ImageSurface {

pub fn create_for_data<D: AsMut<[u8]> + 'static>(data: D, format: Format, width: i32, height: i32,
stride: i32) -> Result<ImageSurface, Status> {
let mut data: Box<AsMut<[u8]> + 'static> = Box::new(data);
let mut data: Box<dyn AsMut<[u8]>> = Box::new(data);

let (ptr, len) = {
let mut data = (*data).as_mut();
let mut data: &mut [u8] = (*data).as_mut();

(data.as_mut_ptr(), data.len())
};

assert!(len >= (height * stride) as usize);
unsafe {
let r = ImageSurface::from_raw_full(
ffi::cairo_image_surface_create_for_data(ptr, format.into(), width, height, stride));
match r {
Ok(surface) => surface.set_user_data(&IMAGE_SURFACE_DATA, Box::new(data)).map (|_| surface),
Err(status) => Err(status)
}
let result = unsafe {
ImageSurface::from_raw_full(
ffi::cairo_image_surface_create_for_data(ptr, format.into(), width, height, stride)
)
};
if let Ok(surface) = &result {
static IMAGE_SURFACE_DATA: crate::UserDataKey<Box<dyn AsMut<[u8]>>> =
crate::UserDataKey::new();
surface.set_user_data(&IMAGE_SURFACE_DATA, Rc::new(data))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this have to be in an Rc?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see, because of the getter

}
result
}

pub fn get_data(&mut self) -> Result<ImageSurfaceData, BorrowError> {
Expand Down Expand Up @@ -103,8 +107,6 @@ impl ImageSurface {
}
}

static IMAGE_SURFACE_DATA: () = ();

#[cfg(feature = "use_glib")]
impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for ImageSurface {
type Storage = &'a Surface;
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ macro_rules! gvalue_impl {
}
}

pub use user_data::UserDataKey;

pub use context::{
Context,
RectangleList,
Expand Down Expand Up @@ -150,6 +152,7 @@ pub use xcb::{

pub mod prelude;

#[macro_use] mod user_data;
mod constants;
pub use constants::*;
mod utils;
Expand Down
16 changes: 16 additions & 0 deletions src/patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ pub enum Pattern {
Mesh(Mesh),
}

impl Pattern {
user_data_methods! {
Pattern::as_ptr,
ffi::cairo_pattern_get_user_data,
ffi::cairo_pattern_set_user_data,
}
}

impl PatternTrait for Pattern {
type PatternType = Pattern;

Expand Down Expand Up @@ -170,6 +178,14 @@ macro_rules! pattern_type(
}
}

impl $pattern_type {
user_data_methods! {
$pattern_type::as_ptr,
ffi::cairo_pattern_get_user_data,
ffi::cairo_pattern_set_user_data,
}
}

impl Clone for $pattern_type {
fn clone(&self) -> Self {
$pattern_type {
Expand Down
36 changes: 11 additions & 25 deletions src/surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use std::mem;
use std::ptr;
use std::slice;
use libc::{c_ulong, c_void};
Expand Down Expand Up @@ -113,6 +112,11 @@ impl Surface {

let user_data = Box::into_raw(b);

unsafe extern "C" fn unbox<T>(data: *mut c_void) {
let data: Box<T> = Box::from_raw(data as *mut T);
drop(data);
}

let status = unsafe {
let mime_type = CString::new(mime_type).unwrap();
ffi::cairo_surface_set_mime_data(self.to_raw_none(),
Expand Down Expand Up @@ -196,6 +200,12 @@ impl Surface {
})
}
}

user_data_methods! {
Surface::to_raw_none,
ffi::cairo_surface_get_user_data,
ffi::cairo_surface_set_user_data,
}
}

#[cfg(feature = "use_glib")]
Expand Down Expand Up @@ -323,30 +333,6 @@ impl fmt::Display for MappedImageSurface {
}
}

pub(crate) trait SurfacePriv {
unsafe fn set_user_data<K, T>(&self, key: &K, data: Box<T>) -> Result<(), Status>;
}

impl<O: AsRef<Surface>> SurfacePriv for O {
unsafe fn set_user_data<K, T>(&self, key: &K, data: Box<T>) -> Result<(), Status> {
let ptr: *mut T = Box::into_raw(data);

assert_eq!(mem::size_of::<*mut c_void>(), mem::size_of_val(&ptr));

let status = ffi::cairo_surface_set_user_data(self.as_ref().0, key as *const _ as *mut _,
ptr as *mut c_void, Some(unbox::<T>));
match Status::from(status) {
Status::Success => Ok(()),
x => Err(x),
}
}
}

unsafe extern "C" fn unbox<T>(data: *mut c_void) {
let data: Box<T> = Box::from_raw(data as *mut T);
drop(data);
}

#[cfg(test)]
mod tests {
use Format;
Expand Down
121 changes: 121 additions & 0 deletions src/user_data.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
use std::marker::PhantomData;

use ffi::cairo_user_data_key_t;

/// A key for indexing user data in various cairo types.
///
/// Some types like [`Surface`] have `get_user_data`, `set_user_data`, and `remove_user_data`
/// methods that take `&'static UserDataKey`, where the address of that reference is significant.
///
/// To reliably have a stable address, the expected usage is to define a `static` item:
///
/// ```
/// use cairo::UserDataKey;
/// static FOO: UserDataKey<String> = UserDataKey::new();
///
/// # fn foo(surface: &cairo::Surface) {
/// surface.get_user_data(&FOO)
/// # ; }
pub struct UserDataKey<T> {
pub(crate) ffi: cairo_user_data_key_t,
marker: PhantomData<*const T>,
}

unsafe impl<T> Sync for UserDataKey<T> {}

impl<T> UserDataKey<T> {
pub const fn new() -> Self {
UserDataKey {
ffi: cairo_user_data_key_t { unused: 0 },
marker: PhantomData,
}
}
}

// In a safe API for user data we can’t make `get_user_data`
// transfer full ownership of the value to the caller (e.g. by returning `Box<T>`)
// because `self` still has a pointer to that value
// and `get_user_data` could be called again with the same key.
//
// We also can’t return a `&T` reference that borrows from `self`
// because the value could be removed with `remove_user_data` or replaced with `set_user_data`
// while the borrow still needs to be valid.
// (Borrowing with `&mut self` would not help as `Self` can be itself reference-counted.)
//
// Therefore the value must be reference-counted.
//
// We use `Rc` over `Arc` because the types implementing these methods are `!Send` and `!Sync`.
// See <https://github.com/gtk-rs/cairo/issues/256>

macro_rules! user_data_methods {
($as_ptr: expr, $ffi_get_user_data: path, $ffi_set_user_data: path,) => {
/// Attach user data to `self` for the given `key`.
pub fn set_user_data<T: 'static>(&self, key: &'static crate::UserDataKey<T>,
value: std::rc::Rc<T>)
{
unsafe extern "C" fn destructor<T>(ptr: *mut libc::c_void) {
let ptr: *const T = ptr as _;
drop(std::rc::Rc::from_raw(ptr))
}
// Safety:
//
// The destructor’s cast and `from_raw` are symetric
// with the `into_raw` and cast below.
// They both transfer ownership of one strong reference:
// neither of them touches the reference count.
let ptr: *const T = std::rc::Rc::into_raw(value);
let ptr = ptr as *mut T as *mut libc::c_void;
let result = unsafe {
$ffi_set_user_data($as_ptr(self), &key.ffi, ptr, Some(destructor::<T>))
};
Status::from(result).ensure_valid()
}

/// Return the user data previously attached to `self` with the given `key`, if any.
pub fn get_user_data<T: 'static>(&self, key: &'static crate::UserDataKey<T>)
-> Option<std::rc::Rc<T>>
{
// Safety:
//
// `Rc::from_raw` would normally take ownership of a strong reference for this pointer.
// But `self` still has a copy of that pointer and `get_user_data` can be called again
// with the same key.
// We use `ManuallyDrop` to avoid running the destructor of that first `Rc`,
// and return a cloned one (which increments the reference count).
//
// If `ffi_get_user_data` returns a non-null pointer,
// there was a previous call to `ffi_set_user_data` with a key with the same address.
// Either:
//
// * This was a call to a Rust `Self::set_user_data` method.
// Because that method takes a `&'static` reference,
// the key used then must live at that address until the end of the process.
// Because `UserDataKey<T>` has a non-zero size regardless of `T`,
// no other `UserDataKey<U>` value can have the same address.
// Therefore the `T` type was the same then at it is now and `cast` is type-safe.
//
// * Or, it is technically possible that the `set` call was to the C function directly,
// with a `cairo_user_data_key_t` in heap-allocated memory that was then freed,
// then `Box::new(UserDataKey::new()).leak()` was used to create a `&'static`
// that happens to have the same address because the allocator for `Box`
// reused that memory region.
// Since this involves a C (or FFI) call *and* is so far out of “typical” use
// of the user data functionality, we consider this a misuse of an unsafe API.
unsafe {
let ptr = $ffi_get_user_data($as_ptr(self), &key.ffi);
let ptr: *mut T = std::ptr::NonNull::new(ptr)?.cast().as_ptr();
let rc = std::mem::ManuallyDrop::new(std::rc::Rc::from_raw(ptr));
Some(std::rc::Rc::clone(&rc))
}
}

/// Unattach from `self` the user data associated with `key`, if any.
/// If there is no other `Rc` strong reference, the data is destroyed.
pub fn remove_user_data<T: 'static>(&self, key: &'static crate::UserDataKey<T>) {
let result = unsafe {
$ffi_set_user_data($as_ptr(self), &key.ffi, std::ptr::null_mut(), None)
};
Status::from(result).ensure_valid()
}
};
}