mvz/ruby-gir-ffi

View on GitHub
lib/gir_ffi/callback_base.rb

Summary

Maintainability
A
0 mins
Test Coverage
# frozen_string_literal: true

require "gir_ffi/type_base"
require "set"

module GirFFI
  # Base module for callbacks and vfuncs.
  # NOTE: Another option would be to derive this class from FFI::Function,
  # allowing a more natural implementation of from_native, to_native and wrap.
  class CallbackBase < Proc
    extend TypeBase
    extend FFI::DataConverter

    def self.native_type
      FFI::Type::POINTER
    end

    # TODO: Return instance of this class
    def self.from_native(value, _context)
      return nil if !value || value.null?

      FFI::Function.new(gir_ffi_builder.return_ffi_type,
                        gir_ffi_builder.argument_ffi_types, value)
    end

    def self.to_native(value, _context)
      case value
      when CallbackBase
        value.to_native
      when FFI::Function
        value
      end
    end

    def self.wrap(ptr)
      from_native ptr, nil
    end

    CALLBACKS = Set.new

    def self.store_callback(prc)
      CALLBACKS.add prc
    end

    def self.drop_callback(prc)
      CALLBACKS.delete prc
    end

    # Create Callback from a Proc. Makes sure arguments are properly wrapped,
    # and the callback is stored to prevent garbage collection.
    def self.from(prc)
      wrap_proc(prc).tap do |cb|
        store_callback cb
      end
    end

    def self.wrap_proc(prc)
      return unless prc

      new do |*args|
        call_with_argument_mapping(prc, *args)
      rescue StandardError => e
        GLib::MainLoop.handle_exception e
      end
    end

    def self.to_ffi_type
      self
    end

    def to_native
      @to_native ||= begin
                       builder = self.class.gir_ffi_builder
                       FFI::Function.new(builder.return_ffi_type,
                                         builder.argument_ffi_types, self)
                     end
    end

    def to_ptr
      to_native.to_ptr
    end

    def self.copy_value_to_pointer(value, pointer, offset = 0)
      pointer.put_pointer offset, to_native(value, nil)
    end

    def self.get_value_from_pointer(pointer, offset)
      from_native pointer.get_pointer(offset), nil
    end
  end
end