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());
119 /* Map from Glib::Variant to native Ruby types. */
120 %typemap(out) Glib::VariantBase {
121 $result = variant_to_ruby($1);
124 /* Map from Glib::VariantContainer to native Ruby types. */
125 %typemap(out) Glib::VariantContainerBase {
126 $result = variant_to_ruby($1);
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");
134 std::shared_ptr<VALUE> proc(new VALUE($input), rb_gc_unregister_address);
135 rb_gc_register_address(proc.get());
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);
141 VALUE string_obj = rb_external_str_new_with_enc(message.c_str(), message.length(), rb_utf8_encoding());
143 VALUE args = rb_ary_new_from_args(2, log_obj, string_obj);
144 rb_proc_call(*proc.get(), args);
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");
153 std::shared_ptr<VALUE> proc(new VALUE($input), rb_gc_unregister_address);
154 rb_gc_register_address(proc.get());
157 rb_proc_call(*proc.get(), rb_ary_new());
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");
166 std::shared_ptr<VALUE> proc(new VALUE($input), rb_gc_unregister_address);
167 rb_gc_register_address(proc.get());
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);
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);
179 VALUE args = rb_ary_new_from_args(2, device_obj, packet_obj);
180 rb_proc_call(*proc.get(), args);
184 /* Cast PacketPayload pointers to correct subclass type. */
185 %ignore sigrok::Packet::payload;
186 %rename sigrok::Packet::_payload payload;
187 %extend sigrok::Packet
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);
215 #include "libsigrokcxx/libsigrokcxx.hpp"
217 /* Convert from a Ruby type to Glib::Variant, according to config key data type. */
218 Glib::VariantBase ruby_to_variant_by_key(VALUE input, const sigrok::ConfigKey *key)
220 enum sr_datatype type = (enum sr_datatype) key->data_type()->id();
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));
239 throw sigrok::Error(SR_ERR_ARG);
242 /* Convert from a Ruby type to Glib::Variant, according to Option data type. */
243 Glib::VariantBase ruby_to_variant_by_option(VALUE input, std::shared_ptr<sigrok::Option> option)
245 Glib::VariantBase variant = option->default_value();
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));
264 throw sigrok::Error(SR_ERR_ARG);
267 struct hash_to_map_options_params {
268 std::map<std::string, std::shared_ptr<sigrok::Option> > options;
269 std::map<std::string, Glib::VariantBase> output;
272 int 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;
276 auto k = string_from_ruby(key);
277 auto v = ruby_to_variant_by_option(val, params->options[k]);
278 params->output[k] = v;
283 /* Convert from a Ruby hash to a std::map<std::string, Glib::VariantBase> */
284 std::map<std::string, Glib::VariantBase> hash_to_map_options(VALUE hash,
285 std::map<std::string, std::shared_ptr<sigrok::Option> > options)
287 if (!RB_TYPE_P(hash, T_HASH))
288 throw sigrok::Error(SR_ERR_ARG);
290 struct hash_to_map_options_params params = { options };
291 rb_hash_foreach(hash, (int (*)(ANYARGS))convert_option, (VALUE)¶ms);
293 return params.output;
296 int 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;
300 auto k = sigrok::ConfigKey::get_by_identifier(string_from_ruby(key));
301 auto v = ruby_to_variant_by_key(val, k);
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;
318 %define %attributevector(Class, Type, Name, Get)
319 %alias sigrok::Class::_ ## Get #Name;
322 %define %attributemap(Class, Type, Name, Get)
323 %alias sigrok::Class::_ ## Get #Name;
326 %define %enumextras(Class)
327 %extend sigrok::Class
331 std::string str = $self->name();
332 return rb_external_str_new_with_enc(str.c_str(), str.length(), rb_utf8_encoding());
335 bool operator==(void *other)
337 return (long) $self == (long) other;
342 %include "../swig/classes.i"
344 /* Replace the original Driver.scan with a keyword arguments version. */
345 %rename sigrok::Driver::_scan scan;
346 %extend sigrok::Driver
348 std::vector<std::shared_ptr<sigrok::HardwareDevice> > _scan(VALUE kwargs = rb_hash_new())
350 if (!RB_TYPE_P(kwargs, T_HASH))
351 throw sigrok::Error(SR_ERR_ARG);
353 std::map<const sigrok::ConfigKey *, Glib::VariantBase> options;
354 rb_hash_foreach(kwargs, (int (*)(ANYARGS))convert_option_by_key, (VALUE)&options);
356 return $self->scan(options);
360 /* Support Input.send() with string argument. */
361 %rename sigrok::Input::_send send;
362 %extend sigrok::Input
364 void _send(VALUE data)
366 data = StringValue(data);
367 return $self->send(RSTRING_PTR(data), RSTRING_LEN(data));
371 /* Support InputFormat.create_input() with keyword arguments. */
372 %rename sigrok::InputFormat::_create_input create_input;
373 %extend sigrok::InputFormat
375 std::shared_ptr<sigrok::Input> _create_input(VALUE hash = rb_hash_new())
377 return $self->create_input(hash_to_map_options(hash, $self->options()));
381 /* Support OutputFormat.create_output() with keyword arguments. */
382 %rename sigrok::OutputFormat::_create_output create_output;
383 %extend sigrok::OutputFormat
385 std::shared_ptr<sigrok::Output> _create_output(
386 std::shared_ptr<sigrok::Device> device, VALUE hash = rb_hash_new())
388 return $self->create_output(device,
389 hash_to_map_options(hash, $self->options()));
392 std::shared_ptr<sigrok::Output> _create_output(string filename,
393 std::shared_ptr<sigrok::Device> device, VALUE hash = rb_hash_new())
395 return $self->create_output(filename, device,
396 hash_to_map_options(hash, $self->options()));
400 /* Support config_set() with Ruby input types. */
401 %extend sigrok::Configurable
403 void config_set(const ConfigKey *key, VALUE input)
405 $self->config_set(key, ruby_to_variant_by_key(input, key));
409 /* Return Ruby array from Analog::data(). */
410 %rename sigrok::Analog::_data data;
411 %extend sigrok::Analog
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]));
424 rb_ary_store(channels, i, samples);