Skip to content
This repository has been archived by the owner on Mar 4, 2024. It is now read-only.

Commit

Permalink
Merge pull request #353 from sdroege/interface-parent-chain-up
Browse files Browse the repository at this point in the history
Add support for chaining up to parent interface implementations
  • Loading branch information
sdroege authored Mar 14, 2021
2 parents 3d65f76 + e3b2217 commit a2568f0
Show file tree
Hide file tree
Showing 8 changed files with 617 additions and 137 deletions.
454 changes: 325 additions & 129 deletions gio/src/subclass/action_group.rs

Large diffs are not rendered by default.

57 changes: 57 additions & 0 deletions gio/src/subclass/action_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,63 @@ pub trait ActionMapImpl: ObjectImpl {
fn remove_action(&self, action_map: &Self::Type, action_name: &str);
}

pub trait ActionMapImplExt: ObjectSubclass {
fn parent_lookup_action(&self, action_map: &Self::Type, action_name: &str) -> Option<Action>;
fn parent_add_action(&self, action_map: &Self::Type, action: &Action);
fn parent_remove_action(&self, action_map: &Self::Type, action_name: &str);
}

impl<T: ActionMapImpl> ActionMapImplExt for T {
fn parent_lookup_action(&self, action_map: &Self::Type, name: &str) -> Option<Action> {
unsafe {
let type_data = Self::type_data();
let parent_iface = type_data.as_ref().get_parent_interface::<ActionMap>()
as *const ffi::GActionMapInterface;

let func = (*parent_iface)
.lookup_action
.expect("no parent \"lookup_action\" implementation");
let ret = func(
action_map.unsafe_cast_ref::<ActionMap>().to_glib_none().0,
name.to_glib_none().0,
);
from_glib_none(ret)
}
}

fn parent_add_action(&self, action_map: &Self::Type, action: &Action) {
unsafe {
let type_data = Self::type_data();
let parent_iface = type_data.as_ref().get_parent_interface::<ActionMap>()
as *const ffi::GActionMapInterface;

let func = (*parent_iface)
.add_action
.expect("no parent \"add_action\" implementation");
func(
action_map.unsafe_cast_ref::<ActionMap>().to_glib_none().0,
action.to_glib_none().0,
);
}
}

fn parent_remove_action(&self, action_map: &Self::Type, action_name: &str) {
unsafe {
let type_data = Self::type_data();
let parent_iface = type_data.as_ref().get_parent_interface::<ActionMap>()
as *const ffi::GActionMapInterface;

let func = (*parent_iface)
.remove_action
.expect("no parent \"remove_action\" implementation");
func(
action_map.unsafe_cast_ref::<ActionMap>().to_glib_none().0,
action_name.to_glib_none().0,
);
}
}
}

unsafe impl<T: ActionMapImpl> IsImplementable<T> for ActionMap
where
<T as ObjectSubclass>::Type: IsA<glib::Object>,
Expand Down
52 changes: 52 additions & 0 deletions gio/src/subclass/list_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,58 @@ pub trait ListModelImpl: ObjectImpl {
fn get_item(&self, list_model: &Self::Type, position: u32) -> Option<glib::Object>;
}

pub trait ListModelImplExt: ObjectSubclass {
fn parent_get_item_type(&self, list_model: &Self::Type) -> glib::Type;
fn parent_get_n_items(&self, list_model: &Self::Type) -> u32;
fn parent_get_item(&self, list_model: &Self::Type, position: u32) -> Option<glib::Object>;
}

impl<T: ListModelImpl> ListModelImplExt for T {
fn parent_get_item_type(&self, list_model: &Self::Type) -> glib::Type {
unsafe {
let type_data = Self::type_data();
let parent_iface = type_data.as_ref().get_parent_interface::<ListModel>()
as *const ffi::GListModelInterface;

let func = (*parent_iface)
.get_item_type
.expect("no parent \"get_item_type\" implementation");
let ret = func(list_model.unsafe_cast_ref::<ListModel>().to_glib_none().0);
from_glib(ret)
}
}

fn parent_get_n_items(&self, list_model: &Self::Type) -> u32 {
unsafe {
let type_data = Self::type_data();
let parent_iface = type_data.as_ref().get_parent_interface::<ListModel>()
as *const ffi::GListModelInterface;

let func = (*parent_iface)
.get_n_items
.expect("no parent \"get_n_items\" implementation");
func(list_model.unsafe_cast_ref::<ListModel>().to_glib_none().0)
}
}

fn parent_get_item(&self, list_model: &Self::Type, position: u32) -> Option<glib::Object> {
unsafe {
let type_data = Self::type_data();
let parent_iface = type_data.as_ref().get_parent_interface::<ListModel>()
as *const ffi::GListModelInterface;

let func = (*parent_iface)
.get_item
.expect("no parent \"get_item\" implementation");
let ret = func(
list_model.unsafe_cast_ref::<ListModel>().to_glib_none().0,
position,
);
from_glib_full(ret)
}
}
}

