1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /*
3 * Libbrasero-media
4 * Copyright (C) Philippe Rouquier 2005-2009 <bonfire-app@wanadoo.fr>
5 *
6 * Libbrasero-media is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * The Libbrasero-media authors hereby grant permission for non-GPL compatible
12 * GStreamer plugins to be used and distributed together with GStreamer
13 * and Libbrasero-media. This permission is above and beyond the permissions granted
14 * by the GPL license by which Libbrasero-media is covered. If you modify this code
15 * you may extend this exception to your version of the code, but you are not
16 * obligated to do so. If you do not wish to do so, delete this exception
17 * statement from your version.
18 *
19 * Libbrasero-media is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Library General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to:
26 * The Free Software Foundation, Inc.,
27 * 51 Franklin Street, Fifth Floor
28 * Boston, MA 02110-1301, USA.
29 */
30
31 #ifdef HAVE_CONFIG_H
32 # include <config.h>
33 #endif
34
35 #include <string.h>
36
37 #include <glib.h>
38 #include <glib/gi18n-lib.h>
39
40 #include <gio/gio.h>
41
42 #include "brasero-media-private.h"
43
44 #include "brasero-drive-priv.h"
45
46 #include "scsi-device.h"
47 #include "scsi-utils.h"
48 #include "scsi-spc1.h"
49
50 #include "brasero-drive.h"
51 #include "brasero-medium.h"
52 #include "brasero-medium-monitor.h"
53
54 typedef struct _BraseroMediumMonitorPrivate BraseroMediumMonitorPrivate;
55 struct _BraseroMediumMonitorPrivate
56 {
57 GSList *drives;
58 GVolumeMonitor *gmonitor;
59
60 GSList *waiting_removal;
61 guint waiting_removal_id;
62
63 gint probing;
64 };
65
66 #define BRASERO_MEDIUM_MONITOR_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), BRASERO_TYPE_MEDIUM_MONITOR, BraseroMediumMonitorPrivate))
67
68 enum
69 {
70 MEDIUM_INSERTED,
71 MEDIUM_REMOVED,
72 DRIVE_ADDED,
73 DRIVE_REMOVED,
74
75 LAST_SIGNAL
76 };
77
78
79 static guint medium_monitor_signals[LAST_SIGNAL] = { 0 };
80
81 G_DEFINE_TYPE (BraseroMediumMonitor, brasero_medium_monitor, G_TYPE_OBJECT);
82
83
84 /**
85 * brasero_medium_monitor_get_drive:
86 * @monitor: a #BraseroMediumMonitor
87 * @device: the path of the device
88 *
89 * Returns the #BraseroDrive object whose path is @path.
90 *
91 * Return value: a #BraseroDrive or NULL. It should be unreffed when no longer in use.
92 **/
93 BraseroDrive *
brasero_medium_monitor_get_drive(BraseroMediumMonitor * monitor,const gchar * device)94 brasero_medium_monitor_get_drive (BraseroMediumMonitor *monitor,
95 const gchar *device)
96 {
97 GSList *iter;
98 BraseroMediumMonitorPrivate *priv;
99
100 g_return_val_if_fail (monitor != NULL, NULL);
101 g_return_val_if_fail (device != NULL, NULL);
102 g_return_val_if_fail (BRASERO_IS_MEDIUM_MONITOR (monitor), NULL);
103
104 priv = BRASERO_MEDIUM_MONITOR_PRIVATE (monitor);
105 for (iter = priv->drives; iter; iter = iter->next) {
106 BraseroDrive *drive;
107 const gchar *drive_device;
108
109 drive = iter->data;
110 drive_device = brasero_drive_get_device (drive);
111 if (drive_device && !strcmp (drive_device, device)) {
112 g_object_ref (drive);
113 return drive;
114 }
115 }
116
117 return NULL;
118 }
119
120 /**
121 * brasero_medium_monitor_is_probing:
122 * @monitor: a #BraseroMediumMonitor
123 *
124 * Returns if the library is still probing some other media.
125 *
126 * Return value: %TRUE if it is still probing some media
127 **/
128 gboolean
brasero_medium_monitor_is_probing(BraseroMediumMonitor * monitor)129 brasero_medium_monitor_is_probing (BraseroMediumMonitor *monitor)
130 {
131 GSList *iter;
132 BraseroMediumMonitorPrivate *priv;
133
134 g_return_val_if_fail (monitor != NULL, FALSE);
135 g_return_val_if_fail (BRASERO_IS_MEDIUM_MONITOR (monitor), FALSE);
136
137 priv = BRASERO_MEDIUM_MONITOR_PRIVATE (monitor);
138
139 for (iter = priv->drives; iter; iter = iter->next) {
140 BraseroDrive *drive;
141
142 drive = iter->data;
143 if (brasero_drive_is_fake (drive))
144 continue;
145
146 if (brasero_drive_probing (drive))
147 return TRUE;
148 }
149
150 return FALSE;
151 }
152
153 /**
154 * brasero_medium_monitor_get_drives:
155 * @monitor: a #BraseroMediumMonitor
156 * @type: a #BraseroDriveType to tell what type of drives to include in the list
157 *
158 * Gets the list of available drives that are of the given type.
159 *
160 * Return value: (element-type BraseroMedia.Drive) (transfer full): a #GSList of #BraseroDrive or NULL. The list must be freed and the element unreffed when finished.
161 **/
162 GSList *
brasero_medium_monitor_get_drives(BraseroMediumMonitor * monitor,BraseroDriveType type)163 brasero_medium_monitor_get_drives (BraseroMediumMonitor *monitor,
164 BraseroDriveType type)
165 {
166 BraseroMediumMonitorPrivate *priv;
167 GSList *drives = NULL;
168 GSList *iter;
169
170 g_return_val_if_fail (monitor != NULL, NULL);
171 g_return_val_if_fail (BRASERO_IS_MEDIUM_MONITOR (monitor), NULL);
172
173 priv = BRASERO_MEDIUM_MONITOR_PRIVATE (monitor);
174
175 for (iter = priv->drives; iter; iter = iter->next) {
176 BraseroDrive *drive;
177
178 drive = iter->data;
179 if (brasero_drive_is_fake (drive)) {
180 if (type & BRASERO_DRIVE_TYPE_FILE)
181 drives = g_slist_prepend (drives, drive);
182
183 continue;
184 }
185
186 if (brasero_drive_can_write (drive)
187 && (type & BRASERO_DRIVE_TYPE_WRITER)) {
188 drives = g_slist_prepend (drives, drive);
189 continue;
190 }
191
192 if (type & BRASERO_DRIVE_TYPE_READER) {
193 drives = g_slist_prepend (drives, drive);
194 continue;
195 }
196 }
197 g_slist_foreach (drives, (GFunc) g_object_ref, NULL);
198
199 return drives;
200 }
201
202 /**
203 * brasero_medium_monitor_get_media:
204 * @monitor: a #BraseroMediumMonitor
205 * @type: the type of #BraseroMedium that should be in the list
206 *
207 * Obtains the list of available media that are of the given type.
208 *
209 * Return value: (element-type BraseroMedia.Medium) (transfer full): a #GSList of #BraseroMedium or NULL. The list must be freed and the element unreffed when finished.
210 **/
211 GSList *
brasero_medium_monitor_get_media(BraseroMediumMonitor * monitor,BraseroMediaType type)212 brasero_medium_monitor_get_media (BraseroMediumMonitor *monitor,
213 BraseroMediaType type)
214 {
215 GSList *iter;
216 GSList *list = NULL;
217 BraseroMediumMonitorPrivate *priv;
218
219 g_return_val_if_fail (monitor != NULL, NULL);
220 g_return_val_if_fail (BRASERO_IS_MEDIUM_MONITOR (monitor), NULL);
221
222 priv = BRASERO_MEDIUM_MONITOR_PRIVATE (monitor);
223
224 for (iter = priv->drives; iter; iter = iter->next) {
225 BraseroMedium *medium;
226 BraseroDrive *drive;
227
228 drive = iter->data;
229
230 medium = brasero_drive_get_medium (drive);
231 if (!medium)
232 continue;
233
234 if ((type & BRASERO_MEDIA_TYPE_CD) == type
235 && (brasero_medium_get_status (medium) & BRASERO_MEDIUM_CD)) {
236 /* If used alone, returns all CDs */
237 list = g_slist_prepend (list, medium);
238 g_object_ref (medium);
239 continue;
240 }
241
242 if ((type & BRASERO_MEDIA_TYPE_ANY_IN_BURNER)
243 && (brasero_drive_can_write (drive))) {
244 if ((type & BRASERO_MEDIA_TYPE_CD)) {
245 if ((brasero_medium_get_status (medium) & BRASERO_MEDIUM_CD)) {
246 list = g_slist_prepend (list, medium);
247 g_object_ref (medium);
248 continue;
249 }
250 }
251 else {
252 list = g_slist_prepend (list, medium);
253 g_object_ref (medium);
254 continue;
255 }
256 continue;
257 }
258
259 if ((type & BRASERO_MEDIA_TYPE_AUDIO)
260 && !(brasero_medium_get_status (medium) & BRASERO_MEDIUM_FILE)
261 && (brasero_medium_get_status (medium) & BRASERO_MEDIUM_HAS_AUDIO)) {
262 if ((type & BRASERO_MEDIA_TYPE_CD)) {
263 if ((brasero_medium_get_status (medium) & BRASERO_MEDIUM_CD)) {
264 list = g_slist_prepend (list, medium);
265 g_object_ref (medium);
266 continue;
267 }
268 }
269 else {
270 list = g_slist_prepend (list, medium);
271 g_object_ref (medium);
272 continue;
273 }
274 continue;
275 }
276
277 if ((type & BRASERO_MEDIA_TYPE_DATA)
278 && !(brasero_medium_get_status (medium) & BRASERO_MEDIUM_FILE)
279 && (brasero_medium_get_status (medium) & BRASERO_MEDIUM_HAS_DATA)) {
280 if ((type & BRASERO_MEDIA_TYPE_CD)) {
281 if ((brasero_medium_get_status (medium) & BRASERO_MEDIUM_CD)) {
282 list = g_slist_prepend (list, medium);
283 g_object_ref (medium);
284 continue;
285 }
286 }
287 else {
288 list = g_slist_prepend (list, medium);
289 g_object_ref (medium);
290 continue;
291 }
292 continue;
293 }
294
295 if (type & BRASERO_MEDIA_TYPE_WRITABLE) {
296 if (brasero_medium_can_be_written (medium)) {
297 if ((type & BRASERO_MEDIA_TYPE_CD)) {
298 if ((brasero_medium_get_status (medium) & BRASERO_MEDIUM_CD)) {
299 list = g_slist_prepend (list, medium);
300 g_object_ref (medium);
301 continue;
302 }
303 }
304 else {
305 list = g_slist_prepend (list, medium);
306 g_object_ref (medium);
307 continue;
308 }
309 }
310 }
311
312 if (type & BRASERO_MEDIA_TYPE_REWRITABLE) {
313 if (brasero_medium_can_be_rewritten (medium)) {
314 if ((type & BRASERO_MEDIA_TYPE_CD)) {
315 if ((brasero_medium_get_status (medium) & BRASERO_MEDIUM_CD)) {
316 list = g_slist_prepend (list, medium);
317 g_object_ref (medium);
318 continue;
319 }
320 }
321 else {
322 list = g_slist_prepend (list, medium);
323 g_object_ref (medium);
324 continue;
325 }
326 }
327 }
328
329 if (type & BRASERO_MEDIA_TYPE_FILE) {
330 /* make sure the drive is indeed a fake one
331 * since it can happen that some medium did
332 * not properly carry out their initialization
333 * and are flagged as BRASERO_MEDIUM_FILE
334 * whereas they are not */
335 if (brasero_drive_is_fake (drive)) {
336 list = g_slist_prepend (list, medium);
337 g_object_ref (medium);
338 }
339 }
340 }
341
342 return list;
343 }
344
345 static void
brasero_medium_monitor_medium_added_cb(BraseroDrive * drive,BraseroMedium * medium,BraseroMediumMonitor * self)346 brasero_medium_monitor_medium_added_cb (BraseroDrive *drive,
347 BraseroMedium *medium,
348 BraseroMediumMonitor *self)
349 {
350 g_signal_emit (self,
351 medium_monitor_signals [MEDIUM_INSERTED],
352 0,
353 medium);
354 }
355
356 static void
brasero_medium_monitor_medium_removed_cb(BraseroDrive * drive,BraseroMedium * medium,BraseroMediumMonitor * self)357 brasero_medium_monitor_medium_removed_cb (BraseroDrive *drive,
358 BraseroMedium *medium,
359 BraseroMediumMonitor *self)
360 {
361 g_signal_emit (self,
362 medium_monitor_signals [MEDIUM_REMOVED],
363 0,
364 medium);
365 }
366
367 static gboolean
brasero_medium_monitor_is_drive(BraseroMediumMonitor * monitor,const gchar * device)368 brasero_medium_monitor_is_drive (BraseroMediumMonitor *monitor,
369 const gchar *device)
370 {
371 BraseroDeviceHandle *handle;
372 BraseroScsiErrCode code;
373 gboolean result;
374
375 BRASERO_MEDIA_LOG ("Testing drive %s", device);
376
377 handle = brasero_device_handle_open (device, FALSE, &code);
378 if (!handle)
379 return FALSE;
380
381 result = (brasero_spc1_inquiry_is_optical_drive (handle, &code) == BRASERO_SCSI_OK);
382 brasero_device_handle_close (handle);
383
384 BRASERO_MEDIA_LOG ("Drive %s", result? "is optical":"is not optical");
385
386 return result;
387 }
388
389 static BraseroDrive *
brasero_medium_monitor_drive_new(BraseroMediumMonitor * self,const gchar * device,GDrive * gdrive)390 brasero_medium_monitor_drive_new (BraseroMediumMonitor *self,
391 const gchar *device,
392 GDrive *gdrive)
393 {
394 BraseroMediumMonitorPrivate *priv;
395 BraseroDrive *drive;
396
397 if (!brasero_medium_monitor_is_drive (self, device))
398 return NULL;
399
400 priv = BRASERO_MEDIUM_MONITOR_PRIVATE (self);
401 drive = g_object_new (BRASERO_TYPE_DRIVE,
402 "device", device,
403 "gdrive", gdrive,
404 NULL);
405
406 priv->drives = g_slist_prepend (priv->drives, drive);
407
408 g_signal_connect (drive,
409 "medium-added",
410 G_CALLBACK (brasero_medium_monitor_medium_added_cb),
411 self);
412 g_signal_connect (drive,
413 "medium-removed",
414 G_CALLBACK (brasero_medium_monitor_medium_removed_cb),
415 self);
416
417 return drive;
418 }
419
420 static void
brasero_medium_monitor_device_added(BraseroMediumMonitor * self,const gchar * device,GDrive * gdrive)421 brasero_medium_monitor_device_added (BraseroMediumMonitor *self,
422 const gchar *device,
423 GDrive *gdrive)
424 {
425 BraseroMediumMonitorPrivate *priv;
426 BraseroDrive *drive = NULL;
427
428 priv = BRASERO_MEDIUM_MONITOR_PRIVATE (self);
429
430 /* See if the drive is waiting removal.
431 * This is necessary as GIO behaves strangely sometimes
432 * since it sends the "disconnected" signal when a medium
433 * is removed soon followed by a "connected" signal */
434 drive = brasero_medium_monitor_get_drive (self, device);
435 if (drive) {
436 /* Just in case that drive was waiting removal */
437 priv->waiting_removal = g_slist_remove (priv->waiting_removal, drive);
438
439 BRASERO_MEDIA_LOG ("Added signal was emitted but the drive is in the removal list. Updating GDrive associated object.");
440 g_object_set (drive,
441 "gdrive", gdrive,
442 NULL);
443
444 g_object_unref (drive);
445 return;
446 }
447
448 /* Make sure it's an optical drive */
449 drive = brasero_medium_monitor_drive_new (self, device, gdrive);
450 if (!drive)
451 return;
452
453 BRASERO_MEDIA_LOG ("New drive added");
454 g_signal_emit (self,
455 medium_monitor_signals [DRIVE_ADDED],
456 0,
457 drive);
458
459 /* check if a medium is inserted */
460 if (brasero_drive_get_medium (drive))
461 g_signal_emit (self,
462 medium_monitor_signals [MEDIUM_INSERTED],
463 0,
464 brasero_drive_get_medium (drive));
465 }
466
467 static void
brasero_medium_monitor_connected_cb(GVolumeMonitor * monitor,GDrive * gdrive,BraseroMediumMonitor * self)468 brasero_medium_monitor_connected_cb (GVolumeMonitor *monitor,
469 GDrive *gdrive,
470 BraseroMediumMonitor *self)
471 {
472 gchar *device;
473
474 BRASERO_MEDIA_LOG ("GDrive addition signal");
475
476 device = g_drive_get_identifier (gdrive, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
477 brasero_medium_monitor_device_added (self, device, gdrive);
478 g_free (device);
479 }
480
481 static void
brasero_medium_monitor_volume_added_cb(GVolumeMonitor * monitor,GVolume * gvolume,BraseroMediumMonitor * self)482 brasero_medium_monitor_volume_added_cb (GVolumeMonitor *monitor,
483 GVolume *gvolume,
484 BraseroMediumMonitor *self)
485 {
486 gchar *device;
487 GDrive *gdrive;
488
489 BRASERO_MEDIA_LOG ("GVolume addition signal");
490
491 /* No need to signal that addition if the GVolume
492 * object has an associated GDrive as this is just
493 * meant to trap blank discs which have no GDrive
494 * associated but a GVolume. */
495 gdrive = g_volume_get_drive (gvolume);
496 if (gdrive) {
497 BRASERO_MEDIA_LOG ("Existing GDrive skipping");
498 g_object_unref (gdrive);
499 return;
500 }
501
502 device = g_volume_get_identifier (gvolume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
503 if (!device)
504 return;
505
506 brasero_medium_monitor_device_added (self, device, NULL);
507 g_free (device);
508 }
509
510 static gboolean
brasero_medium_monitor_disconnected_real(gpointer data)511 brasero_medium_monitor_disconnected_real (gpointer data)
512 {
513 BraseroMediumMonitor *self = BRASERO_MEDIUM_MONITOR (data);
514 BraseroMediumMonitorPrivate *priv;
515 BraseroMedium *medium;
516 BraseroDrive *drive;
517
518 priv = BRASERO_MEDIUM_MONITOR_PRIVATE (self);
519
520 if (!priv->waiting_removal) {
521 priv->waiting_removal_id = 0;
522 return FALSE;
523 }
524
525 drive = priv->waiting_removal->data;
526 priv->waiting_removal = g_slist_remove (priv->waiting_removal, drive);
527
528 BRASERO_MEDIA_LOG ("Drive removed");
529 medium = brasero_drive_get_medium (drive);
530
531 /* disconnect the signal handlers to avoid having the "medium-removed" fired twice */
532 g_signal_handlers_disconnect_by_func (drive,
533 brasero_medium_monitor_medium_added_cb,
534 self);
535 g_signal_handlers_disconnect_by_func (drive,
536 brasero_medium_monitor_medium_removed_cb,
537 self);
538
539 if (medium)
540 g_signal_emit (self,
541 medium_monitor_signals [MEDIUM_REMOVED],
542 0,
543 medium);
544
545 priv->drives = g_slist_remove (priv->drives, drive);
546 g_signal_emit (self,
547 medium_monitor_signals [DRIVE_REMOVED],
548 0,
549 drive);
550 g_object_unref (drive);
551
552 /* in case there are more */
553 return TRUE;
554 }
555
556 static void
brasero_medium_monitor_device_removed(BraseroMediumMonitor * self,const gchar * device,GDrive * gdrive)557 brasero_medium_monitor_device_removed (BraseroMediumMonitor *self,
558 const gchar *device,
559 GDrive *gdrive)
560 {
561 BraseroMediumMonitorPrivate *priv;
562 GDrive *associated_gdrive;
563 BraseroDrive *drive;
564
565 priv = BRASERO_MEDIUM_MONITOR_PRIVATE (self);
566
567 /* Make sure it's one already detected */
568 /* GIO behaves strangely: every time a medium
569 * is removed from a drive it emits the disconnected
570 * signal (which IMO it shouldn't) soon followed by
571 * a connected signal.
572 * So delay the removal by one or two seconds. */
573
574 drive = brasero_medium_monitor_get_drive (self, device);
575 if (!drive)
576 return;
577
578 if (G_UNLIKELY (g_slist_find (priv->waiting_removal, drive) != NULL)) {
579 g_object_unref (drive);
580 return;
581 }
582
583 associated_gdrive = brasero_drive_get_gdrive (drive);
584 if (associated_gdrive == gdrive) {
585 BRASERO_MEDIA_LOG ("Found device to remove");
586 priv->waiting_removal = g_slist_append (priv->waiting_removal, drive);
587
588 if (!priv->waiting_removal_id)
589 priv->waiting_removal_id = g_timeout_add_seconds (2,
590 brasero_medium_monitor_disconnected_real,
591 self);
592 }
593 /* else do nothing and wait for a "drive-disconnected" signal */
594
595 if (associated_gdrive)
596 g_object_unref (associated_gdrive);
597
598 g_object_unref (drive);
599 }
600
601 static void
brasero_medium_monitor_volume_removed_cb(GVolumeMonitor * monitor,GVolume * gvolume,BraseroMediumMonitor * self)602 brasero_medium_monitor_volume_removed_cb (GVolumeMonitor *monitor,
603 GVolume *gvolume,
604 BraseroMediumMonitor *self)
605 {
606 gchar *device;
607 GDrive *gdrive;
608
609 BRASERO_MEDIA_LOG ("Volume removal signal");
610
611 /* No need to signal that removal if the GVolume
612 * object has an associated GDrive as this is just
613 * meant to trap blank discs which have no GDrive
614 * associated but a GVolume. */
615 gdrive = g_volume_get_drive (gvolume);
616 if (gdrive) {
617 g_object_unref (gdrive);
618 return;
619 }
620
621 device = g_volume_get_identifier (gvolume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
622 if (!device)
623 return;
624
625 brasero_medium_monitor_device_removed (self, device, NULL);
626 g_free (device);
627 }
628
629 static void
brasero_medium_monitor_disconnected_cb(GVolumeMonitor * monitor,GDrive * gdrive,BraseroMediumMonitor * self)630 brasero_medium_monitor_disconnected_cb (GVolumeMonitor *monitor,
631 GDrive *gdrive,
632 BraseroMediumMonitor *self)
633 {
634 gchar *device;
635
636 BRASERO_MEDIA_LOG ("Drive removal signal");
637
638 device = g_drive_get_identifier (gdrive, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
639 brasero_medium_monitor_device_removed (self, device, gdrive);
640 g_free (device);
641 }
642
643 static void
brasero_medium_monitor_init(BraseroMediumMonitor * object)644 brasero_medium_monitor_init (BraseroMediumMonitor *object)
645 {
646 GList *iter;
647 GList *drives;
648 GList *volumes;
649 BraseroDrive *drive;
650 BraseroMediumMonitorPrivate *priv;
651
652 priv = BRASERO_MEDIUM_MONITOR_PRIVATE (object);
653
654 BRASERO_MEDIA_LOG ("Probing drives and media");
655
656 priv->gmonitor = g_volume_monitor_get ();
657
658 drives = g_volume_monitor_get_connected_drives (priv->gmonitor);
659 BRASERO_MEDIA_LOG ("Found %d drives", g_list_length (drives));
660
661 for (iter = drives; iter; iter = iter->next) {
662 GDrive *gdrive;
663 gchar *device;
664
665 gdrive = iter->data;
666
667 device = g_drive_get_identifier (gdrive, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
668 brasero_medium_monitor_drive_new (object, device, gdrive);
669 g_free (device);
670 }
671 g_list_foreach (drives, (GFunc) g_object_unref, NULL);
672 g_list_free (drives);
673
674 volumes = g_volume_monitor_get_volumes (priv->gmonitor);
675 BRASERO_MEDIA_LOG ("Found %d volumes", g_list_length (volumes));
676
677 for (iter = volumes; iter; iter = iter->next) {
678 GVolume *gvolume;
679 gchar *device;
680
681 gvolume = iter->data;
682 device = g_volume_get_identifier (gvolume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
683 if (!device)
684 continue;
685
686 /* make sure it isn't already in our list */
687 drive = brasero_medium_monitor_get_drive (object, device);
688 if (drive) {
689 g_free (device);
690 g_object_unref (drive);
691 continue;
692 }
693
694 brasero_medium_monitor_drive_new (object, device, NULL);
695 g_free (device);
696 }
697 g_list_foreach (volumes, (GFunc) g_object_unref, NULL);
698 g_list_free (volumes);
699
700 g_signal_connect (priv->gmonitor,
701 "volume-added",
702 G_CALLBACK (brasero_medium_monitor_volume_added_cb),
703 object);
704 g_signal_connect (priv->gmonitor,
705 "volume-removed",
706 G_CALLBACK (brasero_medium_monitor_volume_removed_cb),
707 object);
708 g_signal_connect (priv->gmonitor,
709 "drive-connected",
710 G_CALLBACK (brasero_medium_monitor_connected_cb),
711 object);
712 g_signal_connect (priv->gmonitor,
713 "drive-disconnected",
714 G_CALLBACK (brasero_medium_monitor_disconnected_cb),
715 object);
716
717 /* add fake/file drive */
718 drive = g_object_new (BRASERO_TYPE_DRIVE,
719 "device", NULL,
720 NULL);
721 priv->drives = g_slist_prepend (priv->drives, drive);
722
723 return;
724 }
725
726 static void
brasero_medium_monitor_finalize(GObject * object)727 brasero_medium_monitor_finalize (GObject *object)
728 {
729 BraseroMediumMonitorPrivate *priv;
730
731 priv = BRASERO_MEDIUM_MONITOR_PRIVATE (object);
732
733 if (priv->waiting_removal_id) {
734 g_source_remove (priv->waiting_removal_id);
735 priv->waiting_removal_id = 0;
736 }
737
738 if (priv->waiting_removal) {
739 g_slist_free (priv->waiting_removal);
740 priv->waiting_removal = NULL;
741 }
742
743 if (priv->drives) {
744 g_slist_foreach (priv->drives, (GFunc) g_object_unref, NULL);
745 g_slist_free (priv->drives);
746 priv->drives = NULL;
747 }
748
749 if (priv->gmonitor) {
750 g_signal_handlers_disconnect_by_func (priv->gmonitor,
751 brasero_medium_monitor_volume_added_cb,
752 object);
753 g_signal_handlers_disconnect_by_func (priv->gmonitor,
754 brasero_medium_monitor_volume_removed_cb,
755 object);
756 g_signal_handlers_disconnect_by_func (priv->gmonitor,
757 brasero_medium_monitor_connected_cb,
758 object);
759 g_signal_handlers_disconnect_by_func (priv->gmonitor,
760 brasero_medium_monitor_disconnected_cb,
761 object);
762 g_object_unref (priv->gmonitor);
763 priv->gmonitor = NULL;
764 }
765
766 G_OBJECT_CLASS (brasero_medium_monitor_parent_class)->finalize (object);
767 }
768
769 static void
brasero_medium_monitor_class_init(BraseroMediumMonitorClass * klass)770 brasero_medium_monitor_class_init (BraseroMediumMonitorClass *klass)
771 {
772 GObjectClass* object_class = G_OBJECT_CLASS (klass);
773
774 g_type_class_add_private (klass, sizeof (BraseroMediumMonitorPrivate));
775
776 object_class->finalize = brasero_medium_monitor_finalize;
777
778 /**
779 * BraseroMediumMonitor::medium-added:
780 * @monitor: the object which received the signal
781 * @medium: the new medium which was added
782 *
783 * This signal gets emitted when a new medium was detected
784 **/
785 medium_monitor_signals[MEDIUM_INSERTED] =
786 g_signal_new ("medium_added",
787 G_OBJECT_CLASS_TYPE (klass),
788 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
789 G_STRUCT_OFFSET (BraseroMediumMonitorClass, medium_added),
790 NULL, NULL,
791 g_cclosure_marshal_VOID__OBJECT,
792 G_TYPE_NONE, 1,
793 BRASERO_TYPE_MEDIUM);
794
795 /**
796 * BraseroMediumMonitor::medium-removed:
797 * @monitor: the object which received the signal
798 * @medium: the medium which was removed
799 *
800 * This signal gets emitted when a medium is not longer available
801 **/
802 medium_monitor_signals[MEDIUM_REMOVED] =
803 g_signal_new ("medium_removed",
804 G_OBJECT_CLASS_TYPE (klass),
805 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
806 G_STRUCT_OFFSET (BraseroMediumMonitorClass, medium_removed),
807 NULL, NULL,
808 g_cclosure_marshal_VOID__OBJECT,
809 G_TYPE_NONE, 1,
810 BRASERO_TYPE_MEDIUM);
811
812 /**
813 * BraseroMediumMonitor::drive-added:
814 * @monitor: the object which received the signal
815 * @medium: the new medium which was added
816 *
817 * This signal gets emitted when a new drive was detected
818 **/
819 medium_monitor_signals[DRIVE_ADDED] =
820 g_signal_new ("drive_added",
821 G_OBJECT_CLASS_TYPE (klass),
822 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
823 G_STRUCT_OFFSET (BraseroMediumMonitorClass, drive_added),
824 NULL, NULL,
825 g_cclosure_marshal_VOID__OBJECT,
826 G_TYPE_NONE, 1,
827 BRASERO_TYPE_DRIVE);
828
829 /**
830 * BraseroMediumMonitor::drive-removed:
831 * @monitor: the object which received the signal
832 * @medium: the medium which was removed
833 *
834 * This signal gets emitted when a drive is not longer available
835 **/
836 medium_monitor_signals[DRIVE_REMOVED] =
837 g_signal_new ("drive_removed",
838 G_OBJECT_CLASS_TYPE (klass),
839 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
840 G_STRUCT_OFFSET (BraseroMediumMonitorClass, drive_removed),
841 NULL, NULL,
842 g_cclosure_marshal_VOID__OBJECT,
843 G_TYPE_NONE, 1,
844 BRASERO_TYPE_DRIVE);
845 }
846
847 static BraseroMediumMonitor *singleton = NULL;
848
849 /**
850 * brasero_medium_monitor_get_default:
851 *
852 * Gets the currently active monitor.
853 *
854 * Return value: a #BraseroMediumMonitor. Unref when it is not needed anymore.
855 **/
856 BraseroMediumMonitor *
brasero_medium_monitor_get_default(void)857 brasero_medium_monitor_get_default (void)
858 {
859 if (singleton) {
860 g_object_ref (singleton);
861 return singleton;
862 }
863
864 singleton = g_object_new (BRASERO_TYPE_MEDIUM_MONITOR, NULL);
865
866 /* keep a reference */
867 g_object_ref (singleton);
868 return singleton;
869 }
870