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.
- data/CHANGELOG.rdoc +9 -0
- data/Rakefile +1 -1
- data/ext/extconf.rb +1 -0
- data/ext/gphoto2camera.c +934 -0
- data/ext/gphoto2camera.h +66 -0
- data/ext/gphoto2camera_event.c +94 -0
- data/ext/gphoto2camera_event.h +44 -0
- data/ext/gphoto2camera_utilities.c +234 -0
- data/ext/gphoto2camera_utilities.h +64 -0
- data/ext/gphoto4ruby.c +23 -1049
- metadata +10 -3
- data/ext/gphoto4ruby.h +0 -86
data/ext/gphoto2camera.c
ADDED
@@ -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
|
+
|