unsafe impl<T: ListModelImpl> IsImplementable<T> for ListModel
where
<T as ObjectSubclass>::Type: IsA<glib::Object>,
Expand Down
8 changes: 4 additions & 4 deletions gio/src/subclass/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ pub mod prelude {
#[doc(hidden)]
pub use glib::subclass::prelude::*;

pub use super::action_group::ActionGroupImpl;
pub use super::action_map::ActionMapImpl;
pub use super::action_group::{ActionGroupImpl, ActionGroupImplExt};
pub use super::action_map::{ActionMapImpl, ActionMapImplExt};
pub use super::application::{ApplicationImpl, ApplicationImplExt};
pub use super::input_stream::{InputStreamImpl, InputStreamImplExt};
pub use super::io_stream::{IOStreamImpl, IOStreamImplExt};
#[cfg(any(feature = "v2_44", feature = "dox"))]
pub use super::list_model::ListModelImpl;
pub use super::list_model::{ListModelImpl, ListModelImplExt};
pub use super::output_stream::{OutputStreamImpl, OutputStreamImplExt};
pub use super::seekable::SeekableImpl;
pub use super::seekable::{SeekableImpl, SeekableImplExt};
}
127 changes: 127 additions & 0 deletions gio/src/subclass/seekable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use glib::SeekType;
use glib::subclass::prelude::*;

use std::mem;
use std::ptr;

use crate::Cancellable;
use crate::Seekable;
Expand All @@ -31,6 +32,132 @@ pub trait SeekableImpl: ObjectImpl + Send {
) -> Result<(), Error>;
}

pub trait SeekableImplExt: ObjectSubclass {
fn parent_tell(&self, seekable: &Self::Type) -> i64;
fn parent_can_seek(&self, seekable: &Self::Type) -> bool;
fn parent_seek(
&self,
seekable: &Self::Type,
offset: i64,
type_: SeekType,
cancellable: Option<&Cancellable>,
) -> Result<(), Error>;
fn parent_can_truncate(&self, seekable: &Self::Type) -> bool;
fn parent_truncate(
&self,
seekable: &Self::Type,
offset: i64,
cancellable: Option<&Cancellable>,
) -> Result<(), Error>;
}

impl<T: SeekableImpl> SeekableImplExt for T {
fn parent_tell(&self, seekable: &Self::Type) -> i64 {
unsafe {
let type_data = Self::type_data();
let parent_iface =
type_data.as_ref().get_parent_interface::<Seekable>() as *const ffi::GSeekableIface;

let func = (*parent_iface)
.tell
.expect("no parent \"tell\" implementation");
func(seekable.unsafe_cast_ref::<Seekable>().to_glib_none().0)
}
}

fn parent_can_seek(&self, seekable: &Self::Type) -> bool {
unsafe {
let type_data = Self::type_data();
let parent_iface =
type_data.as_ref().get_parent_interface::<Seekable>() as *const ffi::GSeekableIface;

let func = (*parent_iface)
.can_seek
.expect("no parent \"can_seek\" implementation");
let ret = func(seekable.unsafe_cast_ref::<Seekable>().to_glib_none().0);
from_glib(ret)
}
}

fn parent_seek(
&self,
seekable: &Self::Type,
offset: i64,
type_: SeekType,
cancellable: Option<&Cancellable>,
) -> Result<(), Error> {
unsafe {
let type_data = Self::type_data();
let parent_iface =
type_data.as_ref().get_parent_interface::<Seekable>() as *const ffi::GSeekableIface;

let func = (*parent_iface)
.seek
.expect("no parent \"seek\" implementation");

let mut err = ptr::null_mut();
func(
seekable.unsafe_cast_ref::<Seekable>().to_glib_none().0,
offset,
type_.to_glib(),
cancellable.to_glib_none().0,
&mut err,
);

if err.is_null() {
Ok(())
} else {
Err(from_glib_full(err))
}
}
}

fn parent_can_truncate(&self, seekable: &Self::Type) -> bool {
unsafe {
let type_data = Self::type_data();
let parent_iface =
type_data.as_ref().get_parent_interface::<Seekable>() as *const ffi::GSeekableIface;

let func = (*parent_iface)
.can_truncate
.expect("no parent \"can_truncate\" implementation");
let ret = func(seekable.unsafe_cast_ref::<Seekable>().to_glib_none().0);
from_glib(ret)
}
}

fn parent_truncate(
&self,
seekable: &Self::Type,
offset: i64,
cancellable: Option<&Cancellable>,
) -> Result<(), Error> {
unsafe {
let type_data = Self::type_data();
let parent_iface =
type_data.as_ref().get_parent_interface::<Seekable>() as *const ffi::GSeekableIface;

let func = (*parent_iface)
.truncate_fn
.expect("no parent \"truncate\" implementation");

let mut err = ptr::null_mut();
func(
seekable.unsafe_cast_ref::<Seekable>().to_glib_none().0,
offset,
cancellable.to_glib_none().0,
&mut err,
);

if err.is_null() {
Ok(())
} else {
Err(from_glib_full(err))
}
}
}
}

