1
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
4
* Copyright (C) Philippe Rouquier 2005-2009 <bonfire-app@wanadoo.fr>
6
* Libbrasero-burn 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.
11
* The Libbrasero-burn authors hereby grant permission for non-GPL compatible
12
* GStreamer plugins to be used and distributed together with GStreamer
13
* and Libbrasero-burn. This permission is above and beyond the permissions granted
14
* by the GPL license by which Libbrasero-burn 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.
19
* Libbrasero-burn 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.
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.
36
#include <glib/gi18n-lib.h>
38
#include "brasero-media-private.h"
40
#include "scsi-device.h"
42
#include "brasero-drive.h"
43
#include "brasero-medium.h"
44
#include "brasero-medium-monitor.h"
45
#include "burn-volume.h"
47
#include "brasero-burn-lib.h"
49
#include "brasero-data-session.h"
50
#include "brasero-data-project.h"
51
#include "brasero-file-node.h"
52
#include "brasero-io.h"
54
#include "libbrasero-marshal.h"
56
typedef struct _BraseroDataSessionPrivate BraseroDataSessionPrivate;
57
struct _BraseroDataSessionPrivate
59
BraseroIOJobBase *load_dir;
61
/* Multisession drives that are inserted */
64
/* Drive whose session is loaded */
65
BraseroMedium *loaded;
67
/* Nodes from the loaded session in the tree */
70
glong size_changed_sig;
76
#define BRASERO_DATA_SESSION_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), BRASERO_TYPE_DATA_SESSION, BraseroDataSessionPrivate))
78
G_DEFINE_TYPE (BraseroDataSession, brasero_data_session, BRASERO_TYPE_DATA_PROJECT);
87
static gulong brasero_data_session_signals [LAST_SIGNAL] = { 0 };
90
* to evaluate the contents of a medium or image async
92
struct _BraseroIOImageContentsData {
99
typedef struct _BraseroIOImageContentsData BraseroIOImageContentsData;
102
brasero_io_image_directory_contents_destroy (BraseroAsyncTaskManager *manager,
104
gpointer callback_data)
106
BraseroIOImageContentsData *data = callback_data;
108
g_free (data->dev_image);
109
brasero_io_job_free (cancelled, BRASERO_IO_JOB (data));
112
static BraseroAsyncTaskResult
113
brasero_io_image_directory_contents_thread (BraseroAsyncTaskManager *manager,
114
GCancellable *cancel,
115
gpointer callback_data)
117
BraseroIOImageContentsData *data = callback_data;
118
BraseroDeviceHandle *handle;
119
GList *children, *iter;
120
GError *error = NULL;
123
handle = brasero_device_handle_open (data->job.uri, FALSE, NULL);
124
vol = brasero_volume_source_open_device_handle (handle, &error);
126
brasero_device_handle_close (handle);
127
brasero_io_return_result (data->job.base,
131
data->job.callback_data);
132
return BRASERO_ASYNC_TASK_FINISHED;
135
children = brasero_volume_load_directory_contents (vol,
139
brasero_volume_source_close (vol);
140
brasero_device_handle_close (handle);
142
for (iter = children; iter; iter = iter->next) {
143
BraseroVolFile *file;
148
info = g_file_info_new ();
149
g_file_info_set_file_type (info, file->isdir? G_FILE_TYPE_DIRECTORY:G_FILE_TYPE_REGULAR);
150
g_file_info_set_name (info, BRASERO_VOLUME_FILE_NAME (file));
153
g_file_info_set_attribute_int64 (info,
154
BRASERO_IO_DIR_CONTENTS_ADDR,
155
file->specific.dir.address);
157
g_file_info_set_size (info, BRASERO_VOLUME_FILE_SIZE (file));
159
brasero_io_return_result (data->job.base,
163
data->job.callback_data);
166
g_list_foreach (children, (GFunc) brasero_volume_file_free, NULL);
167
g_list_free (children);
169
return BRASERO_ASYNC_TASK_FINISHED;
172
static const BraseroAsyncTaskType image_contents_type = {
173
brasero_io_image_directory_contents_thread,
174
brasero_io_image_directory_contents_destroy
178
brasero_io_load_image_directory (const gchar *dev_image,
179
gint64 session_block,
181
const BraseroIOJobBase *base,
182
BraseroIOFlags options,
185
BraseroIOImageContentsData *data;
186
BraseroIOResultCallbackData *callback_data = NULL;
189
callback_data = g_new0 (BraseroIOResultCallbackData, 1);
190
callback_data->callback_data = user_data;
193
data = g_new0 (BraseroIOImageContentsData, 1);
195
data->session_block = session_block;
197
brasero_io_set_job (BRASERO_IO_JOB (data),
203
brasero_io_push_job (BRASERO_IO_JOB (data),
204
&image_contents_type);
209
brasero_data_session_check_size (BraseroDataSession *self)
211
BraseroDataSessionPrivate *priv;
212
gint64 max_sectors = 0;
213
gint64 medium_sect = 0;
216
priv = BRASERO_DATA_SESSION_PRIVATE (self);
218
sectors = brasero_data_project_get_sectors (BRASERO_DATA_PROJECT (self));
219
brasero_medium_get_free_space (priv->loaded,
223
/* NOTE: This is not good since with a DVD 3% of 4.3G may be too much
224
* with 3% we are slightly over the limit of the most overburnable discs
225
* but at least users can try to overburn as much as they can. */
227
/* The idea would be to test write the disc with cdrecord from /dev/null
228
* until there is an error and see how much we were able to write. So,
229
* when we propose overburning to the user, we could ask if he wants
230
* us to determine how much data can be written to a particular disc
231
* provided he has chosen a real disc. */
232
max_sectors = medium_sect * 103 / 100;
234
if (medium_sect < sectors) {
236
if (!priv->is_oversized || priv->is_overburn) {
239
/* see if overburn is possible */
240
overburn = (sectors < max_sectors);
241
if (!priv->is_overburn && overburn)
243
brasero_data_session_signals [OVERSIZE_SIGNAL],
249
brasero_data_session_signals [OVERSIZE_SIGNAL],
254
priv->is_overburn = overburn;
257
priv->is_oversized = TRUE;
260
if (priv->is_oversized || priv->is_overburn)
262
brasero_data_session_signals [OVERSIZE_SIGNAL],
267
priv->is_oversized = FALSE;
268
priv->is_overburn = FALSE;
273
brasero_data_session_size_changed (BraseroDataProject *project,
276
brasero_data_session_check_size (BRASERO_DATA_SESSION (project));
280
brasero_data_session_remove_last (BraseroDataSession *self)
282
BraseroDataSessionPrivate *priv;
285
priv = BRASERO_DATA_SESSION_PRIVATE (self);
290
/* go through the top nodes and remove all the imported nodes */
291
for (iter = priv->nodes; iter; iter = iter->next) {
292
BraseroFileNode *node;
295
brasero_data_project_destroy_node (BRASERO_DATA_PROJECT (self), node);
298
g_slist_free (priv->nodes);
302
brasero_data_session_signals [LOADED_SIGNAL],
308
g_object_unref (priv->loaded);
312
if (priv->size_changed_sig) {
313
g_signal_handler_disconnect (self, priv->size_changed_sig);
314
priv->size_changed_sig = 0;
317
priv->is_oversized = FALSE;
318
priv->is_overburn = FALSE;
322
brasero_data_session_load_dir_destroy (GObject *object,
327
BraseroFileNode *parent;
330
reference = GPOINTER_TO_INT (data);
334
parent = brasero_data_project_reference_get (BRASERO_DATA_PROJECT (object), reference);
336
parent->is_exploring = FALSE;
338
brasero_data_project_reference_free (BRASERO_DATA_PROJECT (object), reference);
342
brasero_data_session_load_dir_result (GObject *owner,
344
const gchar *dev_image,
348
BraseroDataSessionPrivate *priv;
349
BraseroFileNode *parent;
350
BraseroFileNode *node;
353
priv = BRASERO_DATA_SESSION_PRIVATE (owner);
356
g_signal_emit (owner,
357
brasero_data_session_signals [LOADED_SIGNAL],
362
/* FIXME: tell the user the error message */
366
reference = GPOINTER_TO_INT (data);
368
parent = brasero_data_project_reference_get (BRASERO_DATA_PROJECT (owner),
373
/* add all the files/folders at the root of the session */
374
node = brasero_data_project_add_imported_session_file (BRASERO_DATA_PROJECT (owner),
379
g_signal_emit (owner,
380
brasero_data_session_signals [LOADED_SIGNAL],
387
/* Only if we're exploring root directory */
389
priv->nodes = g_slist_prepend (priv->nodes, node);
391
g_signal_emit (owner,
392
brasero_data_session_signals [LOADED_SIGNAL],
399
brasero_data_session_load_directory_contents_real (BraseroDataSession *self,
400
BraseroFileNode *node,
403
BraseroDataSessionPrivate *priv;
404
goffset session_block;
408
if (node && !node->is_fake)
411
priv = BRASERO_DATA_SESSION_PRIVATE (self);
412
device = brasero_drive_get_device (brasero_medium_get_drive (priv->loaded));
413
brasero_medium_get_last_data_track_address (priv->loaded,
418
priv->load_dir = brasero_io_register (G_OBJECT (self),
419
brasero_data_session_load_dir_result,
420
brasero_data_session_load_dir_destroy,
423
/* If there aren't any node then that's root */
425
reference = brasero_data_project_reference_new (BRASERO_DATA_PROJECT (self), node);
426
node->is_exploring = TRUE;
429
brasero_io_load_image_directory (device,
431
BRASERO_FILE_NODE_IMPORTED_ADDRESS (node),
433
BRASERO_IO_INFO_URGENT,
434
GINT_TO_POINTER (reference));
437
node->is_fake = FALSE;
443
brasero_data_session_load_directory_contents (BraseroDataSession *self,
444
BraseroFileNode *node,
447
return brasero_data_session_load_directory_contents_real (self, node, error);
451
brasero_data_session_add_last (BraseroDataSession *self,
452
BraseroMedium *medium,
455
BraseroDataSessionPrivate *priv;
457
priv = BRASERO_DATA_SESSION_PRIVATE (self);
458
priv->loaded = medium;
459
g_object_ref (medium);
461
priv->size_changed_sig = g_signal_connect (self,
463
G_CALLBACK (brasero_data_session_size_changed),
466
return brasero_data_session_load_directory_contents_real (self, NULL, error);
470
brasero_data_session_has_available_media (BraseroDataSession *self)
472
BraseroDataSessionPrivate *priv;
474
priv = BRASERO_DATA_SESSION_PRIVATE (self);
476
return priv->media != NULL;
480
brasero_data_session_get_available_media (BraseroDataSession *self)
483
BraseroDataSessionPrivate *priv;
485
priv = BRASERO_DATA_SESSION_PRIVATE (self);
487
retval = g_slist_copy (priv->media);
488
g_slist_foreach (retval, (GFunc) g_object_ref, NULL);
494
brasero_data_session_get_loaded_medium (BraseroDataSession *self)
496
BraseroDataSessionPrivate *priv;
498
priv = BRASERO_DATA_SESSION_PRIVATE (self);
499
if (!priv->media || !priv->nodes)
506
brasero_data_session_is_valid_multi (BraseroMedium *medium)
509
BraseroMedia media_status;
511
media = brasero_medium_get_status (medium);
512
media_status = brasero_burn_library_get_media_capabilities (media);
514
return (media_status & BRASERO_MEDIUM_WRITABLE) &&
515
(media & BRASERO_MEDIUM_HAS_DATA) &&
516
(brasero_medium_get_last_data_track_address (medium, NULL, NULL) != -1);
520
brasero_data_session_disc_added_cb (BraseroMediumMonitor *monitor,
521
BraseroMedium *medium,
522
BraseroDataSession *self)
524
BraseroDataSessionPrivate *priv;
526
priv = BRASERO_DATA_SESSION_PRIVATE (self);
528
if (!brasero_data_session_is_valid_multi (medium))
531
g_object_ref (medium);
532
priv->media = g_slist_prepend (priv->media, medium);
535
brasero_data_session_signals [AVAILABLE_SIGNAL],
542
brasero_data_session_disc_removed_cb (BraseroMediumMonitor *monitor,
543
BraseroMedium *medium,
544
BraseroDataSession *self)
548
BraseroDataSessionPrivate *priv;
550
priv = BRASERO_DATA_SESSION_PRIVATE (self);
552
/* see if that's the current loaded one */
553
if (priv->loaded && priv->loaded == medium)
554
brasero_data_session_remove_last (self);
556
/* remove it from our list */
557
for (iter = priv->media; iter; iter = next) {
558
BraseroMedium *iter_medium;
560
iter_medium = iter->data;
563
if (medium == iter_medium) {
565
brasero_data_session_signals [AVAILABLE_SIGNAL],
570
priv->media = g_slist_remove (priv->media, iter_medium);
571
g_object_unref (iter_medium);
577
brasero_data_session_init (BraseroDataSession *object)
580
BraseroMediumMonitor *monitor;
581
BraseroDataSessionPrivate *priv;
583
priv = BRASERO_DATA_SESSION_PRIVATE (object);
585
monitor = brasero_medium_monitor_get_default ();
586
g_signal_connect (monitor,
588
G_CALLBACK (brasero_data_session_disc_added_cb),
590
g_signal_connect (monitor,
592
G_CALLBACK (brasero_data_session_disc_removed_cb),
595
list = brasero_medium_monitor_get_media (monitor,
596
BRASERO_MEDIA_TYPE_WRITABLE|
597
BRASERO_MEDIA_TYPE_REWRITABLE);
598
g_object_unref (monitor);
600
/* check for a multisession medium already in */
601
for (iter = list; iter; iter = iter->next) {
602
BraseroMedium *medium;
605
if (brasero_data_session_is_valid_multi (medium)) {
606
g_object_ref (medium);
607
priv->media = g_slist_prepend (priv->media, medium);
610
g_slist_foreach (list, (GFunc) g_object_unref, NULL);
615
brasero_data_session_stop_io (BraseroDataSession *self)
617
BraseroDataSessionPrivate *priv;
619
priv = BRASERO_DATA_SESSION_PRIVATE (self);
621
if (priv->load_dir) {
622
brasero_io_cancel_by_base (priv->load_dir);
623
g_free (priv->load_dir);
624
priv->load_dir = NULL;
629
brasero_data_session_reset (BraseroDataProject *project,
632
brasero_data_session_stop_io (BRASERO_DATA_SESSION (project));
634
/* chain up this function except if we invalidated the node */
635
if (BRASERO_DATA_PROJECT_CLASS (brasero_data_session_parent_class)->reset)
636
BRASERO_DATA_PROJECT_CLASS (brasero_data_session_parent_class)->reset (project, num_nodes);
640
brasero_data_session_finalize (GObject *object)
642
BraseroDataSessionPrivate *priv;
644
priv = BRASERO_DATA_SESSION_PRIVATE (object);
646
g_object_unref (priv->loaded);
651
g_slist_foreach (priv->media, (GFunc) g_object_unref, NULL);
652
g_slist_free (priv->media);
657
g_slist_free (priv->nodes);
661
/* NOTE no need to clean up size_changed_sig since it's connected to
662
* ourselves. It disappears with use. */
664
brasero_data_session_stop_io (BRASERO_DATA_SESSION (object));
666
/* don't care about the nodes since they will be automatically
669
G_OBJECT_CLASS (brasero_data_session_parent_class)->finalize (object);
674
brasero_data_session_class_init (BraseroDataSessionClass *klass)
676
GObjectClass* object_class = G_OBJECT_CLASS (klass);
677
BraseroDataProjectClass *project_class = BRASERO_DATA_PROJECT_CLASS (klass);
679
g_type_class_add_private (klass, sizeof (BraseroDataSessionPrivate));
681
object_class->finalize = brasero_data_session_finalize;
683
project_class->reset = brasero_data_session_reset;
685
brasero_data_session_signals [AVAILABLE_SIGNAL] =
686
g_signal_new ("session_available",
687
G_TYPE_FROM_CLASS (klass),
691
brasero_marshal_VOID__OBJECT_BOOLEAN,
696
brasero_data_session_signals [LOADED_SIGNAL] =
697
g_signal_new ("session_loaded",
698
G_TYPE_FROM_CLASS (klass),
702
brasero_marshal_VOID__OBJECT_BOOLEAN,
707
brasero_data_session_signals [OVERSIZE_SIGNAL] =
708
g_signal_new ("oversize",
709
G_TYPE_FROM_CLASS (klass),
713
brasero_marshal_VOID__BOOLEAN_BOOLEAN,