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