]> sigrok.org Git - libsigrok.git/blob - bindings/ruby/classes.i
Bindings: Make Ruby bindings build with Ruby 2.0
[libsigrok.git] / bindings / ruby / classes.i
1 /*
2  * This file is part of the libsigrok project.
3  *
4  * Copyright (C) 2016 Aurelien Jacobs <aurel@gnuage.org>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 %define DOCSTRING
21 "
22 = Introduction
23
24 The sigrok Ruby API provides an object-oriented Ruby interface to the
25 functionality in libsigrok. It is built on top of the libsigrokcxx C++ API.
26
27 = Getting started
28
29 Usage of the sigrok Ruby API needs to begin with a call to Context.create().
30 This will create the global libsigrok context and returns a Context object.
31 Methods on this object provide access to the hardware drivers, input and output
32 formats supported by the library, as well as means of creating other objects
33 such as sessions and triggers.
34
35 = Error handling
36
37 When any libsigrok C API call returns an error, an Error exception is raised,
38 which provides access to the error code and description."
39 %enddef
40
41 %module(docstring=DOCSTRING) sigrok
42
43 %{
44 #include <stdio.h>
45 #include <glibmm.h>
46
47 #include "config.h"
48 %}
49
50 %include "../swig/templates.i"
51
52 %{
53 static const char *string_from_ruby(VALUE obj)
54 {
55     switch (TYPE(obj)) {
56     case T_STRING:
57         return StringValueCStr(obj);
58     case T_SYMBOL:
59         return rb_id2name(SYM2ID(obj));
60     default:
61         throw sigrok::Error(SR_ERR_ARG);
62     }
63 }
64
65 /* Convert from Glib::Variant to native Ruby types. */
66 static VALUE variant_to_ruby(Glib::VariantBase variant)
67 {
68     if (variant.is_of_type(Glib::VARIANT_TYPE_BOOL)) {
69         return Glib::VariantBase::cast_dynamic< Glib::Variant<bool> >(variant).get() ? Qtrue : Qfalse;
70     } else if (variant.is_of_type(Glib::VARIANT_TYPE_BYTE)) {
71         return UINT2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant<unsigned char> >(variant).get());
72     } else if (variant.is_of_type(Glib::VARIANT_TYPE_INT16)) {
73         return INT2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant<gint16> >(variant).get());
74     } else if (variant.is_of_type(Glib::VARIANT_TYPE_UINT16)) {
75         return UINT2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant<guint16> >(variant).get());
76     } else if (variant.is_of_type(Glib::VARIANT_TYPE_INT32)) {
77         return INT2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant<gint32> >(variant).get());
78     } else if (variant.is_of_type(Glib::VARIANT_TYPE_UINT32)) {
79         return UINT2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant<guint32> >(variant).get());
80     } else if (variant.is_of_type(Glib::VARIANT_TYPE_INT64)) {
81         return LL2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant<gint64> >(variant).get());
82     } else if (variant.is_of_type(Glib::VARIANT_TYPE_UINT64)) {
83         return ULL2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant<guint64> >(variant).get());
84     } else if (variant.is_of_type(Glib::VARIANT_TYPE_DOUBLE)) {
85         return DBL2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant<double> >(variant).get());
86     } else if (variant.is_of_type(Glib::VARIANT_TYPE_STRING)) {
87         auto str = Glib::VariantBase::cast_dynamic< Glib::Variant<std::string> >(variant).get();
88         return rb_str_new(str.c_str(), str.length());
89     } else if (variant.is_of_type(Glib::VARIANT_TYPE_VARIANT)) {
90         auto var = Glib::VariantBase::cast_dynamic< Glib::Variant<Glib::VariantBase> >(variant).get();
91         return variant_to_ruby(var);
92     } else if (variant.is_container()) {
93         Glib::VariantContainerBase container = Glib::VariantBase::cast_dynamic< Glib::VariantContainerBase > (variant);
94         gsize count = container.get_n_children();
95         if (container.is_of_type(Glib::VARIANT_TYPE_DICTIONARY)) {
96             VALUE hash = rb_hash_new();
97             for (gsize i = 0; i < count; i++) {
98                 Glib::VariantContainerBase entry = Glib::VariantBase::cast_dynamic< Glib::VariantContainerBase > (container.get_child(i));
99                 VALUE key = variant_to_ruby(entry.get_child(0));
100                 VALUE val = variant_to_ruby(entry.get_child(1));
101                 rb_hash_aset(hash, key, val);
102             }
103             return hash;
104         } else if (container.is_of_type(Glib::VARIANT_TYPE_ARRAY) ||
105                    container.is_of_type(Glib::VARIANT_TYPE_TUPLE)) {
106             VALUE array = rb_ary_new2(count);
107             for (gsize i = 0; i < count; i++) {
108                 VALUE val = variant_to_ruby(container.get_child(i));
109                 rb_ary_store(array, i, val);
110             }
111             return array;
112         }
113     } else {
114         SWIG_exception(SWIG_TypeError, ("TODO: GVariant(" + variant.get_type().get_string() + ") -> Ruby").c_str());
115     }
116 }
117 %}
118
119 /* Map from Glib::Variant to native Ruby types. */
120 %typemap(out) Glib::VariantBase {
121     $result = variant_to_ruby($1);
122 }
123
124 /* Map from Glib::VariantContainer to native Ruby types. */
125 %typemap(out) Glib::VariantContainerBase {
126     $result = variant_to_ruby($1);
127 }
128
129 /* Map from callable Ruby Proc to LogCallbackFunction */
130 %typemap(in) sigrok::LogCallbackFunction {
131     if (!rb_obj_is_proc($input))
132         SWIG_exception(SWIG_TypeError, "Expected a callable Ruby object");
133
134     std::shared_ptr<VALUE> proc(new VALUE($input), rb_gc_unregister_address);
135     rb_gc_register_address(proc.get());
136
137     $1 = [=] (const sigrok::LogLevel *loglevel, std::string message) {
138         VALUE log_obj = SWIG_NewPointerObj(
139                 SWIG_as_voidptr(loglevel), SWIGTYPE_p_sigrok__LogLevel, 0);
140
141         VALUE string_obj = rb_external_str_new_with_enc(message.c_str(), message.length(), rb_utf8_encoding());
142
143         VALUE args = rb_ary_new3(2, log_obj, string_obj);
144         rb_proc_call(*proc.get(), args);
145     };
146 }
147
148 /* Map from callable Ruby Proc to SessionStoppedCallback */
149 %typemap(in) sigrok::SessionStoppedCallback {
150     if (!rb_obj_is_proc($input))
151         SWIG_exception(SWIG_TypeError, "Expected a callable Ruby object");
152
153     std::shared_ptr<VALUE> proc(new VALUE($input), rb_gc_unregister_address);
154     rb_gc_register_address(proc.get());
155
156     $1 = [=] () {
157         rb_proc_call(*proc.get(), rb_ary_new());
158     };
159 }
160
161 /* Map from callable Ruby Proc to DatafeedCallbackFunction */
162 %typemap(in) sigrok::DatafeedCallbackFunction {
163     if (!rb_obj_is_proc($input))
164         SWIG_exception(SWIG_TypeError, "Expected a callable Ruby object");
165
166     std::shared_ptr<VALUE> proc(new VALUE($input), rb_gc_unregister_address);
167     rb_gc_register_address(proc.get());
168
169     $1 = [=] (std::shared_ptr<sigrok::Device> device,
170             std::shared_ptr<sigrok::Packet> packet) {
171         VALUE device_obj = SWIG_NewPointerObj(
172             SWIG_as_voidptr(new std::shared_ptr<sigrok::Device>(device)),
173             SWIGTYPE_p_std__shared_ptrT_sigrok__Device_t, SWIG_POINTER_OWN);
174
175         VALUE packet_obj = SWIG_NewPointerObj(
176             SWIG_as_voidptr(new std::shared_ptr<sigrok::Packet>(packet)),
177             SWIGTYPE_p_std__shared_ptrT_sigrok__Packet_t, SWIG_POINTER_OWN);
178
179         VALUE args = rb_ary_new3(2, device_obj, packet_obj);
180         rb_proc_call(*proc.get(), args);
181     };
182 }
183
184 /* Cast PacketPayload pointers to correct subclass type. */
185 %ignore sigrok::Packet::payload;
186 %rename sigrok::Packet::_payload payload;
187 %extend sigrok::Packet
188 {
189     VALUE _payload()
190     {
191         if ($self->type() == sigrok::PacketType::HEADER) {
192             return SWIG_NewPointerObj(
193                 SWIG_as_voidptr(new std::shared_ptr<sigrok::Header>(dynamic_pointer_cast<sigrok::Header>($self->payload()))),
194                 SWIGTYPE_p_std__shared_ptrT_sigrok__Header_t, SWIG_POINTER_OWN);
195         } else if ($self->type() == sigrok::PacketType::META) {
196             return SWIG_NewPointerObj(
197                 SWIG_as_voidptr(new std::shared_ptr<sigrok::Meta>(dynamic_pointer_cast<sigrok::Meta>($self->payload()))),
198                 SWIGTYPE_p_std__shared_ptrT_sigrok__Meta_t, SWIG_POINTER_OWN);
199         } else if ($self->type() == sigrok::PacketType::ANALOG) {
200             return SWIG_NewPointerObj(
201                 SWIG_as_voidptr(new std::shared_ptr<sigrok::Analog>(dynamic_pointer_cast<sigrok::Analog>($self->payload()))),
202                 SWIGTYPE_p_std__shared_ptrT_sigrok__Analog_t, SWIG_POINTER_OWN);
203         } else if ($self->type() == sigrok::PacketType::LOGIC) {
204             return SWIG_NewPointerObj(
205                 SWIG_as_voidptr(new std::shared_ptr<sigrok::Logic>(dynamic_pointer_cast<sigrok::Logic>($self->payload()))),
206                 SWIGTYPE_p_std__shared_ptrT_sigrok__Logic_t, SWIG_POINTER_OWN);
207         } else {
208             return Qnil;
209         }
210     }
211 }
212
213 %{
214
215 #include "libsigrokcxx/libsigrokcxx.hpp"
216
217 /* Convert from a Ruby type to Glib::Variant, according to config key data type. */
218 Glib::VariantBase ruby_to_variant_by_key(VALUE input, const sigrok::ConfigKey *key)
219 {
220     enum sr_datatype type = (enum sr_datatype) key->data_type()->id();
221
222     if (type == SR_T_UINT64 && RB_TYPE_P(input, T_FIXNUM))
223         return Glib::Variant<guint64>::create(NUM2ULL(input));
224     if (type == SR_T_UINT64 && RB_TYPE_P(input, T_BIGNUM))
225         return Glib::Variant<guint64>::create(NUM2ULL(input));
226     else if (type == SR_T_STRING && RB_TYPE_P(input, T_STRING))
227         return Glib::Variant<Glib::ustring>::create(string_from_ruby(input));
228     else if (type == SR_T_STRING && RB_TYPE_P(input, T_SYMBOL))
229         return Glib::Variant<Glib::ustring>::create(string_from_ruby(input));
230     else if (type == SR_T_BOOL && RB_TYPE_P(input, T_TRUE))
231         return Glib::Variant<bool>::create(true);
232     else if (type == SR_T_BOOL && RB_TYPE_P(input, T_FALSE))
233         return Glib::Variant<bool>::create(false);
234     else if (type == SR_T_FLOAT && RB_TYPE_P(input, T_FLOAT))
235         return Glib::Variant<double>::create(RFLOAT_VALUE(input));
236     else if (type == SR_T_INT32 && RB_TYPE_P(input, T_FIXNUM))
237         return Glib::Variant<gint32>::create(NUM2INT(input));
238     else
239         throw sigrok::Error(SR_ERR_ARG);
240 }
241
242 /* Convert from a Ruby type to Glib::Variant, according to Option data type. */
243 Glib::VariantBase ruby_to_variant_by_option(VALUE input, std::shared_ptr<sigrok::Option> option)
244 {
245     Glib::VariantBase variant = option->default_value();
246
247     if (variant.is_of_type(Glib::VARIANT_TYPE_UINT64) && RB_TYPE_P(input, T_FIXNUM))
248         return Glib::Variant<guint64>::create(NUM2ULL(input));
249     else if (variant.is_of_type(Glib::VARIANT_TYPE_UINT64) && RB_TYPE_P(input, T_BIGNUM))
250         return Glib::Variant<guint64>::create(NUM2ULL(input));
251     else if (variant.is_of_type(Glib::VARIANT_TYPE_STRING) && RB_TYPE_P(input, T_STRING))
252         return Glib::Variant<Glib::ustring>::create(string_from_ruby(input));
253     else if (variant.is_of_type(Glib::VARIANT_TYPE_STRING) && RB_TYPE_P(input, T_SYMBOL))
254         return Glib::Variant<Glib::ustring>::create(string_from_ruby(input));
255     else if (variant.is_of_type(Glib::VARIANT_TYPE_BOOL) && RB_TYPE_P(input, T_TRUE))
256         return Glib::Variant<bool>::create(true);
257     else if (variant.is_of_type(Glib::VARIANT_TYPE_BOOL) && RB_TYPE_P(input, T_FALSE))
258         return Glib::Variant<bool>::create(false);
259     else if (variant.is_of_type(Glib::VARIANT_TYPE_DOUBLE) && RB_TYPE_P(input, T_FLOAT))
260         return Glib::Variant<double>::create(RFLOAT_VALUE(input));
261     else if (variant.is_of_type(Glib::VARIANT_TYPE_INT32) && RB_TYPE_P(input, T_FIXNUM))
262         return Glib::Variant<gint32>::create(NUM2INT(input));
263     else
264         throw sigrok::Error(SR_ERR_ARG);
265 }
266
267 struct hash_to_map_options_params {
268     std::map<std::string, std::shared_ptr<sigrok::Option> > options;
269     std::map<std::string, Glib::VariantBase> output;
270 };
271
272 int convert_option(VALUE key, VALUE val, VALUE in) {
273     struct hash_to_map_options_params *params;
274     params = (struct hash_to_map_options_params *)in;
275
276     auto k = string_from_ruby(key);
277     auto v = ruby_to_variant_by_option(val, params->options[k]);
278     params->output[k] = v;
279
280     return ST_CONTINUE;
281 }
282
283 /* Convert from a Ruby hash to a std::map<std::string, Glib::VariantBase> */
284 std::map<std::string, Glib::VariantBase> hash_to_map_options(VALUE hash,
285     std::map<std::string, std::shared_ptr<sigrok::Option> > options)
286 {
287     if (!RB_TYPE_P(hash, T_HASH))
288         throw sigrok::Error(SR_ERR_ARG);
289
290     struct hash_to_map_options_params params = { options };
291     rb_hash_foreach(hash, (int (*)(ANYARGS))convert_option, (VALUE)&params);
292
293     return params.output;
294 }
295
296 int convert_option_by_key(VALUE key, VALUE val, VALUE in) {
297     std::map<const sigrok::ConfigKey *, Glib::VariantBase> *options;
298     options = (std::map<const sigrok::ConfigKey *, Glib::VariantBase> *)in;
299
300     auto k = sigrok::ConfigKey::get_by_identifier(string_from_ruby(key));
301     auto v = ruby_to_variant_by_key(val, k);
302     (*options)[k] = v;
303
304     return ST_CONTINUE;
305 }
306
307 %}
308
309 /* Ignore these methods, we will override them below. */
310 %ignore sigrok::Analog::data;
311 %ignore sigrok::Driver::scan;
312 %ignore sigrok::Input::send;
313 %ignore sigrok::InputFormat::create_input;
314 %ignore sigrok::OutputFormat::create_output;
315
316 %include "doc.i"
317
318 %define %attributevector(Class, Type, Name, Get)
319 %alias sigrok::Class::_ ## Get #Name;
320 %enddef
321
322 %define %attributemap(Class, Type, Name, Get)
323 %alias sigrok::Class::_ ## Get #Name;
324 %enddef
325
326 %define %enumextras(Class)
327 %extend sigrok::Class
328 {
329     VALUE to_s()
330     {
331         std::string str = $self->name();
332         return rb_external_str_new_with_enc(str.c_str(), str.length(), rb_utf8_encoding());
333     }
334
335     bool operator==(void *other)
336     {
337         return (long) $self == (long) other;
338     }
339 }
340 %enddef
341
342 %include "../swig/classes.i"
343
344 /* Replace the original Driver.scan with a keyword arguments version. */
345 %rename sigrok::Driver::_scan scan;
346 %extend sigrok::Driver
347 {
348     std::vector<std::shared_ptr<sigrok::HardwareDevice> > _scan(VALUE kwargs = rb_hash_new())
349     {
350         if (!RB_TYPE_P(kwargs, T_HASH))
351             throw sigrok::Error(SR_ERR_ARG);
352
353         std::map<const sigrok::ConfigKey *, Glib::VariantBase> options;
354         rb_hash_foreach(kwargs, (int (*)(ANYARGS))convert_option_by_key, (VALUE)&options);
355
356         return $self->scan(options);
357     }
358 }
359
360 /* Support Input.send() with string argument. */
361 %rename sigrok::Input::_send send;
362 %extend sigrok::Input
363 {
364     void _send(VALUE data)
365     {
366         data = StringValue(data);
367         return $self->send(RSTRING_PTR(data), RSTRING_LEN(data));
368     }
369 }
370
371 /* Support InputFormat.create_input() with keyword arguments. */
372 %rename sigrok::InputFormat::_create_input create_input;
373 %extend sigrok::InputFormat
374 {
375     std::shared_ptr<sigrok::Input> _create_input(VALUE hash = rb_hash_new())
376     {
377         return $self->create_input(hash_to_map_options(hash, $self->options()));
378     }
379 }
380
381 /* Support OutputFormat.create_output() with keyword arguments. */
382 %rename sigrok::OutputFormat::_create_output create_output;
383 %extend sigrok::OutputFormat
384 {
385     std::shared_ptr<sigrok::Output> _create_output(
386         std::shared_ptr<sigrok::Device> device, VALUE hash = rb_hash_new())
387     {
388         return $self->create_output(device,
389             hash_to_map_options(hash, $self->options()));
390     }
391
392     std::shared_ptr<sigrok::Output> _create_output(string filename,
393         std::shared_ptr<sigrok::Device> device, VALUE hash = rb_hash_new())
394     {
395         return $self->create_output(filename, device,
396             hash_to_map_options(hash, $self->options()));
397     }
398 }
399
400 /* Support config_set() with Ruby input types. */
401 %extend sigrok::Configurable
402 {
403     void config_set(const ConfigKey *key, VALUE input)
404     {
405         $self->config_set(key, ruby_to_variant_by_key(input, key));
406     }
407 }
408
409 /* Return Ruby array from Analog::data(). */
410 %rename sigrok::Analog::_data data;
411 %extend sigrok::Analog
412 {
413     VALUE _data()
414     {
415         int num_channels = $self->channels().size();
416         int num_samples  = $self->num_samples();
417         float *data = (float *) $self->data_pointer();
418         VALUE channels = rb_ary_new2(num_channels);
419         for(int i = 0; i < num_channels; i++) {
420             VALUE samples = rb_ary_new2(num_samples);
421             for (int j = 0; j < num_samples; j++) {
422                 rb_ary_store(samples, j, DBL2NUM(data[i*num_samples+j]));
423             }
424             rb_ary_store(channels, i, samples);
425         }
426         return channels;
427     }
428 }