1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /*
3  * Libbrasero-burn
4  * Copyright (C) Philippe Rouquier 2005-2009 <bonfire-app@wanadoo.fr>
5  *
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.
10  *
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.
18  *
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.
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 <math.h>
36 
37 #include <glib.h>
38 #include <glib-object.h>
39 #include <glib/gi18n-lib.h>
40 
41 #include "burn-basics.h"
42 #include "brasero-session.h"
43 #include "brasero-session-helper.h"
44 #include "burn-debug.h"
45 #include "burn-task-ctx.h"
46 
47 typedef struct _BraseroTaskCtxPrivate BraseroTaskCtxPrivate;
48 struct _BraseroTaskCtxPrivate
49 {
50 	/* these two are set at creation time and can't be changed */
51 	BraseroTaskAction action;
52 	BraseroBurnSession *session;
53 
54 	GMutex *lock;
55 
56 	BraseroTrack *current_track;
57 	GSList *tracks;
58 
59 	/* used to poll for progress (every 0.5 sec) */
60 	gdouble progress;
61 	goffset track_bytes;
62 	goffset session_bytes;
63 
64 	goffset size;
65 	goffset blocks;
66 
67 	/* keep track of time */
68 	GTimer *timer;
69 	goffset first_written;
70 	gdouble first_progress;
71 
72 	/* used for immediate rate */
73 	gdouble current_elapsed;
74 	gdouble last_elapsed;
75 
76 	goffset last_written;
77 	gdouble last_progress;
78 
79 	/* used for remaining time */
80 	GSList *times;
81 	gdouble total_time;
82 
83 	/* used for rates that certain jobs are able to report */
84 	guint64 rate;
85 
86 	/* the current action */
87 	BraseroBurnAction current_action;
88 	gchar *action_string;
89 
90 	guint dangerous;
91 
92 	guint fake:1;
93 	guint action_changed:1;
94 	guint update_action_string:1;
95 
96 	guint written_changed:1;
97 	guint progress_changed:1;
98 	guint use_average_rate:1;
99 };
100 
101 #define BRASERO_TASK_CTX_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE ((o), BRASERO_TYPE_TASK_CTX, BraseroTaskCtxPrivate))
102 
103 G_DEFINE_TYPE (BraseroTaskCtx, brasero_task_ctx, G_TYPE_OBJECT);
104 
105 #define MAX_VALUE_AVERAGE	16
106 
107 enum _BraseroTaskCtxSignalType {
108 	ACTION_CHANGED_SIGNAL,
109 	PROGRESS_CHANGED_SIGNAL,
110 	LAST_SIGNAL
111 };
112 static guint brasero_task_ctx_signals [LAST_SIGNAL] = { 0 };
113 
114 enum
115 {
116 	PROP_0,
117 	PROP_ACTION,
118 	PROP_SESSION
119 };
120 
121 static GObjectClass* parent_class = NULL;
122 
123 void
brasero_task_ctx_set_dangerous(BraseroTaskCtx * self,gboolean value)124 brasero_task_ctx_set_dangerous (BraseroTaskCtx *self, gboolean value)
125 {
126 	BraseroTaskCtxPrivate *priv;
127 
128 	priv = BRASERO_TASK_CTX_PRIVATE (self);
129 	if (value)
130 		priv->dangerous ++;
131 	else
132 		priv->dangerous --;
133 }
134 
135 guint
brasero_task_ctx_get_dangerous(BraseroTaskCtx * self)136 brasero_task_ctx_get_dangerous (BraseroTaskCtx *self)
137 {
138 	BraseroTaskCtxPrivate *priv;
139 
140 	priv = BRASERO_TASK_CTX_PRIVATE (self);
141 	return priv->dangerous;
142 }
143 
144 void
brasero_task_ctx_reset(BraseroTaskCtx * self)145 brasero_task_ctx_reset (BraseroTaskCtx *self)
146 {
147 	BraseroTaskCtxPrivate *priv;
148 	GSList *tracks;
149 
150 	priv = BRASERO_TASK_CTX_PRIVATE (self);
151 
152 	if (priv->tracks) {
153 		g_slist_foreach (priv->tracks, (GFunc) g_object_unref, NULL);
154 		g_slist_free (priv->tracks);
155 		priv->tracks = NULL;
156 	}
157 
158 	tracks = brasero_burn_session_get_tracks (priv->session);
159 	BRASERO_BURN_LOG ("Setting current track (%i tracks)", g_slist_length (tracks));
160 	if (priv->current_track)
161 		g_object_unref (priv->current_track);
162 
163 	if (tracks) {
164 		priv->current_track = tracks->data;
165 		g_object_ref (priv->current_track);
166 	}
167 	else
168 		BRASERO_BURN_LOG ("no tracks");
169 
170 	if (priv->timer) {
171 		g_timer_destroy (priv->timer);
172 		priv->timer = NULL;
173 	}
174 
175 	priv->dangerous = 0;
176 	priv->progress = -1.0;
177 	priv->track_bytes = -1;
178 	priv->session_bytes = -1;
179 	priv->written_changed = 0;
180 
181 	priv->current_elapsed = 0;
182 	priv->last_written = 0;
183 	priv->last_elapsed = 0;
184 	priv->last_progress = 0;
185 
186 	if (priv->times) {
187 		g_slist_free (priv->times);
188 		priv->times = NULL;
189 	}
190 
191 	g_signal_emit (self,
192 		       brasero_task_ctx_signals [PROGRESS_CHANGED_SIGNAL],
193 		       0);
194 }
195 
196 void
brasero_task_ctx_set_fake(BraseroTaskCtx * ctx,gboolean fake)197 brasero_task_ctx_set_fake (BraseroTaskCtx *ctx,
198 			   gboolean fake)
199 {
200 	BraseroTaskCtxPrivate *priv;
201 
202 	priv = BRASERO_TASK_CTX_PRIVATE (ctx);
203 	priv->fake = fake;
204 }
205 
206 /**
207  * Used to get config
208  */
209 
210 BraseroBurnSession *
brasero_task_ctx_get_session(BraseroTaskCtx * self)211 brasero_task_ctx_get_session (BraseroTaskCtx *self)
212 {
213 	BraseroTaskCtxPrivate *priv;
214 
215 	g_return_val_if_fail (BRASERO_IS_TASK_CTX (self), NULL);
216 
217 	priv = BRASERO_TASK_CTX_PRIVATE (self);
218 	if (!priv->session)
219 		return NULL;
220 
221 	return priv->session;
222 }
223 
224 BraseroBurnResult
brasero_task_ctx_get_stored_tracks(BraseroTaskCtx * self,GSList ** tracks)225 brasero_task_ctx_get_stored_tracks (BraseroTaskCtx *self,
226 				    GSList **tracks)
227 {
228 	BraseroTaskCtxPrivate *priv;
229 
230 	priv = BRASERO_TASK_CTX_PRIVATE (self);
231 	if (!priv->current_track)
232 		return BRASERO_BURN_ERR;
233 
234 	if (tracks)
235 		*tracks = priv->tracks;
236 
237 	/* If no track has been added let the caller
238 	 * know with BRASERO_BURN_NOT_READY */
239 	if (!priv->tracks)
240 		return BRASERO_BURN_NOT_READY;
241 
242 	return BRASERO_BURN_OK;
243 }
244 
245 BraseroBurnResult
brasero_task_ctx_get_current_track(BraseroTaskCtx * self,BraseroTrack ** track)246 brasero_task_ctx_get_current_track (BraseroTaskCtx *self,
247 				    BraseroTrack **track)
248 {
249 	BraseroTaskCtxPrivate *priv;
250 
251 	g_return_val_if_fail (track != NULL, BRASERO_BURN_ERR);
252 
253 	priv = BRASERO_TASK_CTX_PRIVATE (self);
254 	if (!priv->current_track)
255 		return BRASERO_BURN_ERR;
256 
257 	*track = priv->current_track;
258 	return BRASERO_BURN_OK;
259 }
260 
261 BraseroTaskAction
brasero_task_ctx_get_action(BraseroTaskCtx * self)262 brasero_task_ctx_get_action (BraseroTaskCtx *self)
263 {
264 	BraseroTaskCtxPrivate *priv;
265 
266 	priv = BRASERO_TASK_CTX_PRIVATE (self);
267 
268 	if (priv->fake)
269 		return BRASERO_TASK_ACTION_NONE;
270 
271 	return priv->action;
272 }
273 
274 /**
275  * Used to report task status
276  */
277 
278 BraseroBurnResult
brasero_task_ctx_add_track(BraseroTaskCtx * self,BraseroTrack * track)279 brasero_task_ctx_add_track (BraseroTaskCtx *self,
280 			    BraseroTrack *track)
281 {
282 	BraseroTaskCtxPrivate *priv;
283 
284 	priv = BRASERO_TASK_CTX_PRIVATE (self);
285 
286 	BRASERO_BURN_LOG ("Adding track %s",
287 //			  brasero_track_get_track_type (track, NULL),
288 			  priv->tracks? "already some tracks":"");
289 
290 	/* Ref the track and store it for later. */
291 	g_object_ref (track);
292 	priv->tracks = g_slist_prepend (priv->tracks, track);
293 	return BRASERO_BURN_OK;
294 }
295 
296 static gboolean
brasero_task_ctx_set_next_track(BraseroTaskCtx * self)297 brasero_task_ctx_set_next_track (BraseroTaskCtx *self)
298 {
299 	BraseroTaskCtxPrivate *priv;
300 	GSList *tracks;
301 	GSList *node;
302 
303 	priv = BRASERO_TASK_CTX_PRIVATE (self);
304 
305 	/* we need to set the next track if our action is NORMAL or CHECKSUM */
306 	if (priv->action != BRASERO_TASK_ACTION_NORMAL
307 	&&  priv->action != BRASERO_TASK_ACTION_CHECKSUM)
308 		return BRASERO_BURN_OK;
309 
310 	/* see if there is another track left */
311 	tracks = brasero_burn_session_get_tracks (priv->session);
312 	node = g_slist_find (tracks, priv->current_track);
313 	if (!node || !node->next)
314 		return BRASERO_BURN_OK;
315 
316 	priv->session_bytes += priv->track_bytes;
317 	priv->track_bytes = 0;
318 	priv->last_written = 0;
319 	priv->progress = 0;
320 
321 	if (priv->current_track)
322 		g_object_unref (priv->current_track);
323 
324 	priv->current_track = node->next->data;
325 	g_object_ref (priv->current_track);
326 
327 	return BRASERO_BURN_RETRY;
328 }
329 
330 BraseroBurnResult
brasero_task_ctx_next_track(BraseroTaskCtx * self)331 brasero_task_ctx_next_track (BraseroTaskCtx *self)
332 
333 {
334 	BraseroBurnResult retval;
335 
336 	g_return_val_if_fail (BRASERO_IS_TASK_CTX (self), BRASERO_BURN_ERR);
337 
338 	retval = brasero_task_ctx_set_next_track (self);
339 	if (retval == BRASERO_BURN_RETRY) {
340 		BraseroTaskCtxClass *klass;
341 
342 		BRASERO_BURN_LOG ("Set next track to be processed");
343 
344 		klass = BRASERO_TASK_CTX_GET_CLASS (self);
345 		if (!klass->finished)
346 			return BRASERO_BURN_NOT_SUPPORTED;
347 
348 		klass->finished (self,
349 				 BRASERO_BURN_RETRY,
350 				 NULL);
351 		return BRASERO_BURN_RETRY;
352 	}
353 
354 	BRASERO_BURN_LOG ("No next track to process");
355 	return BRASERO_BURN_OK;
356 }
357 
358 BraseroBurnResult
brasero_task_ctx_finished(BraseroTaskCtx * self)359 brasero_task_ctx_finished (BraseroTaskCtx *self)
360 {
361 	BraseroTaskCtxPrivate *priv;
362 	BraseroTaskCtxClass *klass;
363 	GError *error = NULL;
364 	GSList *iter;
365 
366 	priv = BRASERO_TASK_CTX_PRIVATE (self);
367 	klass = BRASERO_TASK_CTX_GET_CLASS (self);
368 	if (!klass->finished)
369 		return BRASERO_BURN_NOT_SUPPORTED;
370 
371 	klass->finished (self,
372 			 BRASERO_BURN_OK,
373 			 error);
374 
375 	if (priv->tracks) {
376 		brasero_burn_session_push_tracks (priv->session);
377 		priv->tracks = g_slist_reverse (priv->tracks);
378 		for (iter = priv->tracks; iter; iter = iter->next) {
379 			BraseroTrack *track;
380 
381 			track = iter->data;
382 			brasero_burn_session_add_track (priv->session, track, NULL);
383 
384 			/* It's good practice to unref the track afterwards as
385 			 * we don't need it anymore. BraseroBurnSession refs it.
386 			 */
387 			g_object_unref (track);
388 		}
389 
390 		g_slist_free (priv->tracks);
391 		priv->tracks = NULL;
392 	}
393 
394 	return BRASERO_BURN_OK;
395 }
396 
397 BraseroBurnResult
brasero_task_ctx_error(BraseroTaskCtx * self,BraseroBurnResult retval,GError * error)398 brasero_task_ctx_error (BraseroTaskCtx *self,
399 			BraseroBurnResult retval,
400 			GError *error)
401 {
402 	BraseroTaskCtxClass *klass;
403 
404 	klass = BRASERO_TASK_CTX_GET_CLASS (self);
405 	if (!klass->finished)
406 		return BRASERO_BURN_NOT_SUPPORTED;
407 
408 	klass->finished (self,
409 			 retval,
410 			 error);
411 
412 	return BRASERO_BURN_OK;
413 }
414 
415 BraseroBurnResult
brasero_task_ctx_start_progress(BraseroTaskCtx * self,gboolean force)416 brasero_task_ctx_start_progress (BraseroTaskCtx *self,
417 				 gboolean force)
418 
419 {
420 	BraseroTaskCtxPrivate *priv;
421 
422 	g_return_val_if_fail (BRASERO_IS_TASK_CTX (self), BRASERO_BURN_ERR);
423 
424 	priv = BRASERO_TASK_CTX_PRIVATE (self);
425 
426 	if (!priv->timer) {
427 		priv->timer = g_timer_new ();
428 		priv->first_written = priv->session_bytes + priv->track_bytes;
429 		priv->first_progress = priv->progress;
430 	}
431 	else if (force) {
432 		g_timer_start (priv->timer);
433 		priv->first_written = priv->session_bytes + priv->track_bytes;
434 		priv->first_progress = priv->progress;
435 	}
436 
437 	return BRASERO_BURN_OK;
438 }
439 
440 static gdouble
brasero_task_ctx_get_average(GSList ** values,gdouble value)441 brasero_task_ctx_get_average (GSList **values, gdouble value)
442 {
443 	const unsigned int scale = 10000;
444 	unsigned int num = 0;
445 	gdouble average;
446 	gint32 int_value;
447 	GSList *l;
448 
449 	if (value * scale < G_MAXINT)
450 		int_value = (gint32) ceil (scale * value);
451 	else if (value / scale < G_MAXINT)
452 		int_value = (gint32) ceil (-1.0 * value / scale);
453 	else
454 		return value;
455 
456 	*values = g_slist_prepend (*values, GINT_TO_POINTER (int_value));
457 
458 	average = 0;
459 	for (l = *values; l; l = l->next) {
460 		gdouble r = (gdouble) GPOINTER_TO_INT (l->data);
461 
462 		if (r < 0)
463 			r *= scale * -1.0;
464 		else
465 			r /= scale;
466 
467 		average += r;
468 		num++;
469 		if (num == MAX_VALUE_AVERAGE && l->next)
470 			l = g_slist_delete_link (l, l->next);
471 	}
472 
473 	average /= num;
474 	return average;
475 }
476 
477 void
brasero_task_ctx_report_progress(BraseroTaskCtx * self)478 brasero_task_ctx_report_progress (BraseroTaskCtx *self)
479 {
480 	BraseroTaskCtxPrivate *priv;
481 	gdouble progress, elapsed;
482 
483 	priv = BRASERO_TASK_CTX_PRIVATE (self);
484 
485 	if (priv->action_changed) {
486 		/* Give a last progress-changed signal
487 		 * setting previous action as completely
488 		 * finished only if the plugin set any
489 		 * progress for it.
490 		 * This helps having the tray icon or the
491 		 * taskbar icon set to be full on quick
492 		 * burns. */
493 
494 		if (priv->progress >= 0.0
495 		||  priv->track_bytes >= 0
496 		||  priv->session_bytes >= 0) {
497 			goffset total = 0;
498 
499 			priv->progress = 1.0;
500 			priv->track_bytes = 0;
501 			brasero_task_ctx_get_session_output_size (self, NULL, &total);
502 			priv->session_bytes = total;
503 
504 			g_signal_emit (self,
505 				       brasero_task_ctx_signals [PROGRESS_CHANGED_SIGNAL],
506 				       0);
507 		}
508 
509 		g_signal_emit (self,
510 			       brasero_task_ctx_signals [ACTION_CHANGED_SIGNAL],
511 			       0,
512 			       priv->current_action);
513 
514 		brasero_task_ctx_reset_progress (self);
515 		g_signal_emit (self,
516 			       brasero_task_ctx_signals [PROGRESS_CHANGED_SIGNAL],
517 			       0);
518 
519 		priv->action_changed = 0;
520 	}
521 	else if (priv->update_action_string) {
522 		g_signal_emit (self,
523 			       brasero_task_ctx_signals [ACTION_CHANGED_SIGNAL],
524 			       0,
525 			       priv->current_action);
526 
527 		priv->update_action_string = 0;
528 	}
529 
530 	if (priv->timer) {
531 		elapsed = g_timer_elapsed (priv->timer, NULL);
532 		if (brasero_task_ctx_get_progress (self, &progress) == BRASERO_BURN_OK) {
533 			gdouble total_time;
534 
535 			total_time = (gdouble) elapsed / (gdouble) progress;
536 
537 			g_mutex_lock (priv->lock);
538 			priv->total_time = brasero_task_ctx_get_average (&priv->times,
539 									 total_time);
540 			g_mutex_unlock (priv->lock);
541 		}
542 	}
543 
544 	if (priv->progress_changed) {
545 		priv->progress_changed = 0;
546 		g_signal_emit (self,
547 			       brasero_task_ctx_signals [PROGRESS_CHANGED_SIGNAL],
548 			       0);
549 	}
550 	else if (priv->written_changed) {
551 		priv->written_changed = 0;
552 		g_signal_emit (self,
553 			       brasero_task_ctx_signals [PROGRESS_CHANGED_SIGNAL],
554 			       0);
555 	}
556 }
557 
558 BraseroBurnResult
brasero_task_ctx_set_rate(BraseroTaskCtx * self,gint64 rate)559 brasero_task_ctx_set_rate (BraseroTaskCtx *self,
560 			   gint64 rate)
561 {
562 	BraseroTaskCtxPrivate *priv;
563 
564 	g_return_val_if_fail (BRASERO_IS_TASK_CTX (self), BRASERO_BURN_ERR);
565 
566 	priv = BRASERO_TASK_CTX_PRIVATE (self);
567 	priv->rate = rate;
568 	return BRASERO_BURN_OK;
569 }
570 
571 /**
572  * This is used by jobs that are imaging to tell what's going to be the output
573  * size for a particular track
574  */
575 
576 BraseroBurnResult
brasero_task_ctx_set_output_size_for_current_track(BraseroTaskCtx * self,goffset sectors,goffset bytes)577 brasero_task_ctx_set_output_size_for_current_track (BraseroTaskCtx *self,
578 						    goffset sectors,
579 						    goffset bytes)
580 {
581 	BraseroTaskCtxPrivate *priv;
582 
583 	/* NOTE: we don't need block size here as it's pretty easy to have it by
584 	 * dividing size by sectors or by guessing it with image or audio format
585 	 * of the output */
586 
587 	g_return_val_if_fail (BRASERO_IS_TASK_CTX (self), BRASERO_BURN_ERR);
588 
589 	priv = BRASERO_TASK_CTX_PRIVATE (self);
590 
591 	/* we only allow plugins to set these values during the init phase of a
592 	 * task when it's fakely running. One exception is if size or blocks are
593 	 * 0 at the start of a task in normal mode */
594 	if (sectors >= 0)
595 		priv->blocks += sectors;
596 
597 	if (bytes >= 0)
598 		priv->size += bytes;
599 
600 	BRASERO_BURN_LOG ("Task output modified %lli blocks %lli bytes",
601 			  priv->blocks,
602 			  priv->size);
603 
604 	return BRASERO_BURN_OK;
605 }
606 
607 BraseroBurnResult
brasero_task_ctx_set_written_track(BraseroTaskCtx * self,gint64 written)608 brasero_task_ctx_set_written_track (BraseroTaskCtx *self,
609 				    gint64 written)
610 {
611 	BraseroTaskCtxPrivate *priv;
612 	gdouble elapsed = 0.0;
613 
614 	g_return_val_if_fail (BRASERO_IS_TASK_CTX (self), BRASERO_BURN_ERR);
615 
616 	priv = BRASERO_TASK_CTX_PRIVATE (self);
617 
618 	priv->written_changed = 1;
619 
620 	if (priv->use_average_rate) {
621 		priv->track_bytes = written;
622 		return BRASERO_BURN_OK;
623 	}
624 
625 	if (priv->timer)
626 		elapsed = g_timer_elapsed (priv->timer, NULL);
627 
628 	if ((elapsed - priv->last_elapsed) > 0.5) {
629 		priv->last_written = priv->track_bytes;
630 		priv->last_elapsed = priv->current_elapsed;
631 		priv->current_elapsed = elapsed;
632 	}
633 
634 	priv->track_bytes = written;
635 	return BRASERO_BURN_OK;
636 }
637 
638 BraseroBurnResult
brasero_task_ctx_set_written_session(BraseroTaskCtx * self,gint64 written)639 brasero_task_ctx_set_written_session (BraseroTaskCtx *self,
640 				      gint64 written)
641 {
642 	BraseroTaskCtxPrivate *priv;
643 
644 	g_return_val_if_fail (BRASERO_IS_TASK_CTX (self), BRASERO_BURN_ERR);
645 
646 	priv = BRASERO_TASK_CTX_PRIVATE (self);
647 
648 	priv->session_bytes = 0;
649 	return brasero_task_ctx_set_written_track (self, written);
650 }
651 
652 BraseroBurnResult
brasero_task_ctx_set_progress(BraseroTaskCtx * self,gdouble progress)653 brasero_task_ctx_set_progress (BraseroTaskCtx *self,
654 			       gdouble progress)
655 {
656 	BraseroTaskCtxPrivate *priv;
657 	gdouble elapsed;
658 
659 	g_return_val_if_fail (BRASERO_IS_TASK_CTX (self), BRASERO_BURN_ERR);
660 
661 	priv = BRASERO_TASK_CTX_PRIVATE (self);
662 
663 	priv->progress_changed = 1;
664 
665 	if (priv->use_average_rate) {
666 		if (priv->progress < progress)
667 			priv->progress = progress;
668 
669 		return BRASERO_BURN_OK;
670 	}
671 
672 	/* here we prefer to use track written bytes instead of progress.
673 	 * NOTE: usually plugins will return only one information. */
674 	if (priv->last_written) {
675 		if (priv->progress < progress)
676 			priv->progress = progress;
677 		return BRASERO_BURN_OK;
678 	}
679 
680 	if (priv->timer) {
681 		elapsed = g_timer_elapsed (priv->timer, NULL);
682 
683 		if ((elapsed - priv->last_elapsed) > 0.5) {
684 			priv->last_progress = priv->progress;
685 			priv->last_elapsed = priv->current_elapsed;
686 			priv->current_elapsed = elapsed;
687 		}
688 	}
689 
690 	if (priv->progress < progress)
691 		priv->progress = progress;
692 
693 	return BRASERO_BURN_OK;
694 }
695 
696 BraseroBurnResult
brasero_task_ctx_reset_progress(BraseroTaskCtx * self)697 brasero_task_ctx_reset_progress (BraseroTaskCtx *self)
698 {
699 	BraseroTaskCtxPrivate *priv;
700 
701 	g_return_val_if_fail (BRASERO_IS_TASK_CTX (self), BRASERO_BURN_ERR);
702 
703 	priv = BRASERO_TASK_CTX_PRIVATE (self);
704 
705 	priv->progress_changed = 1;
706 
707 	if (priv->timer) {
708 		g_timer_destroy (priv->timer);
709 		priv->timer = NULL;
710 	}
711 
712 	priv->dangerous = 0;
713 	priv->progress = -1.0;
714 	priv->track_bytes = -1;
715 	priv->session_bytes = -1;
716 
717 	priv->current_elapsed = 0;
718 	priv->last_written = 0;
719 	priv->last_elapsed = 0;
720 	priv->last_progress = 0;
721 
722 	if (priv->times) {
723 		g_slist_free (priv->times);
724 		priv->times = NULL;
725 	}
726 
727 	return BRASERO_BURN_OK;
728 }
729 
730 BraseroBurnResult
brasero_task_ctx_set_current_action(BraseroTaskCtx * self,BraseroBurnAction action,const gchar * string,gboolean force)731 brasero_task_ctx_set_current_action (BraseroTaskCtx *self,
732 				     BraseroBurnAction action,
733 				     const gchar *string,
734 				     gboolean force)
735 {
736 	BraseroTaskCtxPrivate *priv;
737 
738 	g_return_val_if_fail (BRASERO_IS_TASK_CTX (self), BRASERO_BURN_ERR);
739 
740 	priv = BRASERO_TASK_CTX_PRIVATE (self);
741 
742 	if (priv->current_action == action) {
743 		if (!force)
744 			return BRASERO_BURN_OK;
745 
746 		g_mutex_lock (priv->lock);
747 
748 		priv->update_action_string = 1;
749 	}
750 	else {
751 		g_mutex_lock (priv->lock);
752 
753 		priv->current_action = action;
754 		priv->action_changed = 1;
755 	}
756 
757 	if (priv->action_string)
758 		g_free (priv->action_string);
759 
760 	priv->action_string = string ? g_strdup (string): NULL;
761 
762 	if (!force) {
763 		g_slist_free (priv->times);
764 		priv->times = NULL;
765 	}
766 
767 	g_mutex_unlock (priv->lock);
768 
769 	return BRASERO_BURN_OK;
770 }
771 
772 BraseroBurnResult
brasero_task_ctx_set_use_average(BraseroTaskCtx * self,gboolean use_average)773 brasero_task_ctx_set_use_average (BraseroTaskCtx *self,
774 				  gboolean use_average)
775 {
776 	BraseroTaskCtxPrivate *priv;
777 
778 	g_return_val_if_fail (BRASERO_IS_TASK_CTX (self), BRASERO_BURN_ERR);
779 
780 	priv = BRASERO_TASK_CTX_PRIVATE (self);
781 	priv->use_average_rate = use_average;
782 	return BRASERO_BURN_OK;
783 }
784 
785 /**
786  * Used to retrieve the values for a given task
787  */
788 
789 BraseroBurnResult
brasero_task_ctx_get_rate(BraseroTaskCtx * self,guint64 * rate)790 brasero_task_ctx_get_rate (BraseroTaskCtx *self,
791 			   guint64 *rate)
792 {
793 	BraseroTaskCtxPrivate *priv;
794 
795 	g_return_val_if_fail (BRASERO_IS_TASK_CTX (self), BRASERO_BURN_ERR);
796 	g_return_val_if_fail (rate != NULL, BRASERO_BURN_ERR);
797 
798 	priv = BRASERO_TASK_CTX_PRIVATE (self);
799 
800 	if (priv->current_action != BRASERO_BURN_ACTION_RECORDING
801 	&&  priv->current_action != BRASERO_BURN_ACTION_DRIVE_COPY) {
802 		*rate = -1;
803 		return BRASERO_BURN_NOT_SUPPORTED;
804 	}
805 
806 	if (priv->rate) {
807 		*rate = priv->rate;
808 		return BRASERO_BURN_OK;
809 	}
810 
811 	if (priv->use_average_rate) {
812 		gdouble elapsed;
813 
814 		if (!priv->timer)
815 			return BRASERO_BURN_NOT_READY;
816 
817 		elapsed = g_timer_elapsed (priv->timer, NULL);
818 
819 		if ((priv->session_bytes + priv->track_bytes) > 0)
820 			*rate = (gdouble) ((priv->session_bytes + priv->track_bytes) - priv->first_written) / elapsed;
821 		else if (priv->progress > 0.0)
822 			*rate = (gdouble) (priv->progress - priv->first_progress) * priv->size / elapsed;
823 		else
824 			return BRASERO_BURN_NOT_READY;
825 	}
826 	else {
827 		if (priv->last_written > 0) {
828 			*rate = (gdouble) (priv->track_bytes - priv->last_written) /
829 				(gdouble) (priv->current_elapsed - priv->last_elapsed);
830 		}
831 		else if (priv->last_progress > 0.0) {
832 			*rate = (gdouble)  priv->size *
833 				(gdouble) (priv->progress - priv->last_progress) /
834 				(gdouble) (priv->current_elapsed - priv->last_elapsed);
835 		}
836 		else
837 			return BRASERO_BURN_NOT_READY;
838 	}
839 
840 	return BRASERO_BURN_OK;
841 }
842 
843 BraseroBurnResult
brasero_task_ctx_get_remaining_time(BraseroTaskCtx * self,long * remaining)844 brasero_task_ctx_get_remaining_time (BraseroTaskCtx *self,
845 				     long *remaining)
846 {
847 	BraseroTaskCtxPrivate *priv;
848 	gdouble elapsed;
849 	gint len;
850 
851 	g_return_val_if_fail (BRASERO_IS_TASK_CTX (self), BRASERO_BURN_ERR);
852 	g_return_val_if_fail (remaining != NULL, BRASERO_BURN_ERR);
853 
854 	priv = BRASERO_TASK_CTX_PRIVATE (self);
855 
856 	g_mutex_lock (priv->lock);
857 	len = g_slist_length (priv->times);
858 	g_mutex_unlock (priv->lock);
859 
860 	if (len < MAX_VALUE_AVERAGE)
861 		return BRASERO_BURN_NOT_READY;
862 
863 	elapsed = g_timer_elapsed (priv->timer, NULL);
864 	*remaining = priv->total_time - elapsed;
865 
866 	return BRASERO_BURN_OK;
867 }
868 
869 BraseroBurnResult
brasero_task_ctx_get_session_output_size(BraseroTaskCtx * self,goffset * blocks,goffset * bytes)870 brasero_task_ctx_get_session_output_size (BraseroTaskCtx *self,
871 					  goffset *blocks,
872 					  goffset *bytes)
873 {
874 	BraseroTaskCtxPrivate *priv;
875 
876 	g_return_val_if_fail (BRASERO_IS_TASK_CTX (self), BRASERO_BURN_ERR);
877 	g_return_val_if_fail (blocks != NULL || bytes != NULL, BRASERO_BURN_ERR);
878 
879 	priv = BRASERO_TASK_CTX_PRIVATE (self);
880 
881 	if (priv->size <= 0 && priv->blocks <= 0)
882 		return BRASERO_BURN_NOT_READY;
883 
884 	if (bytes)
885 		*bytes = priv->size;
886 
887 	if (blocks)
888 		*blocks = priv->blocks;
889 
890 	return BRASERO_BURN_OK;
891 }
892 
893 BraseroBurnResult
brasero_task_ctx_get_written(BraseroTaskCtx * self,gint64 * written)894 brasero_task_ctx_get_written (BraseroTaskCtx *self,
895 			      gint64 *written)
896 {
897 	BraseroTaskCtxPrivate *priv;
898 
899 	g_return_val_if_fail (BRASERO_IS_TASK_CTX (self), BRASERO_BURN_ERR);
900 	g_return_val_if_fail (written != NULL, BRASERO_BURN_ERR);
901 
902 	priv = BRASERO_TASK_CTX_PRIVATE (self);
903 
904 	if (priv->track_bytes + priv->session_bytes <= 0)
905 		return BRASERO_BURN_NOT_READY;
906 
907 	if (!written)
908 		return BRASERO_BURN_OK;
909 
910 	*written = priv->track_bytes + priv->session_bytes;
911 	return BRASERO_BURN_OK;
912 }
913 
914 BraseroBurnResult
brasero_task_ctx_get_current_action_string(BraseroTaskCtx * self,BraseroBurnAction action,gchar ** string)915 brasero_task_ctx_get_current_action_string (BraseroTaskCtx *self,
916 					    BraseroBurnAction action,
917 					    gchar **string)
918 {
919 	BraseroTaskCtxPrivate *priv;
920 
921 	g_return_val_if_fail (string != NULL, BRASERO_BURN_ERR);
922 
923 	priv = BRASERO_TASK_CTX_PRIVATE (self);
924 
925 	if (action != priv->current_action)
926 		return BRASERO_BURN_ERR;
927 
928 	*string = priv->action_string ? g_strdup (priv->action_string):
929 					g_strdup (brasero_burn_action_to_string (priv->current_action));
930 
931 	return BRASERO_BURN_OK;
932 }
933 
934 BraseroBurnResult
brasero_task_ctx_get_progress(BraseroTaskCtx * self,gdouble * progress)935 brasero_task_ctx_get_progress (BraseroTaskCtx *self,
936 			       gdouble *progress)
937 {
938 	BraseroTaskCtxPrivate *priv;
939 	gdouble track_num = 0;
940 	gdouble track_nb = 0;
941 	goffset total = 0;
942 
943 	priv = BRASERO_TASK_CTX_PRIVATE (self);
944 
945 	/* The following can happen when we're blanking since there's no track */
946 	if (priv->action == BRASERO_TASK_ACTION_ERASE) {
947 		track_num = 1.0;
948 		track_nb = 0.0;
949 	}
950 	else {
951 		GSList *tracks;
952 
953 		tracks = brasero_burn_session_get_tracks (priv->session);
954 		track_num = g_slist_length (tracks);
955 		track_nb = g_slist_index (tracks, priv->current_track);
956 	}
957 
958 	if (priv->progress >= 0.0) {
959 		if (progress)
960 			*progress = (gdouble) (track_nb + priv->progress) / (gdouble) track_num;
961 
962 		return BRASERO_BURN_OK;
963 	}
964 
965 	total = 0;
966 	brasero_task_ctx_get_session_output_size (self, NULL, &total);
967 
968 	if ((priv->session_bytes + priv->track_bytes) <= 0 || total <= 0) {
969 		/* if brasero_task_ctx_start_progress () was called (and a timer
970 		 * created), assume that the task will report either a progress
971 		 * of the written bytes. Then it means we just started. */
972 		if (priv->timer) {
973 			if (progress)
974 				*progress = 0.0;
975 
976 			return BRASERO_BURN_OK;
977 		}
978 
979 		if (progress)
980 			*progress = -1.0;
981 
982 		return BRASERO_BURN_NOT_READY;
983 	}
984 
985 	if (!progress)
986 		return BRASERO_BURN_OK;
987 
988 	*progress = (gdouble) ((gdouble) (priv->track_bytes + priv->session_bytes) / (gdouble)  total);
989 	return BRASERO_BURN_OK;
990 }
991 
992 BraseroBurnResult
brasero_task_ctx_get_current_action(BraseroTaskCtx * self,BraseroBurnAction * action)993 brasero_task_ctx_get_current_action (BraseroTaskCtx *self,
994 				     BraseroBurnAction *action)
995 {
996 	BraseroTaskCtxPrivate *priv;
997 
998 	g_return_val_if_fail (action != NULL, BRASERO_BURN_ERR);
999 
1000 	priv = BRASERO_TASK_CTX_PRIVATE (self);
1001 
1002 	g_mutex_lock (priv->lock);
1003 	*action = priv->current_action;
1004 	g_mutex_unlock (priv->lock);
1005 
1006 	return BRASERO_BURN_OK;
1007 }
1008 
1009 void
brasero_task_ctx_stop_progress(BraseroTaskCtx * self)1010 brasero_task_ctx_stop_progress (BraseroTaskCtx *self)
1011 {
1012 	BraseroTaskCtxPrivate *priv;
1013 
1014 	priv = BRASERO_TASK_CTX_PRIVATE (self);
1015 
1016 	/* one last report */
1017 	g_signal_emit (self,
1018 		       brasero_task_ctx_signals [PROGRESS_CHANGED_SIGNAL],
1019 		       0);
1020 
1021 	priv->current_action = BRASERO_BURN_ACTION_NONE;
1022 	priv->action_changed = 0;
1023 	priv->update_action_string = 0;
1024 
1025 	if (priv->timer) {
1026 		g_timer_destroy (priv->timer);
1027 		priv->timer = NULL;
1028 	}
1029 	priv->first_written = 0;
1030 	priv->first_progress = 0.0;
1031 
1032 	g_mutex_lock (priv->lock);
1033 
1034 	if (priv->action_string) {
1035 		g_free (priv->action_string);
1036 		priv->action_string = NULL;
1037 	}
1038 
1039 	if (priv->times) {
1040 		g_slist_free (priv->times);
1041 		priv->times = NULL;
1042 	}
1043 
1044 	g_mutex_unlock (priv->lock);
1045 }
1046 
1047 static void
brasero_task_ctx_init(BraseroTaskCtx * object)1048 brasero_task_ctx_init (BraseroTaskCtx *object)
1049 {
1050 	BraseroTaskCtxPrivate *priv;
1051 
1052 	priv = BRASERO_TASK_CTX_PRIVATE (object);
1053 	priv->lock = g_mutex_new ();
1054 }
1055 
1056 static void
brasero_task_ctx_finalize(GObject * object)1057 brasero_task_ctx_finalize (GObject *object)
1058 {
1059 	BraseroTaskCtxPrivate *priv;
1060 
1061 	priv = BRASERO_TASK_CTX_PRIVATE (object);
1062 
1063 	if (priv->lock) {
1064 		g_mutex_free (priv->lock);
1065 		priv->lock = NULL;
1066 	}
1067 
1068 	if (priv->timer) {
1069 		g_timer_destroy (priv->timer);
1070 		priv->timer = NULL;
1071 	}
1072 
1073 	if (priv->current_track) {
1074 		g_object_unref (priv->current_track);
1075 		priv->current_track = NULL;
1076 	}
1077 
1078 	if (priv->tracks) {
1079 		g_slist_foreach (priv->tracks, (GFunc) g_object_unref, NULL);
1080 		g_slist_free (priv->tracks);
1081 		priv->tracks = NULL;
1082 	}
1083 
1084 	if (priv->session) {
1085 		g_object_unref (priv->session);
1086 		priv->session = NULL;
1087 	}
1088 
1089 	G_OBJECT_CLASS (parent_class)->finalize (object);
1090 }
1091 
1092 static void
brasero_task_ctx_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1093 brasero_task_ctx_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
1094 {
1095 	BraseroTaskCtx *self;
1096 	BraseroTaskCtxPrivate *priv;
1097 
1098 	g_return_if_fail (BRASERO_IS_TASK_CTX (object));
1099 
1100 	self = BRASERO_TASK_CTX (object);
1101 	priv = BRASERO_TASK_CTX_PRIVATE (self);
1102 
1103 	switch (prop_id)
1104 	{
1105 	case PROP_ACTION:
1106 		priv->action = g_value_get_int (value);
1107 		break;
1108 	case PROP_SESSION:
1109 		priv->session = g_value_get_object (value);
1110 		g_object_ref (priv->session);
1111 		break;
1112 	default:
1113 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1114 		break;
1115 	}
1116 }
1117 
1118 static void
brasero_task_ctx_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1119 brasero_task_ctx_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
1120 {
1121 	BraseroTaskCtx *self;
1122 	BraseroTaskCtxPrivate *priv;
1123 
1124 	g_return_if_fail (BRASERO_IS_TASK_CTX (object));
1125 
1126 	self = BRASERO_TASK_CTX (object);
1127 	priv = BRASERO_TASK_CTX_PRIVATE (self);
1128 
1129 	switch (prop_id)
1130 	{
1131 	case PROP_ACTION:
1132 		g_value_set_int (value, priv->action);
1133 		break;
1134 	case PROP_SESSION:
1135 		g_value_set_object (value, priv->session);
1136 		break;
1137 	default:
1138 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1139 		break;
1140 	}
1141 }
1142 
1143 static void
brasero_task_ctx_class_init(BraseroTaskCtxClass * klass)1144 brasero_task_ctx_class_init (BraseroTaskCtxClass *klass)
1145 {
1146 	GObjectClass* object_class = G_OBJECT_CLASS (klass);
1147 	parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
1148 
1149 	g_type_class_add_private (klass, sizeof (BraseroTaskCtxPrivate));
1150 
1151 	object_class->finalize = brasero_task_ctx_finalize;
1152 	object_class->set_property = brasero_task_ctx_set_property;
1153 	object_class->get_property = brasero_task_ctx_get_property;
1154 
1155 	brasero_task_ctx_signals [PROGRESS_CHANGED_SIGNAL] =
1156 	    g_signal_new ("progress_changed",
1157 			  G_TYPE_FROM_CLASS (klass),
1158 			  G_SIGNAL_RUN_LAST,
1159 			  0,
1160 			  NULL, NULL,
1161 			  g_cclosure_marshal_VOID__VOID,
1162 			  G_TYPE_NONE,
1163 			  0);
1164 
1165 	brasero_task_ctx_signals [ACTION_CHANGED_SIGNAL] =
1166 	    g_signal_new ("action_changed",
1167 			  G_TYPE_FROM_CLASS (klass),
1168 			  G_SIGNAL_RUN_LAST,
1169 			  0,
1170 			  NULL, NULL,
1171 			  g_cclosure_marshal_VOID__INT,
1172 			  G_TYPE_NONE,
1173 			  1,
1174 			  G_TYPE_INT);
1175 
1176 	g_object_class_install_property (object_class,
1177 	                                 PROP_ACTION,
1178 	                                 g_param_spec_int ("action",
1179 							   "The action the task must perform",
1180 							   "The action the task must perform",
1181 							   BRASERO_TASK_ACTION_ERASE,
1182 							   BRASERO_TASK_ACTION_CHECKSUM,
1183 							   BRASERO_TASK_ACTION_NORMAL,
1184 							   G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1185 
1186 	g_object_class_install_property (object_class,
1187 	                                 PROP_SESSION,
1188 	                                 g_param_spec_object ("session",
1189 	                                                      "The session this object is tied to",
1190 	                                                      "The session this object is tied to",
1191 	                                                      BRASERO_TYPE_BURN_SESSION,
1192 	                                                      G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1193 }
1194