Remove generated files
[framework/connectivity/libgphoto2.git] / camlibs / st2205 / library.c
1 /* Sitronix st2205 picframe access library
2  *
3  * Copyright (c) 2010 Hans de Goede <hdegoede@redhat.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation; either version 2.1 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19 #include "config.h"
20
21 #include <string.h>
22 #include <stdlib.h>
23 #ifdef HAVE_ICONV
24 # include <langinfo.h>
25 #endif
26 #ifdef HAVE_GD
27 # include <gd.h>
28 #endif
29
30 #include <gphoto2/gphoto2-library.h>
31 #include <gphoto2/gphoto2-result.h>
32 #include <gphoto2/gphoto2-port.h>
33 #include <gphoto2/gphoto2-setting.h>
34 #include "st2205.h"
35
36 #ifdef ENABLE_NLS
37 #  include <libintl.h>
38 #  undef _
39 #  define _(String) dgettext (GETTEXT_PACKAGE, String)
40 #  ifdef gettext_noop
41 #    define N_(String) gettext_noop (String)
42 #  else
43 #    define N_(String) (String)
44 #  endif
45 #else
46 #  define _(String) (String)
47 #  define N_(String) (String)
48 #endif
49
50 int
51 camera_id (CameraText *id)
52 {
53         strcpy (id->text, "ST2205 USB picture frame");
54
55         return GP_OK;
56 }
57
58 int
59 camera_abilities (CameraAbilitiesList *list)
60 {
61         CameraAbilities a;
62
63         memset (&a, 0, sizeof(a));
64         strcpy (a.model, "ST2205 USB picture frame");
65         a.status = GP_DRIVER_STATUS_TESTING;
66         a.port   = GP_PORT_USB_DISK_DIRECT;
67         a.speed[0] = 0;
68         a.usb_vendor = 0x1403;
69         a.usb_product= 0x0001;
70         a.operations = GP_OPERATION_NONE;
71         a.folder_operations = GP_FOLDER_OPERATION_PUT_FILE | 
72                               GP_FOLDER_OPERATION_DELETE_ALL;
73         a.file_operations   = GP_FILE_OPERATION_DELETE | GP_FILE_OPERATION_RAW;
74         return gp_abilities_list_append (list, a);
75 }
76
77 static int
78 camera_summary (Camera *camera, CameraText *summary, GPContext *context)
79 {
80         sprintf (summary->text,
81                  _("Your USB picture frame has a ST2205 chipset\n"));
82         return GP_OK;
83 }
84
85 static int
86 camera_manual (Camera *camera, CameraText *manual, GPContext *context)
87 {
88         strcpy(manual->text,
89         _(
90         "ST2205 based picture frames come with a variety of resolutions.\n"
91         "The gphoto driver for these devices allows you to download,\n"
92         "upload and delete pictures from the picture frame."
93         ));
94
95         return GP_OK;
96 }
97
98 static int
99 camera_about (Camera *camera, CameraText *about, GPContext *context)
100 {
101         strcpy (about->text,
102         _(
103         "ST2205 USB picture frame driver\n"
104         "Hans de Goede <hdegoede@redhat.com>\n"
105         "This driver allows you to download, upload and delete pictures\n"
106         "from the picture frame."
107         ));
108
109         return GP_OK;
110 }
111
112 static int get_file_idx(CameraPrivateLibrary *pl, const char *folder,
113         const char *filename)
114 {
115         int i;
116
117         if (strcmp (folder, "/"))
118                 return GP_ERROR_DIRECTORY_NOT_FOUND;
119
120         for (i = 0; i < ST2205_MAX_NO_FILES; i++) {
121                 if (!strcmp (filename, pl->filenames[i]))
122                         break;
123         }
124
125         if (i == ST2205_MAX_NO_FILES)
126                 return GP_ERROR_FILE_NOT_FOUND;
127
128         return i;
129 }
130
131 #ifdef HAVE_GD
132 static void
133 rotate90 (gdImagePtr src, gdImagePtr dest)
134 {
135         int x, y;
136
137         for (y = 0; y < dest->sy; y++)
138                 for (x = 0; x < dest->sx; x++)
139                         dest->tpixels[y][x] =
140                                 src->tpixels[src->sy - x - 1][y];
141 }
142
143 static void
144 rotate270 (gdImagePtr src, gdImagePtr dest)
145 {
146         int x, y;
147
148         for (y = 0; y < dest->sy; y++)
149                 for (x = 0; x < dest->sx; x++)
150                         dest->tpixels[y][x] =
151                                 src->tpixels[x][src->sx - y - 1];
152 }
153
154 static int
155 needs_rotation (Camera *camera)
156 {
157         int display_orientation, user_orientation = camera->pl->orientation;
158
159         if (camera->pl->width > camera->pl->height)
160                 display_orientation = ORIENTATION_LANDSCAPE;
161         else
162                 display_orientation = ORIENTATION_PORTRAIT;
163
164         if (user_orientation == ORIENTATION_AUTO) {
165                 if (camera->pl->width == 240 && camera->pl->height == 320)
166                         user_orientation = ORIENTATION_LANDSCAPE;
167                 else
168                         user_orientation = display_orientation;
169         }
170
171         return display_orientation != user_orientation;
172 }
173 #endif
174
175 static int
176 get_file_func (CameraFilesystem *fs, const char *folder, const char *filename,
177                CameraFileType type, CameraFile *file, void *data,
178                GPContext *context)
179 {
180         Camera *camera = data;
181         int idx, size;
182 #ifdef HAVE_GD
183         int ret;
184         gdImagePtr im, rotated;
185         void *gdpng;
186 #endif
187
188         idx = get_file_idx(camera->pl, folder, filename);
189         if (idx < 0)
190                 return idx;
191
192         if (type == GP_FILE_TYPE_RAW) {
193                 unsigned char *raw;
194
195                 size = st2205_read_raw_file (camera, idx, &raw);
196                 if (size < 0) return size;
197
198                 gp_file_set_mime_type (file, GP_MIME_RAW);
199                 gp_file_set_name (file, filename);
200                 gp_file_set_data_and_size (file, (char *)raw, size);
201
202                 return GP_OK;
203         }
204
205 #ifdef HAVE_GD
206         if (type != GP_FILE_TYPE_NORMAL)
207                 return GP_ERROR_NOT_SUPPORTED;
208
209         im = gdImageCreateTrueColor(camera->pl->width, camera->pl->height);
210         if (im == NULL)
211                 return GP_ERROR_NO_MEMORY;
212
213         ret = st2205_read_file(camera, idx, im->tpixels);
214         if (ret < 0) {
215                 gdImageDestroy (im);
216                 return ret;
217         }
218
219         if (needs_rotation (camera)) {
220                 rotated = gdImageCreateTrueColor (im->sy, im->sx);
221                 if (rotated == NULL) {
222                         gdImageDestroy (im);
223                         return GP_ERROR_NO_MEMORY;
224                 }
225                 rotate270 (im, rotated);
226                 gdImageDestroy (im);
227                 im = rotated;
228         }
229
230         gdpng = gdImagePngPtr(im, &size);
231         gdImageDestroy (im);
232         if (gdpng == NULL)
233                 return GP_ERROR_NO_MEMORY;
234
235         ret = gp_file_set_mime_type (file, GP_MIME_PNG);
236         if (ret < 0) { gdFree (gdpng); return ret; }
237
238         ret = gp_file_set_name (file, filename); 
239         if (ret < 0) { gdFree (gdpng); return ret; }
240
241         ret = gp_file_append (file, gdpng, size);
242         gdFree (gdpng);
243         return ret;
244 #else
245         gp_log(GP_LOG_ERROR,"st2205", "GD decompression not supported - no libGD present during build");
246         return GP_ERROR_NOT_SUPPORTED;
247 #endif
248 }
249
250 static int
251 put_file_func (CameraFilesystem *fs, const char *folder, CameraFile *file,
252                void *data, GPContext *context)
253 {
254 #ifdef HAVE_GD
255         Camera *camera = data;
256         char *c, *in_name, *out_name, *filedata = NULL;
257         const char *name;
258         int ret, in_width, in_height, in_x, in_y;
259         size_t inc, outc;
260         double aspect_in, aspect_out;
261 #ifdef HAVE_ICONV
262         char *in, *out;
263 #endif
264         unsigned long filesize = 0;
265         gdImagePtr rotated, im_out, im_in = NULL;
266
267         if (strcmp (folder, "/"))
268                 return GP_ERROR_DIRECTORY_NOT_FOUND;
269
270         gp_file_get_name (file, &name);
271
272         inc = strlen (name);
273         in_name = strdup (name);
274         outc = inc;
275         out_name = malloc (outc + 1);
276         if (!in_name || !out_name) {
277                 free (in_name);
278                 free (out_name);
279                 return GP_ERROR_NO_MEMORY;
280         }
281
282         /* Convert name to ASCII */
283 #ifdef HAVE_ICONV
284         in = in_name;
285         out = out_name;
286         if (iconv (camera->pl->cd, &in, &inc, &out, &outc) == -1) {
287                 free (in_name);
288                 free (out_name);
289                 gp_log (GP_LOG_ERROR, "iconv",
290                         "Failed to convert filename to ASCII");
291                 return GP_ERROR_OS_FAILURE;
292         }
293         outc = out - out_name;
294         out_name[outc] = 0;
295 #else
296         for (i = 0; i < inc; i++) {
297                 if ((uint8_t)in_name[i] < 0x20 || (uint8_t)in_name[i] > 0x7E)
298                         out_name[i] = '?';
299                 else
300                         out_name[i] = in_name[i];
301         }
302         out_name[i] = 0;
303 #endif
304         free (in_name);
305
306         /* Remove file extension, and if necessary truncate the name */
307         c = strrchr (out_name, '.');
308         if (c)
309                 *c = 0;
310         if (outc > ST2205_FILENAME_LENGTH)
311                 out_name[ST2205_FILENAME_LENGTH] = 0;
312
313         ret = gp_file_get_data_and_size (file, (const char **)&filedata,
314                                          &filesize);
315         if (ret < 0) { free (out_name); return ret; }
316
317         /* Try loading the file using gd, starting with the most often
318            used types first */
319
320         /* gdImageCreateFromJpegPtr is chatty on error, don't call it on
321            non JPEG files */
322         if (filesize > 2 &&
323             (uint8_t)filedata[0] == 0xff && (uint8_t)filedata[1] == 0xd8)
324                 im_in = gdImageCreateFromJpegPtr(filesize, filedata);
325         if (im_in == NULL)
326                 im_in = gdImageCreateFromPngPtr(filesize, filedata);
327         if (im_in == NULL)
328                 im_in = gdImageCreateFromGifPtr(filesize, filedata);
329         if (im_in == NULL)
330                 im_in = gdImageCreateFromWBMPPtr(filesize, filedata);
331         if (im_in == NULL) {
332                 gp_log (GP_LOG_ERROR, "st2205",
333                         "Unrecognized file format for file: %s%s",
334                         folder, name);
335                 free (out_name);
336                 return GP_ERROR_BAD_PARAMETERS;
337         }
338
339         if (needs_rotation (camera)) {
340                 rotated = gdImageCreateTrueColor (im_in->sy, im_in->sx);
341                 if (rotated == NULL) {
342                         gdImageDestroy (im_in);
343                         free (out_name);
344                         return GP_ERROR_NO_MEMORY;
345                 }
346                 rotate90 (im_in, rotated);
347                 gdImageDestroy (im_in);
348                 im_in = rotated;
349         }
350
351         im_out = gdImageCreateTrueColor(camera->pl->width, camera->pl->height);
352         if (im_out == NULL) {
353                 gdImageDestroy (im_in);
354                 free (out_name);
355                 return GP_ERROR_NO_MEMORY;
356         }
357
358         /* Keep aspect */
359         aspect_in  = (double)im_in->sx / im_in->sy;
360         aspect_out = (double)im_out->sx / im_out->sy;
361         if (aspect_in > aspect_out) {
362                 /* Reduce in width (crop left and right) */
363                 in_width = (im_in->sx / aspect_in) * aspect_out;
364                 in_x = (im_in->sx - in_width) / 2;
365                 in_height = im_in->sy;
366                 in_y = 0;
367         } else {
368                 /* Reduce in height (crop top and bottom) */
369                 in_width = im_in->sx;
370                 in_x = 0;
371                 in_height = (im_in->sy * aspect_in) / aspect_out;
372                 in_y = (im_in->sy - in_height) / 2;
373         }
374
375         gdImageCopyResampled (im_out, im_in, 0, 0, in_x, in_y,
376                               im_out->sx, im_out->sy,
377                               in_width, in_height);
378
379         if (im_in->sx != im_out->sx ||
380             im_in->sy != im_out->sy)
381                 gdImageSharpen(im_out, 100);
382
383         ret = st2205_write_file (camera, out_name, im_out->tpixels);
384         if (ret >= 0) {
385                 /* Add to our filenames list */
386                 ST2205_SET_FILENAME(camera->pl->filenames[ret], out_name, ret);
387                 /* And commit the changes to the device */
388                 ret = st2205_commit(camera);
389         }
390
391         gdImageDestroy (im_in);
392         gdImageDestroy (im_out);
393         free (out_name);
394         return ret;
395 #else
396         gp_log(GP_LOG_ERROR,"st2205", "GD compression not supported - no libGD present during build");
397         return GP_ERROR_NOT_SUPPORTED;
398 #endif
399 }
400
401 static int
402 delete_file_func (CameraFilesystem *fs, const char *folder,
403                   const char *filename, void *data, GPContext *context)
404 {
405         Camera *camera = data;
406         int ret, idx;
407
408         idx = get_file_idx(camera->pl, folder, filename);
409         if (idx < 0)
410                 return idx;
411
412         ret = st2205_delete_file(camera, idx);
413         if (ret < 0) return ret;
414
415         /* Also remove the file from our cached filelist */
416         camera->pl->filenames[idx][0] = 0;
417
418         return st2205_commit(camera);
419 }
420
421 static int
422 delete_all_func (CameraFilesystem *fs, const char *folder, void *data,
423                  GPContext *context)
424 {
425         Camera *camera = data;
426
427         CHECK (st2205_delete_all (camera))
428
429         return st2205_commit (camera);
430 }
431
432 static int
433 get_info_func (CameraFilesystem *fs, const char *folder, const char *filename,
434                CameraFileInfo *info, void *data, GPContext *context)
435 {
436         memset (info, 0, sizeof(CameraFileInfo));
437         /* FIXME: fill in some stuff? */
438         return GP_OK;
439 }
440
441 static int
442 file_list_func (CameraFilesystem *fs, const char *folder, CameraList *list,
443                 void *data, GPContext *context)
444 {
445         Camera *camera = data; 
446         int i, ret;
447
448         for (i = 0; i < ST2205_MAX_NO_FILES; i++) {
449                 if (camera->pl->filenames[i][0]) {
450                         ret = gp_list_append (list, camera->pl->filenames[i],
451                                               NULL);
452                         if (ret < 0) return ret;
453                 }
454         }
455
456         return GP_OK;
457 }
458
459 static int
460 storage_info_func (CameraFilesystem *fs,
461                 CameraStorageInformation **sinfos,
462                 int *nrofsinfos,
463                 void *data, GPContext *context)
464 {
465         Camera *camera = (Camera*)data;
466         CameraStorageInformation *sinfo;
467         int free;
468
469         free = st2205_get_free_mem_size (camera);
470         if (free < 0) return free;
471
472         sinfo = malloc(sizeof(CameraStorageInformation));
473         if (!sinfo) return GP_ERROR_NO_MEMORY;
474
475         *sinfos = sinfo;
476         *nrofsinfos = 1;
477
478         sinfo->fields  = GP_STORAGEINFO_BASE;
479         strcpy(sinfo->basedir, "/");
480
481         sinfo->fields |= GP_STORAGEINFO_ACCESS;
482         sinfo->access  = GP_STORAGEINFO_AC_READWRITE;
483         sinfo->fields |= GP_STORAGEINFO_STORAGETYPE;
484         sinfo->type    = GP_STORAGEINFO_ST_FIXED_RAM;
485         sinfo->fields |= GP_STORAGEINFO_FILESYSTEMTYPE;
486         sinfo->fstype  = GP_STORAGEINFO_FST_GENERICFLAT;
487         sinfo->fields |= GP_STORAGEINFO_MAXCAPACITY;
488         sinfo->capacitykbytes = st2205_get_mem_size (camera) / 1024;
489         sinfo->fields |= GP_STORAGEINFO_FREESPACEKBYTES;
490         sinfo->freekbytes = free / 1024;
491
492         return GP_OK;
493 }
494
495 static CameraFilesystemFuncs fsfuncs = {
496         .file_list_func = file_list_func,
497         .get_info_func = get_info_func,
498         .get_file_func = get_file_func,
499         .del_file_func = delete_file_func,
500         .put_file_func = put_file_func,
501         .delete_all_func = delete_all_func,
502         .storage_info_func = storage_info_func
503 };
504
505 static char *
506 orientation_to_string (int orientation)
507 {
508         switch (orientation) {
509         case ORIENTATION_AUTO:
510                 return  _("Auto");
511         case ORIENTATION_LANDSCAPE:
512                 return  _("Landscape");
513         case ORIENTATION_PORTRAIT:
514                 return  _("Portrait");
515         }
516         /* Never reached */
517         return NULL;
518 }
519
520 static int
521 string_to_orientation (const char *str)
522 {
523         if (strcmp (str, _("Auto")) == 0)
524                 return ORIENTATION_AUTO;
525         else if (strcmp (str, _("Landscape")) == 0)
526                 return ORIENTATION_LANDSCAPE;
527         else if (strcmp (str, _("Portrait")) == 0)
528                 return ORIENTATION_PORTRAIT;
529         else
530                 return GP_ERROR_NOT_SUPPORTED;
531 }
532
533 static int
534 camera_get_config (Camera *camera, CameraWidget **window, GPContext *context)
535 {
536         CameraWidget *child;
537
538         GP_DEBUG ("*** camera_get_config");
539
540         gp_widget_new (GP_WIDGET_WINDOW,
541                         _("Picture Frame Configuration"), window);
542
543         gp_widget_new (GP_WIDGET_TOGGLE,
544                         _("Synchronize frame data and time with PC"), &child);
545         gp_widget_set_value (child, &camera->pl->syncdatetime);
546         gp_widget_append (*window, child);
547
548         gp_widget_new (GP_WIDGET_RADIO, _("Orientation"), &child);
549         gp_widget_add_choice (child, orientation_to_string (0));
550         gp_widget_add_choice (child, orientation_to_string (1));
551         gp_widget_add_choice (child, orientation_to_string (2));
552         gp_widget_set_value (child,
553                              orientation_to_string (camera->pl->orientation));
554         gp_widget_append (*window, child);
555
556         return GP_OK;
557 }
558
559 static int
560 camera_set_config (Camera *camera, CameraWidget *window, GPContext *context)
561 {
562         CameraWidget *child;
563         int ret;
564
565         GP_DEBUG ("*** camera_set_config");
566
567         ret = gp_widget_get_child_by_label (window,
568                         _("Synchronize frame data and time with PC"), &child);
569         if (ret == GP_OK)
570                 gp_widget_get_value (child, &camera->pl->syncdatetime);
571
572         ret = gp_widget_get_child_by_label (window, _("Orientation"), &child);
573         if (ret == GP_OK) {
574                 char *value;
575                 int orientation;
576                 gp_widget_get_value (child, &value);
577                 orientation = string_to_orientation (value);
578                 if (orientation < 0) return orientation;
579                 camera->pl->orientation = orientation;
580         }
581
582         return GP_OK;
583 }
584
585 static int
586 camera_exit (Camera *camera, GPContext *context) 
587 {
588         char buf[2];
589
590         if (camera->pl != NULL) {
591                 buf[0] = '0' + camera->pl->syncdatetime;
592                 buf[1] = 0;
593                 gp_setting_set ("st2205", "syncdatetime", buf);
594                 gp_setting_set ("st2205", "orientation", orientation_to_string
595                                                 (camera->pl->orientation));
596 #ifdef HAVE_ICONV
597                 if (camera->pl->cd != (iconv_t) -1)
598                         iconv_close (camera->pl->cd);
599 #endif
600                 st2205_close (camera);
601                 free (camera->pl);
602                 camera->pl = NULL;
603         }
604         return GP_OK;
605 }
606
607 int
608 camera_init (Camera *camera, GPContext *context) 
609 {
610         int i, j, ret;
611 #ifdef HAVE_ICONV
612         char *curloc;
613 #endif
614         char buf[256];
615         st2205_filename clean_name;
616
617         /* First, set up all the function pointers */
618         camera->functions->exit    = camera_exit;
619         camera->functions->summary = camera_summary;
620         camera->functions->manual  = camera_manual;
621         camera->functions->about   = camera_about;
622         camera->functions->get_config = camera_get_config;
623         camera->functions->set_config = camera_set_config;
624         /* FIXME add gp_camera_get_storageinfo support */
625
626         /* Tell the CameraFilesystem where to get lists from */
627         gp_filesystem_set_funcs (camera->fs, &fsfuncs, camera);
628
629         camera->pl = calloc (1, sizeof(CameraPrivateLibrary));
630         if (!camera->pl) return GP_ERROR_NO_MEMORY;
631
632         ret = gp_setting_get("st2205", "syncdatetime", buf);
633         if (ret == GP_OK)
634                 camera->pl->syncdatetime = buf[0] == '1';
635         else
636                 camera->pl->syncdatetime = 1;
637
638         ret = gp_setting_get("st2205", "orientation", buf);
639         if (ret == GP_OK) {
640                 ret = string_to_orientation (buf);
641                 if (ret >= 0)
642                         camera->pl->orientation = ret;
643         }
644
645 #ifdef HAVE_ICONV
646         curloc = nl_langinfo (CODESET);
647         if (!curloc)
648                 curloc="UTF-8";
649         camera->pl->cd = iconv_open("ASCII", curloc);
650         if (camera->pl->cd == (iconv_t) -1) {
651                 gp_log (GP_LOG_ERROR, "iconv",
652                         "Failed to create iconv converter");
653                 camera_exit (camera, context);
654                 return GP_ERROR_OS_FAILURE;
655         }
656 #endif
657
658 #if 1
659         ret = st2205_open_device (camera);
660 #else
661         ret = st2205_open_dump (camera,
662                                 "/home/hans/st2205tool/memdump.bin", 128, 128);
663 #endif
664         if (ret != GP_OK) {
665                 camera_exit (camera, context);
666                 return ret;
667         }
668
669         GP_DEBUG ("st2205 memory size: %d, free: %d",
670                   st2205_get_mem_size (camera),
671                   st2205_get_free_mem_size (camera));
672
673         /* Get the filenames from the picframe */
674         ret = st2205_get_filenames (camera, camera->pl->filenames);
675         if (ret != GP_OK) {
676                 camera_exit (camera, context);
677                 return ret;
678         }
679
680         /* And clean them up and make them unique */
681         for (i = 0; i < ST2205_MAX_NO_FILES; i++) {
682                 if (!camera->pl->filenames[i][0])
683                         continue;
684
685                 /* Filter out non ASCII chars (some frames ship
686                    with sample photo's with garbage in the names) */
687                 for (j = 0; camera->pl->filenames[i][j]; j++) {
688                         if ((uint8_t)camera->pl->filenames[i][j] < 0x20 ||
689                             (uint8_t)camera->pl->filenames[i][j] > 0x7E)
690                                 clean_name[j] = '?';
691                         else
692                                 clean_name[j] = camera->pl->filenames[i][j];
693                 }
694                 clean_name[j] = 0;
695
696                 ST2205_SET_FILENAME(camera->pl->filenames[i], clean_name, i);
697         }
698
699         /* Sync time if requested */
700         if (camera->pl->syncdatetime) {
701                 struct tm tm;
702                 time_t t;
703
704                 t = time (NULL);
705                 if (localtime_r (&t , &tm)) {
706                         ret = st2205_set_time_and_date (camera, &tm);
707                         if (ret != GP_OK) {
708                                 camera_exit (camera, context);
709                                 return ret;
710                         }
711                 }
712         }
713
714         return GP_OK;
715 }