/** \file * * Implement Camera object representing a camera attached to the system. * * \author Copyright 2000 Scott Fritzinger * \author Copyright 2001-2002 Lutz Mueller * * \note * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * \note * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * \note * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "libgphoto2/i18n.h" #define CAMERA_UNUSED(c,ctx) \ { \ (c)->pc->used--; \ if (!(c)->pc->used) { \ if ((c)->pc->exit_requested) \ gp_camera_exit ((c), (ctx)); \ if (!(c)->pc->ref_count) \ gp_camera_free (c); \ } \ } #define CR(c,result,ctx) \ { \ int r1 = (result); \ \ if (r1 < 0) { \ /* libgphoto2_port doesn't have a GPContext */ \ gp_context_error ((ctx), _("An error occurred " \ "in the io-library ('%s'): %s"), \ gp_port_result_as_string (r1), \ gp_port_get_error ((c) ? (c)->port : NULL)); \ if (c) \ CAMERA_UNUSED (c,ctx); \ return (r1); \ } \ } /* * HAVE_MULTI * ---------- * * The problem: Several different programs (gtkam, gphoto2, gimp) accessing * one camera. * The solutions: * (1) gp_port_open before each operation, gp_port_close after. This has * shown to not work with some drivers (digita/dc240) for serial ports, * because the camera will notice that [1], * reset itself and will therefore need to be reinitialized. If you want * this behaviour, #define HAVE_MULTI. * (2) Leave it up to the frontend to release the camera by calling * gp_camera_exit after camera operations. This is what is implemented * right now. The drawback is that re-initialization takes more time than * just reopening the port. However, it works for all camera drivers. * * [1] Marr writes: * * With the Digita-OS cameras at least, one of the RS-232 lines is tied * to a 'Reset' signal on the camera. I quote from the Digita 'Host * Interface Specification' document: * * "The Reset signal is a pulse on the Reset/Att line (which cooresponds * [sic] to pin 2 at the camera side) sent from the host computer to the * camera. This pulse must be at least 50us." */ #ifdef HAVE_MULTI #define CHECK_OPEN(c,ctx) \ { \ int r2; \ \ if (strcmp ((c)->pc->a.model,"Directory Browse")) { \ r2 = gp_port_open ((c)->port); \ if (r2 < 0) { \ CAMERA_UNUSED (c,ctx); \ return (r2); \ } \ } \ if ((c)->functions->pre_func) { \ r2 = (c)->functions->pre_func (c,ctx); \ if (r2 < 0) { \ CAMERA_UNUSED (c,ctx); \ return (r2); \ } \ } \ } #else #define CHECK_OPEN(c,ctx) \ { \ if ((c)->functions->pre_func) { \ int r2 = (c)->functions->pre_func (c,ctx); \ if (r2 < 0) { \ CAMERA_UNUSED (c,ctx); \ return (r2); \ } \ } \ } #endif #ifdef HAVE_MULTI #define CHECK_CLOSE(c,ctx) \ { \ if (strcmp ((c)->pc->a.model,"Directory Browse")) \ gp_port_close ((c)->port); \ if ((c)->functions->post_func) { \ int r3 = (c)->functions->post_func (c,ctx); \ if (r3 < 0) { \ CAMERA_UNUSED (c,ctx); \ return (r3); \ } \ } \ } #else #define CHECK_CLOSE(c,ctx) \ { \ if ((c)->functions->post_func) { \ int r3 = (c)->functions->post_func (c,ctx); \ if (r3 < 0) { \ CAMERA_UNUSED (c,ctx); \ return (r3); \ } \ } \ } #endif #define CRS(c,res,ctx) \ { \ int r4 = (res); \ \ if (r4 < 0) { \ CAMERA_UNUSED (c,ctx); \ return (r4); \ } \ } #define CRSL(c,res,ctx,list) \ { \ int r5 = (res); \ \ if (r5 < 0) { \ CAMERA_UNUSED (c,ctx); \ gp_list_free (list); \ return (r5); \ } \ } #define CHECK_RESULT_OPEN_CLOSE(c,result,ctx) \ { \ int r6; \ \ CHECK_OPEN (c,ctx); \ r6 = (result); \ if (r6 < 0) { \ GP_LOG_E ("'%s' failed: %d", #result, r6); \ CHECK_CLOSE (c,ctx); \ CAMERA_UNUSED (c,ctx); \ return (r6); \ } \ CHECK_CLOSE (c,ctx); \ } #define CHECK_INIT(c,ctx) \ { \ if ((c)->pc->used) \ return (GP_ERROR_CAMERA_BUSY); \ (c)->pc->used++; \ if (!(c)->pc->lh) \ CR((c), gp_camera_init (c, ctx), ctx); \ } struct _CameraPrivateCore { /* Some information about the port */ unsigned int speed; /* The abilities of this camera */ CameraAbilities a; /* Library handle */ lt_dlhandle lh; char error[2048]; unsigned int ref_count; unsigned char used; unsigned char exit_requested; int initialized; /* Timeout functions */ CameraTimeoutStartFunc timeout_start_func; CameraTimeoutStopFunc timeout_stop_func; void *timeout_data; unsigned int *timeout_ids; unsigned int timeout_ids_len; }; /** * Close connection to camera. * * @param camera a #Camera object * @param context a #GPContext object * @return a gphoto2 error code. * * Closes a connection to the camera and therefore gives other application * the possibility to access the camera, too. * * It is recommended that you * call this function when you currently don't need the camera. The camera * will get reinitialized by gp_camera_init() automatically if you try to * access the camera again. * */ int gp_camera_exit (Camera *camera, GPContext *context) { int exit_result = GP_OK; C_PARAMS (camera); GP_LOG_D ("Exiting camera ('%s')...", camera->pc->a.model); /* * We have to postpone this operation if the camera is currently * in use. gp_camera_exit will be called again if the * camera->pc->used will drop to zero. */ if (camera->pc->used) { camera->pc->exit_requested = 1; return (GP_OK); } /* Remove every timeout that is still pending */ while (camera->pc->timeout_ids_len) gp_camera_stop_timeout (camera, camera->pc->timeout_ids[0]); free (camera->pc->timeout_ids); camera->pc->timeout_ids = NULL; if (camera->functions->exit) { #ifdef HAVE_MULTI gp_port_open (camera->port); #endif exit_result = camera->functions->exit (camera, context); } gp_port_close (camera->port); memset (camera->functions, 0, sizeof (CameraFunctions)); if (camera->pc->lh) { #if !defined(VALGRIND) gpi_libltdl_lock(); lt_dlclose (camera->pc->lh); lt_dlexit (); gpi_libltdl_unlock(); #endif camera->pc->lh = NULL; } gp_filesystem_reset (camera->fs); return exit_result; } /** * Allocates the memory for a #Camera. * * @param camera the #Camera object to initialize. * @return a gphoto2 error code * */ int gp_camera_new (Camera **camera) { int result; C_PARAMS (camera); C_MEM (*camera = calloc (1, sizeof (Camera))); (*camera)->functions = calloc (1, sizeof (CameraFunctions)); (*camera)->pc = calloc (1, sizeof (CameraPrivateCore)); if (!(*camera)->functions || !(*camera)->pc) { result = GP_ERROR_NO_MEMORY; goto error; } (*camera)->pc->ref_count = 1; /* Create the filesystem */ result = gp_filesystem_new (&(*camera)->fs); if (result < GP_OK) goto error; /* Create the port */ result = gp_port_new (&(*camera)->port); if (result < GP_OK) goto error; return(GP_OK); error: gp_camera_free (*camera); return result; } /** * \brief Sets the camera abilities. * * @param camera a #Camera * @param abilities the #CameraAbilities to be set * @return a gphoto2 error code * * You need to call this function before calling #gp_camera_init the * first time unless you want gphoto2 to autodetect cameras and choose * the first detected one. By setting the \c abilities, you * tell gphoto2 what model the \c camera is and what camera driver should * be used for accessing the \c camera. You can get \c abilities by calling * #gp_abilities_list_get_abilities. * */ int gp_camera_set_abilities (Camera *camera, CameraAbilities abilities) { GP_LOG_D ("Setting abilities ('%s')...", abilities.model); C_PARAMS (camera); /* * If the camera is currently initialized, terminate that connection. * We don't care if we are successful or not. */ if (camera->pc->lh) gp_camera_exit (camera, NULL); memcpy (&camera->pc->a, &abilities, sizeof (CameraAbilities)); return (GP_OK); } /** * Retrieve the \c abilities of the \c camera. * * @param camera a #Camera * @param abilities * @return a gphoto2 error code * */ int gp_camera_get_abilities (Camera *camera, CameraAbilities *abilities) { C_PARAMS (camera && abilities); memcpy (abilities, &camera->pc->a, sizeof (CameraAbilities)); return (GP_OK); } int gp_camera_get_port_info (Camera *camera, GPPortInfo *info) { C_PARAMS (camera && info); CR (camera, gp_port_get_info (camera->port, info), NULL); return (GP_OK); } int gp_camera_set_port_info (Camera *camera, GPPortInfo info) { char *name, *path; C_PARAMS (camera); /* * If the camera is currently initialized, terminate that connection. * We don't care if we are successful or not. */ if (camera->pc->lh) gp_camera_exit (camera, NULL); gp_port_info_get_name (info, &name); gp_port_info_get_path (info, &path); GP_LOG_D ("Setting port info for port '%s' at '%s'...", name, path); CR (camera, gp_port_set_info (camera->port, info), NULL); return (GP_OK); } /** * Set the camera speed. * * @param camera a #Camera * @param speed the speed * @return a gphoto2 error code * * This function is typically used prior first initialization * using #gp_camera_init for debugging purposes. Normally, a camera driver * will try to figure out the current speed of the camera and set the speed * to the optimal one automatically. Note that this function only works with * serial ports. In other words, you have to set the camera's port to a * serial one (using #gp_camera_set_port_path or #gp_camera_set_port_name) * prior calling this function. * */ int gp_camera_set_port_speed (Camera *camera, int speed) { GPPortSettings settings; C_PARAMS (camera); C_PARAMS_MSG (camera->port, "You need to set a port prior trying to set the speed"); C_PARAMS_MSG (camera->port->type == GP_PORT_SERIAL, "You can specify a speed only with serial ports"); /* * If the camera is currently initialized, terminate that connection. * We don't care if we are successful or not. */ if (camera->pc->lh) gp_camera_exit (camera, NULL); CR (camera, gp_port_get_settings (camera->port, &settings), NULL); settings.serial.speed = speed; CR (camera, gp_port_set_settings (camera->port, settings), NULL); camera->pc->speed = speed; return (GP_OK); } /** * Retrieve the current speed. * * @param camera a #Camera * @return The current speed or a gphoto2 error code * */ int gp_camera_get_port_speed (Camera *camera) { C_PARAMS (camera); return (camera->pc->speed); } /** * Increment the reference count of a \c camera. * * @param camera a #Camera * @return a gphoto2 error code * */ int gp_camera_ref (Camera *camera) { C_PARAMS (camera); camera->pc->ref_count += 1; return (GP_OK); } /** * Decrements the reference count of a #Camera. * * @param camera a #Camera * @return a gphoto2 error code * * If the reference count reaches %0, the \c camera will be freed * automatically. * */ int gp_camera_unref (Camera *camera) { C_PARAMS (camera); if (!camera->pc->ref_count) { GP_LOG_E ("gp_camera_unref on a camera with ref_count == 0 " "should not happen at all"); return (GP_ERROR); } camera->pc->ref_count -= 1; if (!camera->pc->ref_count) { /* We cannot free a camera that is currently in use */ if (!camera->pc->used) gp_camera_free (camera); } return (GP_OK); } /** * Free the \c camera. * * @param camera a #Camera * @return a gphoto2 error code * * \deprecated * This function should never be used. Please use #gp_camera_unref instead. * */ int gp_camera_free (Camera *camera) { C_PARAMS (camera); GP_LOG_D ("Freeing camera..."); /* * If the camera is currently initialized, close the connection. * We don't care if we are successful or not. */ if (camera->port && camera->pc && camera->pc->lh) gp_camera_exit (camera, NULL); /* We don't care if anything goes wrong */ if (camera->port) { gp_port_free (camera->port); camera->port = NULL; } if (camera->pc) { free (camera->pc->timeout_ids); free (camera->pc); camera->pc = NULL; } if (camera->fs) { gp_filesystem_free (camera->fs); camera->fs = NULL; } if (camera->functions) { free (camera->functions); camera->functions = NULL; } free (camera); return (GP_OK); } /** * Autodetect all detectable camera * * @param list a #CameraList that receives the autodetected cameras * @param context a #GPContext * @return the number of cameras detected (0 to n) or a gphoto2 error code (<0) * * This camera will autodetected all cameras that can be autodetected. * This will for instance detect all USB cameras. * * CameraList *list; * gp_list_new (&list); * gp_camera_autodetect (list, context); * ... done! ... */ int gp_camera_autodetect (CameraList *list, GPContext *context) { CameraAbilitiesList *al = NULL; GPPortInfoList *il = NULL; int ret, i; CameraList *xlist = NULL; ret = gp_list_new (&xlist); if (ret < GP_OK) goto out; if (!il) { /* Load all the port drivers we have... */ ret = gp_port_info_list_new (&il); if (ret < GP_OK) goto out; ret = gp_port_info_list_load (il); if (ret < 0) goto out; ret = gp_port_info_list_count (il); if (ret < 0) goto out; } /* Load all the camera drivers we have... */ ret = gp_abilities_list_new (&al); if (ret < GP_OK) goto out; ret = gp_abilities_list_load (al, context); if (ret < GP_OK) goto out; /* ... and autodetect the currently attached cameras. */ ret = gp_abilities_list_detect (al, il, xlist, context); if (ret < GP_OK) goto out; /* Filter out the "usb:" entry */ ret = gp_list_count (xlist); if (ret < GP_OK) goto out; for (i=0;ipc->exit_requested = 0; /* * If the model hasn't been indicated, try to * figure it out (USB only). Beware of "Directory Browse". */ if (strcasecmp (camera->pc->a.model, "Directory Browse") && !strcmp ("", camera->pc->a.model)) { CameraAbilitiesList *al; GPPortInfoList *il; int m, p; char *ppath; GPPortType ptype; GPPortInfo info; CameraList *list; result = gp_list_new (&list); if (result < GP_OK) return result; GP_LOG_D ("Neither port nor model set. Trying auto-detection..."); /* Call auto-detect and choose the first camera */ CRSL (camera, gp_abilities_list_new (&al), context, list); CRSL (camera, gp_abilities_list_load (al, context), context, list); CRSL (camera, gp_port_info_list_new (&il), context, list); CRSL (camera, gp_port_info_list_load (il), context, list); CRSL (camera, gp_abilities_list_detect (al, il, list, context), context, list); if (!gp_list_count (list)) { gp_abilities_list_free (al); gp_port_info_list_free (il); gp_context_error (context, _("Could not detect " "any camera")); gp_list_free (list); return (GP_ERROR_MODEL_NOT_FOUND); } p = 0; CRSL (camera, gp_port_get_info (camera->port, &info), context, list); CRSL (camera, gp_port_info_get_path (info, &ppath), context, list); CRSL (camera, gp_port_info_get_type (info, &ptype), context, list); /* if the port was set before, then use that entry, but not if it is "usb:" */ if ((ptype == GP_PORT_USB) && strlen(ppath) && strcmp(ppath, "usb:")) { for (p = gp_list_count (list);p--;) { const char *xp; gp_list_get_value (list, p, &xp); if (!strcmp (xp, ppath)) break; } if (p<0) { gp_abilities_list_free (al); gp_port_info_list_free (il); gp_context_error (context, _("Could not detect any camera at port %s"), ppath); gp_list_free (list); return (GP_ERROR_FILE_NOT_FOUND); } } CRSL (camera, gp_list_get_name (list, p, &model), context, list); m = gp_abilities_list_lookup_model (al, model); CRSL (camera, m, context, list); CRSL (camera, gp_abilities_list_get_abilities (al, m, &a), context, list); gp_abilities_list_free (al); CRSL (camera, gp_camera_set_abilities (camera, a), context, list); CRSL (camera, gp_list_get_value (list, p, &port), context, list); p = gp_port_info_list_lookup_path (il, port); CRSL (camera, p, context, list); CRSL (camera, gp_port_info_list_get_info (il, p, &info), context, list); CRSL (camera, gp_camera_set_port_info (camera, info), context, list); gp_port_info_list_free (il); gp_list_free (list); } if (strcasecmp (camera->pc->a.model, "Directory Browse")) { switch (camera->port->type) { case GP_PORT_NONE: gp_context_error (context, _("You have to set the " "port prior to initialization of the camera.")); return (GP_ERROR_UNKNOWN_PORT); case GP_PORT_USB: if (gp_port_usb_find_device (camera->port, camera->pc->a.usb_vendor, camera->pc->a.usb_product) != GP_OK) { CRS (camera, gp_port_usb_find_device_by_class (camera->port, camera->pc->a.usb_class, camera->pc->a.usb_subclass, camera->pc->a.usb_protocol), context); } break; default: break; } } /* Load the library. */ GP_LOG_D ("Loading '%s'...", camera->pc->a.library); gpi_libltdl_lock(); lt_dlinit (); camera->pc->lh = lt_dlopenext (camera->pc->a.library); gpi_libltdl_unlock(); if (!camera->pc->lh) { gpi_libltdl_lock(); gp_context_error (context, _("Could not load required " "camera driver '%s' (%s)."), camera->pc->a.library, lt_dlerror ()); lt_dlexit (); gpi_libltdl_unlock(); return (GP_ERROR_LIBRARY); } /* Initialize the camera */ gpi_libltdl_lock(); init_func = lt_dlsym (camera->pc->lh, "camera_init"); gpi_libltdl_unlock(); if (!init_func) { gpi_libltdl_lock(); lt_dlclose (camera->pc->lh); lt_dlexit (); gpi_libltdl_unlock(); camera->pc->lh = NULL; gp_context_error (context, _("Camera driver '%s' is " "missing the 'camera_init' function."), camera->pc->a.library); return (GP_ERROR_LIBRARY); } if (strcasecmp (camera->pc->a.model, "Directory Browse")) { result = gp_port_open (camera->port); if (result < 0) { gpi_libltdl_lock(); lt_dlclose (camera->pc->lh); lt_dlexit (); gpi_libltdl_unlock(); camera->pc->lh = NULL; return (result); } } result = init_func (camera, context); if (result < 0) { gp_port_close (camera->port); gpi_libltdl_lock(); lt_dlclose (camera->pc->lh); lt_dlexit (); gpi_libltdl_unlock(); camera->pc->lh = NULL; memset (camera->functions, 0, sizeof (CameraFunctions)); return (result); } /* We don't care if that goes wrong */ #ifdef HAVE_MULTI gp_port_close (camera->port); #endif return (GP_OK); } /** * Retrieve a configuration \c window for the \c camera. * * @param camera a #Camera * @param window a #CameraWidget * @param context a #GPContext * @return gphoto2 error code * * This \c window can be used for construction of a configuration dialog. * */ int gp_camera_get_config (Camera *camera, CameraWidget **window, GPContext *context) { C_PARAMS (camera); CHECK_INIT (camera, context); if (!camera->functions->get_config) { gp_context_error (context, _("This camera does " "not provide any configuration options.")); CAMERA_UNUSED (camera, context); return (GP_ERROR_NOT_SUPPORTED); } CHECK_RESULT_OPEN_CLOSE (camera, camera->functions->get_config ( camera, window, context), context); CAMERA_UNUSED (camera, context); return (GP_OK); } /** * Retrieve a single configuration \c widget for the \c camera. * * @param camera a #Camera * @param name the name of a configuration widget * @param widget a #CameraWidget * @param context a #GPContext * @return gphoto2 error code * * This \c widget will then contain the current and the possible values and the type. * */ int gp_camera_get_single_config (Camera *camera, const char *name, CameraWidget **widget, GPContext *context) { CameraWidget *rootwidget, *child; CameraWidgetType type; const char *label; int ret, ro; C_PARAMS (camera); CHECK_INIT (camera, context); if (camera->functions->get_single_config) { CHECK_RESULT_OPEN_CLOSE (camera, camera->functions->get_single_config ( camera, name, widget, context), context); CAMERA_UNUSED (camera, context); return GP_OK; } if (!camera->functions->get_config) { gp_context_error (context, _("This camera does not provide any configuration options.")); CAMERA_UNUSED (camera, context); return GP_ERROR_NOT_SUPPORTED; } /* emulate it ... */ CHECK_OPEN (camera, context); ret = camera->functions->get_config ( camera, &rootwidget, context); if (ret != GP_OK) { CHECK_CLOSE (camera, context); CAMERA_UNUSED (camera, context); return ret; } ret = gp_widget_get_child_by_name (rootwidget, name, &child); if (ret != GP_OK) { gp_widget_free (rootwidget); CHECK_CLOSE (camera, context); CAMERA_UNUSED (camera, context); return ret; } /* We need to duplicate the widget, as we will free the widgettree */ gp_widget_get_type (child, &type); gp_widget_get_label (child, &label); gp_widget_get_readonly (child, &ro); ret = gp_widget_new (type, label, widget); if (ret != GP_OK) goto out; gp_widget_set_name (*widget, name); gp_widget_set_readonly (*widget, ro); switch (type) { case GP_WIDGET_MENU: case GP_WIDGET_RADIO: { char *value; int i, nrofchoices; nrofchoices = gp_widget_count_choices (child); for (i = 0; i < nrofchoices; i++) { const char *choice; gp_widget_get_choice (child, i, &choice); gp_widget_add_choice (*widget, choice); } gp_widget_get_value (child, &value); gp_widget_set_value (*widget, value); break; } case GP_WIDGET_TEXT: { char *value; gp_widget_get_value (child, &value); gp_widget_set_value (*widget, value); break; } case GP_WIDGET_RANGE: { float value, rmin, rmax, rstep; gp_widget_get_range (child, &rmin, &rmax, &rstep); gp_widget_set_range (*widget, rmin, rmax, rstep); gp_widget_get_value (child, &value); gp_widget_set_value (*widget, &value); break; } case GP_WIDGET_TOGGLE: case GP_WIDGET_DATE: { int value; gp_widget_get_value (child, &value); gp_widget_set_value (*widget, &value); break; } case GP_WIDGET_BUTTON: case GP_WIDGET_SECTION: case GP_WIDGET_WINDOW: default: ret = GP_ERROR_BAD_PARAMETERS; break; } out: gp_widget_free (rootwidget); CHECK_CLOSE (camera, context); CAMERA_UNUSED (camera, context); return ret; } /** * Retrieve a configuration \c list for the \c camera. * * @param camera a #Camera * @param list a #CameraList * @param context a #GPContext * @return gphoto2 error code * * The names in list can be used for the single set and get configuration calls. * */ static void _get_widget_names (CameraWidget *widget, CameraList *list) { CameraWidgetType type; gp_widget_get_type (widget, &type); switch (type) { case GP_WIDGET_MENU: case GP_WIDGET_RADIO: case GP_WIDGET_TEXT: case GP_WIDGET_RANGE: case GP_WIDGET_TOGGLE: case GP_WIDGET_DATE: { const char *name; gp_widget_get_name (widget, &name); gp_list_append (list, name, NULL); break; } case GP_WIDGET_SECTION: case GP_WIDGET_WINDOW: { int i, nrofchildren; nrofchildren = gp_widget_count_children (widget); for (i = 0; i < nrofchildren; i++) { CameraWidget *child; gp_widget_get_child (widget, i, &child); _get_widget_names (child, list); } break; } case GP_WIDGET_BUTTON: default: break; } } int gp_camera_list_config (Camera *camera, CameraList *list, GPContext *context) { CameraWidget *rootwidget; int ret; C_PARAMS (camera); CHECK_INIT (camera, context); if (camera->functions->list_config) { CHECK_RESULT_OPEN_CLOSE (camera, camera->functions->list_config ( camera, list, context), context); CAMERA_UNUSED (camera, context); return GP_OK; } if (!camera->functions->get_config) { gp_context_error (context, _("This camera does not provide any configuration options.")); CAMERA_UNUSED (camera, context); return GP_ERROR_NOT_SUPPORTED; } /* emulate it ... */ CHECK_OPEN (camera, context); ret = camera->functions->get_config ( camera, &rootwidget, context); if (ret != GP_OK) { CHECK_CLOSE (camera, context); CAMERA_UNUSED (camera, context); return ret; } _get_widget_names (rootwidget, list); gp_widget_free (rootwidget); CHECK_CLOSE (camera, context); CAMERA_UNUSED (camera, context); return ret; } /** * Sets the configuration. * * @param camera a #Camera * @param window a #CameraWidget * @param context a #GPContext * @return a gphoto2 error code * * Typically, a \c window is retrieved using #gp_camera_get_config and passed * to this function in order to adjust the settings on the camera. * **/ int gp_camera_set_config (Camera *camera, CameraWidget *window, GPContext *context) { C_PARAMS (camera && window); CHECK_INIT (camera, context); if (!camera->functions->set_config) { gp_context_error (context, _("This camera does " "not support setting configuration options.")); CAMERA_UNUSED (camera, context); return (GP_ERROR_NOT_SUPPORTED); } CHECK_RESULT_OPEN_CLOSE (camera, camera->functions->set_config (camera, window, context), context); CAMERA_UNUSED (camera, context); return (GP_OK); } /** * Set a single configuration \c widget for the \c camera. * * @param camera a #Camera * @param name the name of a configuration widget * @param widget a #CameraWidget * @param context a #GPContext * @return gphoto2 error code * * This \c widget contains the new value of the widget to set. * */ int gp_camera_set_single_config (Camera *camera, const char *name, CameraWidget *widget, GPContext *context) { CameraWidget *rootwidget, *child; CameraWidgetType type; int ret; C_PARAMS (camera); CHECK_INIT (camera, context); if (camera->functions->set_single_config) { CHECK_RESULT_OPEN_CLOSE (camera, camera->functions->set_single_config ( camera, name, widget, context), context); CAMERA_UNUSED (camera, context); return GP_OK; } if (!camera->functions->set_config) { gp_context_error (context, _("This camera does not provide any configuration options.")); CAMERA_UNUSED (camera, context); return GP_ERROR_NOT_SUPPORTED; } /* emulate single config with the full tree */ CHECK_OPEN (camera, context); ret = camera->functions->get_config ( camera, &rootwidget, context); if (ret != GP_OK) { CHECK_CLOSE (camera, context); CAMERA_UNUSED (camera, context); return ret; } ret = gp_widget_get_child_by_name (rootwidget, name, &child); if (ret != GP_OK) { gp_widget_free (rootwidget); CHECK_CLOSE (camera, context); CAMERA_UNUSED (camera, context); return ret; } gp_widget_get_type (child, &type); ret = GP_OK; switch (type) { case GP_WIDGET_MENU: case GP_WIDGET_RADIO: case GP_WIDGET_TEXT: { char *value; gp_widget_get_value (widget, &value); gp_widget_set_value (child, value); break; } case GP_WIDGET_RANGE: { float value; gp_widget_get_value (widget, &value); gp_widget_set_value (child, &value); break; } case GP_WIDGET_TOGGLE: case GP_WIDGET_DATE: { int value; gp_widget_get_value (widget, &value); gp_widget_set_value (child, &value); break; } case GP_WIDGET_BUTTON: case GP_WIDGET_SECTION: case GP_WIDGET_WINDOW: default: ret = GP_ERROR_BAD_PARAMETERS; break; } gp_widget_set_changed (child, 1); if (ret == GP_OK) ret = camera->functions->set_config (camera, rootwidget, context); gp_widget_free (rootwidget); CHECK_CLOSE (camera, context); CAMERA_UNUSED (camera, context); return ret; } /** * Retrieves a camera summary. * * @param camera a #Camera * @param summary a #CameraText * @param context a #GPContext * @return a gphoto2 error code * * This summary typically contains information like manufacturer, pictures * taken, or generally information that is not configurable. * **/ int gp_camera_get_summary (Camera *camera, CameraText *summary, GPContext *context) { C_PARAMS (camera && summary); CHECK_INIT (camera, context); if (!camera->functions->summary) { gp_context_error (context, _("This camera does " "not support summaries.")); CAMERA_UNUSED (camera, context); return (GP_ERROR_NOT_SUPPORTED); } CHECK_RESULT_OPEN_CLOSE (camera, camera->functions->summary (camera, summary, context), context); CAMERA_UNUSED (camera, context); return (GP_OK); } /** * Retrieves the \c manual for given \c camera. * * @param camera a #Camera * @param manual a #CameraText * @param context a #GPContext * @return a gphoto2 error code * * This manual typically contains information about using the camera. * **/ int gp_camera_get_manual (Camera *camera, CameraText *manual, GPContext *context) { C_PARAMS (camera && manual); CHECK_INIT (camera, context); if (!camera->functions->manual) { gp_context_error (context, _("This camera " "does not provide a manual.")); CAMERA_UNUSED (camera, context); return (GP_ERROR_NOT_SUPPORTED); } CHECK_RESULT_OPEN_CLOSE (camera, camera->functions->manual (camera, manual, context), context); CAMERA_UNUSED (camera, context); return (GP_OK); } /** * Retrieves information about the camera driver. * * @param camera a #Camera * @param about a #CameraText * @param context a #GPContext * @return a gphoto2 error code * * Typically, this information contains name and address of the author, * acknowledgements, etc. * **/ int gp_camera_get_about (Camera *camera, CameraText *about, GPContext *context) { C_PARAMS (camera && about); CHECK_INIT (camera, context); if (!camera->functions->about) { gp_context_error (context, _("This camera does " "not provide information about the driver.")); CAMERA_UNUSED (camera, context); return (GP_ERROR_NOT_SUPPORTED); } CHECK_RESULT_OPEN_CLOSE (camera, camera->functions->about (camera, about, context), context); CAMERA_UNUSED (camera, context); return (GP_OK); } /** * Captures an image, movie, or sound clip depending on the given \c type. * * @param camera a #Camera * @param type a #CameraCaptureType * @param path a #CameraFilePath * @param context a #GPContext * @return a gphoto2 error code * * The resulting file will be stored on the camera. The location gets stored * in \c path. The file can then be downloaded using #gp_camera_file_get. * **/ int gp_camera_capture (Camera *camera, CameraCaptureType type, CameraFilePath *path, GPContext *context) { C_PARAMS (camera); CHECK_INIT (camera, context); if (!camera->functions->capture) { gp_context_error (context, _("This camera can not capture.")); CAMERA_UNUSED (camera, context); return (GP_ERROR_NOT_SUPPORTED); } CHECK_RESULT_OPEN_CLOSE (camera, camera->functions->capture (camera, type, path, context), context); CAMERA_UNUSED (camera, context); return (GP_OK); } /** * Triggers capture of one or more images. * * @param camera a #Camera * @param context a #GPContext * @return a gphoto2 error code * * This functions just remotely causes the shutter release and returns * immediately. You will want to run #gp_camera_wait_event until a image * is added which can be downloaded using #gp_camera_file_get. **/ int gp_camera_trigger_capture (Camera *camera, GPContext *context) { C_PARAMS (camera); CHECK_INIT (camera, context); if (!camera->functions->trigger_capture) { gp_context_error (context, _("This camera can not trigger capture.")); CAMERA_UNUSED (camera, context); return (GP_ERROR_NOT_SUPPORTED); } CHECK_RESULT_OPEN_CLOSE (camera, camera->functions->trigger_capture (camera, context), context); CAMERA_UNUSED (camera, context); return (GP_OK); } /** * Captures a preview that won't be stored on the camera but returned in * supplied file. * * @param camera a #Camera * @param file a #CameraFile * @param context a #GPContext * @return a gphoto2 error code * * For example, you could use gp_capture_preview() for taking some sample * pictures before calling gp_capture(). * **/ int gp_camera_capture_preview (Camera *camera, CameraFile *file, GPContext *context) { char *xname; C_PARAMS (camera && file); CHECK_INIT (camera, context); CR (camera, gp_file_clean (file), context); if (!camera->functions->capture_preview) { gp_context_error (context, _("This camera can " "not capture previews.")); CAMERA_UNUSED (camera, context); return (GP_ERROR_NOT_SUPPORTED); } CHECK_RESULT_OPEN_CLOSE (camera, camera->functions->capture_preview ( camera, file, context), context); gp_file_get_name_by_type (file, "capture_preview", GP_FILE_TYPE_NORMAL, &xname); /* FIXME: Marcus ... will go away, just keep compatible now. */ gp_file_set_name (file, xname); free (xname); CAMERA_UNUSED (camera, context); return (GP_OK); } /** * Wait and retrieve an event from the camera. * * @param camera a Camera * @param timeout amount of time to wait in 1/1000 seconds * @param eventtype received CameraEventType [out] * @param eventdata received event specific data [out] * @param context a GPContext * @return gphoto2 error code * * This function blocks and waits for an event to come from the camera. If * a timeout occurs before an event is received then * eventtype will be GP_EVENT_TIMEOUT and eventdata is left unchanged. * * If an event is received then eventtype is set to the type of event, and * eventdata is set to event specific data. See the CameraEventType enum * to see which eventtypes match to which types of eventdata. * * Note that this function will return one event after each other, you need * to be able to call it multiple times, e.g. in a loop, when waiting for specific * events. */ int gp_camera_wait_for_event (Camera *camera, int timeout, CameraEventType *eventtype, void **eventdata, GPContext *context) { C_PARAMS (camera); CHECK_INIT (camera, context); if (!camera->functions->wait_for_event) { CAMERA_UNUSED (camera, context); return (GP_ERROR_NOT_SUPPORTED); } CHECK_RESULT_OPEN_CLOSE (camera, camera->functions->wait_for_event ( camera, timeout, eventtype, eventdata, context), context); CAMERA_UNUSED (camera, context); return (GP_OK); } /** * Lists the files in supplied \c folder. * * @param camera a #Camera * @param folder a folder * @param list a #CameraList * @param context a #GPContext * @return a gphoto2 error code * **/ int gp_camera_folder_list_files (Camera *camera, const char *folder, CameraList *list, GPContext *context) { GP_LOG_D ("Listing files in '%s'...", folder); C_PARAMS (camera && folder && list); CHECK_INIT (camera, context); CR (camera, gp_list_reset (list), context); CHECK_RESULT_OPEN_CLOSE (camera, gp_filesystem_list_files (camera->fs, folder, list, context), context); CR (camera, gp_list_sort (list), context); CAMERA_UNUSED (camera, context); return (GP_OK); } /** * Lists the folders in supplied \c folder. * * @param camera a #Camera * @param folder a folder * @param list a #CameraList * @param context a #GPContext * @return a gphoto2 error code * **/ int gp_camera_folder_list_folders (Camera *camera, const char* folder, CameraList *list, GPContext *context) { GP_LOG_D ("Listing folders in '%s'...", folder); C_PARAMS (camera && folder && list); CHECK_INIT (camera, context); CR (camera, gp_list_reset (list), context); CHECK_RESULT_OPEN_CLOSE (camera, gp_filesystem_list_folders ( camera->fs, folder, list, context), context); CR (camera, gp_list_sort (list), context); CAMERA_UNUSED (camera, context); return (GP_OK); } /** * Deletes all files in a given \c folder. * * @param camera a #Camera * @param folder a folder * @param context a #GPContext * @return a gphoto2 error code * **/ int gp_camera_folder_delete_all (Camera *camera, const char *folder, GPContext *context) { GP_LOG_D ("Deleting all files in '%s'...", folder); C_PARAMS (camera && folder); CHECK_INIT (camera, context); CHECK_RESULT_OPEN_CLOSE (camera, gp_filesystem_delete_all (camera->fs, folder, context), context); CAMERA_UNUSED (camera, context); return (GP_OK); } /** * Uploads a file into given \c folder. * * @param camera a #Camera * @param folder a folder * @param file a #CameraFile * @param context a #GPContext * @return a gphoto2 error code * **/ int gp_camera_folder_put_file (Camera *camera, const char *folder, const char *filename, CameraFileType type, CameraFile *file, GPContext *context) { GP_LOG_D ("Uploading file into '%s'...", folder); C_PARAMS (camera && folder && file); CHECK_INIT (camera, context); CHECK_RESULT_OPEN_CLOSE (camera, gp_filesystem_put_file (camera->fs, folder, filename, type, file, context), context); CAMERA_UNUSED (camera, context); return (GP_OK); } /** * Retrieves information about a file. * * @param camera a #Camera * @param folder a folder * @param file the name of the file * @param info * @param context a #GPContext * @return a gphoto2 error code * **/ int gp_camera_file_get_info (Camera *camera, const char *folder, const char *file, CameraFileInfo *info, GPContext *context) { int result = GP_OK; const char *mime_type; const char *data; /* long int size; */ CameraFile *cfile; GP_LOG_D ("Getting file info for '%s' in '%s'...", file, folder); C_PARAMS (camera && folder && file && info); CHECK_INIT (camera, context); memset (info, 0, sizeof (CameraFileInfo)); /* Check first if the camera driver supports the filesystem */ CHECK_OPEN (camera, context); result = gp_filesystem_get_info (camera->fs, folder, file, info, context); CHECK_CLOSE (camera, context); if (result != GP_ERROR_NOT_SUPPORTED) { CAMERA_UNUSED (camera, context); return (result); } /* * The CameraFilesystem doesn't support file info. We simply get * the preview and the file and look for ourselves... */ /* It takes too long to get the file */ info->file.fields = GP_FILE_INFO_NONE; /* Get the preview */ info->preview.fields = GP_FILE_INFO_NONE; CRS (camera, gp_file_new (&cfile), context); if (gp_camera_file_get (camera, folder, file, GP_FILE_TYPE_PREVIEW, cfile, context) == GP_OK) { unsigned long size; info->preview.fields |= GP_FILE_INFO_SIZE | GP_FILE_INFO_TYPE; gp_file_get_data_and_size (cfile, &data, &size); info->preview.size = size; gp_file_get_mime_type (cfile, &mime_type); strncpy (info->preview.type, mime_type, sizeof (info->preview.type)); } gp_file_unref (cfile); CAMERA_UNUSED (camera, context); return (GP_OK); } /** * Sets some file properties like name or permissions. * * @param camera a #Camera * @param folder a folder * @param file the name of a file * @param info the #CameraFileInfo * @param context a #GPContext * @return a gphoto2 error code * **/ int gp_camera_file_set_info (Camera *camera, const char *folder, const char *file, CameraFileInfo info, GPContext *context) { C_PARAMS (camera && folder && file); CHECK_INIT (camera, context); CHECK_RESULT_OPEN_CLOSE (camera, gp_filesystem_set_info (camera->fs, folder, file, info, context), context); CAMERA_UNUSED (camera, context); return (GP_OK); } /** * Retrieves a file from the #Camera. * * @param camera a #Camera * @param folder a folder * @param file the name of a file * @param type the #CameraFileType * @param camera_file a #CameraFile * @param context a #GPContext * @return a gphoto2 error code * **/ int gp_camera_file_get (Camera *camera, const char *folder, const char *file, CameraFileType type, CameraFile *camera_file, GPContext *context) { GP_LOG_D ("Getting file '%s' in folder '%s'...", file, folder); C_PARAMS (camera && folder && file && camera_file); CHECK_INIT (camera, context); CR (camera, gp_file_clean (camera_file), context); /* Did we get reasonable foldername/filename? */ if (strlen (folder) == 0) { CAMERA_UNUSED (camera, context); return (GP_ERROR_DIRECTORY_NOT_FOUND); } if (strlen (file) == 0) { CAMERA_UNUSED (camera, context); return (GP_ERROR_FILE_NOT_FOUND); } CHECK_RESULT_OPEN_CLOSE (camera, gp_filesystem_get_file (camera->fs, folder, file, type, camera_file, context), context); CAMERA_UNUSED (camera, context); return (GP_OK); } /** * Reads a file partially from the #Camera. * * @param camera a #Camera * @param folder a folder * @param file the name of a file * @param type the #CameraFileType * @param offset the offset into the camera file * @param data the buffer receiving the data * @param size the size to be read and that was read. (Note: size should not exceed 32 bits) * @param context a #GPContext * @return a gphoto2 error code * **/ int gp_camera_file_read (Camera *camera, const char *folder, const char *file, CameraFileType type, uint64_t offset, char *buf, uint64_t *size, GPContext *context) { GP_LOG_D ("Getting file '%s' in folder '%s'...", file, folder); C_PARAMS (camera && folder && file && buf && size); CHECK_INIT (camera, context); /* Did we get reasonable foldername/filename? */ if (strlen (folder) == 0) { CAMERA_UNUSED (camera, context); return (GP_ERROR_DIRECTORY_NOT_FOUND); } if (strlen (file) == 0) { CAMERA_UNUSED (camera, context); return (GP_ERROR_FILE_NOT_FOUND); } CHECK_RESULT_OPEN_CLOSE (camera, gp_filesystem_read_file (camera->fs, folder, file, type, offset, buf, size, context), context); CAMERA_UNUSED (camera, context); return (GP_OK); } /** * Deletes the file from \c folder. * * \param camera a #Camera * \param folder a folder * \param file the name of a file * \param context a #GPContext * \return a gphoto2 error code * **/ int gp_camera_file_delete (Camera *camera, const char *folder, const char *file, GPContext *context) { GP_LOG_D ("Deleting file '%s' in folder '%s'...", file, folder); C_PARAMS (camera && folder && file); CHECK_INIT (camera, context); CHECK_RESULT_OPEN_CLOSE (camera, gp_filesystem_delete_file ( camera->fs, folder, file, context), context); CAMERA_UNUSED (camera, context); return (GP_OK); } /** * Creates a new directory called \c name in the given \c folder. * * @param camera a #Camera * @param folder the location where to create the new directory * @param name the name of the directory to be created * @param context a #GPContext * @return a gphoto2 error code * **/ int gp_camera_folder_make_dir (Camera *camera, const char *folder, const char *name, GPContext *context) { C_PARAMS (camera && folder && name); CHECK_INIT (camera, context); CHECK_RESULT_OPEN_CLOSE (camera, gp_filesystem_make_dir (camera->fs, folder, name, context), context); CAMERA_UNUSED (camera, context); return (GP_OK); } /** * Removes an (empty) directory called \c name from the given \c folder. * * @param camera a #Camera * @param folder the folder from which to remove the directory * @param name the name of the directory to be removed * @param context a #GPContext * @return a gphoto2 error code * */ int gp_camera_folder_remove_dir (Camera *camera, const char *folder, const char *name, GPContext *context) { C_PARAMS (camera && folder && name); CHECK_INIT (camera, context); CHECK_RESULT_OPEN_CLOSE (camera, gp_filesystem_remove_dir (camera->fs, folder, name, context), context); CAMERA_UNUSED (camera, context); return (GP_OK); } /** * \brief Gets information on the camera attached storage. * * \param camera a #Camera * \param sifs Pointer to receive a pointer to/array of storage info items * \param nrofsifs Pointer to receive number of array entries * \param context a #GPContext * \return a gphoto2 error code * * Retrieves the storage information, like maximum and free space, for * the specified filesystem, if supported by the device. The storage * information is returned in an newly allocated array of * #CameraStorageInformation objects, to which the pointer pointed to * by #sifs will be set. * * The variable pointed to by #nrofsifs will be set to the number of * elements in that array. * * It is the caller's responsibility to free the memory of the array. * **/ int gp_camera_get_storageinfo ( Camera *camera, CameraStorageInformation **sifs, int *nrofsifs, GPContext *context) { C_PARAMS (camera && sifs && nrofsifs); CHECK_INIT (camera, context); CHECK_RESULT_OPEN_CLOSE (camera, gp_filesystem_get_storageinfo ( camera->fs, sifs, nrofsifs, context ), context ); CAMERA_UNUSED (camera, context); return (GP_OK); } /** * @param camera a Camera * @param start_func * @param stop_func * @param data * @return a gphoto2 error code * * If your frontend has something like idle loops, it is recommended you * use #gp_camera_set_timeout_funcs in order to give the camera driver the * possibility to keep up the connection to the camera. * */ void gp_camera_set_timeout_funcs (Camera *camera, CameraTimeoutStartFunc start_func, CameraTimeoutStopFunc stop_func, void *data) { if (!camera || !camera->pc) return; camera->pc->timeout_start_func = start_func; camera->pc->timeout_stop_func = stop_func; camera->pc->timeout_data = data; } /** * @param camera a #Camera * @param timeout number of seconds that should pass between each call to * \c func * @param func the function that should be called each \c timeout seconds * @return The id of the background process or a gphoto2 error code * * This function should be called by the camera driver during camera_init() * if the camera needs to be sent messages periodically in order to prevent * it from shutting down. * */ int gp_camera_start_timeout (Camera *camera, unsigned int timeout, CameraTimeoutFunc func) { int id; C_PARAMS (camera && camera->pc); if (!camera->pc->timeout_start_func) return (GP_ERROR_NOT_SUPPORTED); /* * We remember the id here in order to automatically remove * the timeout on gp_camera_exit. */ C_MEM (camera->pc->timeout_ids = realloc (camera->pc->timeout_ids, sizeof (int) * (camera->pc->timeout_ids_len + 1))); id = camera->pc->timeout_start_func (camera, timeout, func, camera->pc->timeout_data); if (id < 0) return (id); camera->pc->timeout_ids[camera->pc->timeout_ids_len] = id; camera->pc->timeout_ids_len++; return (id); } /** * Stop periodic calls to keepalive function. * * @param camera a #Camera * @param id the id of the background process previously returned by * #gp_camera_start_timeout * * Call this function in the camera driver if you want to stop a periodic * call to a function that has been started using #gp_camera_start_timeout. * */ void gp_camera_stop_timeout (Camera *camera, unsigned int id) { unsigned int i; if (!camera || !camera->pc) return; if (!camera->pc->timeout_stop_func) return; /* Check if we know this id. If yes, remove it. */ for (i = 0; i < camera->pc->timeout_ids_len; i++) if (camera->pc->timeout_ids[i] == id) break; if (i == camera->pc->timeout_ids_len) return; memmove (camera->pc->timeout_ids + i, camera->pc->timeout_ids + i + 1, sizeof (int) * (camera->pc->timeout_ids_len - i - 1)); camera->pc->timeout_ids_len--; camera->pc->timeout_ids = realloc (camera->pc->timeout_ids, sizeof (int) * camera->pc->timeout_ids_len); camera->pc->timeout_stop_func (camera, id, camera->pc->timeout_data); }