2 * Entangle: Tethered Camera Control & Capture
4 * Copyright (C) 2009-2015 Daniel P. Berrange
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.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include <glib/gi18n.h>
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"
43 #define ENTANGLE_CAMERA_GET_PRIVATE(obj) \
44 (G_TYPE_INSTANCE_GET_PRIVATE((obj), ENTANGLE_TYPE_CAMERA, EntangleCameraPrivate))
46 #define ENTANGLE_ERROR(err, msg...) \
48 g_quark_from_string("entangle-camera"), \
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)
59 struct _EntangleCameraPrivate {
65 CameraAbilitiesList *caps;
66 GPPortInfoList *ports;
70 CameraWidget *widgets;
71 EntangleControlGroup *controls;
72 GHashTable *controlPaths;
74 EntangleProgress *progress;
78 char *model; /* R/O */
88 gboolean hasViewfinder;
91 G_DEFINE_TYPE(EntangleCamera, entangle_camera, G_TYPE_OBJECT);
107 #define ENTANGLE_CAMERA_ERROR entangle_camera_error_quark ()
109 static GQuark entangle_camera_error_quark(void)
111 return g_quark_from_static_string("entangle-camera-error-quark");
115 static EntangleControl *do_build_controls(EntangleCamera *cam,
117 CameraWidget *widget,
119 static gboolean do_load_controls(EntangleCamera *cam,
121 CameraWidget *widget,
124 struct EntangleCameraEventData {
131 static gboolean entangle_camera_emit_idle(gpointer opaque)
133 struct EntangleCameraEventData *data = opaque;
135 g_signal_emit_by_name(data->cam, data->signame, data->arg);
137 g_free(data->signame);
138 g_object_unref(data->cam);
140 g_object_unref(data->arg);
146 static void entangle_camera_emit_deferred(EntangleCamera *cam,
150 struct EntangleCameraEventData *data = g_new0(struct EntangleCameraEventData, 1);
153 data->signame = g_strdup(signame);
158 g_idle_add(entangle_camera_emit_idle, data);
162 static void entangle_camera_begin_job(EntangleCamera *cam)
164 EntangleCameraPrivate *priv = cam->priv;
168 while (priv->jobActive) {
169 g_cond_wait(priv->jobCond, priv->lock);
172 priv->jobActive = TRUE;
173 g_mutex_unlock(priv->lock);
177 static void entangle_camera_end_job(EntangleCamera *cam)
179 EntangleCameraPrivate *priv = cam->priv;
181 priv->jobActive = FALSE;
182 g_cond_broadcast(priv->jobCond);
183 g_mutex_lock(priv->lock);
188 static void entangle_camera_get_property(GObject *object,
193 EntangleCamera *cam = ENTANGLE_CAMERA(object);
194 EntangleCameraPrivate *priv = cam->priv;
199 g_value_set_string(value, priv->model);
203 g_value_set_string(value, priv->port);
207 g_value_set_string(value, priv->manual);
211 g_value_set_string(value, priv->summary);
215 g_value_set_string(value, priv->driver);
219 g_value_set_object(value, priv->progress);
222 case PROP_HAS_CAPTURE:
223 g_value_set_boolean(value, priv->hasCapture);
226 case PROP_HAS_PREVIEW:
227 g_value_set_boolean(value, priv->hasPreview);
230 case PROP_HAS_SETTINGS:
231 g_value_set_boolean(value, priv->hasSettings);
234 case PROP_HAS_VIEWFINDER:
235 g_value_set_boolean(value, priv->hasViewfinder);
239 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
243 static void entangle_camera_set_property(GObject *object,
248 EntangleCamera *cam = ENTANGLE_CAMERA(object);
249 EntangleCameraPrivate *priv = cam->priv;
255 priv->model = g_value_dup_string(value);
260 priv->port = g_value_dup_string(value);
264 entangle_camera_set_progress(cam, g_value_get_object(value));
267 case PROP_HAS_CAPTURE:
268 priv->hasCapture = g_value_get_boolean(value);
269 ENTANGLE_DEBUG("Set has capture %d", priv->hasCapture);
272 case PROP_HAS_PREVIEW:
273 priv->hasPreview = g_value_get_boolean(value);
274 ENTANGLE_DEBUG("Set has preview %d", priv->hasPreview);
277 case PROP_HAS_SETTINGS:
278 priv->hasSettings = g_value_get_boolean(value);
279 ENTANGLE_DEBUG("Set has settings %d", priv->hasSettings);
282 case PROP_HAS_VIEWFINDER:
283 priv->hasViewfinder = g_value_get_boolean(value);
284 ENTANGLE_DEBUG("Set has viewfinder %d", priv->hasViewfinder);
288 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
293 static void entangle_camera_finalize(GObject *object)
295 EntangleCamera *cam = ENTANGLE_CAMERA(object);
296 EntangleCameraPrivate *priv = cam->priv;
298 ENTANGLE_DEBUG("Finalize camera %p", object);
301 g_object_unref(priv->progress);
303 gp_camera_exit(priv->cam, priv->ctx);
304 gp_camera_free(priv->cam);
307 gp_widget_unref(priv->widgets);
309 g_object_unref(priv->controls);
310 if (priv->controlPaths)
311 g_hash_table_unref(priv->controlPaths);
313 gp_port_info_list_free(priv->ports);
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);
322 g_free(priv->lastError);
323 g_mutex_free(priv->lock);
324 g_cond_free(priv->jobCond);
326 G_OBJECT_CLASS(entangle_camera_parent_class)->finalize(object);
330 static void entangle_camera_class_init(EntangleCameraClass *klass)
332 GObjectClass *object_class = G_OBJECT_CLASS(klass);
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;
339 g_signal_new("camera-file-added",
340 G_TYPE_FROM_CLASS(klass),
342 G_STRUCT_OFFSET(EntangleCameraClass, camera_file_added),
344 g_cclosure_marshal_VOID__OBJECT,
347 ENTANGLE_TYPE_CAMERA_FILE);
349 g_signal_new("camera-file-captured",
350 G_TYPE_FROM_CLASS(klass),
352 G_STRUCT_OFFSET(EntangleCameraClass, camera_file_captured),
354 g_cclosure_marshal_VOID__OBJECT,
357 ENTANGLE_TYPE_CAMERA_FILE);
359 g_signal_new("camera-file-previewed",
360 G_TYPE_FROM_CLASS(klass),
362 G_STRUCT_OFFSET(EntangleCameraClass, camera_file_previewed),
364 g_cclosure_marshal_VOID__OBJECT,
367 ENTANGLE_TYPE_CAMERA_FILE);
369 g_signal_new("camera-file-downloaded",
370 G_TYPE_FROM_CLASS(klass),
372 G_STRUCT_OFFSET(EntangleCameraClass, camera_file_downloaded),
374 g_cclosure_marshal_VOID__OBJECT,
377 ENTANGLE_TYPE_CAMERA_FILE);
379 g_signal_new("camera-file-deleted",
380 G_TYPE_FROM_CLASS(klass),
382 G_STRUCT_OFFSET(EntangleCameraClass, camera_file_deleted),
384 g_cclosure_marshal_VOID__OBJECT,
387 ENTANGLE_TYPE_CAMERA_FILE);
389 g_signal_new("camera-connected",
390 G_TYPE_FROM_CLASS(klass),
392 G_STRUCT_OFFSET(EntangleCameraClass, camera_connected),
394 g_cclosure_marshal_VOID__VOID,
398 g_signal_new("camera-disconnected",
399 G_TYPE_FROM_CLASS(klass),
401 G_STRUCT_OFFSET(EntangleCameraClass, camera_disconnected),
403 g_cclosure_marshal_VOID__VOID,
407 g_signal_new("camera-controls-changed",
408 G_TYPE_FROM_CLASS(klass),
410 G_STRUCT_OFFSET(EntangleCameraClass, camera_controls_changed),
412 g_cclosure_marshal_VOID__VOID,
417 g_object_class_install_property(object_class,
419 g_param_spec_string("model",
421 "Model name of the camera",
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,
430 g_param_spec_string("port",
432 "Device port of the camera",
435 G_PARAM_CONSTRUCT_ONLY |
436 G_PARAM_STATIC_NAME |
437 G_PARAM_STATIC_NICK |
438 G_PARAM_STATIC_BLURB));
440 g_object_class_install_property(object_class,
442 g_param_spec_string("summary",
447 G_PARAM_STATIC_NAME |
448 G_PARAM_STATIC_NICK |
449 G_PARAM_STATIC_BLURB));
451 g_object_class_install_property(object_class,
453 g_param_spec_string("manual",
458 G_PARAM_STATIC_NAME |
459 G_PARAM_STATIC_NICK |
460 G_PARAM_STATIC_BLURB));
462 g_object_class_install_property(object_class,
464 g_param_spec_string("driver",
465 "Camera driver info",
466 "Camera driver information",
469 G_PARAM_STATIC_NAME |
470 G_PARAM_STATIC_NICK |
471 G_PARAM_STATIC_BLURB));
473 g_object_class_install_property(object_class,
475 g_param_spec_object("progress",
477 "Operation progress updater",
478 ENTANGLE_TYPE_PROGRESS,
480 G_PARAM_STATIC_NAME |
481 G_PARAM_STATIC_NICK |
482 G_PARAM_STATIC_BLURB));
484 g_object_class_install_property(object_class,
486 g_param_spec_boolean("has-capture",
488 "Whether image capture is supported",
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,
497 g_param_spec_boolean("has-preview",
499 "Whether image preview is supported",
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,
508 g_param_spec_boolean("has-settings",
509 "Settings supported",
510 "Whether camera settings configuration is supported",
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,
519 g_param_spec_boolean("has-viewfinder",
520 "Viewfinder supported",
521 "Whether camera viewfinder configuration is supported",
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");
530 g_type_class_add_private(klass, sizeof(EntangleCameraPrivate));
534 EntangleCamera *entangle_camera_new(const char *model,
538 gboolean hasSettings)
540 return ENTANGLE_CAMERA(g_object_new(ENTANGLE_TYPE_CAMERA,
543 "has-capture", hasCapture,
544 "has-preview", hasPreview,
545 "has-settings", hasSettings,
550 static void entangle_camera_init(EntangleCamera *cam)
552 cam->priv = ENTANGLE_CAMERA_GET_PRIVATE(cam);
553 cam->priv->lock = g_mutex_new();
554 cam->priv->jobCond = g_cond_new();
559 * entangle_camera_get_model:
560 * @cam: (transfer none): the camera
562 * Get the camera model name
564 * Returns: (transfer none): the model name
566 const char *entangle_camera_get_model(EntangleCamera *cam)
568 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), NULL);
570 EntangleCameraPrivate *priv = cam->priv;
576 * entangle_camera_get_port:
577 * @cam: (transfer none): the camera
579 * Get the camera port name
581 * Returns: (transfer none): the port name
583 const char *entangle_camera_get_port(EntangleCamera *cam)
585 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), NULL);
587 EntangleCameraPrivate *priv = cam->priv;
591 struct EntangleCameraProgressData {
594 ENTANGLE_CAMERA_PROGRESS_START,
595 ENTANGLE_CAMERA_PROGRESS_UPDATE,
596 ENTANGLE_CAMERA_PROGRESS_STOP
602 static gboolean entangle_camera_progress_idle(gpointer opaque)
604 struct EntangleCameraProgressData *data = opaque;
606 if (data->cam->priv->progress) {
608 case ENTANGLE_CAMERA_PROGRESS_START:
609 entangle_progress_start(data->cam->priv->progress,
614 case ENTANGLE_CAMERA_PROGRESS_UPDATE:
615 entangle_progress_update(data->cam->priv->progress,
619 case ENTANGLE_CAMERA_PROGRESS_STOP:
620 entangle_progress_stop(data->cam->priv->progress);
628 if (data->op == ENTANGLE_CAMERA_PROGRESS_START)
630 g_object_unref(data->cam);
637 static unsigned int do_entangle_camera_progress_start(GPContext *ctx G_GNUC_UNUSED,
642 EntangleCamera *cam = opaque;
643 struct EntangleCameraProgressData *data = g_new0(struct EntangleCameraProgressData, 1);
645 data->cam = g_object_ref(cam);
646 data->op = ENTANGLE_CAMERA_PROGRESS_START;
647 data->value = target;
648 data->msg = g_strdup(msg);
650 g_idle_add(entangle_camera_progress_idle, data);
652 return 0; /* XXX what is this actually useful for ? */
655 static unsigned int do_entangle_camera_progress_start(GPContext *ctx G_GNUC_UNUSED,
661 EntangleCamera *cam = opaque;
662 struct EntangleCameraProgressData *data = g_new0(struct EntangleCameraProgressData, 1);
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);
669 g_idle_add(entangle_camera_progress_idle, data);
671 return 0; /* XXX what is this actually useful for ? */
675 static void do_entangle_camera_progress_update(GPContext *ctx G_GNUC_UNUSED,
676 unsigned int id G_GNUC_UNUSED,
680 EntangleCamera *cam = opaque;
681 struct EntangleCameraProgressData *data = g_new0(struct EntangleCameraProgressData, 1);
683 data->cam = g_object_ref(cam);
684 data->op = ENTANGLE_CAMERA_PROGRESS_UPDATE;
685 data->value = current;
687 g_idle_add(entangle_camera_progress_idle, data);
690 static void do_entangle_camera_progress_stop(GPContext *ctx G_GNUC_UNUSED,
691 unsigned int id G_GNUC_UNUSED,
694 EntangleCamera *cam = opaque;
695 struct EntangleCameraProgressData *data = g_new0(struct EntangleCameraProgressData, 1);
697 data->cam = g_object_ref(cam);
698 data->op = ENTANGLE_CAMERA_PROGRESS_STOP;
700 g_idle_add(entangle_camera_progress_idle, data);
703 static void entangle_camera_reset_last_error(EntangleCamera *cam)
705 EntangleCameraPrivate *priv = cam->priv;
707 g_free(priv->lastError);
708 priv->lastError = NULL;
713 static void do_entangle_camera_error(GPContext *ctx G_GNUC_UNUSED,
717 EntangleCamera *cam = data;
718 EntangleCameraPrivate *priv = cam->priv;
720 entangle_camera_reset_last_error(cam);
721 priv->lastError = g_strdup(msg);
722 ENTANGLE_DEBUG("Got error %s", priv->lastError);
725 static void do_entangle_camera_error(GPContext *ctx G_GNUC_UNUSED,
730 EntangleCamera *cam = data;
731 EntangleCameraPrivate *priv = cam->priv;
733 entangle_camera_reset_last_error(cam);
734 priv->lastError = g_strdup_vprintf(fmt, args);
735 ENTANGLE_DEBUG("Got error %s", priv->lastError);
741 * entangle_camera_connect:
742 * @cam: (transfer none): the camera
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.
748 * This block execution of the caller until completion.
750 * Returns: TRUE if the camera is connected, FALSE on error
752 gboolean entangle_camera_connect(EntangleCamera *cam,
755 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
757 EntangleCameraPrivate *priv = cam->priv;
763 gboolean ret = FALSE;
765 ENTANGLE_DEBUG("Conencting to cam");
767 g_mutex_lock(priv->lock);
769 if (priv->cam != NULL) {
774 priv->ctx = gp_context_new();
776 if (gp_abilities_list_new(&priv->caps) != GP_OK) {
777 g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
778 _("Cannot initialize gphoto2 abilities"));
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"));
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"));
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"));
800 gp_context_set_error_func(priv->ctx,
801 do_entangle_camera_error,
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,
809 i = gp_port_info_list_lookup_path(priv->ports, priv->port);
810 gp_port_info_list_get_info(priv->ports, i, &port);
812 i = gp_abilities_list_lookup_model(priv->caps, priv->model);
813 gp_abilities_list_get_abilities(priv->caps, i, &cap);
815 gp_camera_new(&priv->cam);
816 gp_camera_set_abilities(priv->cam, cap);
817 gp_camera_set_port_info(priv->cam, port);
819 entangle_camera_begin_job(cam);
820 err = gp_camera_init(priv->cam, priv->ctx);
821 entangle_camera_end_job(cam);
824 gp_camera_unref(priv->cam);
826 g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
827 _("Unable to initialize camera"));
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;
841 gp_camera_get_summary(priv->cam, &txt, priv->ctx);
842 priv->summary = g_strdup(txt.text);
844 gp_camera_get_manual(priv->cam, &txt, priv->ctx);
845 priv->manual = g_strdup(txt.text);
847 gp_camera_get_about(priv->cam, &txt, priv->ctx);
848 priv->driver = g_strdup(txt.text);
850 ENTANGLE_DEBUG("ok");
854 g_mutex_unlock(priv->lock);
856 entangle_camera_emit_deferred(cam, "camera-connected", NULL);
861 static void entangle_camera_connect_helper(GSimpleAsyncResult *result,
863 GCancellable *cancellable G_GNUC_UNUSED)
865 GError *error = NULL;
867 if (!entangle_camera_connect(ENTANGLE_CAMERA(object), &error)) {
868 g_simple_async_result_set_from_error(result, error);
875 * entangle_camera_connect_async:
876 * @cam: (transfer none): the camera
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.
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
886 void entangle_camera_connect_async(EntangleCamera *cam,
887 GCancellable *cancellable,
888 GAsyncReadyCallback callback,
891 g_return_if_fail(ENTANGLE_IS_CAMERA(cam));
893 GSimpleAsyncResult *result = g_simple_async_result_new(G_OBJECT(cam),
896 entangle_camera_connect_async);
898 g_simple_async_result_run_in_thread(result,
899 entangle_camera_connect_helper,
902 g_object_unref(result);
907 * entangle_camera_connect_finish:
908 * @cam: (transfer none): the camera
910 * Check the completion status of a previous call to
911 * entangle_camera_connect_async
913 * Returns: TRUE if the camera is connected, FALSE on error
915 gboolean entangle_camera_connect_finish(EntangleCamera *cam,
916 GAsyncResult *result,
919 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
921 return !g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(result),
927 * entangle_camera_disconnect:
928 * @cam: (transfer none): the camera
930 * Disconnect from the camera, enabling it to be used by
931 * other applications.
933 * This block execution of the caller until completion.
935 * Returns: TRUE if the camera is disconnected, FALSE on error
937 gboolean entangle_camera_disconnect(EntangleCamera *cam,
938 GError **error G_GNUC_UNUSED)
940 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
942 EntangleCameraPrivate *priv = cam->priv;
943 gboolean ret = FALSE;
945 ENTANGLE_DEBUG("Disconnecting from cam");
947 g_mutex_lock(priv->lock);
949 if (priv->cam == NULL) {
954 entangle_camera_begin_job(cam);
955 gp_camera_exit(priv->cam, priv->ctx);
956 entangle_camera_end_job(cam);
959 gp_widget_unref(priv->widgets);
960 priv->widgets = NULL;
962 if (priv->controls) {
963 g_object_unref(priv->controls);
964 priv->controls = NULL;
966 if (priv->controlPaths) {
967 g_hash_table_unref(priv->controlPaths);
968 priv->controlPaths = NULL;
971 g_free(priv->driver);
972 g_free(priv->manual);
973 g_free(priv->summary);
974 priv->driver = priv->manual = priv->summary = NULL;
977 gp_port_info_list_free(priv->ports);
981 gp_abilities_list_free(priv->caps);
984 gp_context_unref(priv->ctx);
987 gp_camera_unref(priv->cam);
989 priv->hasViewfinder = FALSE;
993 g_mutex_unlock(priv->lock);
995 entangle_camera_emit_deferred(cam, "camera-disconnected", NULL);
1000 static void entangle_camera_disconnect_helper(GSimpleAsyncResult *result,
1002 GCancellable *cancellable G_GNUC_UNUSED)
1004 GError *error = NULL;
1006 if (!entangle_camera_disconnect(ENTANGLE_CAMERA(object), &error)) {
1007 g_simple_async_result_set_from_error(result, error);
1008 g_error_free(error);
1014 * entangle_camera_disconnect_async:
1015 * @cam: (transfer none): the camera
1017 * Disconnect from the camera, enabling it to be used by
1018 * other applications.
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
1024 * Returns: TRUE if the camera is disconnected, FALSE on error
1026 void entangle_camera_disconnect_async(EntangleCamera *cam,
1027 GCancellable *cancellable,
1028 GAsyncReadyCallback callback,
1031 g_return_if_fail(ENTANGLE_IS_CAMERA(cam));
1033 GSimpleAsyncResult *result = g_simple_async_result_new(G_OBJECT(cam),
1036 entangle_camera_disconnect_async);
1038 g_simple_async_result_run_in_thread(result,
1039 entangle_camera_disconnect_helper,
1042 g_object_unref(result);
1047 * entangle_camera_disconnect_finish:
1048 * @cam: (transfer none): the camera
1050 * Check the completion status of a previous call to
1051 * entangle_camera_disconnect_async
1053 * Returns: TRUE if the camera is disconnected, FALSE on error
1055 gboolean entangle_camera_disconnect_finish(EntangleCamera *cam,
1056 GAsyncResult *result,
1059 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
1061 return !g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(result),
1067 * entangle_camera_get_connected:
1068 * @cam: (transfer none): the camera
1070 * Determine if the camera is currently connected
1072 * Returns: TRUE if the camera is connected, FALSE otherwise
1074 gboolean entangle_camera_get_connected(EntangleCamera *cam)
1076 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
1078 EntangleCameraPrivate *priv = cam->priv;
1081 g_mutex_lock(priv->lock);
1082 ret = priv->cam != NULL ? TRUE : FALSE;
1083 g_mutex_unlock(priv->lock);
1089 * entangle_camera_get_summary:
1090 * @cam: (transfer none): the camera
1092 * Get the camera summary text. This is only available
1093 * while the camera is connected
1095 * Returns: (transfer full): the camera summary
1097 char *entangle_camera_get_summary(EntangleCamera *cam)
1099 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), NULL);
1101 EntangleCameraPrivate *priv = cam->priv;
1104 g_mutex_lock(priv->lock);
1105 ret = g_strdup(priv->summary);
1106 g_mutex_unlock(priv->lock);
1114 * entangle_camera_get_manual:
1115 * @cam: (transfer none): the camera
1117 * Get the camera manual text. This is only available
1118 * while the camera is connected
1120 * Returns: (transfer full): the camera manual
1122 char *entangle_camera_get_manual(EntangleCamera *cam)
1124 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), NULL);
1126 EntangleCameraPrivate *priv = cam->priv;
1129 g_mutex_lock(priv->lock);
1130 ret = g_strdup(priv->manual);
1131 g_mutex_unlock(priv->lock);
1138 * entangle_camera_get_driver:
1139 * @cam: (transfer none): the camera
1141 * Get the camera driver information text. This is only available
1142 * while the camera is connected
1144 * Returns: (transfer full): the camera driver information
1146 char *entangle_camera_get_driver(EntangleCamera *cam)
1148 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), NULL);
1150 EntangleCameraPrivate *priv = cam->priv;
1153 g_mutex_lock(priv->lock);
1154 ret = g_strdup(priv->driver);
1155 g_mutex_unlock(priv->lock);
1162 * entangle_camera_capture_image:
1163 * @cam: (transfer none): the camera
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
1171 * This can only be invoked when the camera is connected.
1173 * This block execution of the caller until completion.
1175 * Returns: (transfer full): the captured image or NULL
1177 EntangleCameraFile *entangle_camera_capture_image(EntangleCamera *cam,
1180 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), NULL);
1182 EntangleCameraPrivate *priv = cam->priv;
1183 CameraFilePath camerapath;
1184 EntangleCameraFile *file = NULL;
1187 g_mutex_lock(priv->lock);
1190 ENTANGLE_ERROR(error, _("Cannot capture image while not connected"));
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,
1201 entangle_camera_end_job(cam);
1203 ENTANGLE_ERROR(error, _("Unable to capture image: %s"), priv->lastError);
1207 file = entangle_camera_file_new(camerapath.folder,
1210 entangle_camera_emit_deferred(cam, "camera-file-captured", G_OBJECT(file));
1213 g_mutex_unlock(priv->lock);
1218 static void entangle_camera_capture_image_helper(GSimpleAsyncResult *result,
1220 GCancellable *cancellable G_GNUC_UNUSED)
1222 EntangleCameraFile *file;
1223 GError *error = NULL;
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);
1230 g_simple_async_result_set_op_res_gpointer(result, file, NULL);
1235 * entangle_camera_capture_image_async:
1236 * @cam: (transfer none): the camera
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
1244 * This can only be invoked when the camera is connected.
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
1250 void entangle_camera_capture_image_async(EntangleCamera *cam,
1251 GCancellable *cancellable,
1252 GAsyncReadyCallback callback,
1255 g_return_if_fail(ENTANGLE_IS_CAMERA(cam));
1257 GSimpleAsyncResult *result = g_simple_async_result_new(G_OBJECT(cam),
1260 entangle_camera_capture_image_async);
1262 g_simple_async_result_run_in_thread(result,
1263 entangle_camera_capture_image_helper,
1266 g_object_unref(result);
1271 * entangle_camera_capture_image_finish:
1272 * @cam: (transfer none): the camera
1274 * Check the completion status of a previous call to
1275 * entangle_camera_capture_image_async.
1277 * Returns: (transfer full): the captured image or NULL
1279 EntangleCameraFile *entangle_camera_capture_image_finish(EntangleCamera *cam,
1280 GAsyncResult *result,
1283 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), NULL);
1285 EntangleCameraFile *file;
1286 if (g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(result),
1290 file = g_simple_async_result_get_op_res_gpointer(G_SIMPLE_ASYNC_RESULT(result));
1297 * entangle_camera_preview_image:
1298 * @cam: (transfer none): the camera
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.
1304 * This can only be invoked when the camera is connected.
1306 * This block execution of the caller until completion.
1308 * Returns: (transfer full): the captured image or NULL
1310 EntangleCameraFile *entangle_camera_preview_image(EntangleCamera *cam,
1313 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), NULL);
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;
1325 g_mutex_lock(priv->lock);
1328 ENTANGLE_ERROR(error, _("Cannot preview image while not connected"));
1332 gp_file_new(&datafile);
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,
1340 entangle_camera_end_job(cam);
1343 ENTANGLE_ERROR(error, _("Unable to capture preview: %s"), priv->lastError);
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);
1353 if (gp_file_get_name(datafile, &name) != GP_OK) {
1354 ENTANGLE_ERROR(error, _("Unable to get filename: %s"), priv->lastError);
1358 file = entangle_camera_file_new(NULL, NULL);
1360 if (gp_file_get_mime_type(datafile, &mimetype) == GP_OK)
1361 entangle_camera_file_set_mimetype(file, mimetype);
1363 data = g_byte_array_new();
1364 g_byte_array_append(data, (const guint8 *)rawdata, rawdatalen);
1366 entangle_camera_file_set_data(file, data);
1367 g_byte_array_unref(data);
1369 entangle_camera_emit_deferred(cam, "camera-file-previewed", G_OBJECT(file));
1373 gp_file_unref(datafile);
1374 g_mutex_unlock(priv->lock);
1379 static void entangle_camera_preview_image_helper(GSimpleAsyncResult *result,
1381 GCancellable *cancellable G_GNUC_UNUSED)
1383 EntangleCameraFile *file;
1384 GError *error = NULL;
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);
1391 g_simple_async_result_set_op_res_gpointer(result, file, NULL);
1396 * entangle_camera_preview_image_async:
1397 * @cam: (transfer none): the camera
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.
1403 * This can only be invoked when the camera is connected.
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
1409 void entangle_camera_preview_image_async(EntangleCamera *cam,
1410 GCancellable *cancellable,
1411 GAsyncReadyCallback callback,
1414 g_return_if_fail(ENTANGLE_IS_CAMERA(cam));
1416 GSimpleAsyncResult *result = g_simple_async_result_new(G_OBJECT(cam),
1419 entangle_camera_preview_image_async);
1421 g_simple_async_result_run_in_thread(result,
1422 entangle_camera_preview_image_helper,
1425 g_object_unref(result);
1430 * entangle_camera_preview_image_finish:
1431 * @cam: (transfer none): the camera
1433 * Check the completion status of a previous call to
1434 * entangle_camera_preview_image_async.
1436 * Returns: (transfer full): the captured image or NULL
1438 EntangleCameraFile *entangle_camera_preview_image_finish(EntangleCamera *cam,
1439 GAsyncResult *result,
1442 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), NULL);
1444 EntangleCameraFile *file;
1445 if (g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(result),
1449 file = g_simple_async_result_get_op_res_gpointer(G_SIMPLE_ASYNC_RESULT(result));
1456 * entangle_camera_download_file:
1457 * @cam: (transfer none): the camera
1458 * @file: (transfer none): the file whose contents to download
1460 * Download the data associated with @file and set the data
1463 * This can only be invoked when the camera is connected.
1465 * This block execution of the caller until completion.
1467 * Returns: TRUE if the file was downloaded, FALSE on error
1469 gboolean entangle_camera_download_file(EntangleCamera *cam,
1470 EntangleCameraFile *file,
1473 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
1474 g_return_val_if_fail(ENTANGLE_IS_CAMERA_FILE(file), FALSE);
1476 EntangleCameraPrivate *priv = cam->priv;
1477 CameraFile *datafile = NULL;
1479 unsigned long int datalen;
1480 GByteArray *filedata;
1481 gboolean ret = FALSE;
1484 g_mutex_lock(priv->lock);
1487 ENTANGLE_ERROR(error, _("Cannot download file while not connected"));
1491 ENTANGLE_DEBUG("Downloading '%s' from '%s'",
1492 entangle_camera_file_get_name(file),
1493 entangle_camera_file_get_folder(file));
1495 gp_file_new(&datafile);
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,
1507 entangle_camera_end_job(cam);
1510 ENTANGLE_ERROR(error, _("Unable to get camera file: %s"), priv->lastError);
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);
1520 filedata = g_byte_array_new();
1521 g_byte_array_append(filedata, (const guint8*)data, datalen);
1523 entangle_camera_file_set_data(file, filedata);
1524 g_byte_array_unref(filedata);
1526 entangle_camera_emit_deferred(cam, "camera-file-downloaded", G_OBJECT(file));
1531 ENTANGLE_DEBUG("Error");
1533 gp_file_unref(datafile);
1534 g_mutex_unlock(priv->lock);
1539 static void entangle_camera_download_file_helper(GSimpleAsyncResult *result,
1541 GCancellable *cancellable G_GNUC_UNUSED)
1543 EntangleCameraFile *file;
1544 GError *error = NULL;
1546 file = g_simple_async_result_get_op_res_gpointer(result);
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);
1556 * entangle_camera_download_file_async:
1557 * @cam: (transfer none): the camera
1558 * @file: (transfer none): the file whose contents to download
1560 * Download the data associated with @file and set the data
1563 * This can only be invoked when the camera is connected.
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
1569 void entangle_camera_download_file_async(EntangleCamera *cam,
1570 EntangleCameraFile *file,
1571 GCancellable *cancellable,
1572 GAsyncReadyCallback callback,
1575 g_return_if_fail(ENTANGLE_IS_CAMERA(cam));
1576 g_return_if_fail(ENTANGLE_IS_CAMERA_FILE(file));
1578 GSimpleAsyncResult *result = g_simple_async_result_new(G_OBJECT(cam),
1581 entangle_camera_download_file_async);
1584 g_simple_async_result_set_op_res_gpointer(result, file, g_object_unref);
1586 g_simple_async_result_run_in_thread(result,
1587 entangle_camera_download_file_helper,
1590 g_object_unref(result);
1595 * entangle_camera_download_file_finish:
1596 * @cam: (transfer none): the camera
1598 * Check the completion status of a previous call to
1599 * entangle_camera_download_file_async.
1601 * Returns: TRUE if the file was downloaded, FALSE on error
1603 gboolean entangle_camera_download_file_finish(EntangleCamera *cam,
1604 GAsyncResult *result,
1607 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
1609 return !g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(result),
1615 * entangle_camera_delete_file:
1616 * @cam: (transfer none): the camera
1617 * @file: (transfer none): the file to delete
1619 * Delete @file from the camera capture target.
1621 * This can only be invoked when the camera is connected.
1623 * This block execution of the caller until completion.
1625 * Returns: TRUE if the file was deleted, FALSE on error
1627 gboolean entangle_camera_delete_file(EntangleCamera *cam,
1628 EntangleCameraFile *file,
1631 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
1632 g_return_val_if_fail(ENTANGLE_IS_CAMERA_FILE(file), FALSE);
1634 EntangleCameraPrivate *priv = cam->priv;
1635 gboolean ret = FALSE;
1638 g_mutex_lock(priv->lock);
1641 ENTANGLE_ERROR(error, _("Cannot delete file while not connected"));
1645 ENTANGLE_DEBUG("Deleting '%s' from '%s'",
1646 entangle_camera_file_get_name(file),
1647 entangle_camera_file_get_folder(file));
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),
1656 entangle_camera_end_job(cam);
1659 ENTANGLE_ERROR(error, _("Unable to delete file: %s"), priv->lastError);
1663 entangle_camera_emit_deferred(cam, "camera-file-deleted", G_OBJECT(file));
1668 g_mutex_unlock(priv->lock);
1673 static void entangle_camera_delete_file_helper(GSimpleAsyncResult *result,
1675 GCancellable *cancellable G_GNUC_UNUSED)
1677 EntangleCameraFile *file;
1678 GError *error = NULL;
1680 file = g_simple_async_result_get_op_res_gpointer(result);
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);
1690 * entangle_camera_delete_file_async:
1691 * @cam: (transfer none): the camera
1692 * @file: (transfer none): the file to delete
1694 * Delete @file from the camera capture target.
1696 * This can only be invoked when the camera is connected.
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
1702 void entangle_camera_delete_file_async(EntangleCamera *cam,
1703 EntangleCameraFile *file,
1704 GCancellable *cancellable,
1705 GAsyncReadyCallback callback,
1708 g_return_if_fail(ENTANGLE_IS_CAMERA(cam));
1709 g_return_if_fail(ENTANGLE_IS_CAMERA_FILE(file));
1711 GSimpleAsyncResult *result = g_simple_async_result_new(G_OBJECT(cam),
1714 entangle_camera_delete_file_async);
1717 g_simple_async_result_set_op_res_gpointer(result, file, g_object_unref);
1719 g_simple_async_result_run_in_thread(result,
1720 entangle_camera_delete_file_helper,
1723 g_object_unref(result);
1728 * entangle_camera_delete_file_finish:
1729 * @cam: (transfer none): the camera
1731 * Check the completion status of a previous call to
1732 * entangle_camera_delete_file_async.
1734 * Returns: TRUE if the file was deleted, FALSE on error
1736 gboolean entangle_camera_delete_file_finish(EntangleCamera *cam,
1737 GAsyncResult *result,
1740 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
1742 return !g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(result),
1748 * entangle_camera_process_events:
1749 * @cam: (transfer none): the camera
1750 * @waitms: the number of milliseconds to wait
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.
1757 * This can only be invoked when the camera is connected.
1759 * This block execution of the caller until completion.
1761 * Returns: TRUE if the file was deleted, FALSE on error
1763 gboolean entangle_camera_process_events(EntangleCamera *cam,
1767 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
1769 EntangleCameraPrivate *priv = cam->priv;
1770 CameraEventType eventType = 0;
1771 void *eventData = NULL;
1773 guint64 startms, endms, donems;
1774 gboolean ret = FALSE;
1777 g_mutex_lock(priv->lock);
1780 ENTANGLE_ERROR(error, _("Cannot wait for events while not connected"));
1784 g_get_current_time(&tv);
1785 startms = (tv.tv_sec * 1000ll) + (tv.tv_usec / 1000ll);
1787 ENTANGLE_DEBUG("Waiting for events start %llu duration %llu",
1788 (unsigned long long)startms,
1789 (unsigned long long)waitms);
1791 entangle_camera_reset_last_error(cam);
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);
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);
1806 ENTANGLE_ERROR(error, _("Unable to wait for events: %s"), priv->lastError);
1809 ENTANGLE_DEBUG("Event type %d", eventType);
1810 switch (eventType) {
1811 case GP_EVENT_UNKNOWN:
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
1820 if (strstr(eventData, "d10d") == NULL)
1821 entangle_camera_emit_deferred(cam, "camera-controls-changed", NULL);
1823 ENTANGLE_DEBUG("Unknown event '%s'", (char *)eventData);
1827 case GP_EVENT_TIMEOUT:
1828 ENTANGLE_DEBUG("Wait timed out");
1831 case GP_EVENT_FILE_ADDED: {
1832 CameraFilePath *camerapath = eventData;
1833 EntangleCameraFile *file;
1835 ENTANGLE_DEBUG("File added '%s' in '%s'", camerapath->name, camerapath->folder);
1837 file = entangle_camera_file_new(camerapath->folder,
1840 entangle_camera_emit_deferred(cam, "camera-file-added", G_OBJECT(file));
1842 g_object_unref(file);
1845 case GP_EVENT_FOLDER_ADDED: {
1846 CameraFilePath *camerapath = eventData;
1848 ENTANGLE_DEBUG("Folder added '%s' in '%s'", camerapath->name, camerapath->folder);
1851 case GP_EVENT_CAPTURE_COMPLETE:
1852 ENTANGLE_DEBUG("Capture is complete");
1856 ENTANGLE_DEBUG("Unexpected event received %d", eventType);
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 &&
1868 ENTANGLE_DEBUG("Done waiting for events %llu",
1869 (unsigned long long)donems);
1875 g_mutex_unlock(priv->lock);
1880 static void entangle_camera_process_events_helper(GSimpleAsyncResult *result,
1882 GCancellable *cancellable G_GNUC_UNUSED)
1885 GError *error = NULL;
1887 waitptr = g_simple_async_result_get_op_res_gpointer(result);
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);
1897 * entangle_camera_process_events_async:
1898 * @cam: (transfer none): the camera
1899 * @waitms: the number of milliseconds to wait
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.
1906 * This can only be invoked when the camera is connected.
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
1912 void entangle_camera_process_events_async(EntangleCamera *cam,
1914 GCancellable *cancellable,
1915 GAsyncReadyCallback callback,
1918 g_return_if_fail(ENTANGLE_IS_CAMERA(cam));
1920 guint64 *waitptr = g_new0(guint64, 1);
1921 GSimpleAsyncResult *result = g_simple_async_result_new(G_OBJECT(cam),
1924 entangle_camera_process_events_async);
1927 g_simple_async_result_set_op_res_gpointer(result, waitptr, g_free);
1929 g_simple_async_result_run_in_thread(result,
1930 entangle_camera_process_events_helper,
1933 g_object_unref(result);
1938 * entangle_camera_process_events_finish:
1939 * @cam: (transfer none): the camera
1941 * Check the completion status of a previous call to
1942 * entangle_camera_process_events_async.
1944 * Returns: TRUE if events were processed, FALSE on error
1946 gboolean entangle_camera_process_events_finish(EntangleCamera *cam,
1947 GAsyncResult *result,
1950 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
1952 return !g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(result),
1957 static CameraWidget *
1958 entangle_camera_find_widget(EntangleCamera *cam,
1961 EntangleCameraPrivate *priv = cam->priv;
1962 gchar **names = g_strsplit(path, "/", 0);
1963 CameraWidget *ret = NULL;
1964 CameraWidget *curr = priv->widgets;
1967 for (i = 0; names[i] != NULL; i++) {
1970 if (g_str_equal(names[i], "") ||
1971 g_str_equal(names[i], "main"))
1974 if (gp_widget_get_child_by_name(curr,
1990 static EntangleControl *do_build_controls(EntangleCamera *cam,
1992 CameraWidget *widget,
1995 EntangleCameraPrivate *priv = cam->priv;
1996 CameraWidgetType type;
1997 EntangleControl *ret = NULL;
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"));
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"));
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);
2024 fullpath = g_strdup_printf("%s/%s", path, name);
2027 /* We treat both window and section as just groups */
2028 case GP_WIDGET_WINDOW:
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);
2043 /* Actions are exposed as normal APIs
2044 * And "Other ptp properties" 90% dups
2046 if (g_str_equal(childname, "actions") ||
2047 g_str_equal(childname, "other"))
2050 if (!(subctl = do_build_controls(cam, fullpath, child, error))) {
2051 g_object_unref(grp);
2055 entangle_control_group_add(grp, subctl);
2058 ret = ENTANGLE_CONTROL(grp);
2061 case GP_WIDGET_SECTION:
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);
2073 if (!(subctl = do_build_controls(cam, fullpath, child, error))) {
2074 g_object_unref(grp);
2078 entangle_control_group_add(grp, subctl);
2081 ret = ENTANGLE_CONTROL(grp);
2084 case GP_WIDGET_BUTTON:
2086 ENTANGLE_DEBUG("Add button %s %d %s", fullpath, id, label);
2087 ret = ENTANGLE_CONTROL(entangle_control_button_new(fullpath, id, label, info, ro));
2090 /* Unclear why these two are the same in libgphoto */
2091 case GP_WIDGET_RADIO:
2092 case GP_WIDGET_MENU:
2094 ENTANGLE_DEBUG("Add menu %s %d %s", fullpath, id, label);
2095 ret = ENTANGLE_CONTROL(entangle_control_choice_new(fullpath, id, label, info, ro));
2098 case GP_WIDGET_DATE:
2100 ENTANGLE_DEBUG("Add date %s %d %s", fullpath, id, label);
2101 ret = ENTANGLE_CONTROL(entangle_control_date_new(fullpath, id, label, info, ro));
2104 case GP_WIDGET_RANGE:
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,
2114 case GP_WIDGET_TEXT:
2116 ENTANGLE_DEBUG("Add date %s %d %s", fullpath, id, label);
2117 ret = ENTANGLE_CONTROL(entangle_control_text_new(fullpath, id, label, info, ro));
2120 case GP_WIDGET_TOGGLE:
2122 ENTANGLE_DEBUG("Add date %s %d %s", fullpath, id, label);
2123 ret = ENTANGLE_CONTROL(entangle_control_toggle_new(fullpath, id, label, info, ro));
2127 g_warn_if_reached();
2131 g_hash_table_insert(priv->controlPaths, g_strdup(fullpath), ret);
2139 static gboolean entangle_str_equal_null(gchar *a, gchar *b)
2145 return g_str_equal(a, b);
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
2155 static gboolean do_load_controls(EntangleCamera *cam,
2157 CameraWidget *widget,
2160 EntangleCameraPrivate *priv = cam->priv;
2161 CameraWidgetType type;
2162 EntangleControl *ctrl = NULL;
2166 gboolean ret = FALSE;
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"));
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"));
2180 gp_widget_get_readonly(widget, &ro);
2182 fullpath = g_strdup_printf("%s/%s", path, name);
2183 ctrl = g_hash_table_lookup(priv->controlPaths, fullpath);
2188 entangle_control_set_readonly(ctrl, ro ? TRUE : FALSE);
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))
2202 case GP_WIDGET_BUTTON: {
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++) {
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);
2224 g_object_set(ctrl, "value", newValue, NULL);
2229 case GP_WIDGET_DATE: {
2231 g_object_set(ctrl, "value", value, NULL);
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);
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);
2263 case GP_WIDGET_TOGGLE: {
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);
2280 g_warn_if_reached();
2284 entangle_control_set_dirty(ctrl, FALSE);
2291 static gboolean do_save_controls(EntangleCamera *cam,
2293 CameraWidget *widget,
2297 EntangleCameraPrivate *priv = cam->priv;
2298 CameraWidgetType type;
2299 EntangleControl *ctrl = NULL;
2302 gboolean ret = FALSE;
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"));
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"));
2316 fullpath = g_strdup_printf("%s/%s", path, name);
2317 ctrl = g_hash_table_lookup(priv->controlPaths, fullpath);
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))
2335 case GP_WIDGET_BUTTON: {
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)) {
2343 g_object_get(ctrl, "value", &value, NULL);
2344 gp_widget_set_value(widget, value);
2350 case GP_WIDGET_DATE:
2351 if (entangle_control_get_dirty(ctrl)) {
2353 g_object_get(ctrl, "value", &value, NULL);
2358 case GP_WIDGET_RANGE:
2359 if (entangle_control_get_dirty(ctrl)) {
2361 g_object_get(ctrl, "value", &value, NULL);
2362 gp_widget_set_value(widget, &value);
2367 case GP_WIDGET_TEXT:
2368 if (entangle_control_get_dirty(ctrl)) {
2370 g_object_get(ctrl, "value", &value, NULL);
2371 gp_widget_set_value(widget, value);
2377 case GP_WIDGET_TOGGLE:
2378 if (entangle_control_get_dirty(ctrl)) {
2381 g_object_get(ctrl, "value", &value, NULL);
2383 gp_widget_set_value(widget, &i);
2389 g_warn_if_reached();
2401 * entangle_camera_load_controls:
2402 * @cam: (transfer none): the camera
2404 * Loads the configuration controls from the camera.
2406 * This can only be invoked when the camera is connected.
2408 * This block execution of the caller until completion.
2410 * Returns: TRUE if the controls were loaded, FALSE on error
2412 gboolean entangle_camera_load_controls(EntangleCamera *cam,
2415 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
2417 EntangleCameraPrivate *priv = cam->priv;
2418 gboolean ret = FALSE;
2421 g_mutex_lock(priv->lock);
2423 if (priv->cam == NULL) {
2424 g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2425 _("Unable to load controls, camera is not connected"));
2429 entangle_camera_begin_job(cam);
2430 ENTANGLE_DEBUG("Loading control values");
2431 err = gp_camera_get_config(priv->cam, &priv->widgets, priv->ctx);
2433 g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2434 _("Unable to fetch camera control configuration"));
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;
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;
2452 ENTANGLE_DEBUG("No viewfinder widget");
2453 priv->hasViewfinder = FALSE;
2457 ret = do_load_controls(cam, "", priv->widgets, error);
2460 entangle_camera_end_job(cam);
2463 g_mutex_unlock(priv->lock);
2468 static void entangle_camera_load_controls_helper(GSimpleAsyncResult *result,
2470 GCancellable *cancellable G_GNUC_UNUSED)
2472 GError *error = NULL;
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);
2482 * entangle_camera_load_controls_async:
2483 * @cam: (transfer none): the camera
2485 * Loads the configuration controls from the camera.
2487 * This can only be invoked when the camera is connected.
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
2493 void entangle_camera_load_controls_async(EntangleCamera *cam,
2494 GCancellable *cancellable,
2495 GAsyncReadyCallback callback,
2498 g_return_if_fail(ENTANGLE_IS_CAMERA(cam));
2500 GSimpleAsyncResult *result = g_simple_async_result_new(G_OBJECT(cam),
2503 entangle_camera_load_controls_async);
2505 g_simple_async_result_run_in_thread(result,
2506 entangle_camera_load_controls_helper,
2509 g_object_unref(result);
2514 * entangle_camera_load_controls_finish:
2515 * @cam: (transfer none): the camera
2517 * Check the completion status of a previous call to
2518 * entangle_camera_load_controls_async.
2520 * Returns: TRUE if the controls were loaded, FALSE on error
2522 gboolean entangle_camera_load_controls_finish(EntangleCamera *cam,
2523 GAsyncResult *result,
2526 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
2528 return !g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(result),
2534 * entangle_camera_save_controls:
2535 * @cam: (transfer none): the camera
2537 * Saves the configuration controls to the camera.
2539 * This can only be invoked when the camera is connected.
2541 * This block execution of the caller until completion.
2543 * Returns: TRUE if the controls were saved, FALSE on error
2545 gboolean entangle_camera_save_controls(EntangleCamera *cam,
2548 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
2550 EntangleCameraPrivate *priv = cam->priv;
2551 gboolean ret = FALSE;
2552 gboolean dirty = FALSE;
2555 g_mutex_lock(priv->lock);
2557 if (priv->cam == NULL) {
2558 g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2559 _("Unable to save controls, camera is not connected"));
2563 if (priv->controls == NULL) {
2564 g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2565 _("Unable to save controls, camera is not configurable"));
2569 entangle_camera_begin_job(cam);
2571 ENTANGLE_DEBUG("Saving controls for %p", cam);
2573 if (!do_save_controls(cam, "", priv->widgets,
2578 ENTANGLE_DEBUG("No widgets dirty, skipping");
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);
2589 if (!do_load_controls(cam, "", priv->widgets, error))
2596 entangle_camera_end_job(cam);
2599 g_mutex_unlock(priv->lock);
2605 static void entangle_camera_save_controls_helper(GSimpleAsyncResult *result,
2607 GCancellable *cancellable G_GNUC_UNUSED)
2609 GError *error = NULL;
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);
2619 * entangle_camera_save_controls_async:
2620 * @cam: (transfer none): the camera
2622 * Saves the configuration controls to the camera.
2624 * This can only be invoked when the camera is connected.
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
2630 void entangle_camera_save_controls_async(EntangleCamera *cam,
2631 GCancellable *cancellable,
2632 GAsyncReadyCallback callback,
2635 g_return_if_fail(ENTANGLE_IS_CAMERA(cam));
2637 GSimpleAsyncResult *result = g_simple_async_result_new(G_OBJECT(cam),
2640 entangle_camera_save_controls_async);
2642 g_simple_async_result_run_in_thread(result,
2643 entangle_camera_save_controls_helper,
2646 g_object_unref(result);
2651 * entangle_camera_save_controls_finish:
2652 * @cam: (transfer none): the camera
2654 * Check the completion status of a previous call to
2655 * entangle_camera_save_controls_async.
2657 * Returns: TRUE if the controls were saved, FALSE on error
2659 gboolean entangle_camera_save_controls_finish(EntangleCamera *cam,
2660 GAsyncResult *result,
2663 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
2665 return !g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(result),
2671 * entangle_camera_get_controls:
2672 * @cam: (transfer none): the camera
2674 * Get the configuration controls associated with the camera.
2676 * This can only be invoked when the camera is connected.
2678 * Returns: (transfer full): the controls, or NULL
2680 EntangleControlGroup *entangle_camera_get_controls(EntangleCamera *cam, GError **error)
2682 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), NULL);
2684 EntangleCameraPrivate *priv = cam->priv;
2685 EntangleControlGroup *ret = NULL;
2687 g_mutex_lock(priv->lock);
2689 if (priv->cam == NULL) {
2690 g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2691 _("Controls not available when camera is disconnected"));
2695 if (priv->controls == NULL) {
2696 g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2697 _("Controls not available for this camera"));
2701 ret = priv->controls;
2705 g_mutex_unlock(priv->lock);
2711 * entangle_camera_set_viewfinder:
2712 * @cam: (transfer none): the camera
2713 * @enabled: TRUE to turn on the view finder
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.
2719 * This can only be invoked when the camera is connected.
2721 * This block execution of the caller until completion.
2723 * Returns: TRUE if the viewer finder state was changed, FALSE on error
2725 gboolean entangle_camera_set_viewfinder(EntangleCamera *cam,
2729 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
2731 EntangleCameraPrivate *priv = cam->priv;
2732 gboolean ret = FALSE;
2733 CameraWidget *widget;
2734 CameraWidgetType type;
2738 g_mutex_lock(priv->lock);
2739 entangle_camera_begin_job(cam);
2741 ENTANGLE_DEBUG("Setting viewfinder state %d", enabled);
2743 if (priv->cam == NULL) {
2744 g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2745 _("Controls not available when camera is disconnected"));
2749 if (priv->widgets == NULL) {
2750 g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2751 _("Controls not available for this camera"));
2755 widget = entangle_camera_find_widget(cam,
2756 "/main/actions/viewfinder");
2758 widget = entangle_camera_find_widget(cam,
2759 "/main/actions/eosviewfinder");
2761 g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2762 _("Viewfinder control not available with this camera"));
2766 if ((err = gp_widget_get_type(widget,
2768 g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2769 _("Unable to fetch widget type"));
2773 if (type != GP_WIDGET_TOGGLE) {
2774 g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2775 _("Viewfinder control was not a toggle widget"));
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.
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);
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);
2801 if ((err = gp_camera_set_config(priv->cam,
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);
2813 entangle_camera_end_job(cam);
2814 g_mutex_unlock(priv->lock);
2819 static void entangle_camera_set_viewfinder_helper(GSimpleAsyncResult *result,
2821 GCancellable *cancellable G_GNUC_UNUSED)
2823 GError *error = NULL;
2827 data = g_object_get_data(G_OBJECT(result),
2830 enabled = GPOINTER_TO_INT(data) == 1 ? TRUE : FALSE;
2832 if (!entangle_camera_set_viewfinder(ENTANGLE_CAMERA(object),
2835 g_simple_async_result_set_from_error(result, error);
2836 g_error_free(error);
2842 * entangle_camera_set_viewfinder_async:
2843 * @cam: (transfer none): the camera
2844 * @enabled: TRUE to turn on the view finder
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.
2850 * This can only be invoked when the camera is connected.
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
2856 void entangle_camera_set_viewfinder_async(EntangleCamera *cam,
2858 GCancellable *cancellable,
2859 GAsyncReadyCallback callback,
2862 g_return_if_fail(ENTANGLE_IS_CAMERA(cam));
2864 GSimpleAsyncResult *result = g_simple_async_result_new(G_OBJECT(cam),
2867 entangle_camera_set_viewfinder_async);
2869 g_object_set_data(G_OBJECT(result),
2871 GINT_TO_POINTER(enabled ? 1 : 0));
2873 g_simple_async_result_run_in_thread(result,
2874 entangle_camera_set_viewfinder_helper,
2877 g_object_unref(result);
2882 * entangle_camera_set_viewfinder_finish:
2883 * @cam: (transfer none): the camera
2885 * Check the completion status of a previous call to
2886 * entangle_camera_set_viewfinder_async.
2888 * Returns: TRUE if the viewfinder state was changed, FALSE on error
2890 gboolean entangle_camera_set_viewfinder_finish(EntangleCamera *cam,
2891 GAsyncResult *result,
2894 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
2896 return !g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(result),
2902 * entangle_camera_autofocus:
2903 * @cam: (transfer none): the camera
2905 * Trigger the autofocus mechanism on the camera, waiting
2906 * until focus is achieved or fails.
2908 * This can only be invoked when the camera is connected.
2910 * This block execution of the caller until completion.
2912 * Returns: TRUE if autofocus was achieved, FALSE on error
2914 gboolean entangle_camera_autofocus(EntangleCamera *cam,
2917 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
2919 EntangleCameraPrivate *priv = cam->priv;
2920 gboolean ret = FALSE;
2921 CameraWidget *widget;
2922 CameraWidgetType type;
2926 g_mutex_lock(priv->lock);
2927 entangle_camera_begin_job(cam);
2929 ENTANGLE_DEBUG("Setting autofocus");
2931 if (priv->cam == NULL) {
2932 g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2933 _("Controls not available when camera is disconnected"));
2937 if (priv->widgets == NULL) {
2938 g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2939 _("Controls not available for this camera"));
2943 widget = entangle_camera_find_widget(cam,
2944 "/main/actions/autofocusdrive");
2946 g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2947 _("Autofocus control not available with this camera"));
2951 if ((err = gp_widget_get_type(widget,
2953 g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2954 _("Unable to fetch widget type"));
2958 if (type != GP_WIDGET_TOGGLE) {
2959 g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
2960 _("Autofocus control was not a toggle widget"));
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);
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);
2980 if ((err = gp_camera_set_config(priv->cam,
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);
2992 entangle_camera_end_job(cam);
2993 g_mutex_unlock(priv->lock);
2998 static void entangle_camera_autofocus_helper(GSimpleAsyncResult *result,
3000 GCancellable *cancellable G_GNUC_UNUSED)
3002 GError *error = NULL;
3004 if (!entangle_camera_autofocus(ENTANGLE_CAMERA(object),
3006 g_simple_async_result_set_from_error(result, error);
3007 g_error_free(error);
3013 * entangle_camera_autofocus_async:
3014 * @cam: (transfer none): the camera
3016 * Trigger the autofocus mechanism on the camera, waiting
3017 * until focus is achieved or fails.
3019 * This can only be invoked when the camera is connected.
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
3025 void entangle_camera_autofocus_async(EntangleCamera *cam,
3026 GCancellable *cancellable,
3027 GAsyncReadyCallback callback,
3030 g_return_if_fail(ENTANGLE_IS_CAMERA(cam));
3032 GSimpleAsyncResult *result = g_simple_async_result_new(G_OBJECT(cam),
3035 entangle_camera_autofocus_async);
3037 g_simple_async_result_run_in_thread(result,
3038 entangle_camera_autofocus_helper,
3041 g_object_unref(result);
3046 * entangle_camera_autofocus_finish:
3047 * @cam: (transfer none): the camera
3049 * Check the completion status of a previous call to
3050 * entangle_camera_autofocus_async.
3052 * Returns: TRUE if autofocus was performed, FALSE on error
3054 gboolean entangle_camera_autofocus_finish(EntangleCamera *cam,
3055 GAsyncResult *result,
3058 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
3060 return !g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(result),
3066 * entangle_camera_manualfocus:
3067 * @cam: (transfer none): the camera
3068 * @step: how much to change focus by
3070 * Trigger the focus mechanism on the camera, to move
3073 * This can only be invoked when the camera is connected.
3075 * This block execution of the caller until completion.
3077 * Returns: TRUE if autofocus was achieved, FALSE on error
3079 gboolean entangle_camera_manualfocus(EntangleCamera *cam,
3080 EntangleCameraManualFocusStep step,
3083 g_return_val_if_fail(ENTANGLE_IS_CAMERA(cam), FALSE);
3085 EntangleCameraPrivate *priv = cam->priv;
3086 gboolean ret = FALSE;
3087 CameraWidget *widget;
3088 CameraWidgetType type;
3091 g_mutex_lock(priv->lock);
3092 entangle_camera_begin_job(cam);
3094 ENTANGLE_DEBUG("Setting manualfocus %d", (int)step);
3096 if (priv->cam == NULL) {
3097 g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
3098 _("Controls not available when camera is disconnected"));
3102 if (priv->widgets == NULL) {
3103 g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
3104 _("Controls not available for this camera"));
3108 widget = entangle_camera_find_widget(cam,
3109 "/main/actions/manualfocusdrive");
3111 g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
3112 _("Manual focus control not available with this camera"));
3116 if ((err = gp_widget_get_type(widget,
3118 g_set_error(error, ENTANGLE_CAMERA_ERROR, 0,
3119 _("Unable to fetch widget type"));
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"));
3130 if (type == GP_WIDGET_RANGE) {
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,
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);
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,
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);