]> sigrok.org Git - libsigrok.git/blob - bindings/ruby/classes.i
output/csv: use intermediate time_t var, silence compiler warning
[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 "config.h"
45
46 #include <stdio.h>
47 #include <glibmm.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     return 0; /* Dummy, to avoid a compiler warning. */
117 }
118 %}
119
120 /* Map from Glib::Variant to native Ruby types. */
121 %typemap(out) Glib::VariantBase {
122     $result = variant_to_ruby($1);
123 }
124
125 /* Map from Glib::VariantContainer to native Ruby types. */
126 %typemap(out) Glib::VariantContainerBase {
127     $result = variant_to_ruby($1);
128 }
129
130 /* Map from callable Ruby Proc to LogCallbackFunction */
131 %typemap(in) sigrok::LogCallbackFunction {
132     if (!rb_obj_is_proc($input))
133         SWIG_exception(SWIG_TypeError, "Expected a callable Ruby object");
134
135     std::shared_ptr<VALUE> proc(new VALUE($input), rb_gc_unregister_address);
136     rb_gc_register_address(proc.get());
137
138     $1 = [=] (const sigrok::LogLevel *loglevel, std::string message) {
139         VALUE log_obj = SWIG_NewPointerObj(
140                 SWIG_as_voidptr(loglevel), SWIGTYPE_p_sigrok__LogLevel, 0);
141
142         VALUE string_obj = rb_external_str_new_with_enc(message.c_str(), message.length(), rb_utf8_encoding());
143
144         VALUE args = rb_ary_new3(2, log_obj, string_obj);
145         rb_proc_call(*proc.get(), args);
146     };
147 }
148
149 /* Map from callable Ruby Proc to SessionStoppedCallback */
150 %typemap(in) sigrok::SessionStoppedCallback {
151     if (!rb_obj_is_proc($input))
152         SWIG_exception(SWIG_TypeError, "Expected a callable Ruby object");
153
154     std::shared_ptr<VALUE> proc(new VALUE($input), rb_gc_unregister_address);
155     rb_gc_register_address(proc.get());
156
157     $1 = [=] () {
158         rb_proc_call(*proc.get(), rb_ary_new());
159     };
160 }
161
162 /* Map from callable Ruby Proc to DatafeedCallbackFunction */
163 %typemap(in) sigrok::DatafeedCallbackFunction {
164     if (!rb_obj_is_proc($input))
165         SWIG_exception(SWIG_TypeError, "Expected a callable Ruby object");
166
167     std::shared_ptr<VALUE> proc(new VALUE($input), rb_gc_unregister_address);
168     rb_gc_register_address(proc.get());
169
170     $1 = [=] (std::shared_ptr<sigrok::Device> device,
171             std::shared_ptr<sigrok::Packet> packet) {
172         VALUE device_obj = SWIG_NewPointerObj(
173             SWIG_as_voidptr(new std::shared_ptr<sigrok::Device>(device)),
174             SWIGTYPE_p_std__shared_ptrT_sigrok__Device_t, SWIG_POINTER_OWN);
175
176         VALUE packet_obj = SWIG_NewPointerObj(
177             SWIG_as_voidptr(new std::shared_ptr<sigrok::Packet>(packet)),
178             SWIGTYPE_p_std__shared_ptrT_sigrok__Packet_t, SWIG_POINTER_OWN);
179
180         VALUE args = rb_ary_new3(2, device_obj, packet_obj);
181         rb_proc_call(*proc.get(), args);
182     };
183 }
184
185 /* Cast PacketPayload pointers to correct subclass type. */
186 %ignore sigrok::Packet::payload;
187 %rename sigrok::Packet::_payload payload;
188 %extend sigrok::Packet
189 {
190     VALUE _payload()
191     {
192         if ($self->type() == sigrok::PacketType::HEADER) {
193             return SWIG_NewPointerObj(
194                 SWIG_as_voidptr(new std::shared_ptr<sigrok::Header>(dynamic_pointer_cast<sigrok::Header>($self->payload()))),
195                 SWIGTYPE_p_std__shared_ptrT_sigrok__Header_t, SWIG_POINTER_OWN);
196         } else if ($self->type() == sigrok::PacketType::META) {
197             return SWIG_NewPointerObj(
198                 SWIG_as_voidptr(new std::shared_ptr<sigrok::Meta>(dynamic_pointer_cast<sigrok::Meta>($self->payload()))),
199                 SWIGTYPE_p_std__shared_ptrT_sigrok__Meta_t, SWIG_POINTER_OWN);
200         } else if ($self->type() == sigrok::PacketType::ANALOG) {
201             return SWIG_NewPointerObj(
202                 SWIG_as_voidptr(new std::shared_ptr<sigrok::Analog>(dynamic_pointer_cast<sigrok::Analog>($self->payload()))),
203                 SWIGTYPE_p_std__shared_ptrT_sigrok__Analog_t, SWIG_POINTER_OWN);
204         } else if ($self->type() == sigrok::PacketType::LOGIC) {
205             return SWIG_NewPointerObj(
206                 SWIG_as_voidptr(new std::shared_ptr<sigrok::Logic>(dynamic_pointer_cast<sigrok::Logic>($self->payload()))),
207                 SWIGTYPE_p_std__shared_ptrT_sigrok__Logic_t, SWIG_POINTER_OWN);
208         } else {
209             return Qnil;
210         }
211     }
212 }
213
214 %{
215
216 #include "libsigrokcxx/libsigrokcxx.hpp"
217
218 /* Convert from a Ruby type to Glib::Variant, according to config key data type. */
219 Glib::VariantBase ruby_to_variant_by_key(VALUE input, const sigrok::ConfigKey *key)
220 {
221     enum sr_datatype type = (enum sr_datatype) key->data_type()->id();
222
223     if (type == SR_T_UINT64 && RB_TYPE_P(input, T_FIXNUM))
224         return Glib::Variant<guint64>::create(NUM2ULL(input));
225     if (type == SR_T_UINT64 && RB_TYPE_P(input, T_BIGNUM))
226         return Glib::Variant<guint64>::create(NUM2ULL(input));
227     else if (type == SR_T_STRING && RB_TYPE_P(input, T_STRING))
228         return Glib::Variant<Glib::ustring>::create(string_from_ruby(input));
229     else if (type == SR_T_STRING && RB_TYPE_P(input, T_SYMBOL))
230         return Glib::Variant<Glib::ustring>::create(string_from_ruby(input));
231     else if (type == SR_T_BOOL && RB_TYPE_P(input, T_TRUE))
232         return Glib::Variant<bool>::create(true);
233     else if (type == SR_T_BOOL && RB_TYPE_P(input, T_FALSE))
234         return Glib::Variant<bool>::create(false);
235     else if (type == SR_T_FLOAT && RB_TYPE_P(input, T_FLOAT))
236         return Glib::Variant<double>::create(RFLOAT_VALUE(input));
237     else if (type == SR_T_INT32 && RB_TYPE_P(input, T_FIXNUM))
238         return Glib::Variant<gint32>::create(NUM2INT(input));
239     else if (type == SR_T_UINT32 && RB_TYPE_P(input, T_FIXNUM))
240         return Glib::Variant<guint32>::create(NUM2UINT(input));
241     else
242         throw sigrok::Error(SR_ERR_ARG);
243 }
244
245 /* Convert from a Ruby type to Glib::Variant, according to Option data type. */
246 Glib::VariantBase ruby_to_variant_by_option(VALUE input, std::shared_ptr<sigrok::Option> option)
247 {
248     Glib::VariantBase variant = option->default_value();
249
250     if (variant.is_of_type(Glib::VARIANT_TYPE_UINT64) && RB_TYPE_P(input, T_FIXNUM))
251         return Glib::Variant<guint64>::create(NUM2ULL(input));
252     else if (variant.is_of_type(Glib::VARIANT_TYPE_UINT64) && RB_TYPE_P(input, T_BIGNUM))
253         return Glib::Variant<guint64>::create(NUM2ULL(input));
254     else if (variant.is_of_type(Glib::VARIANT_TYPE_STRING) && RB_TYPE_P(input, T_STRING))
255         return Glib::Variant<Glib::ustring>::create(string_from_ruby(input));
256     else if (variant.is_of_type(Glib::VARIANT_TYPE_STRING) && RB_TYPE_P(input, T_SYMBOL))
257         return Glib::Variant<Glib::ustring>::create(string_from_ruby(input));
258     else if (variant.is_of_type(Glib::VARIANT_TYPE_BOOL) && RB_TYPE_P(input, T_TRUE))
259         return Glib::Variant<bool>::create(true);
260     else if (variant.is_of_type(Glib::VARIANT_TYPE_BOOL) && RB_TYPE_P(input, T_FALSE))
261         return Glib::Variant<bool>::create(false);
262     else if (variant.is_of_type(Glib::VARIANT_TYPE_DOUBLE) && RB_TYPE_P(input, T_FLOAT))
263         return Glib::Variant<double>::create(RFLOAT_VALUE(input));
264     else if (variant.is_of_type(Glib::VARIANT_TYPE_INT32) && RB_TYPE_P(input, T_FIXNUM))
265         return Glib::Variant<gint32>::create(NUM2INT(input));
266     else if (variant.is_of_type(Glib::VARIANT_TYPE_UINT32) && RB_TYPE_P(input, T_FIXNUM))
267         return Glib::Variant<guint32>::create(NUM2UINT(input));
268     else
269         throw sigrok::Error(SR_ERR_ARG);
270 }
271
272 struct hash_to_map_options_params {
273     std::map<std::string, std::shared_ptr<sigrok::Option> > options;
274     std::map<std::string, Glib::VariantBase> output;
275 };
276
277 int convert_option(VALUE key, VALUE val, VALUE in) {
278     struct hash_to_map_options_params *params;
279     params = (struct hash_to_map_options_params *)in;
280
281     auto k = string_from_ruby(key);
282     auto v = ruby_to_variant_by_option(val, params->options[k]);
283     params->output[k] = v;
284
285     return ST_CONTINUE;
286 }
287
288 /* Convert from a Ruby hash to a std::map<std::string, Glib::VariantBase> */
289 std::map<std::string, Glib::VariantBase> hash_to_map_options(VALUE hash,
290     std::map<std::string, std::shared_ptr<sigrok::Option> > options)
291 {
292     if (!RB_TYPE_P(hash, T_HASH))
293         throw sigrok::Error(SR_ERR_ARG);
294
295     struct hash_to_map_options_params params = { options };
296     rb_hash_foreach(hash, (int (*)(ANYARGS))convert_option, (VALUE)&params);
297
298     return params.output;
299 }
300
301 int convert_option_by_key(VALUE key, VALUE val, VALUE in) {
302     std::map<const sigrok::ConfigKey *, Glib::VariantBase> *options;
303     options = (std::map<const sigrok::ConfigKey *, Glib::VariantBase> *)in;
304
305     auto k = sigrok::ConfigKey::get_by_identifier(string_from_ruby(key));
306     auto v = ruby_to_variant_by_key(val, k);
307     (*options)[k] = v;
308
309     return ST_CONTINUE;
310 }
311
312 %}
313
314 /* Ignore these methods, we will override them below. */
315 %ignore sigrok::Analog::data;
316 %ignore sigrok::Driver::scan;
317 %ignore sigrok::Input::send;
318 %ignore sigrok::InputFormat::create_input;
319 %ignore sigrok::OutputFormat::create_output;
320
321 %include "doc.i"
322
323 %define %attributevector(Class, Type, Name, Get)
324 %alias sigrok::Class::_ ## Get #Name;
325 %enddef
326
327 %define %attributemap(Class, Type, Name, Get)
328 %alias sigrok::Class::_ ## Get #Name;
329 %enddef
330
331 %define %enumextras(Class)
332 %extend sigrok::Class
333 {
334     VALUE to_s()
335     {
336         std::string str = $self->name();
337         return rb_external_str_new_with_enc(str.c_str(), str.length(), rb_utf8_encoding());
338     }
339
340     bool operator==(void *other)
341     {
342         return (long) $self == (long) other;
343     }
344 }
345 %enddef
346
347 %include "../swig/classes.i"
348
349 /* Replace the original Driver.scan with a keyword arguments version. */
350 %rename sigrok::Driver::_scan scan;
351 %extend sigrok::Driver
352 {
353     std::vector<std::shared_ptr<sigrok::HardwareDevice> > _scan(VALUE kwargs = rb_hash_new())
354     {
355         if (!RB_TYPE_P(kwargs, T_HASH))
356             throw sigrok::Error(SR_ERR_ARG);
357
358         std::map<const sigrok::ConfigKey *, Glib::VariantBase> options;
359         rb_hash_foreach(kwargs, (int (*)(ANYARGS))convert_option_by_key, (VALUE)&options);
360
361         return $self->scan(options);
362     }
363 }
364
365 /* Support Input.send() with string argument. */
366 %rename sigrok::Input::_send send;
367 %extend sigrok::Input
368 {
369     void _send(VALUE data)
370     {
371         data = StringValue(data);
372         return $self->send(RSTRING_PTR(data), RSTRING_LEN(data));
373     }
374 }
375
376 /* Support InputFormat.create_input() with keyword arguments. */
377 %rename sigrok::InputFormat::_create_input create_input;
378 %extend sigrok::InputFormat
379 {
380     std::shared_ptr<sigrok::Input> _create_input(VALUE hash = rb_hash_new())
381     {
382         return $self->create_input(hash_to_map_options(hash, $self->options()));
383     }
384 }
385
386 /* Support OutputFormat.create_output() with keyword arguments. */
387 %rename sigrok::OutputFormat::_create_output create_output;
388 %extend sigrok::OutputFormat
389 {
390     std::shared_ptr<sigrok::Output> _create_output(
391         std::shared_ptr<sigrok::Device> device, VALUE hash = rb_hash_new())
392     {
393         return $self->create_output(device,
394             hash_to_map_options(hash, $self->options()));
395     }
396
397     std::shared_ptr<sigrok::Output> _create_output(string filename,
398         std::shared_ptr<sigrok::Device> device, VALUE hash = rb_hash_new())
399     {
400         return $self->create_output(filename, device,
401             hash_to_map_options(hash, $self->options()));
402     }
403 }
404
405 /* Support config_set() with Ruby input types. */
406 %extend sigrok::Configurable
407 {
408     void config_set(const ConfigKey *key, VALUE input)
409     {
410         $self->config_set(key, ruby_to_variant_by_key(input, key));
411     }
412 }
413
414 /* Return Ruby array from Analog::data(). */
415 %rename sigrok::Analog::_data data;
416 %extend sigrok::Analog
417 {
418     VALUE _data()
419     {
420         int num_channels = $self->channels().size();
421         int num_samples  = $self->num_samples();
422         float *data = (float *) $self->data_pointer();
423         VALUE channels = rb_ary_new2(num_channels);
424         for(int i = 0; i < num_channels; i++) {
425             VALUE samples = rb_ary_new2(num_samples);
426             for (int j = 0; j < num_samples; j++) {
427                 rb_ary_store(samples, j, DBL2NUM(data[i*num_samples+j]));
428             }
429             rb_ary_store(channels, i, samples);
430         }
431         return channels;
432     }
433 }