]> sigrok.org Git - libsigrok.git/blame - bindings/ruby/classes.i
bindings/ruby: include config.h before any other header file
[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%{
90cc5226
GS
44#include "config.h"
45
27d44cf6
AJ
46#include <stdio.h>
47#include <glibmm.h>
27d44cf6
AJ
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 }
63248050 116 return 0; /* Dummy, to avoid a compiler warning. */
27d44cf6
AJ
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
f504779c 144 VALUE args = rb_ary_new3(2, log_obj, string_obj);
27d44cf6
AJ
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
f504779c 180 VALUE args = rb_ary_new3(2, device_obj, packet_obj);
27d44cf6
AJ
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. */
219Glib::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
240 throw sigrok::Error(SR_ERR_ARG);
241}
242
243/* Convert from a Ruby type to Glib::Variant, according to Option data type. */
244Glib::VariantBase ruby_to_variant_by_option(VALUE input, std::shared_ptr<sigrok::Option> option)
245{
246 Glib::VariantBase variant = option->default_value();
247
248 if (variant.is_of_type(Glib::VARIANT_TYPE_UINT64) && RB_TYPE_P(input, T_FIXNUM))
249 return Glib::Variant<guint64>::create(NUM2ULL(input));
250 else if (variant.is_of_type(Glib::VARIANT_TYPE_UINT64) && RB_TYPE_P(input, T_BIGNUM))
251 return Glib::Variant<guint64>::create(NUM2ULL(input));
252 else if (variant.is_of_type(Glib::VARIANT_TYPE_STRING) && RB_TYPE_P(input, T_STRING))
253 return Glib::Variant<Glib::ustring>::create(string_from_ruby(input));
254 else if (variant.is_of_type(Glib::VARIANT_TYPE_STRING) && RB_TYPE_P(input, T_SYMBOL))
255 return Glib::Variant<Glib::ustring>::create(string_from_ruby(input));
256 else if (variant.is_of_type(Glib::VARIANT_TYPE_BOOL) && RB_TYPE_P(input, T_TRUE))
257 return Glib::Variant<bool>::create(true);
258 else if (variant.is_of_type(Glib::VARIANT_TYPE_BOOL) && RB_TYPE_P(input, T_FALSE))
259 return Glib::Variant<bool>::create(false);
260 else if (variant.is_of_type(Glib::VARIANT_TYPE_DOUBLE) && RB_TYPE_P(input, T_FLOAT))
261 return Glib::Variant<double>::create(RFLOAT_VALUE(input));
262 else if (variant.is_of_type(Glib::VARIANT_TYPE_INT32) && RB_TYPE_P(input, T_FIXNUM))
263 return Glib::Variant<gint32>::create(NUM2INT(input));
264 else
265 throw sigrok::Error(SR_ERR_ARG);
266}
267
268struct hash_to_map_options_params {
269 std::map<std::string, std::shared_ptr<sigrok::Option> > options;
270 std::map<std::string, Glib::VariantBase> output;
271};
272
273int convert_option(VALUE key, VALUE val, VALUE in) {
274 struct hash_to_map_options_params *params;
275 params = (struct hash_to_map_options_params *)in;
276
277 auto k = string_from_ruby(key);
278 auto v = ruby_to_variant_by_option(val, params->options[k]);
279 params->output[k] = v;
280
281 return ST_CONTINUE;
282}
283
284/* Convert from a Ruby hash to a std::map<std::string, Glib::VariantBase> */
285std::map<std::string, Glib::VariantBase> hash_to_map_options(VALUE hash,
286 std::map<std::string, std::shared_ptr<sigrok::Option> > options)
287{
288 if (!RB_TYPE_P(hash, T_HASH))
289 throw sigrok::Error(SR_ERR_ARG);
290
291 struct hash_to_map_options_params params = { options };
292 rb_hash_foreach(hash, (int (*)(ANYARGS))convert_option, (VALUE)&params);
293
294 return params.output;
295}
296
297int convert_option_by_key(VALUE key, VALUE val, VALUE in) {
298 std::map<const sigrok::ConfigKey *, Glib::VariantBase> *options;
299 options = (std::map<const sigrok::ConfigKey *, Glib::VariantBase> *)in;
300
301 auto k = sigrok::ConfigKey::get_by_identifier(string_from_ruby(key));
302 auto v = ruby_to_variant_by_key(val, k);
303 (*options)[k] = v;
304
305 return ST_CONTINUE;
306}
307
308%}
309
310/* Ignore these methods, we will override them below. */
311%ignore sigrok::Analog::data;
312%ignore sigrok::Driver::scan;
313%ignore sigrok::Input::send;
314%ignore sigrok::InputFormat::create_input;
315%ignore sigrok::OutputFormat::create_output;
316
317%include "doc.i"
318
319%define %attributevector(Class, Type, Name, Get)
320%alias sigrok::Class::_ ## Get #Name;
321%enddef
322
323%define %attributemap(Class, Type, Name, Get)
324%alias sigrok::Class::_ ## Get #Name;
325%enddef
326
327%define %enumextras(Class)
328%extend sigrok::Class
329{
330 VALUE to_s()
331 {
332 std::string str = $self->name();
333 return rb_external_str_new_with_enc(str.c_str(), str.length(), rb_utf8_encoding());
334 }
335
336 bool operator==(void *other)
337 {
338 return (long) $self == (long) other;
339 }
340}
341%enddef
342
343%include "../swig/classes.i"
344
345/* Replace the original Driver.scan with a keyword arguments version. */
346%rename sigrok::Driver::_scan scan;
347%extend sigrok::Driver
348{
349 std::vector<std::shared_ptr<sigrok::HardwareDevice> > _scan(VALUE kwargs = rb_hash_new())
350 {
351 if (!RB_TYPE_P(kwargs, T_HASH))
352 throw sigrok::Error(SR_ERR_ARG);
353
354 std::map<const sigrok::ConfigKey *, Glib::VariantBase> options;
355 rb_hash_foreach(kwargs, (int (*)(ANYARGS))convert_option_by_key, (VALUE)&options);
356
357 return $self->scan(options);
358 }
359}
360
361/* Support Input.send() with string argument. */
362%rename sigrok::Input::_send send;
363%extend sigrok::Input
364{
365 void _send(VALUE data)
366 {
367 data = StringValue(data);
368 return $self->send(RSTRING_PTR(data), RSTRING_LEN(data));
369 }
370}
371
372/* Support InputFormat.create_input() with keyword arguments. */
373%rename sigrok::InputFormat::_create_input create_input;
374%extend sigrok::InputFormat
375{
376 std::shared_ptr<sigrok::Input> _create_input(VALUE hash = rb_hash_new())
377 {
378 return $self->create_input(hash_to_map_options(hash, $self->options()));
379 }
380}
381
382/* Support OutputFormat.create_output() with keyword arguments. */
383%rename sigrok::OutputFormat::_create_output create_output;
384%extend sigrok::OutputFormat
385{
386 std::shared_ptr<sigrok::Output> _create_output(
387 std::shared_ptr<sigrok::Device> device, VALUE hash = rb_hash_new())
388 {
389 return $self->create_output(device,
390 hash_to_map_options(hash, $self->options()));
391 }
392
393 std::shared_ptr<sigrok::Output> _create_output(string filename,
394 std::shared_ptr<sigrok::Device> device, VALUE hash = rb_hash_new())
395 {
396 return $self->create_output(filename, device,
397 hash_to_map_options(hash, $self->options()));
398 }
399}
400
401/* Support config_set() with Ruby input types. */
402%extend sigrok::Configurable
403{
404 void config_set(const ConfigKey *key, VALUE input)
405 {
406 $self->config_set(key, ruby_to_variant_by_key(input, key));
407 }
408}
409
410/* Return Ruby array from Analog::data(). */
411%rename sigrok::Analog::_data data;
412%extend sigrok::Analog
413{
414 VALUE _data()
415 {
416 int num_channels = $self->channels().size();
417 int num_samples = $self->num_samples();
418 float *data = (float *) $self->data_pointer();
419 VALUE channels = rb_ary_new2(num_channels);
420 for(int i = 0; i < num_channels; i++) {
421 VALUE samples = rb_ary_new2(num_samples);
422 for (int j = 0; j < num_samples; j++) {
423 rb_ary_store(samples, j, DBL2NUM(data[i*num_samples+j]));
424 }
425 rb_ary_store(channels, i, samples);
426 }
427 return channels;
428 }
429}