File: value.rb

package info (click to toggle)
ruby-gir-ffi 0.16.1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 924 kB
  • sloc: ruby: 6,849; makefile: 4
file content (204 lines) | stat: -rw-r--r-- 5,263 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# frozen_string_literal: true

GObject.load_class :Value

module GObject
  # Overrides for GValue, GObject's generic value container structure.
  class Value
    setup_instance_method! :init

    METHOD_MAP = {
      TYPE_INVALID => [:get_none, :set_none],
      # TYPE_NONE is skipped
      TYPE_INTERFACE => [:get_object, :set_instance_enhanced],
      TYPE_CHAR => [:get_schar, :set_schar],
      TYPE_UCHAR => [:get_uchar, :set_uchar],
      TYPE_BOOLEAN => [:get_boolean, :set_boolean],
      TYPE_INT => [:get_int, :set_int],
      TYPE_UINT => [:get_uint,           :set_uint],
      TYPE_LONG => [:get_long,           :set_long],
      TYPE_ULONG => [:get_ulong,          :set_ulong],
      TYPE_INT64 => [:get_int64,          :set_int64],
      TYPE_UINT64 => [:get_uint64, :set_uint64],
      TYPE_ENUM => [:get_enum_enhanced, :set_enum_enhanced],
      TYPE_FLAGS => [:get_flags_enhanced, :set_flags_enhanced],
      TYPE_FLOAT => [:get_float,          :set_float],
      TYPE_DOUBLE => [:get_double,         :set_double],
      TYPE_STRING => [:get_string,         :set_string],
      TYPE_POINTER => [:get_pointer, :set_pointer],
      TYPE_BOXED => [:get_boxed,          :set_boxed],
      TYPE_PARAM => [:get_param,          :set_param],
      TYPE_OBJECT => [:get_object, :set_instance_enhanced],
      TYPE_GTYPE => [:get_gtype, :set_gtype],
      TYPE_VARIANT => [:get_variant, :set_variant]
    }.freeze

    # TODO: Give more generic name
    def self.wrap_ruby_value(val)
      new.tap { |gv| gv.__send__ :set_ruby_value, val }
    end

    def self.from(val)
      case val
      when self
        val
      else
        wrap_ruby_value val
      end
    end

    def self.for_gtype(gtype)
      new.tap do |it|
        it.init gtype
      end
    end

    # TODO: Combine with wrap_ruby_value
    def self.wrap_instance(instance)
      new.tap do |it|
        it.init GObject.type_from_instance instance
        it.set_instance instance
      end
    end

    def self.copy_value_to_pointer(value, pointer, offset = 0)
      target = wrap(pointer + offset)
      target.init(value.current_gtype)
      Lib.g_value_copy value, target unless value.uninitialized?
    end

    CLASS_TO_GTYPE_MAP = {
      NilClass => TYPE_INVALID,
      TrueClass => TYPE_BOOLEAN,
      FalseClass => TYPE_BOOLEAN,
      Integer => TYPE_INT,
      String => TYPE_STRING
    }.freeze

    # Overrides for existing Value methods
    module Overrides
      def set_value(val)
        send set_method, val
      end

      alias_method :value=, :set_value

      def current_gtype
        struct[:g_type]
      end

      def current_fundamental_type
        GObject.type_fundamental current_gtype
      end

      def current_gtype_name
        GObject.type_name current_gtype
      end

      def get_value
        value = get_value_plain
        if current_fundamental_type == TYPE_BOXED
          wrap_boxed value
        else
          value
        end
      end

      def get_value_plain
        send get_method
      end

      def uninitialized?
        current_gtype == TYPE_INVALID
      end

      def init(type)
        Lib.g_value_init self, type unless [TYPE_NONE, TYPE_INVALID].include? type
        self
      end

      private

      def set_ruby_value(val)
        init_for_ruby_value val if uninitialized?
        set_value val
      end

      def init_for_ruby_value(val)
        return init val.class.gtype if val.class.respond_to? :gtype

        CLASS_TO_GTYPE_MAP.each do |klass, type|
          return init type if val.is_a? klass
        end
        raise "Can't handle #{val.class}"
      end

      def set_none(_val); end

      def get_none; end

      def set_instance_enhanced(val)
        check_type_compatibility val if val
        set_instance val
      end

      def set_enum_enhanced(val)
        val = current_gtype_class.to_native(val, nil)
        set_enum val
      end

      def get_enum_enhanced
        current_gtype_class.wrap(get_enum)
      end

      def set_flags_enhanced(val)
        val = current_gtype_class.to_native(val, nil)
        set_flags val
      end

      def get_flags_enhanced
        current_gtype_class.wrap(get_flags)
      end

      def current_gtype_class
        GirFFI::Builder.build_by_gtype(current_gtype)
      end

      def check_type_compatibility(val)
        if GObject::Value.type_compatible(GObject.type_from_instance(val), current_gtype)
          return
        end

        raise ArgumentError, "#{val.class} is incompatible with #{current_gtype_name}"
      end

      def wrap_boxed(boxed)
        case current_gtype
        when TYPE_STRV
          GLib::Strv.wrap boxed
        when TYPE_HASH_TABLE
          GLib::HashTable.wrap [:gpointer, :gpointer], boxed
        when TYPE_ARRAY
          GLib::Array.wrap nil, boxed
        else
          current_gtype_class.wrap(boxed) unless boxed.null?
        end
      end

      def get_method
        method_map_entry.first
      end

      def set_method
        method_map_entry.last
      end

      def method_map_entry
        METHOD_MAP[current_gtype] || METHOD_MAP[current_fundamental_type] ||
          raise("No method map entry for '#{current_gtype_name}'")
      end
    end

    prepend Overrides
  end
end