No issues found
Tool | Failure ID | Location | Function | Message | Data |
---|---|---|---|---|---|
clang-analyzer | no-output-found | rb-mtp-thread.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None |
1 /*
2 * Copyright (C) 2009 Jonathan Matthew <jonathan@d14n.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * The Rhythmbox authors hereby grant permission for non-GPL compatible
10 * GStreamer plugins to be used and distributed together with GStreamer
11 * and Rhythmbox. This permission is above and beyond the permissions granted
12 * by the GPL license by which Rhythmbox is covered. If you modify this code
13 * you may extend this exception to your version of the code, but you are not
14 * obligated to do so. If you do not wish to do so, delete this exception
15 * statement from your version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
25 *
26 */
27
28 #include <config.h>
29
30 #include <string.h>
31 #include <stdlib.h>
32
33 #include <glib.h>
34 #include <glib/gi18n.h>
35 #include <gtk/gtk.h>
36
37 #include "rb-mtp-thread.h"
38 #include "rb-file-helpers.h"
39 #include "rb-dialog.h"
40 #include "rb-debug.h"
41
42 G_DEFINE_DYNAMIC_TYPE(RBMtpThread, rb_mtp_thread, G_TYPE_OBJECT)
43
44
45 typedef struct {
46 enum {
47 OPEN_DEVICE = 1,
48 CLOSE_DEVICE,
49 SET_DEVICE_NAME,
50 THREAD_CALLBACK,
51
52 CREATE_FOLDER,
53
54 ADD_TO_ALBUM,
55 REMOVE_FROM_ALBUM,
56 SET_ALBUM_IMAGE,
57
58 GET_TRACK_LIST,
59 DELETE_TRACK,
60 UPLOAD_TRACK,
61 DOWNLOAD_TRACK
62 } task;
63
64 LIBMTP_raw_device_t *raw_device;
65 LIBMTP_track_t *track;
66 uint32_t track_id;
67 uint32_t folder_id;
68 uint32_t storage_id;
69 char *album;
70 char *filename;
71 GdkPixbuf *image;
72 char *name;
73 char **path;
74
75 gpointer callback;
76 gpointer user_data;
77 GDestroyNotify destroy_data;
78 } RBMtpThreadTask;
79
80 static char *
81 task_name (RBMtpThreadTask *task)
82 {
83 switch (task->task) {
84 case OPEN_DEVICE: return g_strdup ("open device");
85 case CLOSE_DEVICE: return g_strdup ("close device");
86 case SET_DEVICE_NAME: return g_strdup_printf ("set device name to %s", task->name);
87 case THREAD_CALLBACK: return g_strdup ("thread callback");
88
89 case CREATE_FOLDER: return g_strdup_printf ("create folder %s", task->path[g_strv_length (task->path)-1]);
90
91 case ADD_TO_ALBUM: return g_strdup_printf ("add track %u to album %s", task->track_id, task->album);
92 case REMOVE_FROM_ALBUM: return g_strdup_printf ("remove track %u from album %s", task->track_id, task->album);
93 case SET_ALBUM_IMAGE: return g_strdup_printf ("set image for album %s", task->album);
94
95 case GET_TRACK_LIST: return g_strdup ("get track list");
96 case DELETE_TRACK: return g_strdup_printf ("delete track %u", task->track_id);
97 case UPLOAD_TRACK: return g_strdup_printf ("upload track from %s", task->filename);
98 case DOWNLOAD_TRACK: return g_strdup_printf ("download track %u to %s",
99 task->track_id,
100 task->filename[0] ? task->filename : "<temporary>");
101 default: return g_strdup_printf ("unknown task type %d", task->task);
102 }
103 }
104
105 static RBMtpThreadTask *
106 create_task (int tasktype)
107 {
108 RBMtpThreadTask *task = g_slice_new0 (RBMtpThreadTask);
109 task->task = tasktype;
110 return task;
111 }
112
113 static void
114 destroy_task (RBMtpThreadTask *task)
115 {
116 /* don't think we ever own the track structure here;
117 * we only have it for uploads, and then we pass it back
118 * to the callback.
119 */
120
121 g_free (task->album);
122 g_free (task->filename);
123 g_free (task->name);
124 g_strfreev (task->path);
125
126 if (task->image) {
127 g_object_unref (task->image);
128 }
129
130 if (task->destroy_data) {
131 task->destroy_data (task->user_data);
132 }
133
134 g_slice_free (RBMtpThreadTask, task);
135 }
136
137
138 static void
139 queue_task (RBMtpThread *thread, RBMtpThreadTask *task)
140 {
141 char *name = task_name (task);
142 rb_debug ("queueing task: %s", name);
143 g_free (name);
144
145 g_async_queue_push (thread->queue, task);
146 }
147
148 static void
149 open_device (RBMtpThread *thread, RBMtpThreadTask *task)
150 {
151 RBMtpOpenCallback cb = task->callback;
152 int retry;
153
154 /* open the device */
155 rb_debug ("attempting to open device");
156 for (retry = 0; retry < 5; retry++) {
157 if (retry > 0) {
158 /* sleep a while before trying again */
159 g_usleep (G_USEC_PER_SEC);
160 }
161
162 thread->device = LIBMTP_Open_Raw_Device (task->raw_device);
163 if (thread->device != NULL) {
164 break;
165 }
166
167 rb_debug ("attempt %d failed..", retry+1);
168 }
169
170 cb (thread->device, task->user_data);
171 }
172
173 static void
174 create_folder (RBMtpThread *thread, RBMtpThreadTask *task)
175 {
176 RBMtpCreateFolderCallback cb = task->callback;
177 LIBMTP_folder_t *folders;
178 LIBMTP_folder_t *f;
179 LIBMTP_folder_t *target = NULL;
180 uint32_t folder_id;
181 uint32_t storage_id;
182 int i;
183
184 folders = LIBMTP_Get_Folder_List (thread->device);
185 if (folders == NULL) {
186 rb_debug ("unable to get folder list");
187 rb_mtp_thread_report_errors (thread, FALSE);
188 cb (0, task->user_data);
189 return;
190 }
191
192 /* first find the default music folder */
193 f = LIBMTP_Find_Folder (folders, thread->device->default_music_folder);
194 if (f == NULL) {
195 rb_debug ("unable to find default music folder");
196 cb (0, task->user_data);
197 LIBMTP_destroy_folder_t (folders);
198 return;
199 }
200 storage_id = f->storage_id;
201 folder_id = f->folder_id;
202
203 /* descend through the folder tree, following the path */
204 i = 0;
205 while (task->path[i] != NULL) {
206
207 /* look for a folder at this level with the same name as the
208 * next path component
209 */
210 target = f->child;
211 while (target != NULL) {
212 if (g_strcmp0 (target->name, task->path[i]) == 0) {
213 rb_debug ("found path element %d: %s", i, target->name);
214 break;
215 }
216 target = target->sibling;
217 }
218
219 if (target == NULL) {
220 rb_debug ("path element %d (%s) not found", i, task->path[i]);
221 break;
222 }
223 f = target;
224 folder_id = f->folder_id;
225 i++;
226 }
227
228 /* now create any path elements that don't already exist */
229 while (task->path[i] != NULL) {
230 folder_id = LIBMTP_Create_Folder (thread->device, task->path[i], folder_id, storage_id);
231 if (folder_id == 0) {
232 rb_debug ("couldn't create path element %d: %s", i, task->path[i]);
233 rb_mtp_thread_report_errors (thread, FALSE);
234 break;
235 }
236 rb_debug ("created path element %d: %s with folder ID %u", i, task->path[i], folder_id);
237 i++;
238 }
239
240 cb (folder_id, task->user_data);
241 LIBMTP_destroy_folder_t (folders);
242 }
243
244 static LIBMTP_album_t *
245 add_track_to_album (RBMtpThread *thread, const char *album_name, uint32_t track_id, uint32_t folder_id, uint32_t storage_id, gboolean *new_album)
246 {
247 LIBMTP_album_t *album;
248
249 album = g_hash_table_lookup (thread->albums, album_name);
250 if (album != NULL) {
251 /* add track to album */
252 album->tracks = realloc (album->tracks, sizeof(uint32_t) * (album->no_tracks+1));
253 album->tracks[album->no_tracks] = track_id;
254 album->no_tracks++;
255 rb_debug ("adding track ID %d to album ID %d; now has %d tracks",
256 track_id,
257 album->album_id,
258 album->no_tracks);
259
260 if (new_album != NULL) {
261 *new_album = FALSE;
262 }
263 } else {
264 /* add new album */
265 album = LIBMTP_new_album_t ();
266 album->name = strdup (album_name);
267 album->no_tracks = 1;
268 album->tracks = malloc (sizeof(uint32_t));
269 album->tracks[0] = track_id;
270 album->parent_id = folder_id;
271 album->storage_id = storage_id;
272
273 rb_debug ("creating new album (%s) for track ID %d", album->name, track_id);
274
275 g_hash_table_insert (thread->albums, album->name, album);
276 if (new_album != NULL) {
277 *new_album = TRUE;
278 }
279 }
280
281 return album;
282 }
283
284 static void
285 write_album_to_device (RBMtpThread *thread, LIBMTP_album_t *album, gboolean new_album)
286 {
287 if (new_album) {
288 if (LIBMTP_Create_New_Album (thread->device, album) != 0) {
289 rb_debug ("LIBMTP_Create_New_Album failed..");
290 rb_mtp_thread_report_errors (thread, FALSE);
291 }
292 } else {
293 if (LIBMTP_Update_Album (thread->device, album) != 0) {
294 rb_debug ("LIBMTP_Update_Album failed..");
295 rb_mtp_thread_report_errors (thread, FALSE);
296 }
297 }
298 }
299
300 static void
301 add_track_to_album_and_update (RBMtpThread *thread, RBMtpThreadTask *task)
302 {
303 LIBMTP_album_t *album;
304 gboolean new_album = FALSE;
305
306 album = add_track_to_album (thread, task->album, task->track_id, task->folder_id, task->storage_id, &new_album);
307 write_album_to_device (thread, album, new_album);
308 }
309
310 static void
311 remove_track_from_album (RBMtpThread *thread, RBMtpThreadTask *task)
312 {
313 LIBMTP_album_t *album;
314 int i;
315
316 album = g_hash_table_lookup (thread->albums, task->album);
317 if (album == NULL) {
318 rb_debug ("Couldn't find an album for %s", task->album);
319 return;
320 }
321
322 for (i = 0; i < album->no_tracks; i++) {
323 if (album->tracks[i] == task->track_id) {
324 break;
325 }
326 }
327
328 if (i == album->no_tracks) {
329 rb_debug ("Couldn't find track %d in album %d", task->track_id, album->album_id);
330 return;
331 }
332
333 memmove (album->tracks + i, album->tracks + i + 1, album->no_tracks - (i+1));
334 album->no_tracks--;
335
336 if (album->no_tracks == 0) {
337 rb_debug ("deleting empty album %d", album->album_id);
338 if (LIBMTP_Delete_Object (thread->device, album->album_id) != 0) {
339 rb_mtp_thread_report_errors (thread, FALSE);
340 }
341 g_hash_table_remove (thread->albums, task->album);
342 } else {
343 rb_debug ("updating album %d: %d tracks remaining", album->album_id, album->no_tracks);
344 if (LIBMTP_Update_Album (thread->device, album) != 0) {
345 rb_mtp_thread_report_errors (thread, FALSE);
346 }
347 }
348 }
349
350 static void
351 set_album_image (RBMtpThread *thread, RBMtpThreadTask *task)
352 {
353 LIBMTP_filesampledata_t *albumart;
354 LIBMTP_album_t *album;
355 GError *error = NULL;
356 char *image_data;
357 gsize image_size;
358 int ret;
359
360 album = g_hash_table_lookup (thread->albums, task->album);
361 if (album == NULL) {
362 rb_debug ("Couldn't find an album for %s", task->album);
363 return;
364 }
365
366 /* probably should scale the image down, since some devices have a size limit and they all have
367 * tiny displays anyway.
368 */
369
370 if (gdk_pixbuf_save_to_buffer (task->image, &image_data, &image_size, "jpeg", &error, NULL) == FALSE) {
371 rb_debug ("unable to convert album art image to a JPEG buffer: %s", error->message);
372 g_error_free (error);
373 return;
374 }
375
376 albumart = LIBMTP_new_filesampledata_t ();
377 albumart->filetype = LIBMTP_FILETYPE_JPEG;
378 albumart->data = image_data;
379 albumart->size = image_size;
380
381 ret = LIBMTP_Send_Representative_Sample (thread->device, album->album_id, albumart);
382 if (ret != 0) {
383 rb_mtp_thread_report_errors (thread, TRUE);
384 } else {
385 rb_debug ("successfully set album art for %s (%" G_GSIZE_FORMAT " bytes)", task->album, image_size);
386 }
387
388 /* libmtp will try to free this if we don't clear the pointer */
389 albumart->data = NULL;
390 LIBMTP_destroy_filesampledata_t (albumart);
391 }
392
393 static void
394 get_track_list (RBMtpThread *thread, RBMtpThreadTask *task)
395 {
396 RBMtpTrackListCallback cb = task->callback;
397 gboolean device_forgets_albums = TRUE;
398 GHashTable *update_albums = NULL;
399 LIBMTP_track_t *tracks = NULL;
400 LIBMTP_album_t *albums;
401 LIBMTP_album_t *album;
402
403 /* get all the albums */
404 albums = LIBMTP_Get_Album_List (thread->device);
405 rb_mtp_thread_report_errors (thread, FALSE);
406 if (albums != NULL) {
407 LIBMTP_album_t *album;
408
409 for (album = albums; album != NULL; album = album->next) {
410 if (album->name == NULL)
411 continue;
412
413 rb_debug ("album: %s, %d tracks", album->name, album->no_tracks);
414 g_hash_table_insert (thread->albums, album->name, album);
415 if (album->no_tracks != 0) {
416 device_forgets_albums = FALSE;
417 }
418 }
419
420 if (device_forgets_albums) {
421 rb_debug ("stupid mtp device detected. will rebuild all albums.");
422 }
423 } else {
424 rb_debug ("No albums");
425 device_forgets_albums = FALSE;
426 }
427
428 tracks = LIBMTP_Get_Tracklisting_With_Callback (thread->device, NULL, NULL);
429 rb_mtp_thread_report_errors (thread, FALSE);
430 if (tracks == NULL) {
431 rb_debug ("no tracks on the device");
432 } else if (device_forgets_albums) {
433 LIBMTP_track_t *track;
434
435 rb_debug ("rebuilding albums");
436 update_albums = g_hash_table_new (g_direct_hash, g_direct_equal);
437 for (track = tracks; track != NULL; track = track->next) {
438 if (track->album != NULL) {
439 gboolean new_album = FALSE;
440 album = add_track_to_album (thread, track->album, track->item_id, track->parent_id, track->storage_id, &new_album);
441 g_hash_table_insert (update_albums, album, GINT_TO_POINTER (new_album));
442 }
443 }
444 rb_debug ("finished rebuilding albums");
445 }
446
447 cb (tracks, task->user_data);
448 /* the callback owns the tracklist */
449
450 if (device_forgets_albums) {
451 GHashTableIter iter;
452 gpointer album_ptr;
453 gpointer new_album_ptr;
454
455 rb_debug ("writing rebuilt albums back to the device");
456 g_hash_table_iter_init (&iter, update_albums);
457 while (g_hash_table_iter_next (&iter, &album_ptr, &new_album_ptr)) {
458 album = album_ptr;
459 rb_debug ("writing album \"%s\"", album->name);
460 write_album_to_device (thread, album, GPOINTER_TO_INT (new_album_ptr));
461 }
462 g_hash_table_destroy (update_albums);
463
464 rb_debug ("removing remaining empty albums");
465 g_hash_table_iter_init (&iter, thread->albums);
466 while (g_hash_table_iter_next (&iter, NULL, &album_ptr)) {
467 int ret;
468
469 album = album_ptr;
470 if (album->no_tracks == 0) {
471 rb_debug ("pruning empty album \"%s\"", album->name);
472 ret = LIBMTP_Delete_Object (thread->device, album->album_id);
473 if (ret != 0) {
474 rb_mtp_thread_report_errors (thread, FALSE);
475 }
476 g_hash_table_iter_remove (&iter);
477 }
478 }
479
480 rb_debug ("finished updating albums on the device");
481 }
482 }
483
484 static void
485 download_track (RBMtpThread *thread, RBMtpThreadTask *task)
486 {
487 LIBMTP_file_t *fileinfo;
488 LIBMTP_error_t *stack;
489 GError *error = NULL;
490 GFile *dir;
491 RBMtpDownloadCallback cb = (RBMtpDownloadCallback) task->callback;
492
493 /* first, check there's enough space to copy it */
494 fileinfo = LIBMTP_Get_Filemetadata (thread->device, task->track_id);
495 if (fileinfo == NULL) {
496 stack = LIBMTP_Get_Errorstack (thread->device);
497 rb_debug ("unable to get track metadata for %u: %s", task->track_id, stack->error_text);
498 error = g_error_new (RB_MTP_THREAD_ERROR,
499 RB_MTP_THREAD_ERROR_GET_TRACK,
500 _("Unable to copy file from MTP device: %s"),
501 stack->error_text);
502 LIBMTP_Clear_Errorstack (thread->device);
503
504 cb (task->track_id, NULL, error, task->user_data);
505 g_error_free (error);
506 return;
507 }
508
509 if (task->filename[0] == '\0') {
510 dir = g_file_new_for_path (g_get_tmp_dir ());
511 } else {
512 GFile *file = g_file_new_for_path (task->filename);
513 dir = g_file_get_parent (file);
514 g_object_unref (file);
515 }
516 rb_debug ("checking for %" G_GINT64_FORMAT " bytes available", fileinfo->filesize);
517 if (rb_check_dir_has_space (dir, fileinfo->filesize) == FALSE) {
518 char *dpath = g_file_get_path (dir);
519 rb_debug ("not enough space in %s", dpath);
520 error = g_error_new (RB_MTP_THREAD_ERROR, RB_MTP_THREAD_ERROR_NO_SPACE,
521 _("Not enough space in %s"), dpath);
522 g_free (dpath);
523 }
524 LIBMTP_destroy_file_t (fileinfo);
525 g_object_unref (dir);
526
527 if (error != NULL) {
528 rb_debug ("bailing out due to error: %s", error->message);
529 cb (task->track_id, NULL, error, task->user_data);
530 g_error_free (error);
531 return;
532 }
533
534 if (task->filename[0] == '\0') {
535 /* download to a temporary file */
536 int fd;
537 GError *tmperror = NULL;
538
539 g_free (task->filename);
540 fd = g_file_open_tmp ("rb-mtp-temp-XXXXXX", &task->filename, &tmperror);
541 if (fd == -1) {
542 rb_debug ("unable to open temporary file: %s", tmperror->message);
543 error = g_error_new (RB_MTP_THREAD_ERROR,
544 RB_MTP_THREAD_ERROR_TEMPFILE,
545 _("Unable to open temporary file: %s"),
546 tmperror->message);
547 g_error_free (tmperror);
548
549 cb (task->track_id, NULL, error, task->user_data);
550 g_error_free (error);
551 return;
552 } else {
553 rb_debug ("downloading track %u to file descriptor %d", task->track_id, fd);
554 if (LIBMTP_Get_Track_To_File_Descriptor (thread->device, task->track_id, fd, NULL, NULL)) {
555 stack = LIBMTP_Get_Errorstack (thread->device);
556 rb_debug ("unable to retrieve track %u: %s", task->track_id, stack->error_text);
557 error = g_error_new (RB_MTP_THREAD_ERROR, RB_MTP_THREAD_ERROR_GET_TRACK,
558 _("Unable to copy file from MTP device: %s"),
559 stack->error_text);
560 LIBMTP_Clear_Errorstack (thread->device);
561
562 cb (task->track_id, NULL, error, task->user_data);
563 g_error_free (error);
564 close (fd);
565 remove (task->filename);
566 return;
567 }
568 rb_debug ("done downloading track");
569
570 close (fd);
571 }
572 } else {
573 if (LIBMTP_Get_Track_To_File (thread->device, task->track_id, task->filename, NULL, NULL)) {
574 stack = LIBMTP_Get_Errorstack (thread->device);
575 error = g_error_new (RB_MTP_THREAD_ERROR, RB_MTP_THREAD_ERROR_GET_TRACK,
576 _("Unable to copy file from MTP device: %s"),
577 stack->error_text);
578 LIBMTP_Clear_Errorstack (thread->device);
579
580 cb (task->track_id, NULL, error, task->user_data);
581 g_error_free (error);
582 return;
583 }
584 }
585
586 cb (task->track_id, task->filename, NULL, task->user_data);
587 }
588
589 static void
590 upload_track (RBMtpThread *thread, RBMtpThreadTask *task)
591 {
592 RBMtpUploadCallback cb = (RBMtpUploadCallback) task->callback;
593 LIBMTP_error_t *stack;
594 GError *error = NULL;
595
596 if (LIBMTP_Send_Track_From_File (thread->device, task->filename, task->track, NULL, NULL)) {
597 stack = LIBMTP_Get_Errorstack (thread->device);
598 rb_debug ("unable to send track: %s", stack->error_text);
599
600 if (stack->errornumber == LIBMTP_ERROR_STORAGE_FULL) {
601 error = g_error_new (RB_MTP_THREAD_ERROR, RB_MTP_THREAD_ERROR_NO_SPACE,
602 _("No space left on MTP device"));
603 } else {
604 error = g_error_new (RB_MTP_THREAD_ERROR, RB_MTP_THREAD_ERROR_SEND_TRACK,
605 _("Unable to send file to MTP device: %s"),
606 stack->error_text);
607 }
608 LIBMTP_Clear_Errorstack (thread->device);
609 task->track->item_id = 0; /* is this actually an invalid item ID? */
610 }
611 cb (task->track, error, task->user_data);
612 g_clear_error (&error);
613 }
614
615 static gboolean
616 run_task (RBMtpThread *thread, RBMtpThreadTask *task)
617 {
618 char *name = task_name (task);
619 rb_debug ("running task: %s", name);
620 g_free (name);
621
622 switch (task->task) {
623 case OPEN_DEVICE:
624 open_device (thread, task);
625 break;
626
627 case CLOSE_DEVICE:
628 return TRUE;
629
630 case SET_DEVICE_NAME:
631 if (LIBMTP_Set_Friendlyname (thread->device, task->name)) {
632 rb_mtp_thread_report_errors (thread, TRUE);
633 }
634 break;
635
636 case THREAD_CALLBACK:
637 {
638 RBMtpThreadCallback cb = (RBMtpThreadCallback)task->callback;
639 cb (thread->device, task->user_data);
640 }
641 break;
642
643 case CREATE_FOLDER:
644 create_folder (thread, task);
645 break;
646
647 case ADD_TO_ALBUM:
648 add_track_to_album_and_update (thread, task);
649 break;
650
651 case REMOVE_FROM_ALBUM:
652 remove_track_from_album (thread, task);
653 break;
654
655 case SET_ALBUM_IMAGE:
656 set_album_image (thread, task);
657 break;
658
659 case GET_TRACK_LIST:
660 get_track_list (thread, task);
661 break;
662
663 case DELETE_TRACK:
664 if (LIBMTP_Delete_Object (thread->device, task->track_id)) {
665 rb_mtp_thread_report_errors (thread, TRUE);
666 }
667 break;
668
669 case UPLOAD_TRACK:
670 upload_track (thread, task);
671 break;
672
673 case DOWNLOAD_TRACK:
674 download_track (thread, task);
675 break;
676
677 default:
678 g_assert_not_reached ();
679 }
680
681 return FALSE;
682 }
683
684 static gpointer
685 task_thread (RBMtpThread *thread)
686 {
687 RBMtpThreadTask *task;
688 gboolean quit = FALSE;
689 GAsyncQueue *queue = g_async_queue_ref (thread->queue);
690
691 rb_debug ("MTP device worker thread starting");
692 while (quit == FALSE) {
693
694 task = g_async_queue_pop (queue);
695 quit = run_task (thread, task);
696 destroy_task (task);
697 }
698
699 rb_debug ("MTP device worker thread exiting");
700
701 /* clean up any queued tasks */
702 while ((task = g_async_queue_try_pop (queue)) != NULL)
703 destroy_task (task);
704
705 g_async_queue_unref (queue);
706 return NULL;
707 }
708
709 /* callable interface */
710
711 void
712 rb_mtp_thread_open_device (RBMtpThread *thread,
713 LIBMTP_raw_device_t *raw_device,
714 RBMtpOpenCallback callback,
715 gpointer data,
716 GDestroyNotify destroy_data)
717 {
718 RBMtpThreadTask *task = create_task (OPEN_DEVICE);
719 task->raw_device = raw_device;
720 task->callback = callback;
721 task->user_data = data;
722 task->destroy_data = destroy_data;
723 queue_task (thread, task);
724 }
725
726 void
727 rb_mtp_thread_set_device_name (RBMtpThread *thread, const char *name)
728 {
729 RBMtpThreadTask *task = create_task (SET_DEVICE_NAME);
730 task->name = g_strdup (name);
731 queue_task (thread, task);
732 }
733
734 void
735 rb_mtp_thread_create_folder (RBMtpThread *thread,
736 const char **path,
737 RBMtpCreateFolderCallback func,
738 gpointer data,
739 GDestroyNotify destroy_data)
740 {
741 RBMtpThreadTask *task = create_task (CREATE_FOLDER);
742 task->path = g_strdupv ((char **)path);
743 task->callback = func;
744 task->user_data = data;
745 task->destroy_data = destroy_data;
746 queue_task (thread, task);
747 }
748
749 void
750 rb_mtp_thread_add_to_album (RBMtpThread *thread, LIBMTP_track_t *track, const char *album)
751 {
752 RBMtpThreadTask *task = create_task (ADD_TO_ALBUM);
753 task->track_id = track->item_id;
754 task->folder_id = track->parent_id;
755 task->storage_id = track->storage_id;
756 task->album = g_strdup (album);
757 queue_task (thread, task);
758 }
759
760 void
761 rb_mtp_thread_remove_from_album (RBMtpThread *thread, LIBMTP_track_t *track, const char *album)
762 {
763 RBMtpThreadTask *task = create_task (REMOVE_FROM_ALBUM);
764 task->track_id = track->item_id;
765 task->storage_id = track->storage_id;
766 task->album = g_strdup (album);
767 queue_task (thread, task);
768 }
769
770 void
771 rb_mtp_thread_set_album_image (RBMtpThread *thread, const char *album, GdkPixbuf *image)
772 {
773 RBMtpThreadTask *task = create_task (SET_ALBUM_IMAGE);
774 task->album = g_strdup (album);
775 task->image = g_object_ref (image);
776 queue_task (thread, task);
777 }
778
779 void
780 rb_mtp_thread_get_track_list (RBMtpThread *thread,
781 RBMtpTrackListCallback callback,
782 gpointer data,
783 GDestroyNotify destroy_data)
784 {
785 RBMtpThreadTask *task = create_task (GET_TRACK_LIST);
786 task->callback = callback;
787 task->user_data = data;
788 task->destroy_data = destroy_data;
789 queue_task (thread, task);
790 }
791
792 void
793 rb_mtp_thread_delete_track (RBMtpThread *thread, LIBMTP_track_t *track)
794 {
795 RBMtpThreadTask *task = create_task (DELETE_TRACK);
796 task->track_id = track->item_id;
797 task->storage_id = track->storage_id;
798 queue_task (thread, task);
799 }
800
801 void
802 rb_mtp_thread_upload_track (RBMtpThread *thread,
803 LIBMTP_track_t *track,
804 const char *filename,
805 RBMtpUploadCallback func,
806 gpointer data,
807 GDestroyNotify destroy_data)
808 {
809 RBMtpThreadTask *task = create_task (UPLOAD_TRACK);
810 task->track = track;
811 task->filename = g_strdup (filename);
812 task->callback = func;
813 task->user_data = data;
814 task->destroy_data = destroy_data;
815 queue_task (thread, task);
816 }
817
818 void
819 rb_mtp_thread_download_track (RBMtpThread *thread,
820 uint32_t track_id,
821 const char *filename,
822 RBMtpDownloadCallback func,
823 gpointer data,
824 GDestroyNotify destroy_data)
825 {
826 RBMtpThreadTask *task = create_task (DOWNLOAD_TRACK);
827 task->track_id = track_id;
828 task->filename = g_strdup (filename);
829 task->callback = func;
830 task->user_data = data;
831 task->destroy_data = destroy_data;
832 queue_task (thread, task);
833 }
834
835 void
836 rb_mtp_thread_queue_callback (RBMtpThread *thread,
837 RBMtpThreadCallback func,
838 gpointer data,
839 GDestroyNotify destroy_data)
840 {
841 RBMtpThreadTask *task = create_task (THREAD_CALLBACK);
842 task->callback = func;
843 task->user_data = data;
844 task->destroy_data = destroy_data;
845 queue_task (thread, task);
846 }
847
848 void
849 rb_mtp_thread_report_errors (RBMtpThread *thread, gboolean use_dialog)
850 {
851 LIBMTP_error_t *stack;
852
853 for (stack = LIBMTP_Get_Errorstack (thread->device); stack != NULL; stack = stack->next) {
854 if (use_dialog) {
855 GDK_THREADS_ENTER ();
856 rb_error_dialog (NULL, _("Media player device error"), "%s", stack->error_text);
857 GDK_THREADS_LEAVE ();
858
859 /* only display one dialog box per error */
860 use_dialog = FALSE;
861 } else {
862 g_warning ("libmtp error: %s", stack->error_text);
863 }
864 }
865
866 LIBMTP_Clear_Errorstack (thread->device);
867 }
868
869 /* GObject things */
870
871 static void
872 impl_finalize (GObject *object)
873 {
874 RBMtpThread *thread = RB_MTP_THREAD (object);
875 RBMtpThreadTask *task;
876
877 rb_debug ("killing MTP worker thread");
878 task = create_task (CLOSE_DEVICE);
879 queue_task (thread, task);
880 if (thread->thread != g_thread_self ()) {
881 g_thread_join (thread->thread);
882 rb_debug ("MTP worker thread exited");
883 } else {
884 rb_debug ("we're on the MTP worker thread..");
885 }
886
887 g_async_queue_unref (thread->queue);
888
889 g_hash_table_destroy (thread->albums);
890
891 if (thread->device != NULL) {
892 LIBMTP_Release_Device (thread->device);
893 }
894
895 G_OBJECT_CLASS (rb_mtp_thread_parent_class)->finalize (object);
896 }
897
898 static void
899 rb_mtp_thread_init (RBMtpThread *thread)
900 {
901 thread->queue = g_async_queue_new ();
902
903 thread->albums = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) LIBMTP_destroy_album_t);
904
905 thread->thread = g_thread_new ("mtp", (GThreadFunc) task_thread, thread);
906 }
907
908 static void
909 rb_mtp_thread_class_init (RBMtpThreadClass *klass)
910 {
911 GObjectClass *object_class = G_OBJECT_CLASS (klass);
912
913 object_class->finalize = impl_finalize;
914 }
915
916 static void
917 rb_mtp_thread_class_finalize (RBMtpThreadClass *klass)
918 {
919 }
920
921 RBMtpThread *
922 rb_mtp_thread_new (void)
923 {
924 return RB_MTP_THREAD (g_object_new (RB_TYPE_MTP_THREAD, NULL));
925 }
926
927 GQuark
928 rb_mtp_thread_error_quark (void)
929 {
930 static GQuark quark = 0;
931 if (!quark)
932 quark = g_quark_from_static_string ("rb_mtp_thread_error");
933
934 return quark;
935 }
936
937 #define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
938
939 GType
940 rb_mtp_thread_error_get_type (void)
941 {
942 static GType etype = 0;
943
944 if (etype == 0) {
945 static const GEnumValue values[] = {
946 ENUM_ENTRY (RB_MTP_THREAD_ERROR_NO_SPACE, "no-space"),
947 ENUM_ENTRY (RB_MTP_THREAD_ERROR_TEMPFILE, "tempfile-failed"),
948 ENUM_ENTRY (RB_MTP_THREAD_ERROR_GET_TRACK, "track-get-failed"),
949 { 0, 0, 0 }
950 };
951
952 etype = g_enum_register_static ("RBMTPThreadError", values);
953 }
954
955 return etype;
956 }
957
958 void
959 _rb_mtp_thread_register_type (GTypeModule *module)
960 {
961 rb_mtp_thread_register_type (module);
962 }