]> sigrok.org Git - libsigrok.git/blame - bindings/ruby/classes.i
output/csv: use intermediate time_t var, silence compiler warning
[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));
0db1b189
MH
239 else if (type == SR_T_UINT32 && RB_TYPE_P(input, T_FIXNUM))
240 return Glib::Variant<guint32>::create(NUM2UINT(input));
27d44cf6
AJ
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. */
246Glib::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));
0db1b189
MH
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));
27d44cf6
AJ
268 else
269 throw sigrok::Error(SR_ERR_ARG);
270}
271
272struct 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
277int 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> */
289std::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
301int 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}