gphoto4ruby 0.1.6 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,934 @@
1
+ /**
2
+ *
3
+ * Copyright 2008 neq4 company <http://neq4.com>
4
+ * Author: Sergey Kruk <sergey.kruk@gmail.com>
5
+ *
6
+ * This file is part of GPhoto4Ruby.
7
+ *
8
+ * GPhoto4Ruby is free software: you can redistribute it and/or
9
+ * modify it under the terms of the GNU Lesser General Public
10
+ * License as published by the Free Software Foundation, either
11
+ * version 3 of the License, or (at your option) any later version.
12
+ *
13
+ * GPhoto4Ruby is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with GPhoto4Ruby. If not, see <http://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+
23
+ #include "gphoto2camera.h"
24
+
25
+ void camera_mark(GPhoto2Camera *c) {
26
+ }
27
+
28
+ void camera_free(GPhoto2Camera *c) {
29
+ gp_result_check(gp_camera_exit(c->camera, c->context));
30
+ gp_result_check(gp_widget_free(c->config));
31
+ gp_result_check(gp_list_free(c->list));
32
+ gp_result_check(gp_file_free(c->file));
33
+ gp_result_check(gp_camera_free(c->camera));
34
+ free(c->virtFolder);
35
+ free(c->context);
36
+ free(c);
37
+ }
38
+
39
+ VALUE camera_allocate(VALUE klass) {
40
+ GPhoto2Camera *c;
41
+ c = (GPhoto2Camera*) malloc(sizeof(GPhoto2Camera));
42
+ c->virtFolder = (char*) malloc(sizeof(char)*100);
43
+ strcpy(c->virtFolder, "/");
44
+ c->context = gp_context_new();
45
+ gp_result_check(gp_camera_new(&(c->camera)));
46
+ gp_result_check(gp_list_new(&(c->list)));
47
+ gp_result_check(gp_file_new(&(c->file)));
48
+ return Data_Wrap_Struct(klass, camera_mark, camera_free, c);
49
+ }
50
+
51
+ void camera_event_mark(GPhoto2CameraEvent *ce) {
52
+ }
53
+
54
+ void camera_event_free(GPhoto2CameraEvent *ce) {
55
+ free(ce);
56
+ }
57
+
58
+ /*
59
+ * call-seq:
60
+ * GPhoto2::Camera.new(port=nil)
61
+ *
62
+ * Returns camera object. Camera should be connected at a time constructor
63
+ * is called. If there is more than one camera connected through usb ports,
64
+ * port parameter can be passed to specify which camera is addressed with
65
+ * object.
66
+ *
67
+ * Examples:
68
+ *
69
+ * GPhoto2::Camera.new
70
+ * GPhoto2::Capera.new(GPhoto2::Camera.ports[0])
71
+ *
72
+ */
73
+ VALUE camera_initialize(int argc, VALUE *argv, VALUE self) {
74
+ GPhoto2Camera *c;
75
+ VALUE cfgs;
76
+
77
+ Data_Get_Struct(self, GPhoto2Camera, c);
78
+
79
+ switch (argc) {
80
+ case 0:
81
+ break;
82
+ case 1:
83
+ Check_Type(argv[0], T_STRING);
84
+ int portIndex;
85
+ GPPortInfoList *portInfoList;
86
+ GPPortInfo p;
87
+
88
+ gp_result_check(gp_port_info_list_new(&portInfoList));
89
+ gp_result_check(gp_port_info_list_load(portInfoList));
90
+ portIndex = gp_result_check(gp_port_info_list_lookup_path(portInfoList, RSTRING(argv[0])->ptr));
91
+ gp_result_check(gp_port_info_list_get_info(portInfoList, portIndex, &p));
92
+ gp_result_check(gp_camera_set_port_info(c->camera, p));
93
+ break;
94
+ default:
95
+ rb_raise(rb_eArgError, "Wrong number of arguments (%d for 0 or 1)", argc);
96
+ return Qnil;
97
+ }
98
+
99
+ gp_result_check(gp_camera_get_config(c->camera, &(c->config), c->context));
100
+ cfgs = rb_hash_new();
101
+ populateWithConfigs(c->config, cfgs);
102
+ rb_iv_set(self, "@configuration", cfgs);
103
+ rb_iv_set(self, "@configs_changed", rb_ary_new());
104
+
105
+ return self;
106
+ }
107
+
108
+ /*
109
+ * call-seq:
110
+ * GPhoto2::Camera.ports => array
111
+ *
112
+ * Returns an array of usb port paths with cameras. Port paths are the same
113
+ * as in <b>gphoto2 --auto-detect</b> output. Assuming that if there are
114
+ * cameras detected with long port paths, then the one with short port path
115
+ * is a duplicate of one of the others.
116
+ *
117
+ * Examples:
118
+ *
119
+ * # with one camera connected
120
+ * GPhoto2::Camera.ports #=> ["usb:"]
121
+ * # with two cameras connected
122
+ * GPhoto2::Camera.ports #=> ["usb:005,004", "usb:005,006"]
123
+ *
124
+ */
125
+ VALUE camera_class_ports(VALUE klass) {
126
+ int i, camsTotal, retVal;
127
+ GPContext *context;
128
+ CameraAbilitiesList *abilList;
129
+ GPPortInfoList *portInfoList;
130
+ CameraList *camList;
131
+ const char *pName = NULL;
132
+ VALUE arr = rb_ary_new();
133
+
134
+ context = gp_context_new();
135
+ retVal = gp_port_info_list_new(&portInfoList);
136
+ if (retVal == GP_OK) {
137
+ retVal = gp_port_info_list_load(portInfoList);
138
+ if (retVal == GP_OK) {
139
+ retVal = gp_abilities_list_new(&abilList);
140
+ if (retVal == GP_OK) {
141
+ retVal = gp_abilities_list_load(abilList, context);
142
+ if (retVal == GP_OK) {
143
+ retVal = gp_list_new(&camList);
144
+ if (retVal == GP_OK) {
145
+ retVal = gp_abilities_list_detect(abilList, portInfoList, camList, context);
146
+ if (retVal == GP_OK) {
147
+ retVal = gp_list_count(camList);
148
+ if (retVal >= GP_OK) {
149
+ camsTotal = retVal;
150
+ for(i = 0; i < camsTotal; i++) {
151
+ retVal = gp_list_get_value(camList, i, &pName);
152
+ if ((retVal == GP_OK) &&
153
+ ((camsTotal == 1) || (strlen(pName) > 4))) {
154
+ rb_ary_push(arr, rb_str_new2(pName));
155
+ }
156
+ }
157
+ }
158
+ }
159
+ gp_list_free(camList);
160
+ }
161
+ }
162
+ gp_abilities_list_free(abilList);
163
+ }
164
+ }
165
+ gp_port_info_list_free(portInfoList);
166
+ }
167
+ free(context);
168
+ if (retVal < GP_OK) {
169
+ rb_raise_gp_result(retVal);
170
+ }
171
+ return arr;
172
+ }
173
+
174
+ /*
175
+ * call-seq:
176
+ * capture(config={}) => camera
177
+ *
178
+ * Sends command to camera to capture image with current or provided
179
+ * configuration. Provided configuration is kept after capture.
180
+ *
181
+ * Examples:
182
+ *
183
+ * c = GPhoto2::Camera.new
184
+ * c.capture
185
+ * c.capture "exptime" => "0.010", "iso" => "400"
186
+ *
187
+ */
188
+ VALUE camera_capture(int argc, VALUE *argv, VALUE self) {
189
+ GPhoto2Camera *c;
190
+
191
+ Data_Get_Struct(self, GPhoto2Camera, c);
192
+
193
+ if (argc == 1) {
194
+ camera_config_merge(self, argv[0]);
195
+ } else if (argc != 0) {
196
+ rb_raise(rb_eArgError, "Wrong number of arguments (%d for 0 or 1)", argc);
197
+ return Qnil;
198
+ }
199
+
200
+ gp_result_check(gp_camera_capture(c->camera, GP_CAPTURE_IMAGE, &(c->path), c->context));
201
+ strcpy(c->virtFolder, c->path.folder);
202
+ // printf("captured: %s/%s\n", c->path.folder, c->path.name);
203
+ return self;
204
+ }
205
+
206
+ /*
207
+ * call-seq:
208
+ * save(options={}) => camera
209
+ *
210
+ * Downloads file from camera to hard drive.
211
+ * Available options are:
212
+ * * :file - Name of the file to download from camera. File is expected
213
+ * to be found in current path. If this option is not specified, last
214
+ * captured image is downloaded. If symbols <b>:first</b> or <b>:last</b>
215
+ * are passed, the first or the last file from current camera path is
216
+ * downloaded.
217
+ * * :new_name - New file name to be used when saving file on hard drive.
218
+ * If this option is not specified, camera file system filename is used.
219
+ * * :to_folder - Folder path on hard drive to save downloaded image to.
220
+ * * :type - Type of file to download from camera. Available types are
221
+ * <b>:normal</b> (default) and <b>:preview</b>
222
+ *
223
+ * Examples:
224
+ *
225
+ * c = GPhoto2::Camera.new
226
+ * c.capture.save :type => :preview, => Downloads preview of
227
+ * :new_name => "PREVIEW.JPG" captured image
228
+ * c.save :file => "DSC_0144.JPG", => Downloads specified file
229
+ * :to_folder => "/home/user", to /home/user/xyz.gf.JPG
230
+ * :new_name => "xyz.gf",
231
+ *
232
+ */
233
+ VALUE camera_save(int argc, VALUE *argv, VALUE self) {
234
+ int i, count;
235
+ int newName = 0;
236
+ CameraFileType fileType = GP_FILE_TYPE_NORMAL;
237
+ GPhoto2Camera *c;
238
+ const char *fData, *key, *val, *name;
239
+ char *fPath, *newNameStr, *pchNew, *pchSrc;
240
+ char fName[100], cFileName[100], cFolderName[100];
241
+ unsigned long int fSize;
242
+ int fd;
243
+ VALUE arr, hVal;
244
+
245
+ Data_Get_Struct(self, GPhoto2Camera, c);
246
+
247
+ strcpy(fName, "");
248
+ strcpy(cFileName, c->path.name);
249
+ strcpy(cFolderName, c->path.folder);
250
+
251
+ gp_result_check(gp_filesystem_reset(c->camera->fs));
252
+
253
+ switch(argc) {
254
+ case 0:
255
+ break;
256
+ case 1:
257
+ Check_Type(argv[0], T_HASH);
258
+ arr = rb_funcall(argv[0], rb_intern("keys"), 0);
259
+ for (i = 0; i < RARRAY(arr)->len; i++) {
260
+ switch(TYPE(RARRAY(arr)->ptr[i])) {
261
+ case T_STRING:
262
+ key = RSTRING(RARRAY(arr)->ptr[i])->ptr;
263
+ break;
264
+ case T_SYMBOL:
265
+ key = rb_id2name(rb_to_id(RARRAY(arr)->ptr[i]));
266
+ break;
267
+ default:
268
+ rb_raise(rb_eTypeError, "Not valid key type");
269
+ return Qnil;
270
+ }
271
+ if (strcmp(key, "to_folder") == 0) {
272
+ fPath = RSTRING(rb_hash_aref(argv[0], RARRAY(arr)->ptr[i]))->ptr;
273
+ if (strlen(fPath) > 0) {
274
+ if (fPath[strlen(fPath)] == '/') {
275
+ strcpy(fName, fPath);
276
+ } else {
277
+ strcpy(fName, fPath);
278
+ strcat(fName, "/");
279
+ }
280
+ }
281
+ } else if (strcmp(key, "new_name") == 0) {
282
+ newNameStr = RSTRING(rb_hash_aref(argv[0], RARRAY(arr)->ptr[i]))->ptr;
283
+ if (strlen(newNameStr) > 0) {
284
+ newName = 1;
285
+ }
286
+ } else if (strcmp(key, "type") == 0) {
287
+ hVal = rb_hash_aref(argv[0], RARRAY(arr)->ptr[i]);
288
+ Check_Type(hVal, T_SYMBOL);
289
+ val = rb_id2name(rb_to_id(hVal));
290
+ if (strcmp(val, "normal") == 0) {
291
+ fileType = GP_FILE_TYPE_NORMAL;
292
+ } else if (strcmp(val, "preview") == 0) {
293
+ fileType = GP_FILE_TYPE_PREVIEW;
294
+ }
295
+ } else if (strcmp(key, "file") == 0) {
296
+ hVal = rb_hash_aref(argv[0], RARRAY(arr)->ptr[i]);
297
+ switch(TYPE(hVal)) {
298
+ case T_STRING:
299
+ strcpy(cFolderName, c->virtFolder);
300
+ strcpy(cFileName, RSTRING(hVal)->ptr);
301
+ break;
302
+ case T_SYMBOL:
303
+ val = rb_id2name(rb_to_id(hVal));
304
+ gp_result_check(gp_camera_folder_list_files(c->camera, c->virtFolder, c->list, c->context));
305
+ if (strcmp(val, "first") == 0) {
306
+ count = 0;
307
+ } else if (strcmp(val, "last") == 0) {
308
+ count = gp_result_check(gp_list_count(c->list)) - 1;
309
+ } else {
310
+ count = -1;
311
+ }
312
+ gp_result_check(gp_list_get_name(c->list, count, &name));
313
+ strcpy(cFileName, name);
314
+ break;
315
+ default:
316
+ rb_raise(rb_eTypeError, "Not valid value type");
317
+ return Qnil;
318
+ }
319
+ }
320
+ }
321
+ break;
322
+ default:
323
+ rb_raise(rb_eArgError, "Wrong number of arguments (%d for 0 or 1)", argc);
324
+ return Qnil;
325
+ }
326
+
327
+ gp_result_check(gp_camera_file_get(c->camera, cFolderName, cFileName, fileType, c->file, c->context));
328
+ gp_result_check(gp_file_get_data_and_size(c->file, &fData, &fSize));
329
+ if (newName == 1) {
330
+ strcat(fName, newNameStr);
331
+ pchNew = strrchr(newNameStr, '.');
332
+ pchSrc = strrchr(cFileName, '.');
333
+ if (pchNew == NULL) {
334
+ strcat(fName, pchSrc);
335
+ } else if (strcmp(pchNew, pchSrc) != 0) {
336
+ strcat(fName, pchSrc);
337
+ }
338
+ } else {
339
+ strcat(fName, cFileName);
340
+ }
341
+ fd = open(fName, O_CREAT | O_WRONLY, 0644);
342
+ write(fd, fData, fSize);
343
+ close(fd);
344
+ return self;
345
+ }
346
+
347
+ /*
348
+ * call-seq:
349
+ * delete(options={}) => camera
350
+ *
351
+ * Deletes file from camera.
352
+ * Available options are:
353
+ * * :file - Name of the file to delete from camera. File is expected
354
+ * to be found in current path. If this option is not specified, last
355
+ * captured image is deleted.
356
+ *
357
+ * Examples:
358
+ *
359
+ * c = GPhoto2::Camera.new
360
+ * c.capture.save.delete
361
+ * c.delete :file => "DSC_0144.JPG"
362
+ * c.delete :all
363
+ *
364
+ */
365
+ VALUE camera_delete(int argc, VALUE *argv, VALUE self) {
366
+ int i;
367
+ int one = 1;
368
+ GPhoto2Camera *c;
369
+ const char *key;
370
+ char cFileName[100], cFolderName[100];
371
+ VALUE arr;
372
+
373
+ Data_Get_Struct(self, GPhoto2Camera, c);
374
+
375
+ strcpy(cFileName, c->path.name);
376
+ strcpy(cFolderName, c->path.folder);
377
+
378
+ switch(argc) {
379
+ case 0:
380
+ break;
381
+ case 1:
382
+ switch(TYPE(argv[0])) {
383
+ case T_HASH:
384
+ arr = rb_funcall(argv[0], rb_intern("keys"), 0);
385
+ for (i = 0; i < RARRAY(arr)->len; i++) {
386
+ switch(TYPE(RARRAY(arr)->ptr[i])) {
387
+ case T_STRING:
388
+ key = RSTRING(RARRAY(arr)->ptr[i])->ptr;
389
+ break;
390
+ case T_SYMBOL:
391
+ key = rb_id2name(rb_to_id(RARRAY(arr)->ptr[i]));
392
+ break;
393
+ default:
394
+ rb_raise(rb_eTypeError, "Not valid key type");
395
+ return Qnil;
396
+ }
397
+ if (strcmp(key, "file") == 0) {
398
+ strcpy(cFolderName, c->virtFolder);
399
+ strcpy(cFileName, RSTRING(rb_hash_aref(argv[0], RARRAY(arr)->ptr[i]))->ptr);
400
+ }
401
+ }
402
+ break;
403
+ case T_SYMBOL:
404
+ key = rb_id2name(rb_to_id(argv[0]));
405
+ if (strcmp(key, "all") == 0) {
406
+ strcpy(cFolderName, c->virtFolder);
407
+ one = 0;
408
+ }
409
+ break;
410
+ }
411
+ break;
412
+ default:
413
+ rb_raise(rb_eArgError, "Wrong number of arguments (%d for 0 or 1)", argc);
414
+ return Qnil;
415
+ }
416
+
417
+ gp_result_check(gp_filesystem_reset(c->camera->fs));
418
+ if (one == 1) {
419
+ gp_result_check(gp_camera_file_delete(c->camera, cFolderName, cFileName, c->context));
420
+ } else {
421
+ gp_result_check(gp_camera_folder_delete_all(c->camera, cFolderName, c->context));
422
+ }
423
+ return self;
424
+ }
425
+
426
+ /*
427
+ * call-seq:
428
+ * config => hash
429
+ *
430
+ * Returns cached hash of adjustable camera configuration with their values.
431
+ *
432
+ * Examples:
433
+ *
434
+ * c = GPhoto2::Camera.new
435
+ * # with Nikon DSC D80
436
+ * c.config.keys #=> ["capturetarget", "imgquality",
437
+ * "imgsize", "whitebalance",
438
+ * "f-number", "focallength",
439
+ * "focusmode", "iso",
440
+ * "exposurebiascompensation",
441
+ * "exptime", "expprogram",
442
+ * "capturemode", "focusmetermode",
443
+ * "exposuremetermode", "flashmode",
444
+ * "burstnumber", "accessmode",
445
+ * "channel", "encryption"]
446
+ *
447
+ */
448
+ VALUE camera_get_config(VALUE self) {
449
+ return rb_iv_get(self, "@configuration");
450
+ }
451
+
452
+ /*
453
+ * call-seq:
454
+ * config_merge(hash) => hash
455
+ *
456
+ * Adjusts camera configuration with given values.
457
+ *
458
+ * Examples:
459
+ *
460
+ * c = GPhoto2::Camera.new
461
+ * # with Nikon DSC D80
462
+ * c.config_merge "f-number" => "f/4", "exptime" => "0.010", "iso" => "200"
463
+ *
464
+ */
465
+ VALUE camera_config_merge(VALUE self, VALUE hash) {
466
+ Check_Type(hash, T_HASH);
467
+
468
+ int i;
469
+ const char *key;
470
+ GPhoto2Camera *c;
471
+ CameraWidgetType widgettype;
472
+ VALUE arr, cfgs, cfg_changed;
473
+
474
+ Data_Get_Struct(self, GPhoto2Camera, c);
475
+
476
+ arr = rb_funcall(hash, rb_intern("keys"), 0);
477
+ cfgs = rb_iv_get(self, "@configuration");
478
+ cfg_changed = rb_iv_get(self, "@configs_changed");
479
+ for (i = 0; i < RARRAY(arr)->len; i++) {
480
+ switch(TYPE(RARRAY(arr)->ptr[i])) {
481
+ case T_STRING:
482
+ key = RSTRING(RARRAY(arr)->ptr[i])->ptr;
483
+ break;
484
+ case T_SYMBOL:
485
+ key = rb_id2name(rb_to_id(RARRAY(arr)->ptr[i]));
486
+ break;
487
+ default:
488
+ rb_raise(rb_eTypeError, "Not valid key type");
489
+ return Qnil;
490
+ }
491
+ if (TYPE(rb_funcall(cfgs, rb_intern("has_key?"), 1, rb_str_new2(key))) == T_TRUE) {
492
+ gp_result_check(gp_widget_get_child_by_name(c->config, key, &(c->childConfig)));
493
+ gp_result_check(gp_widget_get_type(c->childConfig, &widgettype));
494
+ switch (widgettype) {
495
+ case GP_WIDGET_RADIO:
496
+ rb_ary_push(cfg_changed, rb_str_new2(key));
497
+ setRadio(self, c, rb_hash_aref(hash, RARRAY(arr)->ptr[i]), 0);
498
+ break;
499
+ case GP_WIDGET_TEXT:
500
+ rb_ary_push(cfg_changed, rb_str_new2(key));
501
+ setText(self, c, rb_hash_aref(hash, RARRAY(arr)->ptr[i]), 0);
502
+ break;
503
+ case GP_WIDGET_RANGE:
504
+ rb_ary_push(cfg_changed, rb_str_new2(key));
505
+ setRange(self, c, rb_hash_aref(hash, RARRAY(arr)->ptr[i]), 0);
506
+ break;
507
+ case GP_WIDGET_TOGGLE:
508
+ rb_ary_push(cfg_changed, rb_str_new2(key));
509
+ setToggle(self, c, rb_hash_aref(hash, RARRAY(arr)->ptr[i]), 0);
510
+ break;
511
+ default:
512
+ break;
513
+ }
514
+ }
515
+ }
516
+ saveConfigs(self, c);
517
+ return cfgs;
518
+ }
519
+
520
+ /*
521
+ * call-seq:
522
+ * cam[cfg] => float or string
523
+ * cam[cfg, :all] => array
524
+ * cam[cfg, :type] => fixnum
525
+ *
526
+ * Returns current value of specified camera configuration. Configuration name
527
+ * (cfg) can be string or symbol and must be in configs method returned array.
528
+ * Configuration is cached in @configuration instance variable.
529
+ *
530
+ * Possible directives:
531
+ * * <b>:no_cache</b> doesn't use cached configuration value
532
+ * * <b>:all</b> returns an array of allowed values;
533
+ * * <b>:type</b> returns one of available CONFIG_TYPE constats
534
+ *
535
+ * Examples:
536
+ *
537
+ * c = GPhoto2::Camera.new
538
+ * # with Nikon DSC D80
539
+ * c["f-number"] #=> "f/4.5"
540
+ * c[:focallength] #=> 10.5
541
+ * c[:focusmode, :all] #=> ["Manual", "AF-S", "AF-C", "AF-A"]
542
+ * c[:exptime, :type] == GPhoto2::Camera::CONFIG_TYPE_RADIO
543
+ * #=> true
544
+ *
545
+ */
546
+ VALUE camera_get_value(int argc, VALUE *argv, VALUE self) {
547
+ const char *name;
548
+ GPhoto2Camera *c;
549
+ CameraWidgetType widgettype;
550
+ VALUE str, dir;
551
+
552
+ switch (argc) {
553
+ case 1:
554
+ str = argv[0];
555
+ break;
556
+ case 2:
557
+ str = argv[0];
558
+ dir = argv[1];
559
+ Check_Type(dir, T_SYMBOL);
560
+ break;
561
+ default:
562
+ rb_raise(rb_eArgError, "Wrong number of arguments (%d for 1 or 2)", argc);
563
+ return Qnil;
564
+ }
565
+
566
+ switch (TYPE(str)) {
567
+ case T_STRING:
568
+ name = RSTRING(str)->ptr;
569
+ break;
570
+ case T_SYMBOL:
571
+ name = rb_id2name(rb_to_id(str));
572
+ break;
573
+ default:
574
+ rb_raise(rb_eTypeError, "Not valid parameter type");
575
+ return Qnil;
576
+ }
577
+
578
+ if (argc == 1) {
579
+ return rb_hash_aref(rb_iv_get(self, "@configuration"), rb_str_new2(name));
580
+ } else {
581
+ Data_Get_Struct(self, GPhoto2Camera, c);
582
+
583
+ gp_result_check(gp_widget_get_child_by_name(c->config, name, &(c->childConfig)));
584
+ gp_result_check(gp_widget_get_type(c->childConfig, &widgettype));
585
+ switch (widgettype) {
586
+ case GP_WIDGET_RADIO:
587
+ if (strcmp(rb_id2name(rb_to_id(dir)), "no_cache") == 0) {
588
+ return getRadio(c->childConfig);
589
+ } else if (strcmp(rb_id2name(rb_to_id(dir)), "all") == 0) {
590
+ return listRadio(c->childConfig);
591
+ } else if (strcmp(rb_id2name(rb_to_id(dir)), "type") == 0) {
592
+ return INT2FIX(GP_WIDGET_RADIO);
593
+ } else {
594
+ rb_raise(rb_cGPhoto2ConfigurationError, "Unknown directive '%s'", rb_id2name(rb_to_id(dir)));
595
+ return Qnil;
596
+ }
597
+ break;
598
+ case GP_WIDGET_TEXT:
599
+ if (strcmp(rb_id2name(rb_to_id(dir)), "no_cache") == 0) {
600
+ return getText(c->childConfig);
601
+ } else if (strcmp(rb_id2name(rb_to_id(dir)), "all") == 0) {
602
+ return rb_ary_new();
603
+ } else if (strcmp(rb_id2name(rb_to_id(dir)), "type") == 0) {
604
+ return INT2FIX(GP_WIDGET_TEXT);
605
+ } else {
606
+ rb_raise(rb_cGPhoto2ConfigurationError, "Unknown directive '%s'", rb_id2name(rb_to_id(dir)));
607
+ return Qnil;
608
+ }
609
+ break;
610
+ case GP_WIDGET_RANGE:
611
+ if (strcmp(rb_id2name(rb_to_id(dir)), "no_cache") == 0) {
612
+ return getRange(c->childConfig);
613
+ } else if (strcmp(rb_id2name(rb_to_id(dir)), "all") == 0) {
614
+ return listRange(c->childConfig);
615
+ } else if (strcmp(rb_id2name(rb_to_id(dir)), "type") == 0) {
616
+ return INT2FIX(GP_WIDGET_RANGE);
617
+ } else {
618
+ rb_raise(rb_cGPhoto2ConfigurationError, "Unknown directive '%s'", rb_id2name(rb_to_id(dir)));
619
+ return Qnil;
620
+ }
621
+ break;
622
+ case GP_WIDGET_TOGGLE:
623
+ if (strcmp(rb_id2name(rb_to_id(dir)), "no_cache") == 0) {
624
+ return getToggle(c->childConfig);
625
+ } else if (strcmp(rb_id2name(rb_to_id(dir)), "all") == 0) {
626
+ VALUE arr = rb_ary_new();
627
+ rb_ary_push(arr, Qtrue);
628
+ rb_ary_push(arr, Qfalse);
629
+ return arr;
630
+ } else if (strcmp(rb_id2name(rb_to_id(dir)), "type") == 0) {
631
+ return INT2FIX(GP_WIDGET_TOGGLE);
632
+ } else {
633
+ rb_raise(rb_cGPhoto2ConfigurationError, "Unknown directive '%s'", rb_id2name(rb_to_id(dir)));
634
+ return Qnil;
635
+ }
636
+ break;
637
+ default:
638
+ rb_raise(rb_cGPhoto2ConfigurationError, "Not supported yet");
639
+ return Qnil;
640
+ }
641
+ }
642
+ }
643
+
644
+ /*
645
+ * call-seq:
646
+ * cam[cfg] = value => float or string
647
+ *
648
+ * Sets specified camera configuration to specified value if value is allowed.
649
+ *
650
+ * Examples:
651
+ *
652
+ * c = GPhoto2::Camera.new
653
+ * # with Nikon DSC D80
654
+ * c["f-number"] = "f/4.5" #=> "f/4.5"
655
+ * c[:focallength] = 10.5 #=> 10.5
656
+ *
657
+ */
658
+ VALUE camera_set_value(VALUE self, VALUE str, VALUE newVal) {
659
+ const char *name;
660
+ GPhoto2Camera *c;
661
+ CameraWidgetType widgettype;
662
+ VALUE cfg_changed;
663
+
664
+ switch (TYPE(str)) {
665
+ case T_STRING:
666
+ name = RSTRING(str)->ptr;
667
+ break;
668
+ case T_SYMBOL:
669
+ name = rb_id2name(rb_to_id(str));
670
+ break;
671
+ default:
672
+ rb_raise(rb_eTypeError, "Not valid parameter type");
673
+ return Qnil;
674
+ }
675
+
676
+ Data_Get_Struct(self, GPhoto2Camera, c);
677
+
678
+ gp_result_check(gp_widget_get_child_by_name(c->config, name, &(c->childConfig)));
679
+ gp_result_check(gp_widget_get_type(c->childConfig, &widgettype));
680
+ switch (widgettype) {
681
+ case GP_WIDGET_RADIO:
682
+ cfg_changed = rb_iv_get(self, "@configs_changed");
683
+ rb_ary_push(cfg_changed, rb_str_new2(name));
684
+ return setRadio(self, c, newVal, 1);
685
+ break;
686
+ case GP_WIDGET_TEXT:
687
+ cfg_changed = rb_iv_get(self, "@configs_changed");
688
+ rb_ary_push(cfg_changed, rb_str_new2(name));
689
+ return setText(self, c, newVal, 1);
690
+ break;
691
+ case GP_WIDGET_RANGE:
692
+ cfg_changed = rb_iv_get(self, "@configs_changed");
693
+ rb_ary_push(cfg_changed, rb_str_new2(name));
694
+ return setRange(self, c, newVal, 1);
695
+ break;
696
+ case GP_WIDGET_TOGGLE:
697
+ cfg_changed = rb_iv_get(self, "@configs_changed");
698
+ rb_ary_push(cfg_changed, rb_str_new2(name));
699
+ return setToggle(self, c, newVal, 1);
700
+ break;
701
+ default:
702
+ rb_raise(rb_cGPhoto2ConfigurationError, "Cannot access this setting");
703
+ return Qnil;
704
+ }
705
+ }
706
+
707
+ /*
708
+ * call-seq:
709
+ * folder => string
710
+ *
711
+ * Returns current camera path. When image is captured, folder changes to
712
+ * path where files are saved on camera.
713
+ *
714
+ * Examples:
715
+ *
716
+ * c = GPhoto2::Camera.new
717
+ * # with Nikon DSC D80
718
+ * c.folder #=> "/"
719
+ * c.capture
720
+ * c.folder #=> "/store_00010001/DCIM/100NCD80"
721
+ *
722
+ */
723
+ VALUE camera_folder(VALUE self) {
724
+ GPhoto2Camera *c;
725
+
726
+ Data_Get_Struct(self, GPhoto2Camera, c);
727
+
728
+ return rb_str_new2(c->virtFolder);
729
+ }
730
+
731
+ /*
732
+ * call-seq:
733
+ * subfolders => array
734
+ *
735
+ * Returns an array of subfolder names in current camera path.
736
+ *
737
+ * Examples:
738
+ *
739
+ * c = GPhoto2::Camera.new
740
+ * # with Nikon DSC D80
741
+ * c.folder #=> "/"
742
+ * c.subfolders #=> ["special", "store_00010001"]
743
+ *
744
+ */
745
+ VALUE camera_subfolders(VALUE self) {
746
+ int i, count;
747
+ const char *name;
748
+ GPhoto2Camera *c;
749
+ VALUE arr;
750
+
751
+ Data_Get_Struct(self, GPhoto2Camera, c);
752
+
753
+ gp_result_check(gp_camera_folder_list_folders(c->camera, c->virtFolder, c->list, c->context));
754
+ count = gp_result_check(gp_list_count(c->list));
755
+ arr = rb_ary_new();
756
+ for (i = 0; i < count; i++) {
757
+ gp_result_check(gp_list_get_name(c->list, i, &name));
758
+ rb_ary_push(arr, rb_str_new2(name));
759
+ }
760
+ return arr;
761
+ }
762
+
763
+ /*
764
+ * call-seq:
765
+ * files => array
766
+ *
767
+ * Returns an array of file names in current camera path.
768
+ *
769
+ * Examples:
770
+ *
771
+ * c = GPhoto2::Camera.new
772
+ * # with Nikon DSC D80
773
+ * c.folder #=> "/"
774
+ * c.files #=> []
775
+ * c.capture
776
+ * c.files #=> ["DSC_0001.JPG", "DSC_0002.JPG",
777
+ * "DSC_0003.JPG", ... ]
778
+ *
779
+ */
780
+ VALUE camera_files(VALUE self) {
781
+ int i, count;
782
+ const char *name;
783
+ GPhoto2Camera *c;
784
+ VALUE arr;
785
+
786
+ Data_Get_Struct(self, GPhoto2Camera, c);
787
+
788
+ gp_result_check(gp_filesystem_reset(c->camera->fs));
789
+ gp_result_check(gp_camera_folder_list_files(c->camera, c->virtFolder, c->list, c->context));
790
+ count = gp_result_check(gp_list_count(c->list));
791
+ arr = rb_ary_new();
792
+ for (i = 0; i < count; i++) {
793
+ gp_result_check(gp_list_get_name(c->list, i, &name));
794
+ rb_ary_push(arr, rb_str_new2(name));
795
+ }
796
+ return arr;
797
+ }
798
+
799
+ /*
800
+ * call-seq:
801
+ * folder_up => camera
802
+ *
803
+ * Changes current camera path one level up.
804
+ *
805
+ * Examples:
806
+ *
807
+ * c = GPhoto2::Camera.new
808
+ * # with Nikon DSC D80
809
+ * c.folder #=> "/"
810
+ * c.capture
811
+ * c.folder #=> "/store_00010001/DCIM/100NCD80"
812
+ * c.folder_up
813
+ * c.folder #=> "/store_00010001/DCIM"
814
+ * c.folder_up.folder_up #=> "/"
815
+ *
816
+ */
817
+ VALUE camera_folder_up(VALUE self) {
818
+ char *pch;
819
+ GPhoto2Camera *c;
820
+
821
+ Data_Get_Struct(self, GPhoto2Camera, c);
822
+
823
+ pch = strrchr(c->virtFolder, '/');
824
+ if ((pch - c->virtFolder) == 0) {
825
+ c->virtFolder[1] = '\0';
826
+ } else {
827
+ c->virtFolder[pch - c->virtFolder] = '\0';
828
+ }
829
+
830
+ return self;
831
+ }
832
+
833
+ /*
834
+ * call-seq:
835
+ * folder_down(name) => camera
836
+ *
837
+ * Changes current camera path one level down into subfolder with
838
+ * specified name.
839
+ *
840
+ * Examples:
841
+ *
842
+ * c = GPhoto2::Camera.new
843
+ * # with Nikon DSC D80
844
+ * c.folder #=> "/"
845
+ * c.folder_down "store_00010001"
846
+ * c.folder #=> "/store_00010001"
847
+ * c.folder_down("DCIM").folder_down("100NCD80")
848
+ * c.folder #=> "/store_00010001/DCIM/100NCD80"
849
+ *
850
+ */
851
+ VALUE camera_folder_down(VALUE self, VALUE folder) {
852
+ Check_Type(folder, T_STRING);
853
+
854
+ const char *name;
855
+ int index;
856
+ GPhoto2Camera *c;
857
+
858
+ Data_Get_Struct(self, GPhoto2Camera, c);
859
+
860
+ name = RSTRING(folder)->ptr;
861
+ gp_result_check(gp_camera_folder_list_folders(c->camera, c->virtFolder, c->list, c->context));
862
+ gp_result_check(gp_list_find_by_name(c->list, &index, name));
863
+ if (strlen(c->virtFolder) > 1) {
864
+ strcat(c->virtFolder, "/");
865
+ }
866
+ strcat(c->virtFolder, name);
867
+ return self;
868
+ }
869
+
870
+ /*
871
+ * call-seq:
872
+ * wait(timeout=2000) => camera event
873
+ *
874
+ * Waits for an event from camera for specified amount of milliseconds.
875
+ * Returns an instance of GPhoto2::CameraEvent. When capturing image manually
876
+ * with camera connected through USB, images are not saved on memory card
877
+ * until you call this method. During tests EVENT_TYPE_FILE_ADDED event
878
+ * was always followed by EVENT_TYPE_UNKNOWN. So in the case of
879
+ * EVENT_TYPE_FILE_ADDED or EVENT_TYPE_FOLDER_ADDED, an extra call is made
880
+ * with 100ms timeout which result is ignored.
881
+ *
882
+ * When image is captured manually and then event is caught, camera virtual
883
+ * folder is changed to the one where files are saved.
884
+ *
885
+ * Examples:
886
+ *
887
+ * c = GPhoto2::Camera.new
888
+ * # capture the image manually
889
+ * evt = c.wait
890
+ * evt.type #=> "file added"
891
+ * evt.type == GPhoto2::CameraEvent::EVENT_TYPE_FILE_ADDED
892
+ * #=> true
893
+ * evt.file #=> "DSC_0384.JPG"
894
+ *
895
+ * # do nothing
896
+ * c.wait(1).type #=> "timeout"
897
+ */
898
+ VALUE camera_wait(int argc, VALUE *argv, VALUE self) {
899
+ GPhoto2Camera *c;
900
+ GPhoto2CameraEvent *ce;
901
+ CameraEventType fakeType;
902
+ void *evtData, *fakeData;
903
+ int to;
904
+
905
+ switch (argc) {
906
+ case 0:
907
+ to = 2000;
908
+ break;
909
+ case 1:
910
+ to = FIX2INT(rb_funcall(argv[0], rb_intern("to_i"), 0));
911
+ break;
912
+ default:
913
+ rb_raise(rb_eArgError, "Wrong number of arguments (%d for 0 or 1)", argc);
914
+ return Qnil;
915
+ }
916
+
917
+ Data_Get_Struct(self, GPhoto2Camera, c);
918
+ ce = (GPhoto2CameraEvent*) malloc(sizeof(GPhoto2CameraEvent));
919
+
920
+ gp_result_check(gp_camera_wait_for_event(c->camera, to, &(ce->type), &evtData, c->context));
921
+
922
+ switch (ce->type) {
923
+ case GP_EVENT_FILE_ADDED:
924
+ case GP_EVENT_FOLDER_ADDED:
925
+ ce->path = (CameraFilePath*)evtData;
926
+ strcpy(c->virtFolder, ce->path->folder);
927
+ gp_result_check(gp_camera_wait_for_event(c->camera, 100, &fakeType, &fakeData, c->context));
928
+ break;
929
+ default:
930
+ break;
931
+ }
932
+ return Data_Wrap_Struct(rb_cGPhoto2CameraEvent, camera_event_mark, camera_event_free, ce);
933
+ }
934
+