# This module provides an interface to the top level bits of GObject # via ruby-ffi. # # Author:: John Cupitt (mailto:jcupitt@gmail.com) # License:: MIT require 'ffi' require 'forwardable' module GObject # we have a number of things we need to inherit in different ways: # # - we want to be able to subclass GObject in Ruby in a simple way # - the layouts of the nested structs need to inherit # - we need to be able to cast between structs which share a base struct # without creating new wrappers or messing up refcounting # - we need automatic gobject refcounting # # the solution is to split the class into four areas which we treat # differently: # # - we have a "wrapper" Ruby class to allow easy subclassing ... this has a # @struct member which holds the actual pointer # - we use "forwardable" to forward the various ffi methods on to the # @struct member ... we arrange things so that subclasses do not need to # do the forwarding themselves # - we have two versions of the struct: a plain one which we can use for # casting that will not change the refcounts # - and a managed one with an unref which we just use for .new # - we separate the struct layout into a separate module to avoid repeating # ourselves class GObject extend Forwardable extend SingleForwardable def_instance_delegators :@struct, :[], :to_ptr def_single_delegators :ffi_struct, :ptr # the layout of the GObject struct module GObjectLayout def self.included base base.class_eval do layout :g_type_instance, :pointer, :ref_count, :uint, :qdata, :pointer end end end # the struct with unref ... manage object lifetime with this class ManagedStruct < FFI::ManagedStruct include GObjectLayout def self.release ptr # GLib::logger.debug("GObject::GObject::ManagedStruct.release") { # "unreffing #{ptr}" # } ::GObject::g_object_unref ptr end end # the plain struct ... cast with this class Struct < FFI::Struct include GObjectLayout end # don't allow ptr == nil, we never want to allocate a GObject struct # ourselves, we just want to wrap GLib-allocated GObjects # # here we use ManagedStruct, not Struct, since this is the ref that will # need the unref def initialize ptr # GLib::logger.debug("GObject::GObject.initialize") {"ptr = #{ptr}"} @struct = ffi_managed_struct.new ptr end # access to the casting struct for this class def ffi_struct self.class.ffi_struct end class << self def ffi_struct self.const_get :Struct end end # access to the managed struct for this class def ffi_managed_struct self.class.ffi_managed_struct end class << self def ffi_managed_struct self.const_get :ManagedStruct end end end class GParamSpec < FFI::Struct # the first few public fields layout :g_type_instance, :pointer, :name, :string, :flags, :uint, :value_type, :GType, :owner_type, :GType end class GParamSpecPtr < FFI::Struct layout :value, GParamSpec.ptr end attach_function :g_param_spec_get_blurb, [GParamSpec.ptr], :string attach_function :g_object_ref, [:pointer], :void attach_function :g_object_unref, [:pointer], :void end