Add workaround for gphoto camera busy problems
[entangle:entangle.git] / src / backend / entangle-camera.c
1 /*
2  *  Entangle: Tethered Camera Control & Capture
3  *
4  *  Copyright (C) 2009-2015 Daniel P. Berrange
5  *
6  *  This program is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include <config.h>
22
23 #include <glib.h>
24 #include <glib/gi18n.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <gphoto2.h>
29 #include <string.h>
30 #include <math.h>
31
32 #include "entangle-debug.h"
33 #include "entangle-camera.h"
34 #include "entangle-camera-enums.h"
35 #include "entangle-control-button.h"
36 #include "entangle-control-choice.h"
37 #include "entangle-control-date.h"
38 #include "entangle-control-group.h"
39 #include "entangle-control-range.h"
40 #include "entangle-control-text.h"
41 #include "entangle-control-toggle.h"
42
43 #define ENTANGLE_CAMERA_GET_PRIVATE(obj)                                \
44     (G_TYPE_INSTANCE_GET_PRIVATE((obj), ENTANGLE_TYPE_CAMERA, EntangleCameraPrivate))
45
46 #define ENTANGLE_ERROR(err, msg...)                     \
47     g_set_error((err),                                  \
48                 g_quark_from_string("entangle-camera"), \
49                 0,                                      \
50                 msg)
51
52 #if GLIB_CHECK_VERSION(2, 31, 0)
53 #define g_mutex_new() g_new0(GMutex, 1)
54 #define g_mutex_free(m) g_free(m)
55 #define g_cond_new() g_new0(GCond, 1)
56 #define g_cond_free(c) g_free(c)
57 #endif
58
59 struct _EntangleCameraPrivate {
60     GMutex *lock;
61     GCond *jobCond;
62     gboolean jobActive;
63
64     GPContext *ctx;
65     CameraAbilitiesList *caps;
66     GPPortInfoList *ports;
67     Camera *cam;
68
69
70     CameraWidget *widgets;
71     EntangleControlGroup *controls;
72     GHashTable *controlPaths;
73
74     EntangleProgress *progress;
75
76     char *lastError;
77
78     char *model;  /* R/O */
79     char *port;   /* R/O */
80
81     char *manual;
82     char *summary;
83     char *driver;
84
85     gboolean hasCapture;
86     gboolean hasPreview;
87     gboolean hasSettings;
88     gboolean hasViewfinder;
89 };
90
91 G_DEFINE_TYPE(EntangleCamera, entangle_camera, G_TYPE_OBJECT);
92
93 enum {
94     PROP_0,
95     PROP_MODEL,
96     PROP_PORT,
97     PROP_MANUAL,
98     PROP_SUMMARY,
99     PROP_DRIVER,
100     PROP_PROGRESS,
101     PROP_HAS_CAPTURE,
102     PROP_HAS_PREVIEW,
103     PROP_HAS_SETTINGS,
104     PROP_HAS_VIEWFINDER,
105 };
106
107 #define ENTANGLE_CAMERA_ERROR entangle_camera_error_quark ()
108
109 static GQuark entangle_camera_error_quark(void)
110 {
111     return g_quark_from_static_string("entangle-camera-error-quark");
112 }
113
114
115 static EntangleControl *do_build_controls(EntangleCamera *cam,
116                                           const char *path,
117                                           CameraWidget *widget,
118                                           GError **error);
119 static gboolean do_load_controls(EntangleCamera *cam,
120                                  const char *path,
121                                  CameraWidget *widget,
122                                  GError **error);
123
124 struct EntangleCameraEventData {
125     EntangleCamera *cam;
126     GObject *arg;
127     char *signame;
128 };
129
130
131 static gboolean entangle_camera_emit_idle(gpointer opaque)
132 {
133     struct EntangleCameraEventData *data = opaque;
134
135     g_signal_emit_by_name(data->cam, data->signame, data->arg);
136
137     g_free(data->signame);
138     g_object_unref(data->cam);
139     if (data->arg)
140         g_object_unref(data->arg);
141     g_free(data);
142     return FALSE;
143 }
144
145
146 static void entangle_camera_emit_deferred(EntangleCamera *cam,
147                                           const char *signame,
148                                           GObject *arg)
149 {
150     struct EntangleCameraEventData *data = g_new0(struct EntangleCameraEventData, 1);
151     data->cam = cam;
152     data->arg = arg;
153     data->signame = g_strdup(signame);
154     g_object_ref(cam);
155     if (arg)
156         g_object_ref(arg);
157
158     g_idle_add(entangle_camera_emit_idle, data);
159 }
160
161
162 static void entangle_camera_begin_job(EntangleCamera *cam)
163 {
164     EntangleCameraPrivate *priv = cam->priv;
165
166     g_object_ref(cam);
167
168     while (priv->jobActive) {
169         g_cond_wait(priv->jobCond, priv->lock);
170     }
171
172     priv->jobActive = TRUE;
173     g_mutex_unlock(priv->lock);
174 }
175
176
177 static void entangle_camera_end_job(EntangleCamera *cam)
178 {
179     EntangleCameraPrivate *priv = cam->priv;
180
181     priv->jobActive = FALSE;
182     g_cond_broadcast(priv->jobCond);
183     g_mutex_lock(priv->lock);
184     g_object_unref(cam);
185 }
186
187
188 static void entangle_camera_get_property(GObject *object,
189                                          guint prop_id,
190                                          GValue *value,
191                                          GParamSpec *pspec)
192 {
193     EntangleCamera *cam = ENTANGLE_CAMERA(object);
194     EntangleCameraPrivate *priv = cam->priv;
195
196     switch (prop_id)
197         {
198         case PROP_MODEL:
199             g_value_set_string(value, priv->model);
200             break;
201
202         case PROP_PORT:
203             g_value_set_string(value, priv->port);
204             break;
205
206         case PROP_MANUAL:
207             g_value_set_string(value, priv->manual);
208             break;
209
210         case PROP_SUMMARY:
211             g_value_set_string(value, priv->summary);
212             break;
213
214         case PROP_DRIVER:
215             g_value_set_string(value, priv->driver);
216             break;
217
218         case PROP_PROGRESS:
219             g_value_set_object(value, priv->progress);
220             break;
221
222         case PROP_HAS_CAPTURE:
223             g_value_set_boolean(value, priv->hasCapture);
224             break;
225
226         case PROP_HAS_PREVIEW:
227             g_value_set_boolean(value, priv->hasPreview);
228             break;
229
230         case PROP_HAS_SETTINGS:
231             g_value_set_boolean(value, priv->hasSettings);
232             break;
233
234         case PROP_HAS_VIEWFINDER:
235             g_value_set_boolean(value, priv->hasViewfinder);
236             break;
237
238         default:
239             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
240         }
241 }
242
243 static void entangle_camera_set_property(GObject *object,
244                                          guint prop_id,
245                                          const GValue *value,
246                                          GParamSpec *pspec)
247 {
248     EntangleCamera *cam = ENTANGLE_CAMERA(object);
249     EntangleCameraPrivate *priv = cam->priv;
250
251     switch (prop_id)
252         {
253         case PROP_MODEL:
254             g_free(priv->model);
255             priv->model = g_value_dup_string(value);
256             break;
257
258         case PROP_PORT:
259             g_free(priv->port);
260             priv->port = g_value_dup_string(value);
261             break;
262
263         case PROP_PROGRESS:
264             entangle_camera_set_progress(cam, g_value_get_object(value));
265             break;
266
267         case PROP_HAS_CAPTURE:
268             priv->hasCapture = g_value_get_boolean(value);
269             ENTANGLE_DEBUG("Set has capture %d", priv->hasCapture);
270             break;
271
272         case PROP_HAS_PREVIEW:
273             priv->hasPreview = g_value_get_boolean(value);
274             ENTANGLE_DEBUG("Set has preview %d", priv->hasPreview);
275             break;
276
277         case PROP_HAS_SETTINGS:
278             priv->hasSettings = g_value_get_boolean(value);
279             ENTANGLE_DEBUG("Set has settings %d", priv->hasSettings);
280             break;
281
282         case PROP_HAS_VIEWFINDER:
283             priv->hasViewfinder = g_value_get_boolean(value);
284             ENTANGLE_DEBUG("Set has viewfinder %d", priv->hasViewfinder);
285             break;
286
287         default:
288             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
289         }
290 }
291
292
293 static void entangle_camera_finalize(GObject *object)
294 {
295     EntangleCamera *cam = ENTANGLE_CAMERA(object);
296     EntangleCameraPrivate *priv = cam->priv;
297
298     ENTANGLE_DEBUG("Finalize camera %p", object);
299
300     if (priv->progress)
301         g_object_unref(priv->progress);
302     if (priv->cam) {
303         gp_camera_exit(priv->cam, priv->ctx);
304         gp_camera_free(priv->cam);
305     }
306     if (priv->widgets)
307         gp_widget_unref(priv->widgets);
308     if (priv->controls)
309         g_object_unref(priv->controls);
310     if (priv->controlPaths)
311         g_hash_table_unref(priv->controlPaths);
312     if (priv->ports)
313         gp_port_info_list_free(priv->ports);
314     if (priv->caps)
315         gp_abilities_list_free(priv->caps);
316     gp_context_unref(priv->ctx);
317     g_free(priv->driver);
318     g_free(priv->summary);
319     g_free(priv->manual);
320     g_free(priv->model);
321     g_free(priv->port);
322     g_free(priv->lastError);
323     g_mutex_free(priv->lock);
324     g_cond_free(priv->jobCond);
325
326     G_OBJECT_CLASS(entangle_camera_parent_class)->finalize(object);
327 }
328
329
330 static void entangle_camera_class_init(EntangleCameraClass *klass)
331 {
332     GObjectClass *object_class = G_OBJECT_CLASS(klass);
333
334     object_class->finalize = entangle_camera_finalize;
335     object_class->get_property = entangle_camera_get_property;
336     object_class->set_property = entangle_camera_set_property;
337
338
339     g_signal_new("camera-file-added",
340                  G_TYPE_FROM_CLASS(klass),
341                  G_SIGNAL_RUN_FIRST,
342                  G_STRUCT_OFFSET(EntangleCameraClass, camera_file_added),
343                  NULL, NULL,
344                  g_cclosure_marshal_VOID__OBJECT,
345                  G_TYPE_NONE,
346                  1,
347                  ENTANGLE_TYPE_CAMERA_FILE);
348
349     g_signal_new("camera-file-captured",
350                  G_TYPE_FROM_CLASS(klass),
351                  G_SIGNAL_RUN_FIRST,
352                  G_STRUCT_OFFSET(EntangleCameraClass, camera_file_captured),
353                  NULL, NULL,
354                  g_cclosure_marshal_VOID__OBJECT,
355                  G_TYPE_NONE,
356                  1,
357                  ENTANGLE_TYPE_CAMERA_FILE);
358
359     g_signal_new("camera-file-previewed",
360                  G_TYPE_FROM_CLASS(klass),
361                  G_SIGNAL_RUN_FIRST,
362                  G_STRUCT_OFFSET(EntangleCameraClass, camera_file_previewed),
363                  NULL, NULL,
364                  g_cclosure_marshal_VOID__OBJECT,
365                  G_TYPE_NONE,
366                  1,
367                  ENTANGLE_TYPE_CAMERA_FILE);
368
369     g_signal_new("camera-file-downloaded",
370                  G_TYPE_FROM_CLASS(klass),
371                  G_SIGNAL_RUN_FIRST,
372                  G_STRUCT_OFFSET(EntangleCameraClass, camera_file_downloaded),
373                  NULL, NULL,
374                  g_cclosure_marshal_VOID__OBJECT,
375                  G_TYPE_NONE,
376                  1,
377                  ENTANGLE_TYPE_CAMERA_FILE);
378
379     g_signal_new("camera-file-deleted",
380                  G_TYPE_FROM_CLASS(klass),
381                  G_SIGNAL_RUN_FIRST,
382                  G_STRUCT_OFFSET(EntangleCameraClass, camera_file_deleted),
383                  NULL, NULL,
384                  g_cclosure_marshal_VOID__OBJECT,
385                  G_TYPE_NONE,
386                  1,
387                  ENTANGLE_TYPE_CAMERA_FILE);
388
389     g_signal_new("camera-connected",
390                  G_TYPE_FROM_CLASS(klass),
391                  G_SIGNAL_RUN_FIRST,
392                  G_STRUCT_OFFSET(EntangleCameraClass, camera_connected),
393                  NULL, NULL,
394                  g_cclosure_marshal_VOID__VOID,
395                  G_TYPE_NONE,
396                  0);
397
398     g_signal_new("camera-disconnected",
399                  G_TYPE_FROM_CLASS(klass),
400                  G_SIGNAL_RUN_FIRST,
401                  G_STRUCT_OFFSET(EntangleCameraClass, camera_disconnected),
402                  NULL, NULL,
403                  g_cclosure_marshal_VOID__VOID,
404                  G_TYPE_NONE,
405                  0);
406
407     g_signal_new("camera-controls-changed",
408                  G_TYPE_FROM_CLASS(klass),
409                  G_SIGNAL_RUN_FIRST,
410                  G_STRUCT_OFFSET(EntangleCameraClass, camera_controls_changed),
411                  NULL, NULL,
412                  g_cclosure_marshal_VOID__VOID,
413                  G_TYPE_NONE,
414                  0);
415
416
417     g_object_class_install_property(object_class,
418                                     PROP_MODEL,
419                                     g_param_spec_string("model",
420                                                         "Camera model",
421                                                         "Model name of the camera",
422                                                         NULL,
423                                                         G_PARAM_READWRITE |
424                                                         G_PARAM_CONSTRUCT_ONLY |
425                                                         G_PARAM_STATIC_NAME |
426                                                         G_PARAM_STATIC_NICK |
427                                                         G_PARAM_STATIC_BLURB));
428     g_object_class_install_property(object_class,
429                                     PROP_PORT,
430                                     g_param_spec_string("port",
431                                                         "Camera port",
432                                                         "Device port of the camera",
433                                                         NULL,
434                                                         G_PARAM_READWRITE |
435                                                         G_PARAM_CONSTRUCT_ONLY |
436                                                         G_PARAM_STATIC_NAME |
437                                                         G_PARAM_STATIC_NICK |
438                                                         G_PARAM_STATIC_BLURB));
439
440     g_object_class_install_property(object_class,
441                                     PROP_SUMMARY,
442                                     g_param_spec_string("summary",
443                                                         "Camera summary",
444                                                         "Camera summary",
445                                                         NULL,
446                                                         G_PARAM_READABLE |
447                                                         G_PARAM_STATIC_NAME |
448                                                         G_PARAM_STATIC_NICK |
449                                                         G_PARAM_STATIC_BLURB));
450
451     g_object_class_install_property(object_class,
452                                     PROP_MANUAL,
453                                     g_param_spec_string("manual",
454                                                         "Camera manual",
455                                                         "Camera manual",
456                                                         NULL,
457                                                         G_PARAM_READABLE |
458                                                         G_PARAM_STATIC_NAME |
459                                                         G_PARAM_STATIC_NICK |
460                                                         G_PARAM_STATIC_BLURB));
461
462     g_object_class_install_property(object_class,
463                                     PROP_DRIVER,
464                                     g_param_spec_string("driver",
465                                                         "Camera driver info",
466                                                         "Camera driver information",
467                                                         NULL,
468                                                         G_PARAM_READABLE |
469                                                         G_PARAM_STATIC_NAME |
470                                                         G_PARAM_STATIC_NICK |
471                                                         G_PARAM_STATIC_BLURB));
472
473     g_object_class_install_property(object_class,
474                                     PROP_PROGRESS,
475                                     g_param_spec_object("progress",
476                                                         "Progress updater",
477                                                         "Operation progress updater",
478                                                         ENTANGLE_TYPE_PROGRESS,
479                                                         G_PARAM_READWRITE |
480                                                         G_PARAM_STATIC_NAME |
481                                                         G_PARAM_STATIC_NICK |
482                                                         G_PARAM_STATIC_BLURB));
483
484     g_object_class_install_property(object_class,
485                                     PROP_HAS_CAPTURE,
486                                     g_param_spec_boolean("has-capture",
487                                                          "Capture supported",
488                                                          "Whether image capture is supported",
489                                                          FALSE,
490                                                          G_PARAM_READWRITE |
491                                                          G_PARAM_CONSTRUCT_ONLY |
492                                                          G_PARAM_STATIC_NAME |
493                                                          G_PARAM_STATIC_NICK |
494                                                          G_PARAM_STATIC_BLURB));
495     g_object_class_install_property(object_class,
496                                     PROP_HAS_PREVIEW,
497                                     g_param_spec_boolean("has-preview",
498                                                          "Preview supported",
499                                                          "Whether image preview is supported",
500                                                          FALSE,
501                                                          G_PARAM_READWRITE |
502                                                          G_PARAM_CONSTRUCT_ONLY |
503                                                          G_PARAM_STATIC_NAME |
504                                                          G_PARAM_STATIC_NICK |
505                                                          G_PARAM_STATIC_BLURB));
506     g_object_class_install_property(object_class,
507                                     PROP_HAS_SETTINGS,
508                                     g_param_spec_boolean("has-settings",
509                                                          "Settings supported",
510                                                          "Whether camera settings configuration is supported",
511                                                          FALSE,
512                                                          G_PARAM_READWRITE |
513                                                          G_PARAM_CONSTRUCT_ONLY |
514                                                          G_PARAM_STATIC_NAME |
515                                                          G_PARAM_STATIC_NICK |
516                                                          G_PARAM_STATIC_BLURB));
517     g_object_class_install_property(object_class,
518                                     PROP_HAS_VIEWFINDER,
519                                     g_param_spec_boolean("has-viewfinder",
520                                                          "Viewfinder supported",
521                                                          "Whether camera viewfinder configuration is supported",
522                                                          FALSE,
523                                                          G_PARAM_READWRITE |
524                                                          G_PARAM_CONSTRUCT_ONLY |
525                                                          G_PARAM_STATIC_NAME |
526                                                          G_PARAM_STATIC_NICK |
527                                                          G_PARAM_STATIC_BLURB));
528     ENTANGLE_DEBUG("install prog done");
529
530     g_type_class_add_private(klass, sizeof(EntangleCameraPrivate));
531 }
532
533
534 EntangleCamera *entangle_camera_new(const char *model,
535                                     const char *port,
536                                     gboolean hasCapture,
537                                     gboolean hasPreview,
538                                     gboolean hasSettings)
539 {
540     return ENTANGLE_CAMERA(g_object_new(ENTANGLE_TYPE_CAMERA,
541                                         "model", model,
542                                         "port", port,
543                                         "has-capture", hasCapture,
544                                         "has-preview", hasPreview,
545                                         "has-settings", hasSettings,
546                                         NULL));
547 }
548
549
550 static void entangle_camera_init(EntangleCamera *cam)
551 {
552     cam->priv = ENTANGLE_CAMERA_GET_PRIVATE(cam);
553     cam->priv->lock = g_mutex_new();
554     cam->priv->jobCond = g_cond_new();
555 }
556
557
558 /**
559  * entangle_camera_get_model:
560  * @cam: (transfer none): the camera
561  *
562  * Get the camera model name
563  *
564  * Returns: (transfer none): the model name
565  */
566 const char *entangle_camera_get_model(EntangleCamera *cam)
567 {
568     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), NULL);
569
570     EntangleCameraPrivate *priv = cam->priv;
571     return priv->model;
572 }
573
574
575 /**
576  * entangle_camera_get_port:
577  * @cam: (transfer none): the camera
578  *
579  * Get the camera port name
580  *
581  * Returns: (transfer none): the port name
582  */
583 const char *entangle_camera_get_port(EntangleCamera *cam)
584 {
585     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), NULL);
586
587     EntangleCameraPrivate *priv = cam->priv;
588     return priv->port;
589 }
590
591 struct EntangleCameraProgressData {
592     EntangleCamera *cam;
593     enum {
594         ENTANGLE_CAMERA_PROGRESS_START,
595         ENTANGLE_CAMERA_PROGRESS_UPDATE,
596         ENTANGLE_CAMERA_PROGRESS_STOP
597     } op;
598     float value;
599     char *msg;
600 };
601
602 static gboolean entangle_camera_progress_idle(gpointer opaque)
603 {
604     struct EntangleCameraProgressData *data = opaque;
605
606     if (data->cam->priv->progress) {
607         switch (data->op) {
608         case ENTANGLE_CAMERA_PROGRESS_START:
609             entangle_progress_start(data->cam->priv->progress,
610                                     data->value,
611                                     data->msg);
612             break;
613
614         case ENTANGLE_CAMERA_PROGRESS_UPDATE:
615             entangle_progress_update(data->cam->priv->progress,
616                                      data->value);
617             break;
618
619         case ENTANGLE_CAMERA_PROGRESS_STOP:
620             entangle_progress_stop(data->cam->priv->progress);
621             break;
622
623         default:
624             break;
625         }
626     }
627
628     if (data->op == ENTANGLE_CAMERA_PROGRESS_START)
629         g_free(data->msg);
630     g_object_unref(data->cam);
631     g_free(data);
632
633     return FALSE;
634 }
635
636 #ifdef HAVE_GPHOTO25
637 static unsigned int do_entangle_camera_progress_start(GPContext *ctx G_GNUC_UNUSED,
638                                                       float target,
639                                                       const char *msg,
640                                                       void *opaque)
641 {
642     EntangleCamera *cam = opaque;
643     struct EntangleCameraProgressData *data = g_new0(struct EntangleCameraProgressData, 1);
644
645     data->cam = g_object_ref(cam);
646     data->op = ENTANGLE_CAMERA_PROGRESS_START;
647     data->value = target;
648     data->msg = g_strdup(msg);
649
650     g_idle_add(entangle_camera_progress_idle, data);
651
652     return 0; /* XXX what is this actually useful for ? */
653 }
654 #else
655 static unsigned int do_entangle_camera_progress_start(GPContext *ctx G_GNUC_UNUSED,
656                                                       float target,
657                                                       const char *format,
658                                                       va_list args,
659                                                       void *opaque)
660 {
661     EntangleCamera *cam = opaque;
662     struct EntangleCameraProgressData *data = g_new0(struct EntangleCameraProgressData, 1);
663
664     data->cam = g_object_ref(cam);
665     data->op = ENTANGLE_CAMERA_PROGRESS_START;
666     data->value = target;
667     data->msg = g_strdup_vprintf(format, args);
668
669     g_idle_add(entangle_camera_progress_idle, data);
670
671     return 0; /* XXX what is this actually useful for ? */
672 }
673 #endif
674
675 static void do_entangle_camera_progress_update(GPContext *ctx G_GNUC_UNUSED,
676                                                unsigned int id G_GNUC_UNUSED,
677                                                float current,
678                                                void *opaque)
679 {
680     EntangleCamera *cam = opaque;
681     struct EntangleCameraProgressData *data = g_new0(struct EntangleCameraProgressData, 1);
682
683     data->cam = g_object_ref(cam);
684     data->op = ENTANGLE_CAMERA_PROGRESS_UPDATE;
685     data->value = current;
686
687     g_idle_add(entangle_camera_progress_idle, data);
688 }
689
690 static void do_entangle_camera_progress_stop(GPContext *ctx G_GNUC_UNUSED,
691                                              unsigned int id G_GNUC_UNUSED,
692                                              void *opaque)
693 {
694     EntangleCamera *cam = opaque;
695     struct EntangleCameraProgressData *data = g_new0(struct EntangleCameraProgressData, 1);
696
697     data->cam = g_object_ref(cam);
698     data->op = ENTANGLE_CAMERA_PROGRESS_STOP;
699
700     g_idle_add(entangle_camera_progress_idle, data);
701 }
702
703 static void entangle_camera_reset_last_error(EntangleCamera *cam)
704 {
705     EntangleCameraPrivate *priv = cam->priv;
706
707     g_free(priv->lastError);
708     priv->lastError = NULL;
709 }
710
711
712 #ifdef HAVE_GPHOTO25
713 static void do_entangle_camera_error(GPContext *ctx G_GNUC_UNUSED,
714                                      const char *msg,
715                                      void *data)
716 {
717     EntangleCamera *cam = data;
718     EntangleCameraPrivate *priv = cam->priv;
719
720     entangle_camera_reset_last_error(cam);
721     priv->lastError = g_strdup(msg);
722     ENTANGLE_DEBUG("Got error %s", priv->lastError);
723 }
724 #else
725 static void do_entangle_camera_error(GPContext *ctx G_GNUC_UNUSED,
726                                      const char *fmt,
727                                      va_list args,
728                                      void *data)
729 {
730     EntangleCamera *cam = data;
731     EntangleCameraPrivate *priv = cam->priv;
732
733     entangle_camera_reset_last_error(cam);
734     priv->lastError = g_strdup_vprintf(fmt, args);
735     ENTANGLE_DEBUG("Got error %s", priv->lastError);
736 }
737 #endif
738
739
740 /**
741  * entangle_camera_connect:
742  * @cam: (transfer none): the camera
743  *
744  * Attempt to connect to and initialize the camera. This
745  * may fail if the camera is in use by another application,
746  * has gone to sleep or has been disconnected from the port.
747  *
748  * This block execution of the caller until completion.
749  *
750  * Returns: TRUE if the camera is connected, FALSE on error
751  */
752 gboolean entangle_camera_connect(EntangleCamera *cam,
753                                  GError **error)
754 {
755     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
756
757     EntangleCameraPrivate *priv = cam->priv;
758     int i;
759     GPPortInfo port;
760     CameraAbilities cap;
761     CameraText txt;
762     int err;
763     gboolean ret = FALSE;
764
765     ENTANGLE_DEBUG("Conencting to cam");
766
767     g_mutex_lock(priv->lock);
768
769     if (priv->cam != NULL) {
770         ret = TRUE;
771         goto cleanup;
772     }
773
774     priv->ctx = gp_context_new();
775
776     if (gp_abilities_list_new(&priv->caps) != GP_OK) {
777         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
778                     _("Cannot initialize gphoto2 abilities"));
779         goto cleanup;
780     }
781
782     if (gp_abilities_list_load(priv->caps, priv->ctx) != GP_OK) {
783         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
784                     _("Cannot load gphoto2 abilities"));
785         goto cleanup;
786     }
787
788     if (gp_port_info_list_new(&priv->ports) != GP_OK) {
789         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
790                     _("Cannot initialize gphoto2 ports"));
791         goto cleanup;
792     }
793
794     if (gp_port_info_list_load(priv->ports) != GP_OK) {
795         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
796                     _("Cannot load gphoto2 ports"));
797         goto cleanup;
798     }
799
800     gp_context_set_error_func(priv->ctx,
801                               do_entangle_camera_error,
802                               cam);
803     gp_context_set_progress_funcs(priv->ctx,
804                                   do_entangle_camera_progress_start,
805                                   do_entangle_camera_progress_update,
806                                   do_entangle_camera_progress_stop,
807                                   cam);
808
809     i = gp_port_info_list_lookup_path(priv->ports, priv->port);
810     gp_port_info_list_get_info(priv->ports, i, &port);
811
812     i = gp_abilities_list_lookup_model(priv->caps, priv->model);
813     gp_abilities_list_get_abilities(priv->caps, i, &cap);
814
815     gp_camera_new(&priv->cam);
816     gp_camera_set_abilities(priv->cam, cap);
817     gp_camera_set_port_info(priv->cam, port);
818
819     entangle_camera_begin_job(cam);
820     err = gp_camera_init(priv->cam, priv->ctx);
821     entangle_camera_end_job(cam);
822
823     if (err != GP_OK) {
824         gp_camera_unref(priv->cam);
825         priv->cam = NULL;
826         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
827                     _("Unable to initialize camera"));
828         goto cleanup;
829     }
830
831     /* Update capabilities as a sanity-check against orignal constructor */
832     priv->hasCapture = priv->hasPreview = priv->hasSettings = FALSE;
833     if (cap.operations & GP_OPERATION_CAPTURE_IMAGE)
834         priv->hasCapture = TRUE;
835     if (cap.operations & GP_OPERATION_CAPTURE_PREVIEW)
836         priv->hasPreview = TRUE;
837     if (cap.operations & GP_OPERATION_CONFIG)
838         priv->hasSettings = TRUE;
839     priv->hasViewfinder = FALSE;
840
841     gp_camera_get_summary(priv->cam, &txt, priv->ctx);
842     priv->summary = g_strdup(txt.text);
843
844     gp_camera_get_manual(priv->cam, &txt, priv->ctx);
845     priv->manual = g_strdup(txt.text);
846
847     gp_camera_get_about(priv->cam, &txt, priv->ctx);
848     priv->driver = g_strdup(txt.text);
849
850     ENTANGLE_DEBUG("ok");
851     ret = TRUE;
852
853  cleanup:
854     g_mutex_unlock(priv->lock);
855     if (ret)
856         entangle_camera_emit_deferred(cam, "camera-connected", NULL);
857     return ret;
858 }
859
860
861 static void entangle_camera_connect_helper(GSimpleAsyncResult *result,
862                                            GObject *object,
863                                            GCancellable *cancellable G_GNUC_UNUSED)
864 {
865     GError *error = NULL;
866
867     if (!entangle_camera_connect(ENTANGLE_CAMERA(object), &error)) {
868         g_simple_async_result_set_from_error(result, error);
869         g_error_free(error);
870     }
871 }
872
873
874 /**
875  * entangle_camera_connect_async:
876  * @cam: (transfer none): the camera
877  *
878  * Attempt to connect to and initialize the camera. This
879  * may fail if the camera is in use by another application,
880  * has gone to sleep or has been disconnected from the port.
881  *
882  * This will execute in the background, and invoke @callback
883  * when complete, whereupon entangle_camera_connect_finish
884  * can be used to check the status
885  */
886 void entangle_camera_connect_async(EntangleCamera *cam,
887                                    GCancellable *cancellable,
888                                    GAsyncReadyCallback callback,
889                                    gpointer user_data)
890 {
891     g_return_if_fail(ENTANGLE_IS_CAMERA(cam));
892
893     GSimpleAsyncResult *result = g_simple_async_result_new(G_OBJECT(cam),
894                                                            callback,
895                                                            user_data,
896                                                            entangle_camera_connect_async);
897
898     g_simple_async_result_run_in_thread(result,
899                                         entangle_camera_connect_helper,
900                                         G_PRIORITY_DEFAULT,
901                                         cancellable);
902     g_object_unref(result);
903 }
904
905
906 /**
907  * entangle_camera_connect_finish:
908  * @cam: (transfer none): the camera
909  *
910  * Check the completion status of a previous call to
911  * entangle_camera_connect_async
912  *
913  * Returns: TRUE if the camera is connected, FALSE on error
914  */
915 gboolean entangle_camera_connect_finish(EntangleCamera *cam,
916                                         GAsyncResult *result,
917                                         GError **error)
918 {
919     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
920
921     return !g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(result),
922                                                   error);
923 }
924
925
926 /**
927  * entangle_camera_disconnect:
928  * @cam: (transfer none): the camera
929  *
930  * Disconnect from the camera, enabling it to be used by
931  * other applications.
932  *
933  * This block execution of the caller until completion.
934  *
935  * Returns: TRUE if the camera is disconnected, FALSE on error
936  */
937 gboolean entangle_camera_disconnect(EntangleCamera *cam,
938                                     GError **error G_GNUC_UNUSED)
939 {
940     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
941
942     EntangleCameraPrivate *priv = cam->priv;
943     gboolean ret = FALSE;
944
945     ENTANGLE_DEBUG("Disconnecting from cam");
946
947     g_mutex_lock(priv->lock);
948
949     if (priv->cam == NULL) {
950         ret = TRUE;
951         goto cleanup;
952     }
953
954     entangle_camera_begin_job(cam);
955     gp_camera_exit(priv->cam, priv->ctx);
956     entangle_camera_end_job(cam);
957
958     if (priv->widgets) {
959         gp_widget_unref(priv->widgets);
960         priv->widgets = NULL;
961     }
962     if (priv->controls) {
963         g_object_unref(priv->controls);
964         priv->controls = NULL;
965     }
966     if (priv->controlPaths) {
967         g_hash_table_unref(priv->controlPaths);
968         priv->controlPaths = NULL;
969     }
970
971     g_free(priv->driver);
972     g_free(priv->manual);
973     g_free(priv->summary);
974     priv->driver = priv->manual = priv->summary = NULL;
975
976     if (priv->ports) {
977         gp_port_info_list_free(priv->ports);
978         priv->ports = NULL;
979     }
980     if (priv->caps) {
981         gp_abilities_list_free(priv->caps);
982         priv->caps = NULL;
983     }
984     gp_context_unref(priv->ctx);
985     priv->ctx = NULL;
986
987     gp_camera_unref(priv->cam);
988     priv->cam = NULL;
989     priv->hasViewfinder = FALSE;
990
991     ret = TRUE;
992  cleanup:
993     g_mutex_unlock(priv->lock);
994     if (ret)
995         entangle_camera_emit_deferred(cam, "camera-disconnected", NULL);
996     return ret;
997 }
998
999
1000 static void entangle_camera_disconnect_helper(GSimpleAsyncResult *result,
1001                                               GObject *object,
1002                                               GCancellable *cancellable G_GNUC_UNUSED)
1003 {
1004     GError *error = NULL;
1005
1006     if (!entangle_camera_disconnect(ENTANGLE_CAMERA(object), &error)) {
1007         g_simple_async_result_set_from_error(result, error);
1008         g_error_free(error);
1009     }
1010 }
1011
1012
1013 /**
1014  * entangle_camera_disconnect_async:
1015  * @cam: (transfer none): the camera
1016  *
1017  * Disconnect from the camera, enabling it to be used by
1018  * other applications.
1019  *
1020  * This will execute in the background, and invoke @callback
1021  * when complete, whereupon entangle_camera_connect_async
1022  * can be used to check the status
1023  *
1024  * Returns: TRUE if the camera is disconnected, FALSE on error
1025  */
1026 void entangle_camera_disconnect_async(EntangleCamera *cam,
1027                                       GCancellable *cancellable,
1028                                       GAsyncReadyCallback callback,
1029                                       gpointer user_data)
1030 {
1031     g_return_if_fail(ENTANGLE_IS_CAMERA(cam));
1032
1033     GSimpleAsyncResult *result = g_simple_async_result_new(G_OBJECT(cam),
1034                                                            callback,
1035                                                            user_data,
1036                                                            entangle_camera_disconnect_async);
1037
1038     g_simple_async_result_run_in_thread(result,
1039                                         entangle_camera_disconnect_helper,
1040                                         G_PRIORITY_DEFAULT,
1041                                         cancellable);
1042     g_object_unref(result);
1043 }
1044
1045
1046 /**
1047  * entangle_camera_disconnect_finish:
1048  * @cam: (transfer none): the camera
1049  *
1050  * Check the completion status of a previous call to
1051  * entangle_camera_disconnect_async
1052  *
1053  * Returns: TRUE if the camera is disconnected, FALSE on error
1054  */
1055 gboolean entangle_camera_disconnect_finish(EntangleCamera *cam,
1056                                            GAsyncResult *result,
1057                                            GError **error)
1058 {
1059     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
1060
1061     return !g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(result),
1062                                                   error);
1063 }
1064
1065
1066 /**
1067  * entangle_camera_get_connected:
1068  * @cam: (transfer none): the camera
1069  *
1070  * Determine if the camera is currently connected
1071  *
1072  * Returns: TRUE if the camera is connected, FALSE otherwise
1073  */
1074 gboolean entangle_camera_get_connected(EntangleCamera *cam)
1075 {
1076     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
1077
1078     EntangleCameraPrivate *priv = cam->priv;
1079     gboolean ret;
1080
1081     g_mutex_lock(priv->lock);
1082     ret = priv->cam != NULL ? TRUE : FALSE;
1083     g_mutex_unlock(priv->lock);
1084     return ret;
1085 }
1086
1087
1088 /**
1089  * entangle_camera_get_summary:
1090  * @cam: (transfer none): the camera
1091  *
1092  * Get the camera summary text. This is only available
1093  * while the camera is connected
1094  *
1095  * Returns: (transfer full): the camera summary
1096  */
1097 char *entangle_camera_get_summary(EntangleCamera *cam)
1098 {
1099     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), NULL);
1100
1101     EntangleCameraPrivate *priv = cam->priv;
1102     char *ret;
1103
1104     g_mutex_lock(priv->lock);
1105     ret = g_strdup(priv->summary);
1106     g_mutex_unlock(priv->lock);
1107
1108     return ret;
1109 }
1110
1111
1112
1113 /**
1114  * entangle_camera_get_manual:
1115  * @cam: (transfer none): the camera
1116  *
1117  * Get the camera manual text. This is only available
1118  * while the camera is connected
1119  *
1120  * Returns: (transfer full): the camera manual
1121  */
1122 char *entangle_camera_get_manual(EntangleCamera *cam)
1123 {
1124     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), NULL);
1125
1126     EntangleCameraPrivate *priv = cam->priv;
1127     char *ret;
1128
1129     g_mutex_lock(priv->lock);
1130     ret = g_strdup(priv->manual);
1131     g_mutex_unlock(priv->lock);
1132
1133     return ret;
1134 }
1135
1136
1137 /**
1138  * entangle_camera_get_driver:
1139  * @cam: (transfer none): the camera
1140  *
1141  * Get the camera driver information text. This is only available
1142  * while the camera is connected
1143  *
1144  * Returns: (transfer full): the camera driver information
1145  */
1146 char *entangle_camera_get_driver(EntangleCamera *cam)
1147 {
1148     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), NULL);
1149
1150     EntangleCameraPrivate *priv = cam->priv;
1151     char *ret;
1152
1153     g_mutex_lock(priv->lock);
1154     ret = g_strdup(priv->driver);
1155     g_mutex_unlock(priv->lock);
1156
1157     return ret;
1158 }
1159
1160
1161 /**
1162  * entangle_camera_capture_image:
1163  * @cam: (transfer none): the camera
1164  *
1165  * Trigger the camera shutter and download the first resulting
1166  * image. If the camera is shooting in multiple formats (eg JPEG
1167  * and RAW) this method will only return the first format captured
1168  * The caller should watch for signal notifications to detect any
1169  * additional images
1170  *
1171  * This can only be invoked when the camera is connected.
1172  *
1173  * This block execution of the caller until completion.
1174  *
1175  * Returns: (transfer full): the captured image or NULL
1176  */
1177 EntangleCameraFile *entangle_camera_capture_image(EntangleCamera *cam,
1178                                                   GError **error)
1179 {
1180     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), NULL);
1181
1182     EntangleCameraPrivate *priv = cam->priv;
1183     CameraFilePath camerapath;
1184     EntangleCameraFile *file = NULL;
1185     int err;
1186
1187     g_mutex_lock(priv->lock);
1188
1189     if (!priv->cam) {
1190         ENTANGLE_ERROR(error, _("Cannot capture image while not connected"));
1191         goto cleanup;
1192     }
1193
1194     ENTANGLE_DEBUG("Starting capture");
1195     entangle_camera_reset_last_error(cam);
1196     entangle_camera_begin_job(cam);
1197     err = gp_camera_capture(priv->cam,
1198                             GP_CAPTURE_IMAGE,
1199                             &camerapath,
1200                             priv->ctx);
1201     entangle_camera_end_job(cam);
1202     if (err!= GP_OK) {
1203         ENTANGLE_ERROR(error, _("Unable to capture image: %s"), priv->lastError);
1204         goto cleanup;
1205     }
1206
1207     file = entangle_camera_file_new(camerapath.folder,
1208                                     camerapath.name);
1209
1210     entangle_camera_emit_deferred(cam, "camera-file-captured", G_OBJECT(file));
1211
1212  cleanup:
1213     g_mutex_unlock(priv->lock);
1214     return file;
1215 }
1216
1217
1218 static void entangle_camera_capture_image_helper(GSimpleAsyncResult *result,
1219                                                  GObject *object,
1220                                                  GCancellable *cancellable G_GNUC_UNUSED)
1221 {
1222     EntangleCameraFile *file;
1223     GError *error = NULL;
1224
1225     if (!(file = entangle_camera_capture_image(ENTANGLE_CAMERA(object), &error))) {
1226         g_simple_async_result_set_from_error(result, error);
1227         g_error_free(error);
1228     }
1229
1230     g_simple_async_result_set_op_res_gpointer(result, file, NULL);
1231 }
1232
1233
1234 /**
1235  * entangle_camera_capture_image_async:
1236  * @cam: (transfer none): the camera
1237  *
1238  * Trigger the camera shutter and download the first resulting
1239  * image. If the camera is shooting in multiple formats (eg JPEG
1240  * and RAW) this method will only return the first format captured
1241  * The caller should watch for signal notifications to detect any
1242  * additional images
1243  *
1244  * This can only be invoked when the camera is connected.
1245  *
1246  * This will execute in the background, and invoke @callback
1247  * when complete, whereupon entangle_camera_capture_image_finish
1248  * can be used to check the status
1249  */
1250 void entangle_camera_capture_image_async(EntangleCamera *cam,
1251                                          GCancellable *cancellable,
1252                                          GAsyncReadyCallback callback,
1253                                          gpointer user_data)
1254 {
1255     g_return_if_fail(ENTANGLE_IS_CAMERA(cam));
1256
1257     GSimpleAsyncResult *result = g_simple_async_result_new(G_OBJECT(cam),
1258                                                            callback,
1259                                                            user_data,
1260                                                            entangle_camera_capture_image_async);
1261
1262     g_simple_async_result_run_in_thread(result,
1263                                         entangle_camera_capture_image_helper,
1264                                         G_PRIORITY_DEFAULT,
1265                                         cancellable);
1266     g_object_unref(result);
1267 }
1268
1269
1270 /**
1271  * entangle_camera_capture_image_finish:
1272  * @cam: (transfer none): the camera
1273  *
1274  * Check the completion status of a previous call to
1275  * entangle_camera_capture_image_async.
1276  *
1277  * Returns: (transfer full): the captured image or NULL
1278  */
1279 EntangleCameraFile *entangle_camera_capture_image_finish(EntangleCamera *cam,
1280                                                          GAsyncResult *result,
1281                                                          GError **error)
1282 {
1283     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), NULL);
1284
1285     EntangleCameraFile *file;
1286     if (g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(result),
1287                                               error))
1288         return NULL;
1289
1290     file = g_simple_async_result_get_op_res_gpointer(G_SIMPLE_ASYNC_RESULT(result));
1291
1292     return file;
1293 }
1294
1295
1296 /**
1297  * entangle_camera_preview_image:
1298  * @cam: (transfer none): the camera
1299  *
1300  * Enable "live view", if not already enabled, and capture a
1301  * low resolution preview image. The "live view" mode will
1302  * remain enabled after execution.
1303  *
1304  * This can only be invoked when the camera is connected.
1305  *
1306  * This block execution of the caller until completion.
1307  *
1308  * Returns: (transfer full): the captured image or NULL
1309  */
1310 EntangleCameraFile *entangle_camera_preview_image(EntangleCamera *cam,
1311                                                   GError **error)
1312 {
1313     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), NULL);
1314
1315     EntangleCameraPrivate *priv = cam->priv;
1316     EntangleCameraFile *file = NULL;
1317     CameraFile *datafile = NULL;
1318     const char *mimetype = NULL;
1319     GByteArray *data = NULL;
1320     const char *rawdata;
1321     unsigned long int rawdatalen;
1322     const char *name;
1323     int err;
1324
1325     g_mutex_lock(priv->lock);
1326
1327     if (!priv->cam) {
1328         ENTANGLE_ERROR(error, _("Cannot preview image while not connected"));
1329         goto cleanup;
1330     }
1331
1332     gp_file_new(&datafile);
1333
1334     ENTANGLE_DEBUG("Starting preview");
1335     entangle_camera_reset_last_error(cam);
1336     entangle_camera_begin_job(cam);
1337     err = gp_camera_capture_preview(priv->cam,
1338                                     datafile,
1339                                     priv->ctx);
1340     entangle_camera_end_job(cam);
1341
1342     if (err != GP_OK) {
1343         ENTANGLE_ERROR(error, _("Unable to capture preview: %s"), priv->lastError);
1344         goto cleanup;
1345     }
1346
1347
1348     if (gp_file_get_data_and_size(datafile, &rawdata, &rawdatalen) != GP_OK) {
1349         ENTANGLE_ERROR(error, _("Unable to get file data: %s"), priv->lastError);
1350         goto cleanup;
1351     }
1352
1353     if (gp_file_get_name(datafile, &name) != GP_OK) {
1354         ENTANGLE_ERROR(error, _("Unable to get filename: %s"), priv->lastError);
1355         goto cleanup;
1356     }
1357
1358     file = entangle_camera_file_new(NULL, NULL);
1359
1360     if (gp_file_get_mime_type(datafile, &mimetype) == GP_OK)
1361         entangle_camera_file_set_mimetype(file, mimetype);
1362
1363     data = g_byte_array_new();
1364     g_byte_array_append(data, (const guint8 *)rawdata, rawdatalen);
1365
1366     entangle_camera_file_set_data(file, data);
1367     g_byte_array_unref(data);
1368
1369     entangle_camera_emit_deferred(cam, "camera-file-previewed", G_OBJECT(file));
1370
1371  cleanup:
1372     if (datafile)
1373         gp_file_unref(datafile);
1374     g_mutex_unlock(priv->lock);
1375     return file;
1376 }
1377
1378
1379 static void entangle_camera_preview_image_helper(GSimpleAsyncResult *result,
1380                                                  GObject *object,
1381                                                  GCancellable *cancellable G_GNUC_UNUSED)
1382 {
1383     EntangleCameraFile *file;
1384     GError *error = NULL;
1385
1386     if (!(file = entangle_camera_preview_image(ENTANGLE_CAMERA(object), &error))) {
1387         g_simple_async_result_set_from_error(result, error);
1388         g_error_free(error);
1389     }
1390
1391     g_simple_async_result_set_op_res_gpointer(result, file, NULL);
1392 }
1393
1394
1395 /**
1396  * entangle_camera_preview_image_async:
1397  * @cam: (transfer none): the camera
1398  *
1399  * Enable "live view", if not already enabled, and capture a
1400  * low resolution preview image. The "live view" mode will
1401  * remain enabled after execution.
1402  *
1403  * This can only be invoked when the camera is connected.
1404  *
1405  * This will execute in the background, and invoke @callback
1406  * when complete, whereupon entangle_camera_preview_image_finish
1407  * can be used to check the status
1408  */
1409 void entangle_camera_preview_image_async(EntangleCamera *cam,
1410                                          GCancellable *cancellable,
1411                                          GAsyncReadyCallback callback,
1412                                          gpointer user_data)
1413 {
1414     g_return_if_fail(ENTANGLE_IS_CAMERA(cam));
1415
1416     GSimpleAsyncResult *result = g_simple_async_result_new(G_OBJECT(cam),
1417                                                            callback,
1418                                                            user_data,
1419                                                            entangle_camera_preview_image_async);
1420
1421     g_simple_async_result_run_in_thread(result,
1422                                         entangle_camera_preview_image_helper,
1423                                         G_PRIORITY_DEFAULT,
1424                                         cancellable);
1425     g_object_unref(result);
1426 }
1427
1428
1429 /**
1430  * entangle_camera_preview_image_finish:
1431  * @cam: (transfer none): the camera
1432  *
1433  * Check the completion status of a previous call to
1434  * entangle_camera_preview_image_async.
1435  *
1436  * Returns: (transfer full): the captured image or NULL
1437  */
1438 EntangleCameraFile *entangle_camera_preview_image_finish(EntangleCamera *cam,
1439                                                          GAsyncResult *result,
1440                                                          GError **error)
1441 {
1442     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), NULL);
1443
1444     EntangleCameraFile *file;
1445     if (g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(result),
1446                                               error))
1447         return NULL;
1448
1449     file = g_simple_async_result_get_op_res_gpointer(G_SIMPLE_ASYNC_RESULT(result));
1450
1451     return file;
1452 }
1453
1454
1455 /**
1456  * entangle_camera_download_file:
1457  * @cam: (transfer none): the camera
1458  * @file: (transfer none): the file whose contents to download
1459  *
1460  * Download the data associated with @file and set the data
1461  * on @file.
1462  *
1463  * This can only be invoked when the camera is connected.
1464  *
1465  * This block execution of the caller until completion.
1466  *
1467  * Returns: TRUE if the file was downloaded, FALSE on error
1468  */
1469 gboolean entangle_camera_download_file(EntangleCamera *cam,
1470                                        EntangleCameraFile *file,
1471                                        GError **error)
1472 {
1473     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
1474     g_return_val_if_fail(ENTANGLE_IS_CAMERA_FILE(file), FALSE);
1475
1476     EntangleCameraPrivate *priv = cam->priv;
1477     CameraFile *datafile = NULL;
1478     const char *data;
1479     unsigned long int datalen;
1480     GByteArray *filedata;
1481     gboolean ret = FALSE;
1482     int err;
1483
1484     g_mutex_lock(priv->lock);
1485
1486     if (!priv->cam) {
1487         ENTANGLE_ERROR(error, _("Cannot download file while not connected"));
1488         goto cleanup;
1489     }
1490
1491     ENTANGLE_DEBUG("Downloading '%s' from '%s'",
1492                    entangle_camera_file_get_name(file),
1493                    entangle_camera_file_get_folder(file));
1494
1495     gp_file_new(&datafile);
1496
1497     ENTANGLE_DEBUG("Getting file data");
1498     entangle_camera_reset_last_error(cam);
1499     entangle_camera_begin_job(cam);
1500     err = gp_camera_file_get(priv->cam,
1501                              entangle_camera_file_get_folder(file),
1502                              entangle_camera_file_get_name(file),
1503                              GP_FILE_TYPE_NORMAL,
1504                              datafile,
1505                              priv->ctx);
1506     g_usleep(1000*100);
1507     entangle_camera_end_job(cam);
1508
1509     if (err != GP_OK) {
1510         ENTANGLE_ERROR(error, _("Unable to get camera file: %s"), priv->lastError);
1511         goto cleanup;
1512     }
1513
1514     ENTANGLE_DEBUG("Fetching data");
1515     if (gp_file_get_data_and_size(datafile, &data, &datalen) != GP_OK) {
1516         ENTANGLE_ERROR(error, _("Unable to get file data: %s"), priv->lastError);
1517         goto cleanup;
1518     }
1519
1520     filedata = g_byte_array_new();
1521     g_byte_array_append(filedata, (const guint8*)data, datalen);
1522
1523     entangle_camera_file_set_data(file, filedata);
1524     g_byte_array_unref(filedata);
1525
1526     entangle_camera_emit_deferred(cam, "camera-file-downloaded", G_OBJECT(file));
1527
1528     ret = TRUE;
1529
1530  cleanup:
1531     ENTANGLE_DEBUG("Error");
1532     if (datafile)
1533         gp_file_unref(datafile);
1534     g_mutex_unlock(priv->lock);
1535     return ret;
1536 }
1537
1538
1539 static void entangle_camera_download_file_helper(GSimpleAsyncResult *result,
1540                                                  GObject *object,
1541                                                  GCancellable *cancellable G_GNUC_UNUSED)
1542 {
1543     EntangleCameraFile *file;
1544     GError *error = NULL;
1545
1546     file = g_simple_async_result_get_op_res_gpointer(result);
1547
1548     if (!entangle_camera_download_file(ENTANGLE_CAMERA(object), file, &error)) {
1549         g_simple_async_result_set_from_error(result, error);
1550         g_error_free(error);
1551     }
1552 }
1553
1554
1555 /**
1556  * entangle_camera_download_file_async:
1557  * @cam: (transfer none): the camera
1558  * @file: (transfer none): the file whose contents to download
1559  *
1560  * Download the data associated with @file and set the data
1561  * on @file.
1562  *
1563  * This can only be invoked when the camera is connected.
1564  *
1565  * This will execute in the background, and invoke @callback
1566  * when complete, whereupon entangle_camera_download_file_finish
1567  * can be used to check the status
1568  */
1569 void entangle_camera_download_file_async(EntangleCamera *cam,
1570                                          EntangleCameraFile *file,
1571                                          GCancellable *cancellable,
1572                                          GAsyncReadyCallback callback,
1573                                          gpointer user_data)
1574 {
1575     g_return_if_fail(ENTANGLE_IS_CAMERA(cam));
1576     g_return_if_fail(ENTANGLE_IS_CAMERA_FILE(file));
1577
1578     GSimpleAsyncResult *result = g_simple_async_result_new(G_OBJECT(cam),
1579                                                            callback,
1580                                                            user_data,
1581                                                            entangle_camera_download_file_async);
1582
1583     g_object_ref(file);
1584     g_simple_async_result_set_op_res_gpointer(result, file, g_object_unref);
1585
1586     g_simple_async_result_run_in_thread(result,
1587                                         entangle_camera_download_file_helper,
1588                                         G_PRIORITY_DEFAULT,
1589                                         cancellable);
1590     g_object_unref(result);
1591
1592 }
1593
1594 /**
1595  * entangle_camera_download_file_finish:
1596  * @cam: (transfer none): the camera
1597  *
1598  * Check the completion status of a previous call to
1599  * entangle_camera_download_file_async.
1600  *
1601  * Returns: TRUE if the file was downloaded, FALSE on error
1602  */
1603 gboolean entangle_camera_download_file_finish(EntangleCamera *cam,
1604                                               GAsyncResult *result,
1605                                               GError **err)
1606 {
1607     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
1608
1609     return !g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(result),
1610                                                   err);
1611 }
1612
1613
1614 /**
1615  * entangle_camera_delete_file:
1616  * @cam: (transfer none): the camera
1617  * @file: (transfer none): the file to delete
1618  *
1619  * Delete @file from the camera capture target.
1620  *
1621  * This can only be invoked when the camera is connected.
1622  *
1623  * This block execution of the caller until completion.
1624  *
1625  * Returns: TRUE if the file was deleted, FALSE on error
1626  */
1627 gboolean entangle_camera_delete_file(EntangleCamera *cam,
1628                                      EntangleCameraFile *file,
1629                                      GError **error)
1630 {
1631     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
1632     g_return_val_if_fail(ENTANGLE_IS_CAMERA_FILE(file), FALSE);
1633
1634     EntangleCameraPrivate *priv = cam->priv;
1635     gboolean ret = FALSE;
1636     int err;
1637
1638     g_mutex_lock(priv->lock);
1639
1640     if (!priv->cam) {
1641         ENTANGLE_ERROR(error, _("Cannot delete file while not connected"));
1642         goto cleanup;
1643     }
1644
1645     ENTANGLE_DEBUG("Deleting '%s' from '%s'",
1646                    entangle_camera_file_get_name(file),
1647                    entangle_camera_file_get_folder(file));
1648
1649     entangle_camera_reset_last_error(cam);
1650     entangle_camera_begin_job(cam);
1651     err = gp_camera_file_delete(priv->cam,
1652                                 entangle_camera_file_get_folder(file),
1653                                 entangle_camera_file_get_name(file),
1654                                 priv->ctx);
1655     g_usleep(1000*100);
1656     entangle_camera_end_job(cam);
1657
1658     if (err != GP_OK) {
1659         ENTANGLE_ERROR(error, _("Unable to delete file: %s"), priv->lastError);
1660         goto cleanup;
1661     }
1662
1663     entangle_camera_emit_deferred(cam, "camera-file-deleted", G_OBJECT(file));
1664
1665     ret = TRUE;
1666
1667  cleanup:
1668     g_mutex_unlock(priv->lock);
1669     return ret;
1670 }
1671
1672
1673 static void entangle_camera_delete_file_helper(GSimpleAsyncResult *result,
1674                                                GObject *object,
1675                                                GCancellable *cancellable G_GNUC_UNUSED)
1676 {
1677     EntangleCameraFile *file;
1678     GError *error = NULL;
1679
1680     file = g_simple_async_result_get_op_res_gpointer(result);
1681
1682     if (!entangle_camera_delete_file(ENTANGLE_CAMERA(object), file, &error)) {
1683         g_simple_async_result_set_from_error(result, error);
1684         g_error_free(error);
1685     }
1686 }
1687
1688
1689 /**
1690  * entangle_camera_delete_file_async:
1691  * @cam: (transfer none): the camera
1692  * @file: (transfer none): the file to delete
1693  *
1694  * Delete @file from the camera capture target.
1695  *
1696  * This can only be invoked when the camera is connected.
1697  *
1698  * This will execute in the background, and invoke @callback
1699  * when complete, whereupon entangle_camera_delete_file_finish
1700  * can be used to check the status
1701  */
1702 void entangle_camera_delete_file_async(EntangleCamera *cam,
1703                                        EntangleCameraFile *file,
1704                                        GCancellable *cancellable,
1705                                        GAsyncReadyCallback callback,
1706                                        gpointer user_data)
1707 {
1708     g_return_if_fail(ENTANGLE_IS_CAMERA(cam));
1709     g_return_if_fail(ENTANGLE_IS_CAMERA_FILE(file));
1710
1711     GSimpleAsyncResult *result = g_simple_async_result_new(G_OBJECT(cam),
1712                                                            callback,
1713                                                            user_data,
1714                                                            entangle_camera_delete_file_async);
1715
1716     g_object_ref(file);
1717     g_simple_async_result_set_op_res_gpointer(result, file, g_object_unref);
1718
1719     g_simple_async_result_run_in_thread(result,
1720                                         entangle_camera_delete_file_helper,
1721                                         G_PRIORITY_DEFAULT,
1722                                         cancellable);
1723     g_object_unref(result);
1724 }
1725
1726
1727 /**
1728  * entangle_camera_delete_file_finish:
1729  * @cam: (transfer none): the camera
1730  *
1731  * Check the completion status of a previous call to
1732  * entangle_camera_delete_file_async.
1733  *
1734  * Returns: TRUE if the file was deleted, FALSE on error
1735  */
1736 gboolean entangle_camera_delete_file_finish(EntangleCamera *cam,
1737                                             GAsyncResult *result,
1738                                             GError **err)
1739 {
1740     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
1741
1742     return !g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(result),
1743                                                   err);
1744 }
1745
1746
1747 /**
1748  * entangle_camera_process_events:
1749  * @cam: (transfer none): the camera
1750  * @waitms: the number of milliseconds to wait
1751  *
1752  * Wait upto @waitms milliseconds for events to arrive from
1753  * the camera. Signals will be emitted for any interesting
1754  * events that arrive. Multiple events will be processed
1755  * until @waitms is exceeded.
1756  *
1757  * This can only be invoked when the camera is connected.
1758  *
1759  * This block execution of the caller until completion.
1760  *
1761  * Returns: TRUE if the file was deleted, FALSE on error
1762  */
1763 gboolean entangle_camera_process_events(EntangleCamera *cam,
1764                                         guint64 waitms,
1765                                         GError **error)
1766 {
1767     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
1768
1769     EntangleCameraPrivate *priv = cam->priv;
1770     CameraEventType eventType = 0;
1771     void *eventData = NULL;
1772     GTimeVal tv;
1773     guint64 startms, endms, donems;
1774     gboolean ret = FALSE;
1775     int err;
1776
1777     g_mutex_lock(priv->lock);
1778
1779     if (!priv->cam) {
1780         ENTANGLE_ERROR(error, _("Cannot wait for events while not connected"));
1781         goto cleanup;
1782     }
1783
1784     g_get_current_time(&tv);
1785     startms = (tv.tv_sec * 1000ll) + (tv.tv_usec / 1000ll);
1786
1787     ENTANGLE_DEBUG("Waiting for events start %llu duration %llu",
1788                    (unsigned long long)startms,
1789                    (unsigned long long)waitms);
1790
1791     entangle_camera_reset_last_error(cam);
1792     donems = 0;
1793     do {
1794         entangle_camera_begin_job(cam);
1795         err = gp_camera_wait_for_event(priv->cam, waitms - donems, &eventType, &eventData, priv->ctx);
1796         entangle_camera_end_job(cam);
1797
1798         if (err != GP_OK) {
1799             /* Some drivers (eg canon native) can't do events, so just do a sleep */
1800             if (err == GP_ERROR_NOT_SUPPORTED) {
1801                 ENTANGLE_DEBUG("Event wait not supported, using usleep");
1802                 g_usleep((waitms-donems)*1000ll);
1803                 ret = TRUE;
1804                 goto cleanup;
1805             }
1806             ENTANGLE_ERROR(error, _("Unable to wait for events: %s"), priv->lastError);
1807             goto cleanup;
1808         }
1809         ENTANGLE_DEBUG("Event type %d", eventType);
1810         switch (eventType) {
1811         case GP_EVENT_UNKNOWN:
1812             if (eventData &&
1813                 strstr((char*)eventData, "PTP Property") &&
1814                 strstr((char*)eventData, "changed")) {
1815                 ENTANGLE_DEBUG("Config changed '%s'", (char *)eventData);
1816                 /* For some reason, every time we request the camera config
1817                  * with gp_camera_get_config, it will be followed by an
1818                  * event with key 'd10d'. So we must ignore that event
1819                  */
1820                 if (strstr(eventData, "d10d") == NULL)
1821                     entangle_camera_emit_deferred(cam, "camera-controls-changed", NULL);
1822             } else {
1823                 ENTANGLE_DEBUG("Unknown event '%s'", (char *)eventData);
1824             }
1825             break;
1826
1827         case GP_EVENT_TIMEOUT:
1828             ENTANGLE_DEBUG("Wait timed out");
1829             break;
1830
1831         case GP_EVENT_FILE_ADDED: {
1832             CameraFilePath *camerapath = eventData;
1833             EntangleCameraFile *file;
1834
1835             ENTANGLE_DEBUG("File added '%s' in '%s'", camerapath->name, camerapath->folder);
1836
1837             file = entangle_camera_file_new(camerapath->folder,
1838                                             camerapath->name);
1839
1840             entangle_camera_emit_deferred(cam, "camera-file-added", G_OBJECT(file));
1841
1842             g_object_unref(file);
1843         }   break;
1844
1845         case GP_EVENT_FOLDER_ADDED: {
1846             CameraFilePath *camerapath = eventData;
1847
1848             ENTANGLE_DEBUG("Folder added '%s' in '%s'", camerapath->name, camerapath->folder);
1849         }   break;
1850
1851         case GP_EVENT_CAPTURE_COMPLETE:
1852             ENTANGLE_DEBUG("Capture is complete");
1853             break;
1854
1855         default:
1856             ENTANGLE_DEBUG("Unexpected event received %d", eventType);
1857             break;
1858         }
1859
1860         free(eventData);
1861         eventData = NULL;
1862         g_get_current_time(&tv);
1863         endms = (tv.tv_sec * 1000ll) + (tv.tv_usec / 1000ll);
1864         donems = endms - startms;
1865     } while (eventType != GP_EVENT_TIMEOUT &&
1866              donems < waitms);
1867
1868     ENTANGLE_DEBUG("Done waiting for events %llu",
1869                    (unsigned long long)donems);
1870
1871     ret = TRUE;
1872
1873  cleanup:
1874     free(eventData);
1875     g_mutex_unlock(priv->lock);
1876     return ret;
1877 }
1878
1879
1880 static void entangle_camera_process_events_helper(GSimpleAsyncResult *result,
1881                                                   GObject *object,
1882                                                   GCancellable *cancellable G_GNUC_UNUSED)
1883 {
1884     guint64 *waitptr;
1885     GError *error = NULL;
1886
1887     waitptr = g_simple_async_result_get_op_res_gpointer(result);
1888
1889     if (!entangle_camera_process_events(ENTANGLE_CAMERA(object), *waitptr, &error)) {
1890         g_simple_async_result_set_from_error(result, error);
1891         g_error_free(error);
1892     }
1893 }
1894
1895
1896 /**
1897  * entangle_camera_process_events_async:
1898  * @cam: (transfer none): the camera
1899  * @waitms: the number of milliseconds to wait
1900  *
1901  * Wait upto @waitms milliseconds for events to arrive from
1902  * the camera. Signals will be emitted for any interesting
1903  * events that arrive. Multiple events will be processed
1904  * until @waitms is exceeded.
1905  *
1906  * This can only be invoked when the camera is connected.
1907  *
1908  * This will execute in the background, and invoke @callback
1909  * when complete, whereupon entangle_camera_process_events_finish
1910  * can be used to check the status
1911  */
1912 void entangle_camera_process_events_async(EntangleCamera *cam,
1913                                           guint64 waitms,
1914                                           GCancellable *cancellable,
1915                                           GAsyncReadyCallback callback,
1916                                           gpointer user_data)
1917 {
1918     g_return_if_fail(ENTANGLE_IS_CAMERA(cam));
1919
1920     guint64 *waitptr = g_new0(guint64, 1);
1921     GSimpleAsyncResult *result = g_simple_async_result_new(G_OBJECT(cam),
1922                                                            callback,
1923                                                            user_data,
1924                                                            entangle_camera_process_events_async);
1925
1926     *waitptr = waitms;
1927     g_simple_async_result_set_op_res_gpointer(result, waitptr, g_free);
1928
1929     g_simple_async_result_run_in_thread(result,
1930                                         entangle_camera_process_events_helper,
1931                                         G_PRIORITY_DEFAULT,
1932                                         cancellable);
1933     g_object_unref(result);
1934 }
1935
1936
1937 /**
1938  * entangle_camera_process_events_finish:
1939  * @cam: (transfer none): the camera
1940  *
1941  * Check the completion status of a previous call to
1942  * entangle_camera_process_events_async.
1943  *
1944  * Returns: TRUE if events were processed, FALSE on error
1945  */
1946 gboolean entangle_camera_process_events_finish(EntangleCamera *cam,
1947                                                GAsyncResult *result,
1948                                                GError **error)
1949 {
1950     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
1951
1952     return !g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(result),
1953                                                   error);
1954 }
1955
1956
1957 static CameraWidget *
1958 entangle_camera_find_widget(EntangleCamera *cam,
1959                             const gchar *path)
1960 {
1961     EntangleCameraPrivate *priv = cam->priv;
1962     gchar **names = g_strsplit(path, "/", 0);
1963     CameraWidget *ret = NULL;
1964     CameraWidget *curr = priv->widgets;
1965     gsize i;
1966
1967     for (i = 0; names[i] != NULL; i++) {
1968         CameraWidget *tmp;
1969
1970         if (g_str_equal(names[i], "") ||
1971             g_str_equal(names[i], "main"))
1972             continue;
1973
1974         if (gp_widget_get_child_by_name(curr,
1975                                         names[i],
1976                                         &tmp) != GP_OK)
1977             goto cleanup;
1978
1979         curr = tmp;
1980     }
1981
1982     ret = curr;
1983
1984  cleanup:
1985     g_strfreev(names);
1986     return ret;
1987 }
1988
1989
1990 static EntangleControl *do_build_controls(EntangleCamera *cam,
1991                                           const char *path,
1992                                           CameraWidget *widget,
1993                                           GError **error)
1994 {
1995     EntangleCameraPrivate *priv = cam->priv;
1996     CameraWidgetType type;
1997     EntangleControl *ret = NULL;
1998     const char *name;
1999     char *fullpath;
2000     int id;
2001     const char *label;
2002     const char *info;
2003     int ro;
2004
2005     if (gp_widget_get_type(widget, &type) != GP_OK) {
2006         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2007                     _("Unable to fetch widget type"));
2008         return NULL;
2009     }
2010
2011     if (gp_widget_get_name(widget, &name) != GP_OK) {
2012         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2013                     _("Unable to fetch widget name"));
2014         return NULL;
2015     }
2016
2017     gp_widget_get_id(widget, &id);
2018     gp_widget_get_label(widget, &label);
2019     gp_widget_get_info(widget, &info);
2020     gp_widget_get_readonly(widget, &ro);
2021     if (info == NULL)
2022         info = label;
2023
2024     fullpath = g_strdup_printf("%s/%s", path, name);
2025
2026     switch (type) {
2027         /* We treat both window and section as just groups */
2028     case GP_WIDGET_WINDOW:
2029         {
2030             EntangleControlGroup *grp;
2031             ENTANGLE_DEBUG("Add group %s %d %s", fullpath, id, label);
2032             grp = entangle_control_group_new(fullpath, id, label, info, ro);
2033             for (int i = 0; i < gp_widget_count_children(widget); i++) {
2034                 CameraWidget *child;
2035                 EntangleControl *subctl;
2036                 const char *childname;
2037                 if (gp_widget_get_child(widget, i, &child) != GP_OK ||
2038                     gp_widget_get_name(child, &childname) != GP_OK) {
2039                     g_object_unref(grp);
2040                     goto error;
2041                 }
2042
2043                 /* Actions are exposed as normal APIs
2044                  * And "Other ptp properties" 90% dups
2045                  */
2046                 if (g_str_equal(childname, "actions") ||
2047                     g_str_equal(childname, "other"))
2048                     continue;
2049
2050                 if (!(subctl = do_build_controls(cam, fullpath, child, error))) {
2051                     g_object_unref(grp);
2052                     goto error;
2053                 }
2054
2055                 entangle_control_group_add(grp, subctl);
2056             }
2057
2058             ret = ENTANGLE_CONTROL(grp);
2059         } break;
2060
2061     case GP_WIDGET_SECTION:
2062         {
2063             EntangleControlGroup *grp;
2064             ENTANGLE_DEBUG("Add group %s %d %s", fullpath, id, label);
2065             grp = entangle_control_group_new(fullpath, id, label, info, ro);
2066             for (int i = 0; i < gp_widget_count_children(widget); i++) {
2067                 CameraWidget *child;
2068                 EntangleControl *subctl;
2069                 if (gp_widget_get_child(widget, i, &child) != GP_OK) {
2070                     g_object_unref(grp);
2071                     goto error;
2072                 }
2073                 if (!(subctl = do_build_controls(cam, fullpath, child, error))) {
2074                     g_object_unref(grp);
2075                     goto error;
2076                 }
2077
2078                 entangle_control_group_add(grp, subctl);
2079             }
2080
2081             ret = ENTANGLE_CONTROL(grp);
2082         } break;
2083
2084     case GP_WIDGET_BUTTON:
2085         {
2086             ENTANGLE_DEBUG("Add button %s %d %s", fullpath, id, label);
2087             ret = ENTANGLE_CONTROL(entangle_control_button_new(fullpath, id, label, info, ro));
2088         } break;
2089
2090         /* Unclear why these two are the same in libgphoto */
2091     case GP_WIDGET_RADIO:
2092     case GP_WIDGET_MENU:
2093         {
2094             ENTANGLE_DEBUG("Add menu %s %d %s", fullpath, id, label);
2095             ret = ENTANGLE_CONTROL(entangle_control_choice_new(fullpath, id, label, info, ro));
2096         } break;
2097
2098     case GP_WIDGET_DATE:
2099         {
2100             ENTANGLE_DEBUG("Add date %s %d %s", fullpath, id, label);
2101             ret = ENTANGLE_CONTROL(entangle_control_date_new(fullpath, id, label, info, ro));
2102         } break;
2103
2104     case GP_WIDGET_RANGE:
2105         {
2106             float min, max, step;
2107             gp_widget_get_range(widget, &min, &max, &step);
2108             ENTANGLE_DEBUG("Add range %s %d %s %f %f %f", fullpath, id, label,
2109                            (double)min, (double)max, (double)step);
2110             ret = ENTANGLE_CONTROL(entangle_control_range_new(fullpath, id, label, info, ro,
2111                                                               min, max, step));
2112         } break;
2113
2114     case GP_WIDGET_TEXT:
2115         {
2116             ENTANGLE_DEBUG("Add date %s %d %s", fullpath, id, label);
2117             ret = ENTANGLE_CONTROL(entangle_control_text_new(fullpath, id, label, info, ro));
2118         } break;
2119
2120     case GP_WIDGET_TOGGLE:
2121         {
2122             ENTANGLE_DEBUG("Add date %s %d %s", fullpath, id, label);
2123             ret = ENTANGLE_CONTROL(entangle_control_toggle_new(fullpath, id, label, info, ro));
2124         } break;
2125
2126     default:
2127         g_warn_if_reached();
2128         break;
2129     }
2130
2131     g_hash_table_insert(priv->controlPaths, g_strdup(fullpath), ret);
2132
2133  error:
2134     g_free(fullpath);
2135     return ret;
2136 }
2137
2138
2139 static gboolean entangle_str_equal_null(gchar *a, gchar *b)
2140 {
2141     if (!a && !b)
2142         return TRUE;
2143     if (!a || !b)
2144         return FALSE;
2145     return g_str_equal(a, b);
2146 }
2147
2148
2149 /*
2150  * XXX this method causes signals to be emitted from controls
2151  * in non-main threads, if triggered via an _async() method.
2152  * Investigate if we can fix this to always run in main thread
2153  * to simplify thread safety for GTK frontend
2154  */
2155 static gboolean do_load_controls(EntangleCamera *cam,
2156                                  const char *path,
2157                                  CameraWidget *widget,
2158                                  GError **error)
2159 {
2160     EntangleCameraPrivate *priv = cam->priv;
2161     CameraWidgetType type;
2162     EntangleControl *ctrl = NULL;
2163     const char *name;
2164     char *fullpath;
2165     int ro;
2166     gboolean ret = FALSE;
2167
2168     if (gp_widget_get_type(widget, &type) != GP_OK) {
2169         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2170                     _("Unable to fetch widget type"));
2171         return FALSE;
2172     }
2173
2174     if (gp_widget_get_name(widget, &name) != GP_OK) {
2175         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2176                     _("Unable to fetch widget name"));
2177         return FALSE;
2178     }
2179
2180     gp_widget_get_readonly(widget, &ro);
2181
2182     fullpath = g_strdup_printf("%s/%s", path, name);
2183     ctrl = g_hash_table_lookup(priv->controlPaths, fullpath);
2184     if (!ctrl) {
2185         ret = TRUE;
2186         goto cleanup;
2187     }
2188     entangle_control_set_readonly(ctrl, ro ? TRUE : FALSE);
2189
2190     switch (type) {
2191         /* We treat both window and section as just groups */
2192     case GP_WIDGET_WINDOW:
2193     case GP_WIDGET_SECTION:
2194         for (int i = 0; i < gp_widget_count_children(widget); i++) {
2195             CameraWidget *child;
2196             if (gp_widget_get_child(widget, i, &child) == GP_OK)
2197                 if (!do_load_controls(cam, fullpath, child, error))
2198                     goto cleanup;
2199         }
2200         break;
2201
2202     case GP_WIDGET_BUTTON: {
2203     }   break;
2204
2205         /* Unclear why these two are the same in libgphoto */
2206     case GP_WIDGET_RADIO:
2207     case GP_WIDGET_MENU: {
2208         gchar *newValue = NULL;
2209         gchar *oldValue = NULL;
2210         g_object_get(ctrl, "value", &oldValue, NULL);
2211         gp_widget_get_value(widget, &newValue);
2212         if (!entangle_str_equal_null(newValue, oldValue)) {
2213             ENTANGLE_DEBUG("Updating value of menu '%s' ('%s') old='%s' new='%s'",
2214                            entangle_control_get_path(ctrl),
2215                            entangle_control_get_label(ctrl),
2216                            oldValue, newValue);
2217             entangle_control_choice_clear_entries(ENTANGLE_CONTROL_CHOICE(ctrl));
2218             for (int i = 0; i < gp_widget_count_choices(widget); i++) {
2219                 const char *choice;
2220                 gp_widget_get_choice(widget, i, &choice);
2221                 ENTANGLE_DEBUG("Add choice '%s'", choice);
2222                 entangle_control_choice_add_entry(ENTANGLE_CONTROL_CHOICE(ctrl), choice);
2223             }
2224             g_object_set(ctrl, "value", newValue, NULL);
2225         }
2226         g_free(oldValue);
2227     }   break;
2228
2229     case GP_WIDGET_DATE: {
2230         int value = 0;
2231         g_object_set(ctrl, "value", value, NULL);
2232     }   break;
2233
2234     case GP_WIDGET_RANGE: {
2235         float newValue = 0.0;
2236         float oldValue = 0.0;
2237         g_object_get(ctrl, "value", &oldValue, NULL);
2238         gp_widget_get_value(widget, &newValue);
2239         if (fabs(newValue - oldValue) > 0.0001) {
2240             ENTANGLE_DEBUG("Updating value of range '%s' ('%s') old='%f' new='%f'",
2241                            entangle_control_get_path(ctrl),
2242                            entangle_control_get_label(ctrl),
2243                            (double)oldValue, (double)newValue);
2244             g_object_set(ctrl, "value", (double)newValue, NULL);
2245         }
2246     }   break;
2247
2248     case GP_WIDGET_TEXT: {
2249         gchar *newValue = NULL;
2250         gchar *oldValue = NULL;
2251         g_object_get(ctrl, "value", &oldValue, NULL);
2252         gp_widget_get_value(widget, &newValue);
2253         if (!entangle_str_equal_null(newValue, oldValue)) {
2254             ENTANGLE_DEBUG("Updating value of text '%s' ('%s') old='%s' new='%s'",
2255                            entangle_control_get_path(ctrl),
2256                            entangle_control_get_label(ctrl),
2257                            oldValue, newValue);
2258             g_object_set(ctrl, "value", newValue, NULL);
2259         }
2260         g_free(oldValue);
2261     }   break;
2262
2263     case GP_WIDGET_TOGGLE: {
2264         int i;
2265         gboolean newValue = 0;
2266         gboolean oldValue = 0;
2267         g_object_get(ctrl, "value", &oldValue, NULL);
2268         gp_widget_get_value(widget, &i);
2269         newValue = i ? TRUE : FALSE;
2270         if (newValue != oldValue) {
2271             ENTANGLE_DEBUG("Updating value of toggle '%s' ('%s') old='%d' new='%d'",
2272                            entangle_control_get_path(ctrl),
2273                            entangle_control_get_label(ctrl),
2274                            oldValue, newValue);
2275             g_object_set(ctrl, "value", newValue, NULL);
2276         }
2277     }   break;
2278
2279     default:
2280         g_warn_if_reached();
2281         break;
2282     }
2283
2284     entangle_control_set_dirty(ctrl, FALSE);
2285     ret = TRUE;
2286  cleanup:
2287     g_free(fullpath);
2288     return ret;
2289 }
2290
2291 static gboolean do_save_controls(EntangleCamera *cam,
2292                                  const char *path,
2293                                  CameraWidget *widget,
2294                                  gboolean *dirty,
2295                                  GError **error)
2296 {
2297     EntangleCameraPrivate *priv = cam->priv;
2298     CameraWidgetType type;
2299     EntangleControl *ctrl = NULL;
2300     const char *name;
2301     char *fullpath;
2302     gboolean ret = FALSE;
2303
2304     if (gp_widget_get_type(widget, &type) != GP_OK) {
2305         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2306                     _("Unable to fetch widget type"));
2307         return FALSE;
2308     }
2309
2310     if (gp_widget_get_name(widget, &name) != GP_OK) {
2311         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2312                     _("Unable to fetch widget name"));
2313         return FALSE;
2314     }
2315
2316     fullpath = g_strdup_printf("%s/%s", path, name);
2317     ctrl = g_hash_table_lookup(priv->controlPaths, fullpath);
2318     if (!ctrl) {
2319         ret = TRUE;
2320         goto cleanup;
2321     }
2322
2323     switch (type) {
2324         /* We treat both window and section as just groups */
2325     case GP_WIDGET_WINDOW:
2326     case GP_WIDGET_SECTION:
2327         for (int i = 0; i < gp_widget_count_children(widget); i++) {
2328             CameraWidget *child;
2329             if (gp_widget_get_child(widget, i, &child) == GP_OK)
2330                 if (!do_save_controls(cam, fullpath, child, dirty, error))
2331                     goto cleanup;
2332         }
2333         break;
2334
2335     case GP_WIDGET_BUTTON: {
2336     }   break;
2337
2338         /* Unclear why these two are the same in libgphoto */
2339     case GP_WIDGET_RADIO:
2340     case GP_WIDGET_MENU:
2341         if (entangle_control_get_dirty(ctrl)) {
2342             char *value = NULL;
2343             g_object_get(ctrl, "value", &value, NULL);
2344             gp_widget_set_value(widget, value);
2345             g_free(value);
2346             *dirty = TRUE;
2347         }
2348         break;
2349
2350     case GP_WIDGET_DATE:
2351         if (entangle_control_get_dirty(ctrl)) {
2352             int value = 0;
2353             g_object_get(ctrl, "value", &value, NULL);
2354             *dirty = TRUE;
2355         }
2356         break;
2357
2358     case GP_WIDGET_RANGE:
2359         if (entangle_control_get_dirty(ctrl)) {
2360             float value = 0.0;
2361             g_object_get(ctrl, "value", &value, NULL);
2362             gp_widget_set_value(widget, &value);
2363             *dirty = TRUE;
2364         }
2365         break;
2366
2367     case GP_WIDGET_TEXT:
2368         if (entangle_control_get_dirty(ctrl)) {
2369             char *value = NULL;
2370             g_object_get(ctrl, "value", &value, NULL);
2371             gp_widget_set_value(widget, value);
2372             g_free(value);
2373             *dirty = TRUE;
2374         }
2375         break;
2376
2377     case GP_WIDGET_TOGGLE:
2378         if (entangle_control_get_dirty(ctrl)) {
2379             gboolean value = 0;
2380             int i;
2381             g_object_get(ctrl, "value", &value, NULL);
2382             i = value ? 1 : 0;
2383             gp_widget_set_value(widget, &i);
2384             *dirty = TRUE;
2385         }
2386         break;
2387
2388     default:
2389         g_warn_if_reached();
2390         break;
2391     }
2392
2393     ret = TRUE;
2394  cleanup:
2395     g_free(fullpath);
2396     return ret;
2397 }
2398
2399
2400 /**
2401  * entangle_camera_load_controls:
2402  * @cam: (transfer none): the camera
2403  *
2404  * Loads the configuration controls from the camera.
2405  *
2406  * This can only be invoked when the camera is connected.
2407  *
2408  * This block execution of the caller until completion.
2409  *
2410  * Returns: TRUE if the controls were loaded, FALSE on error
2411  */
2412 gboolean entangle_camera_load_controls(EntangleCamera *cam,
2413                                        GError **error)
2414 {
2415     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
2416
2417     EntangleCameraPrivate *priv = cam->priv;
2418     gboolean ret = FALSE;
2419     int err;
2420
2421     g_mutex_lock(priv->lock);
2422
2423     if (priv->cam == NULL) {
2424         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2425                     _("Unable to load controls, camera is not connected"));
2426         goto cleanup;
2427     }
2428
2429     entangle_camera_begin_job(cam);
2430     ENTANGLE_DEBUG("Loading control values");
2431     err = gp_camera_get_config(priv->cam, &priv->widgets, priv->ctx);
2432     if (err != GP_OK) {
2433         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2434                     _("Unable to fetch camera control configuration"));
2435         goto endjob;
2436     }
2437
2438     if (priv->controls == NULL) {
2439         ENTANGLE_DEBUG("Building controls");
2440         priv->controlPaths = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
2441         if (!(priv->controls = ENTANGLE_CONTROL_GROUP(do_build_controls(cam, "", priv->widgets, error)))) {
2442             g_hash_table_unref(priv->controlPaths);
2443             priv->controlPaths = NULL;
2444             goto endjob;
2445         }
2446
2447         if (entangle_camera_find_widget(cam, "/main/actions/viewfinder") ||
2448             entangle_camera_find_widget(cam, "/main/actions/eosviewfinder")) {
2449             ENTANGLE_DEBUG("Found a viewfinder widget");
2450             priv->hasViewfinder = TRUE;
2451         } else {
2452             ENTANGLE_DEBUG("No viewfinder widget");
2453             priv->hasViewfinder = FALSE;
2454         }
2455     }
2456
2457     ret = do_load_controls(cam, "", priv->widgets, error);
2458
2459  endjob:
2460     entangle_camera_end_job(cam);
2461
2462  cleanup:
2463     g_mutex_unlock(priv->lock);
2464     return ret;
2465 }
2466
2467
2468 static void entangle_camera_load_controls_helper(GSimpleAsyncResult *result,
2469                                                  GObject *object,
2470                                                  GCancellable *cancellable G_GNUC_UNUSED)
2471 {
2472     GError *error = NULL;
2473
2474     if (!entangle_camera_load_controls(ENTANGLE_CAMERA(object), &error)) {
2475         g_simple_async_result_set_from_error(result, error);
2476         g_error_free(error);
2477     }
2478 }
2479
2480
2481 /**
2482  * entangle_camera_load_controls_async:
2483  * @cam: (transfer none): the camera
2484  *
2485  * Loads the configuration controls from the camera.
2486  *
2487  * This can only be invoked when the camera is connected.
2488  *
2489  * This will execute in the background, and invoke @callback
2490  * when complete, whereupon entangle_camera_load_controls_finish
2491  * can be used to check the status
2492  */
2493 void entangle_camera_load_controls_async(EntangleCamera *cam,
2494                                          GCancellable *cancellable,
2495                                          GAsyncReadyCallback callback,
2496                                          gpointer user_data)
2497 {
2498     g_return_if_fail(ENTANGLE_IS_CAMERA(cam));
2499
2500     GSimpleAsyncResult *result = g_simple_async_result_new(G_OBJECT(cam),
2501                                                            callback,
2502                                                            user_data,
2503                                                            entangle_camera_load_controls_async);
2504
2505     g_simple_async_result_run_in_thread(result,
2506                                         entangle_camera_load_controls_helper,
2507                                         G_PRIORITY_DEFAULT,
2508                                         cancellable);
2509     g_object_unref(result);
2510 }
2511
2512
2513 /**
2514  * entangle_camera_load_controls_finish:
2515  * @cam: (transfer none): the camera
2516  *
2517  * Check the completion status of a previous call to
2518  * entangle_camera_load_controls_async.
2519  *
2520  * Returns: TRUE if the controls were loaded, FALSE on error
2521  */
2522 gboolean entangle_camera_load_controls_finish(EntangleCamera *cam,
2523                                               GAsyncResult *result,
2524                                               GError **error)
2525 {
2526     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
2527
2528     return !g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(result),
2529                                                   error);
2530 }
2531
2532
2533 /**
2534  * entangle_camera_save_controls:
2535  * @cam: (transfer none): the camera
2536  *
2537  * Saves the configuration controls to the camera.
2538  *
2539  * This can only be invoked when the camera is connected.
2540  *
2541  * This block execution of the caller until completion.
2542  *
2543  * Returns: TRUE if the controls were saved, FALSE on error
2544  */
2545 gboolean entangle_camera_save_controls(EntangleCamera *cam,
2546                                        GError **error)
2547 {
2548     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
2549
2550     EntangleCameraPrivate *priv = cam->priv;
2551     gboolean ret = FALSE;
2552     gboolean dirty = FALSE;
2553     int err;
2554
2555     g_mutex_lock(priv->lock);
2556
2557     if (priv->cam == NULL) {
2558         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2559                     _("Unable to save controls, camera is not connected"));
2560         goto cleanup;
2561     }
2562
2563     if (priv->controls == NULL) {
2564         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2565                     _("Unable to save controls, camera is not configurable"));
2566         goto cleanup;
2567     }
2568
2569     entangle_camera_begin_job(cam);
2570
2571     ENTANGLE_DEBUG("Saving controls for %p", cam);
2572
2573     if (!do_save_controls(cam, "", priv->widgets,
2574                           &dirty, error))
2575         goto endjob;
2576
2577     if (!dirty) {
2578         ENTANGLE_DEBUG("No widgets dirty, skipping");
2579         goto done;
2580     }
2581
2582     if ((err = gp_camera_set_config(priv->cam, priv->widgets, priv->ctx)) != GP_OK) {
2583         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2584                     _("Unable to save camera control configuration: %s %d"),
2585                     gp_port_result_as_string(err), err);
2586         goto endjob;
2587     }
2588
2589     if (!do_load_controls(cam, "", priv->widgets, error))
2590         goto endjob;
2591
2592  done:
2593     ret = TRUE;
2594
2595  endjob:
2596     entangle_camera_end_job(cam);
2597
2598  cleanup:
2599     g_mutex_unlock(priv->lock);
2600     return ret;
2601 }
2602
2603
2604
2605 static void entangle_camera_save_controls_helper(GSimpleAsyncResult *result,
2606                                                  GObject *object,
2607                                                  GCancellable *cancellable G_GNUC_UNUSED)
2608 {
2609     GError *error = NULL;
2610
2611     if (!entangle_camera_save_controls(ENTANGLE_CAMERA(object), &error)) {
2612         g_simple_async_result_set_from_error(result, error);
2613         g_error_free(error);
2614     }
2615 }
2616
2617
2618 /**
2619  * entangle_camera_save_controls_async:
2620  * @cam: (transfer none): the camera
2621  *
2622  * Saves the configuration controls to the camera.
2623  *
2624  * This can only be invoked when the camera is connected.
2625  *
2626  * This will execute in the background, and invoke @callback
2627  * when complete, whereupon entangle_camera_save_controls_finish
2628  * can be used to check the status
2629  */
2630 void entangle_camera_save_controls_async(EntangleCamera *cam,
2631                                          GCancellable *cancellable,
2632                                          GAsyncReadyCallback callback,
2633                                          gpointer user_data)
2634 {
2635     g_return_if_fail(ENTANGLE_IS_CAMERA(cam));
2636
2637     GSimpleAsyncResult *result = g_simple_async_result_new(G_OBJECT(cam),
2638                                                            callback,
2639                                                            user_data,
2640                                                            entangle_camera_save_controls_async);
2641
2642     g_simple_async_result_run_in_thread(result,
2643                                         entangle_camera_save_controls_helper,
2644                                         G_PRIORITY_DEFAULT,
2645                                         cancellable);
2646     g_object_unref(result);
2647 }
2648
2649
2650 /**
2651  * entangle_camera_save_controls_finish:
2652  * @cam: (transfer none): the camera
2653  *
2654  * Check the completion status of a previous call to
2655  * entangle_camera_save_controls_async.
2656  *
2657  * Returns: TRUE if the controls were saved, FALSE on error
2658  */
2659 gboolean entangle_camera_save_controls_finish(EntangleCamera *cam,
2660                                               GAsyncResult *result,
2661                                               GError **error)
2662 {
2663     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
2664
2665     return !g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(result),
2666                                                   error);
2667 }
2668
2669
2670 /**
2671  * entangle_camera_get_controls:
2672  * @cam: (transfer none): the camera
2673  *
2674  * Get the configuration controls associated with the camera.
2675  *
2676  * This can only be invoked when the camera is connected.
2677  *
2678  * Returns: (transfer full): the controls, or NULL
2679  */
2680 EntangleControlGroup *entangle_camera_get_controls(EntangleCamera *cam, GError **error)
2681 {
2682     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), NULL);
2683
2684     EntangleCameraPrivate *priv = cam->priv;
2685     EntangleControlGroup *ret = NULL;
2686
2687     g_mutex_lock(priv->lock);
2688
2689     if (priv->cam == NULL) {
2690         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2691                     _("Controls not available when camera is disconnected"));
2692         goto cleanup;
2693     }
2694
2695     if (priv->controls == NULL) {
2696         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2697                     _("Controls not available for this camera"));
2698         goto cleanup;
2699     }
2700
2701     ret = priv->controls;
2702     g_object_ref(ret);
2703
2704  cleanup:
2705     g_mutex_unlock(priv->lock);
2706     return ret;
2707 }
2708
2709
2710 /**
2711  * entangle_camera_set_viewfinder:
2712  * @cam: (transfer none): the camera
2713  * @enabled: TRUE to turn on the view finder
2714  *
2715  * If @enabled is TRUE, the view finder will be activated
2716  * allowing preview images to be captured. If @enabled is
2717  * FALSE, the view finder will be deactivated.
2718  *
2719  * This can only be invoked when the camera is connected.
2720  *
2721  * This block execution of the caller until completion.
2722  *
2723  * Returns: TRUE if the viewer finder state was changed, FALSE on error
2724  */
2725 gboolean entangle_camera_set_viewfinder(EntangleCamera *cam,
2726                                         gboolean enabled,
2727                                         GError **error)
2728 {
2729     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
2730
2731     EntangleCameraPrivate *priv = cam->priv;
2732     gboolean ret = FALSE;
2733     CameraWidget *widget;
2734     CameraWidgetType type;
2735     int value;
2736     int err;
2737
2738     g_mutex_lock(priv->lock);
2739     entangle_camera_begin_job(cam);
2740
2741     ENTANGLE_DEBUG("Setting viewfinder state %d", enabled);
2742
2743     if (priv->cam == NULL) {
2744         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2745                     _("Controls not available when camera is disconnected"));
2746         goto cleanup;
2747     }
2748
2749     if (priv->widgets == NULL) {
2750         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2751                     _("Controls not available for this camera"));
2752         goto cleanup;
2753     }
2754
2755     widget = entangle_camera_find_widget(cam,
2756                                          "/main/actions/viewfinder");
2757     if (!widget)
2758         widget = entangle_camera_find_widget(cam,
2759                                              "/main/actions/eosviewfinder");
2760     if (!widget) {
2761         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2762                     _("Viewfinder control not available with this camera"));
2763         goto cleanup;
2764     }
2765
2766     if ((err = gp_widget_get_type(widget,
2767                                   &type)) != GP_OK) {
2768         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2769                     _("Unable to fetch widget type"));
2770         goto cleanup;
2771     }
2772
2773     if (type != GP_WIDGET_TOGGLE) {
2774         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2775                     _("Viewfinder control was not a toggle widget"));
2776         goto cleanup;
2777     }
2778
2779     /*
2780      * Sigh. gphoto attempts to detect when the value has
2781      * changed and thus often doesn't apply the change in
2782      * value we request. We set the value twice, first to
2783      * the wrong value, then the right value. This ensures
2784      * gphoto always sees the value as changed.
2785      */
2786     value = enabled ? 0 : 1;
2787     if ((err = gp_widget_set_value(widget, &value)) != GP_OK) {
2788         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2789                     _("Failed to set viewfinder state: %s %d"),
2790                     gp_port_result_as_string(err), err);
2791         goto cleanup;
2792     }
2793     value = enabled ? 1 : 0;
2794     if ((err = gp_widget_set_value(widget, &value)) != GP_OK) {
2795         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2796                     _("Failed to set viewfinder state: %s %d"),
2797                     gp_port_result_as_string(err), err);
2798         goto cleanup;
2799     }
2800
2801     if ((err = gp_camera_set_config(priv->cam,
2802                                     priv->widgets,
2803                                     priv->ctx)) != GP_OK) {
2804         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2805                     _("Unable to save camera control configuration: %s %d"),
2806                     gp_port_result_as_string(err), err);
2807         goto cleanup;
2808     }
2809
2810     ret = TRUE;
2811
2812  cleanup:
2813     entangle_camera_end_job(cam);
2814     g_mutex_unlock(priv->lock);
2815     return ret;
2816 }
2817
2818
2819 static void entangle_camera_set_viewfinder_helper(GSimpleAsyncResult *result,
2820                                                   GObject *object,
2821                                                   GCancellable *cancellable G_GNUC_UNUSED)
2822 {
2823     GError *error = NULL;
2824     gpointer data;
2825     gboolean enabled;
2826
2827     data = g_object_get_data(G_OBJECT(result),
2828                              "enabled");
2829
2830     enabled = GPOINTER_TO_INT(data) == 1 ? TRUE : FALSE;
2831
2832     if (!entangle_camera_set_viewfinder(ENTANGLE_CAMERA(object),
2833                                         enabled,
2834                                         &error)) {
2835         g_simple_async_result_set_from_error(result, error);
2836         g_error_free(error);
2837     }
2838 }
2839
2840
2841 /**
2842  * entangle_camera_set_viewfinder_async:
2843  * @cam: (transfer none): the camera
2844  * @enabled: TRUE to turn on the view finder
2845  *
2846  * If @enabled is TRUE, the view finder will be activated
2847  * allowing preview images to be captured. If @enabled is
2848  * FALSE, the view finder will be deactivated.
2849  *
2850  * This can only be invoked when the camera is connected.
2851  *
2852  * This will execute in the background, and invoke @callback
2853  * when complete, whereupon entangle_camera_set_viewfinder_finish
2854  * can be used to check the status
2855  */
2856 void entangle_camera_set_viewfinder_async(EntangleCamera *cam,
2857                                           gboolean enabled,
2858                                           GCancellable *cancellable,
2859                                           GAsyncReadyCallback callback,
2860                                           gpointer user_data)
2861 {
2862     g_return_if_fail(ENTANGLE_IS_CAMERA(cam));
2863
2864     GSimpleAsyncResult *result = g_simple_async_result_new(G_OBJECT(cam),
2865                                                            callback,
2866                                                            user_data,
2867                                                            entangle_camera_set_viewfinder_async);
2868
2869     g_object_set_data(G_OBJECT(result),
2870                       "enabled",
2871                       GINT_TO_POINTER(enabled ? 1 : 0));
2872
2873     g_simple_async_result_run_in_thread(result,
2874                                         entangle_camera_set_viewfinder_helper,
2875                                         G_PRIORITY_DEFAULT,
2876                                         cancellable);
2877     g_object_unref(result);
2878 }
2879
2880
2881 /**
2882  * entangle_camera_set_viewfinder_finish:
2883  * @cam: (transfer none): the camera
2884  *
2885  * Check the completion status of a previous call to
2886  * entangle_camera_set_viewfinder_async.
2887  *
2888  * Returns: TRUE if the viewfinder state was changed, FALSE on error
2889  */
2890 gboolean entangle_camera_set_viewfinder_finish(EntangleCamera *cam,
2891                                                GAsyncResult *result,
2892                                                GError **error)
2893 {
2894     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
2895
2896     return !g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(result),
2897                                                   error);
2898 }
2899
2900
2901 /**
2902  * entangle_camera_autofocus:
2903  * @cam: (transfer none): the camera
2904  *
2905  * Trigger the autofocus mechanism on the camera, waiting
2906  * until focus is achieved or fails.
2907  *
2908  * This can only be invoked when the camera is connected.
2909  *
2910  * This block execution of the caller until completion.
2911  *
2912  * Returns: TRUE if autofocus was achieved, FALSE on error
2913  */
2914 gboolean entangle_camera_autofocus(EntangleCamera *cam,
2915                                    GError **error)
2916 {
2917     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
2918
2919     EntangleCameraPrivate *priv = cam->priv;
2920     gboolean ret = FALSE;
2921     CameraWidget *widget;
2922     CameraWidgetType type;
2923     int value;
2924     int err;
2925
2926     g_mutex_lock(priv->lock);
2927     entangle_camera_begin_job(cam);
2928
2929     ENTANGLE_DEBUG("Setting autofocus");
2930
2931     if (priv->cam == NULL) {
2932         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2933                     _("Controls not available when camera is disconnected"));
2934         goto cleanup;
2935     }
2936
2937     if (priv->widgets == NULL) {
2938         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2939                     _("Controls not available for this camera"));
2940         goto cleanup;
2941     }
2942
2943     widget = entangle_camera_find_widget(cam,
2944                                          "/main/actions/autofocusdrive");
2945     if (!widget) {
2946         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2947                     _("Autofocus control not available with this camera"));
2948         goto cleanup;
2949     }
2950
2951     if ((err = gp_widget_get_type(widget,
2952                                   &type)) != GP_OK) {
2953         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2954                     _("Unable to fetch widget type"));
2955         goto cleanup;
2956     }
2957
2958     if (type != GP_WIDGET_TOGGLE) {
2959         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2960                     _("Autofocus control was not a toggle widget"));
2961         goto cleanup;
2962     }
2963
2964     value = 0;
2965     if ((err = gp_widget_set_value(widget, &value)) != GP_OK) {
2966         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2967                     _("Failed to set autofocus state: %s %d"),
2968                     gp_port_result_as_string(err), err);
2969         goto cleanup;
2970     }
2971
2972     value = 1;
2973     if ((err = gp_widget_set_value(widget, &value)) != GP_OK) {
2974         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2975                     _("Failed to set autofocus state: %s %d"),
2976                     gp_port_result_as_string(err), err);
2977         goto cleanup;
2978     }
2979
2980     if ((err = gp_camera_set_config(priv->cam,
2981                                     priv->widgets,
2982                                     priv->ctx)) != GP_OK) {
2983         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2984                     _("Unable to save camera control configuration: %s %d"),
2985                     gp_port_result_as_string(err), err);
2986         goto cleanup;
2987     }
2988
2989     ret = TRUE;
2990
2991  cleanup:
2992     entangle_camera_end_job(cam);
2993     g_mutex_unlock(priv->lock);
2994     return ret;
2995 }
2996
2997
2998 static void entangle_camera_autofocus_helper(GSimpleAsyncResult *result,
2999                                              GObject *object,
3000                                              GCancellable *cancellable G_GNUC_UNUSED)
3001 {
3002     GError *error = NULL;
3003
3004     if (!entangle_camera_autofocus(ENTANGLE_CAMERA(object),
3005                                    &error)) {
3006         g_simple_async_result_set_from_error(result, error);
3007         g_error_free(error);
3008     }
3009 }
3010
3011
3012 /**
3013  * entangle_camera_autofocus_async:
3014  * @cam: (transfer none): the camera
3015  *
3016  * Trigger the autofocus mechanism on the camera, waiting
3017  * until focus is achieved or fails.
3018  *
3019  * This can only be invoked when the camera is connected.
3020  *
3021  * This will execute in the background, and invoke @callback
3022  * when complete, whereupon entangle_camera_autofocus_finish
3023  * can be used to check the status
3024  */
3025 void entangle_camera_autofocus_async(EntangleCamera *cam,
3026                                      GCancellable *cancellable,
3027                                      GAsyncReadyCallback callback,
3028                                      gpointer user_data)
3029 {
3030     g_return_if_fail(ENTANGLE_IS_CAMERA(cam));
3031
3032     GSimpleAsyncResult *result = g_simple_async_result_new(G_OBJECT(cam),
3033                                                            callback,
3034                                                            user_data,
3035                                                            entangle_camera_autofocus_async);
3036
3037     g_simple_async_result_run_in_thread(result,
3038                                         entangle_camera_autofocus_helper,
3039                                         G_PRIORITY_DEFAULT,
3040                                         cancellable);
3041     g_object_unref(result);
3042 }
3043
3044
3045 /**
3046  * entangle_camera_autofocus_finish:
3047  * @cam: (transfer none): the camera
3048  *
3049  * Check the completion status of a previous call to
3050  * entangle_camera_autofocus_async.
3051  *
3052  * Returns: TRUE if autofocus was performed, FALSE on error
3053  */
3054 gboolean entangle_camera_autofocus_finish(EntangleCamera *cam,
3055                                           GAsyncResult *result,
3056                                           GError **error)
3057 {
3058     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
3059
3060     return !g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(result),
3061                                                   error);
3062 }
3063
3064
3065 /**
3066  * entangle_camera_manualfocus:
3067  * @cam: (transfer none): the camera
3068  * @step: how much to change focus by
3069  *
3070  * Trigger the focus mechanism on the camera, to move
3071  * by @step.
3072  *
3073  * This can only be invoked when the camera is connected.
3074  *
3075  * This block execution of the caller until completion.
3076  *
3077  * Returns: TRUE if autofocus was achieved, FALSE on error
3078  */
3079 gboolean entangle_camera_manualfocus(EntangleCamera *cam,
3080                                      EntangleCameraManualFocusStep step,
3081                                      GError **error)
3082 {
3083     g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
3084
3085     EntangleCameraPrivate *priv = cam->priv;
3086     gboolean ret = FALSE;
3087     CameraWidget *widget;
3088     CameraWidgetType type;
3089     int err;
3090
3091     g_mutex_lock(priv->lock);
3092     entangle_camera_begin_job(cam);
3093
3094     ENTANGLE_DEBUG("Setting manualfocus %d", (int)step);
3095
3096     if (priv->cam == NULL) {
3097         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
3098                     _("Controls not available when camera is disconnected"));
3099         goto cleanup;
3100     }
3101
3102     if (priv->widgets == NULL) {
3103         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
3104                     _("Controls not available for this camera"));
3105         goto cleanup;
3106     }
3107
3108     widget = entangle_camera_find_widget(cam,
3109                                          "/main/actions/manualfocusdrive");
3110     if (!widget) {
3111         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
3112                     _("Manual focus control not available with this camera"));
3113         goto cleanup;
3114     }
3115
3116     if ((err = gp_widget_get_type(widget,
3117                                   &type)) != GP_OK) {
3118         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
3119                     _("Unable to fetch widget type"));
3120         goto cleanup;
3121     }
3122
3123     if (type != GP_WIDGET_RANGE &&
3124         type != GP_WIDGET_RADIO) {
3125         g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
3126                     _("Manual focus control was not a range or radio widget"));
3127         goto cleanup;
3128     }
3129
3130     if (type == GP_WIDGET_RANGE) {
3131         /* Nikon */
3132         float values[] = {
3133             [ENTANGLE_CAMERA_MANUAL_FOCUS_STEP_IN_COARSE] = 512,
3134             [ENTANGLE_CAMERA_MANUAL_FOCUS_STEP_IN_MEDIUM] = 64,
3135             [ENTANGLE_CAMERA_MANUAL_FOCUS_STEP_IN_FINE] = 16,
3136             [ENTANGLE_CAMERA_MANUAL_FOCUS_STEP_OUT_COARSE] = -512,
3137             [ENTANGLE_CAMERA_MANUAL_FOCUS_STEP_OUT_MEDIUM] = -64,
3138             [ENTANGLE_CAMERA_MANUAL_FOCUS_STEP_OUT_FINE] = -16,
3139         };
3140
3141         float value = values[step];
3142         ENTANGLE_DEBUG("Setting manualfocus range %d", (int)value);
3143         if ((err = gp_widget_set_value(widget, &value)) != GP_OK) {
3144             g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
3145                         _("Failed to set manual focus state: %s %d"),
3146                         gp_port_result_as_string(err), err);
3147             goto cleanup;
3148         }
3149     } else {
3150         /* Canon */
3151         int idx[] = {
3152             [ENTANGLE_CAMERA_MANUAL_FOCUS_STEP_IN_COARSE] = 2,
3153             [ENTANGLE_CAMERA_MANUAL_FOCUS_STEP_IN_MEDIUM] = 1,
3154             [ENTANGLE_CAMERA_MANUAL_FOCUS_STEP_IN_FINE] = 0,
3155             [ENTANGLE_CAMERA_MANUAL_FOCUS_STEP_OUT_COARSE] = 6,
3156             [ENTANGLE_CAMERA_MANUAL_FOCUS_STEP_OUT_MEDIUM] = 5,
3157             [ENTANGLE_CAMERA_MANUAL_FOCUS_STEP_OUT_FINE] = 4,
3158         };
3159         const gchar *value;
3160         if ((err = gp_widget_get_choice(widget, idx[step], &value)) != GP_OK) {
3161             g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
3162                         _("Failed to read manual focus choice %d: %s %d"),
3163                         step, gp_port_result_as_string(err), err);
3164             goto cleanup;
3165         }