unsafe impl<T: SeekableImpl> IsImplementable<T> for Seekable {
fn interface_init(iface: &mut glib::Interface<Self>) {
let iface = iface.as_mut();
Expand Down
1 change: 1 addition & 0 deletions glib-macros/src/object_subclass_attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ pub fn impl_object_subclass(input: &syn::ItemImpl) -> TokenStream {
static mut DATA: #crate_ident::subclass::TypeData = #crate_ident::subclass::TypeData {
type_: #crate_ident::Type::INVALID,
parent_class: std::ptr::null_mut(),
parent_ifaces: None,
class_data: None,
private_offset: 0,
private_imp_offset: 0,
Expand Down
1 change: 1 addition & 0 deletions glib/src/subclass/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@ mod test {
}
}

#[derive(Clone, Copy)]
#[repr(C)]
pub struct DummyInterface {
parent: gobject_ffi::GTypeInterface,
Expand Down
54 changes: 50 additions & 4 deletions glib/src/subclass/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,10 @@ pub unsafe trait IsSubclassable<T: ObjectSubclass>: crate::object::IsClass {
}

/// Trait for implementable interfaces.
pub unsafe trait IsImplementable<T: ObjectSubclass>: crate::object::IsInterface {
pub unsafe trait IsImplementable<T: ObjectSubclass>: crate::object::IsInterface
where
<Self as ObjectType>::GlibClassType: Copy,
{
/// Override the virtual methods of this interface for the given subclass and do other
/// interface initialization.
///
Expand All @@ -145,8 +148,24 @@ pub unsafe trait IsImplementable<T: ObjectSubclass>: crate::object::IsInterface
unsafe extern "C" fn interface_init<T: ObjectSubclass, A: IsImplementable<T>>(
iface: ffi::gpointer,
_iface_data: ffi::gpointer,
) {
) where
<A as ObjectType>::GlibClassType: Copy,
{
let iface = &mut *(iface as *mut crate::Interface<A>);

let mut data = T::type_data();
if data.as_ref().parent_ifaces.is_none() {
data.as_mut().parent_ifaces = Some(HashMap::new());
}
{
let copy = Box::new(*iface.as_ref());
data.as_mut()
.parent_ifaces
.as_mut()
.unwrap()
.insert(A::static_type(), Box::into_raw(copy) as ffi::gpointer);
}

A::interface_init(iface);
}

Expand All @@ -167,7 +186,10 @@ impl<T: ObjectSubclass> InterfaceList<T> for () {
fn instance_init(_instance: &mut InitializingObject<T>) {}
}

impl<T: ObjectSubclass, A: IsImplementable<T>> InterfaceList<T> for (A,) {
impl<T: ObjectSubclass, A: IsImplementable<T>> InterfaceList<T> for (A,)
where
<A as ObjectType>::GlibClassType: Copy,
{
fn iface_infos() -> Vec<(ffi::GType, gobject_ffi::GInterfaceInfo)> {
vec![(
A::static_type().to_glib(),
Expand Down Expand Up @@ -205,7 +227,10 @@ macro_rules! interface_list_trait(
// and then implements the trait on (A, B, C).
macro_rules! interface_list_trait_impl(
($($name:ident),+) => (
impl<T: ObjectSubclass, $($name: IsImplementable<T>),+> InterfaceList<T> for ( $($name),+ ) {
impl<T: ObjectSubclass, $($name: IsImplementable<T>),+> InterfaceList<T> for ( $($name),+ )
where
$(<$name as ObjectType>::GlibClassType: Copy),+
{
fn iface_infos() -> Vec<(ffi::GType, gobject_ffi::GInterfaceInfo)> {
let mut types = Vec::new();
interface_list_trait_inner_iface_infos!(types, $($name)+)
Expand Down Expand Up @@ -287,6 +312,8 @@ pub struct TypeData {
#[doc(hidden)]
pub parent_class: ffi::gpointer,
#[doc(hidden)]
pub parent_ifaces: Option<HashMap<Type, ffi::gpointer>>,
#[doc(hidden)]
pub class_data: Option<HashMap<Type, Box<dyn Any + Send + Sync>>>,
#[doc(hidden)]
pub private_offset: isize,
Expand All @@ -308,9 +335,28 @@ impl TypeData {
/// This is used for chaining up to the parent class' implementation
/// of virtual methods.
pub fn get_parent_class(&self) -> ffi::gpointer {
debug_assert!(!self.parent_class.is_null());
self.parent_class
}

/// Returns a pointer to the native parent interface struct for interface `type_`.
///
/// This is used for chaining up to the parent interface's implementation
/// of virtual methods.
///
/// # Panics
///
/// This function panics if the type to which the `TypeData` belongs does not implement the
/// given interface or was not registered yet.
pub fn get_parent_interface<I: crate::object::IsInterface>(&self) -> ffi::gpointer {
match self.parent_ifaces {
None => unreachable!("No parent interfaces"),
Some(ref parent_ifaces) => *parent_ifaces
.get(&I::static_type())
.expect("Parent interface not found"),
}
}

/// Returns a pointer to the class implementation specific data.
///
/// This is used for class implementations to store additional data.
Expand Down

0 comments on commit a2568f0

Please sign in to comment.