2 * This file is part of the libsigrok project.
4 * Copyright (C) 2016 Aurelien Jacobs <aurel@gnuage.org>
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.
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.
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/>.
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.
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.
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."
41 %module(docstring=DOCSTRING) sigrok
50 %include "../swig/templates.i"
53 static const char *string_from_ruby(VALUE obj)
57 return StringValueCStr(obj);
59 return rb_id2name(SYM2ID(obj));
61 throw sigrok::Error(SR_ERR_ARG);
65 /* Convert from Glib::Variant to native Ruby types. */
66 static VALUE variant_to_ruby(Glib::VariantBase variant)
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);
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);
114 SWIG_exception(SWIG_TypeError, ("TODO: GVariant(" + variant.get_type().get_string() + ") -> Ruby").c_str());
116 return 0; /* Dummy, to avoid a compiler warning. */
120 /* Map from Glib::Variant to native Ruby types. */
121 %typemap(out) Glib::VariantBase {
122 $result = variant_to_ruby($1);
125 /* Map from Glib::VariantContainer to native Ruby types. */
126 %typemap(out) Glib::VariantContainerBase {
127 $result = variant_to_ruby($1);
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");
135 std::shared_ptr<VALUE> proc(new VALUE($input), rb_gc_unregister_address);
136 rb_gc_register_address(proc.get());
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);
142 VALUE string_obj = rb_external_str_new_with_enc(message.c_str(), message.length(), rb_utf8_encoding());
144 VALUE args = rb_ary_new3(2, log_obj, string_obj);
145 rb_proc_call(*proc.get(), args);
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");
154 std::shared_ptr<VALUE> proc(new VALUE($input), rb_gc_unregister_address);
155 rb_gc_register_address(proc.get());
158 rb_proc_call(*proc.get(), rb_ary_new());
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");
167 std::shared_ptr<VALUE> proc(new VALUE($input), rb_gc_unregister_address);
168 rb_gc_register_address(proc.get());
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);
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);
180 VALUE args = rb_ary_new3(2, device_obj, packet_obj);
181 rb_proc_call(*proc.get(), args);
185 /* Cast PacketPayload pointers to correct subclass type. */
186 %ignore sigrok::Packet::payload;
187 %rename sigrok::Packet::_payload payload;
188 %extend sigrok::Packet
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);
216 #include "libsigrokcxx/libsigrokcxx.hpp"
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)
221 enum sr_datatype type = (enum sr_datatype) key->data_type()->id();
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));
240 throw sigrok::Error(SR_ERR_ARG);
243 /* Convert from a Ruby type to Glib::Variant, according to Option data type. */
244 Glib::VariantBase ruby_to_variant_by_option(VALUE input, std::shared_ptr<sigrok::Option> option)
246 Glib::VariantBase variant = option->default_value();
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));
265 throw sigrok::Error(SR_ERR_ARG);
268 struct hash_to_map_options_params {
269 std::map<std::string, std::shared_ptr<sigrok::Option> > options;
270 std::map<std::string, Glib::VariantBase> output;
273 int 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;
277 auto k = string_from_ruby(key);
278 auto v = ruby_to_variant_by_option(val, params->options[k]);
279 params->output[k] = v;
284 /* Convert from a Ruby hash to a std::map<std::string, Glib::VariantBase> */
285 std::map<std::string, Glib::VariantBase> hash_to_map_options(VALUE hash,
286 std::map<std::string, std::shared_ptr<sigrok::Option> > options)
288 if (!RB_TYPE_P(hash, T_HASH))
289 throw sigrok::Error(SR_ERR_ARG);
291 struct hash_to_map_options_params params = { options };
292 rb_hash_foreach(hash, (int (*)(ANYARGS))convert_option, (VALUE)¶ms);
294 return params.output;
297 int 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;
301 auto k = sigrok::ConfigKey::get_by_identifier(string_from_ruby(key));
302 auto v = ruby_to_variant_by_key(val, k);
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;
319 %define %attributevector(Class, Type, Name, Get)
320 %alias sigrok::Class::_ ## Get #Name;
323 %define %attributemap(Class, Type, Name, Get)
324 %alias sigrok::Class::_ ## Get #Name;
327 %define %enumextras(Class)
328 %extend sigrok::Class
332 std::string str = $self->name();
333 return rb_external_str_new_with_enc(str.c_str(), str.length(), rb_utf8_encoding());
336 bool operator==(void *other)
338 return (long) $self == (long) other;
343 %include "../swig/classes.i"
345 /* Replace the original Driver.scan with a keyword arguments version. */
346 %rename sigrok::Driver::_scan scan;
347 %extend sigrok::Driver
349 std::vector<std::shared_ptr<sigrok::HardwareDevice> > _scan(VALUE kwargs = rb_hash_new())
351 if (!RB_TYPE_P(kwargs, T_HASH))
352 throw sigrok::Error(SR_ERR_ARG);
354 std::map<const sigrok::ConfigKey *, Glib::VariantBase> options;
355 rb_hash_foreach(kwargs, (int (*)(ANYARGS))convert_option_by_key, (VALUE)&options);
357 return $self->scan(options);
361 /* Support Input.send() with string argument. */
362 %rename sigrok::Input::_send send;
363 %extend sigrok::Input
365 void _send(VALUE data)
367 data = StringValue(data);
368 return $self->send(RSTRING_PTR(data), RSTRING_LEN(data));
372 /* Support InputFormat.create_input() with keyword arguments. */
373 %rename sigrok::InputFormat::_create_input create_input;
374 %extend sigrok::InputFormat
376 std::shared_ptr<sigrok::Input> _create_input(VALUE hash = rb_hash_new())
378 return $self->create_input(hash_to_map_options(hash, $self->options()));
382 /* Support OutputFormat.create_output() with keyword arguments. */
383 %rename sigrok::OutputFormat::_create_output create_output;
384 %extend sigrok::OutputFormat
386 std::shared_ptr<sigrok::Output> _create_output(
387 std::shared_ptr<sigrok::Device> device, VALUE hash = rb_hash_new())
389 return $self->create_output(device,
390 hash_to_map_options(hash, $self->options()));
393 std::shared_ptr<sigrok::Output> _create_output(string filename,
394 std::shared_ptr<sigrok::Device> device, VALUE hash = rb_hash_new())
396 return $self->create_output(filename, device,
397 hash_to_map_options(hash, $self->options()));
401 /* Support config_set() with Ruby input types. */
402 %extend sigrok::Configurable
404 void config_set(const ConfigKey *key, VALUE input)
406 $self->config_set(key, ruby_to_variant_by_key(input, key));
410 /* Return Ruby array from Analog::data(). */
411 %rename sigrok::Analog::_data data;
412 %extend sigrok::Analog
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]));
425 rb_ary_store(channels, i, samples);