]> sigrok.org Git - libsigrok.git/blame - bindings/ruby/classes.i
Bindings: Make Ruby bindings build with Ruby 2.0
[libsigrok.git] / bindings / ruby / classes.i
CommitLineData
27d44cf6
AJ
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
24The sigrok Ruby API provides an object-oriented Ruby interface to the
25functionality in libsigrok. It is built on top of the libsigrokcxx C++ API.
26
27= Getting started
28
29Usage of the sigrok Ruby API needs to begin with a call to Context.create().
30This will create the global libsigrok context and returns a Context object.
31Methods on this object provide access to the hardware drivers, input and output
32formats supported by the library, as well as means of creating other objects
33such as sessions and triggers.
34
35= Error handling
36
37When any libsigrok C API call returns an error, an Error exception is raised,
38which 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%{
53static 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. */
66static 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
f504779c 143 VALUE args = rb_ary_new3(2, log_obj, string_obj);
27d44cf6
AJ
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
f504779c 179 VALUE args = rb_ary_new3(2, device_obj, packet_obj);
27d44cf6
AJ
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. */
218Glib::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. */
243Glib::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
267struct 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
272int 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> */
284std::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
296int 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}