Logo Search packages:      
Sourcecode: nautilus-cd-burner version File versions  Download package

nautilus-burn-recorder.c

/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
 *
 * nautilus-burn-recorder.c
 * 
 * Copyright (C) 2002-2004 Bastien Nocera <hadess@hadess.net>
 * Copyright (C) 2005 William Jon McCann <mccann@jhu.edu>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * Authors: Bastien Nocera <hadess@hadess.net>
 *          William Jon McCann <mccann@jhu.edu>
 */

#include "config.h"

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#ifdef __FreeBSD__
#include <sys/uio.h>
#endif /* __FreeBSD__ */
#include <signal.h>
#include <time.h>

#include <glib.h>
#include <glib/gi18n.h>

#ifdef USE_HAL
#include <libhal.h>
#endif

#include "nautilus-burn-recorder.h"
#include "nautilus-burn-recorder-marshal.h"

#define CDR_SPEED 153600
#define DVD_SPEED 1385000

typedef struct _NautilusBurnProcess      NautilusBurnProcess;
typedef struct _NautilusBurnProcessFuncs NautilusBurnProcessFuncs;

struct _NautilusBurnProcess {
      GMainLoop                *loop;
      GPid                      pid;

      int                       result;
      char                     *last_error;

      GString                  *line;
      GString                  *line_stderr;
      NautilusBurnProcessFuncs *funcs;

      int                       pipe_stdin;

      time_t                    start_time;
      gint64                    start_num;
      GList                    *rates;

      gboolean                  changed_text;
      gboolean                  expect_process_to_die;

      gboolean                  dangerous;
      gboolean                  debug;
};

typedef gboolean (*NautilusBurnProcessLineFunc) (NautilusBurnProcess *process,
                                     const char          *line,
                                     gpointer             data);

struct _NautilusBurnProcessFuncs {
      gboolean (*out_line) (NautilusBurnProcess *process,
                        const char          *line,
                        gpointer             data);
      gboolean (*err_line) (NautilusBurnProcess *process,
                        const char          *line,
                        gpointer             data);
};

#define NAUTILUS_BURN_RECORDER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NAUTILUS_BURN_TYPE_RECORDER, NautilusBurnRecorderPrivate))

struct NautilusBurnRecorderPrivate {
      NautilusBurnProcess *process;

      GList               *tracks;
      gint32               track_count;
      guint32              current_track_num;
      guint64              current_track_end_pos;
      guint64              tracks_total_bytes;
      gboolean             debug;
      gboolean             can_rewrite;

#ifdef USE_HAL
      LibHalContext *ctx;
#endif
};

/* Signals */
enum {
      PROGRESS_CHANGED,
      ACTION_CHANGED,
      ANIMATION_CHANGED,
      INSERT_MEDIA_REQUEST,
      WARN_DATA_LOSS,
      LAST_SIGNAL
};

static int nautilus_burn_recorder_table_signals [LAST_SIGNAL] = { 0 };

static GObjectClass *parent_class = NULL;

static void nautilus_burn_recorder_class_init      (NautilusBurnRecorderClass     *class);
static void nautilus_burn_recorder_init            (NautilusBurnRecorder          *recorder);

static int  nautilus_burn_recorder_write_cdrecord  (NautilusBurnRecorder          *recorder,
                                        NautilusBurnDrive             *drive,
                                        GList                         *tracks,
                                        int                            speed,
                                        NautilusBurnRecorderWriteFlags flags,
                                        GError                       **error);
static int  nautilus_burn_recorder_write_growisofs (NautilusBurnRecorder          *recorder,
                                        NautilusBurnDrive             *drive,
                                        GList                         *tracks,
                                        int                            speed,
                                        NautilusBurnRecorderWriteFlags flags,
                                        GError                       **error);

G_DEFINE_TYPE(NautilusBurnRecorder, nautilus_burn_recorder, G_TYPE_OBJECT);

GQuark
nautilus_burn_recorder_error_quark (void)
{
      static GQuark quark = 0;
      if (! quark)
            quark = g_quark_from_static_string ("nautilus_burn_recorder_error");

      return quark;
}

static NautilusBurnProcess *
nautilus_burn_process_new ()
{
      NautilusBurnProcess      *process;
      NautilusBurnProcessFuncs *funcs;

      process = g_new0 (NautilusBurnProcess, 1);
      funcs = g_new0 (NautilusBurnProcessFuncs, 1);
      process->funcs = funcs;

      return process;
}

static void
nautilus_burn_process_free (NautilusBurnProcess *process)
{
      if (! process) {
            return;
      }

      g_free (process->last_error);

      if (process->line) {
            g_string_free (process->line, TRUE);
      }

      if (process->line_stderr) {
            g_string_free (process->line_stderr, TRUE);
      }

      g_list_free (process->rates);

      g_free (process->funcs);
      g_free (process);
}

static gboolean
nautilus_burn_process_cancel (NautilusBurnProcess *process,
                        gboolean             skip_if_dangerous)
{

      if (!process->dangerous || !skip_if_dangerous) {
            kill (process->pid, SIGINT);

            process->result = NAUTILUS_BURN_RECORDER_RESULT_CANCEL;

            if (g_main_loop_is_running (process->loop))
                  g_main_loop_quit (process->loop);

            return TRUE;
      }

      return FALSE;
}

/**
 * nautilus_burn_recorder_track_free:
 * @track: #NautilusBurnRecorderTrack
 *
 * Free track data.
 *
 **/
void
nautilus_burn_recorder_track_free (NautilusBurnRecorderTrack *track)
{
      switch (track->type) {
      case NAUTILUS_BURN_RECORDER_TRACK_TYPE_CUE:
            g_free (track->contents.cue.filename);
            break;
      case NAUTILUS_BURN_RECORDER_TRACK_TYPE_DATA:
            g_free (track->contents.data.filename);
            break;
      case NAUTILUS_BURN_RECORDER_TRACK_TYPE_AUDIO:
            g_free (track->contents.audio.filename);
            g_free (track->contents.audio.cdtext);
            break;
      default:
            g_warning ("Invalid track type %d", track->type);
            break;
      }

      g_free (track);
}

static gboolean
can_burn_dvds (NautilusBurnDrive *recorder)
{
      if (!(recorder->type & NAUTILUS_BURN_DRIVE_TYPE_DVD_RW_RECORDER) &&
          !(recorder->type & NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_R_RECORDER) &&
          !(recorder->type & NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_R_DL_RECORDER) &&
          !(recorder->type & NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_RW_RECORDER)) {
            return FALSE;
      }

      return TRUE;
}

static gboolean
cd_write_needs_growisofs (NautilusBurnDrive    *recorder,
                    NautilusBurnMediaType type,
                    GList                *tracks)
{
      GList *l;

      /* If we cannot burn DVDs, then we don't need growisofs */
      if (can_burn_dvds (recorder) == FALSE) {
            return FALSE;
      }

      if (type == NAUTILUS_BURN_MEDIA_TYPE_DVDR
          || type == NAUTILUS_BURN_MEDIA_TYPE_DVDRW
          || type == NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_R
          || type == NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_R_DL
          || type == NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_RW) {
            return TRUE;
      }

      /* If we have audio tracks, we're using cdrecord */
      for (l = tracks; l != NULL; l = l->next) {
            NautilusBurnRecorderTrack *track = l->data;
            if (track->type == NAUTILUS_BURN_RECORDER_TRACK_TYPE_AUDIO) {
                  return FALSE;
            }
      }

      return FALSE;
}

/**
 * nautilus_burn_recorder_cancel:
 * @recorder: #NautilusBurnRecorder
 * @skip_if_dangerous: 
 *
 * Cancel active writing process.
 *
 * Return value: %TRUE if succesfully cancelled, %FALSE otherwise
 **/
gboolean
nautilus_burn_recorder_cancel (NautilusBurnRecorder *recorder,
                         gboolean              skip_if_dangerous)
{
      gboolean res;

      g_return_val_if_fail (recorder != NULL, FALSE);

      g_return_val_if_fail (recorder->priv->process != NULL, FALSE);

      res = nautilus_burn_process_cancel (recorder->priv->process,
                                  skip_if_dangerous);

      return res;
}

static void
insert_cd_retry (NautilusBurnProcess *process,
             gboolean             cancel,
             gboolean             is_reload,
             gboolean             send_return)
{
      if (cancel) {
            /* Shouldn't be dangerous on reload */
            nautilus_burn_process_cancel (process, FALSE);
      } else if (is_reload) {
            if (send_return) {
                  write (process->pipe_stdin, "\n", 1);
            } else {
                  kill (process->pid, SIGUSR1);
            }
      } else {
            process->result = NAUTILUS_BURN_RECORDER_RESULT_RETRY;
            if (g_main_loop_is_running (process->loop))
                  g_main_loop_quit (process->loop);
      }
}

static long
compute_time_remaining (gint64 bytes,
                  double bytes_per_sec)
{
      long secs;

      if (bytes_per_sec <= 1) {
            return -1;
      }

      secs = bytes / bytes_per_sec;

      return secs;
}

static gboolean
growisofs_stdout_line (NautilusBurnProcess   *process,
                   const char            *line,
                   gpointer               data)
{
      NautilusBurnRecorder *recorder = data;
      int                   perc_1, perc_2;
      long long             b_written, b_total;
      float                 speed;

      if (line && process->debug) {
            g_print ("growisofs stdout: %s", line);
      }

      if (sscanf (line, "%10lld/%lld ( %2d.%1d%%) @%fx,", &b_written, &b_total, &perc_1, &perc_2, &speed) == 5) {
            double percent;
            long   secs;

            if (!process->changed_text) {
                  g_signal_emit (recorder,
                               nautilus_burn_recorder_table_signals [ACTION_CHANGED], 0,
                               NAUTILUS_BURN_RECORDER_ACTION_WRITING,
                               NAUTILUS_BURN_RECORDER_MEDIA_DVD);
            }

            percent = (perc_1 + ((float) perc_2 / 10)) / 100;
            secs = compute_time_remaining (b_total - b_written, (double)speed * DVD_SPEED);

            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [PROGRESS_CHANGED], 0,
                         percent, secs);
      } else if (strstr (line, "About to execute") != NULL) {
            process->dangerous = TRUE;
      }

      return TRUE;
}

static gboolean
growisofs_blank_stdout_line (NautilusBurnProcess   *process,
                       const char            *line,
                       gpointer               data)
{
      NautilusBurnRecorder *recorder = data;
      int                   perc_1, perc_2;
      long long             b_written, b_total;
      float                 speed;

      if (line && process->debug) {
            g_print ("growisofs blank stdout: %s", line);
      }

      if (sscanf (line, "%10lld/%lld ( %2d.%1d%%) @%fx,", &b_written, &b_total, &perc_1, &perc_2, &speed) == 5) {
            double percent;
            long   secs;

            if (!process->changed_text) {
                  g_signal_emit (recorder,
                               nautilus_burn_recorder_table_signals [ACTION_CHANGED], 0,
                               NAUTILUS_BURN_RECORDER_ACTION_BLANKING,
                               NAUTILUS_BURN_RECORDER_MEDIA_DVD);
            }

            percent = (perc_1 + ((float) perc_2 / 10)) / 100;
            secs = compute_time_remaining (b_total - b_written, (double)speed * DVD_SPEED);

            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [PROGRESS_CHANGED], 0,
                         percent, secs);
      } else if (strstr (line, "About to execute") != NULL) {
            process->dangerous = TRUE;
      }

      return TRUE;
}

static gdouble
get_average_rate (GList  **rates,
              gdouble  rate)
{
      const unsigned int max_num = 16;
      const unsigned int scale  = 1000;
      gdouble            average;
      gint32             int_rate;
      GList             *l;

      if (g_list_length (*rates) > max_num) {
            *rates = g_list_delete_link (*rates, *rates);
      }
      int_rate = (gint32)ceil (scale * rate);

      *rates = g_list_append (*rates, GINT_TO_POINTER (int_rate));

      average = 0;
      for (l = *rates; l != NULL; l = l->next) {
            gdouble r = (gdouble)GPOINTER_TO_INT (l->data) / scale;
            average += r;
      }

      average /= g_list_length (*rates);

      return average;
}

static gboolean
cdrecord_stdout_line (NautilusBurnProcess   *process,
                  const char            *line,
                  gpointer               data)
{
      NautilusBurnRecorder *recorder = data;
      unsigned int          track, mb_written, mb_total;
      float                 speed;
      int                   tmp;

      if (line && process->debug) {
            g_print ("cdrecord stdout: %s", line);
      }

      if (sscanf (line, "Track %2u: %d of %d MB written (fifo %d%%) [buf %d%%] %fx.",
                &track, &mb_written, &mb_total, &tmp, &tmp, &speed) == 6) {
            double       percent;
            gint64       bytes;
            gint64       this_remain;
            gint64       total;
            long         secs;
                        
            process->result = NAUTILUS_BURN_RECORDER_RESULT_ERROR;
                        
            if (recorder->priv->tracks_total_bytes > 0) {
                  total = recorder->priv->tracks_total_bytes;
            } else {
                  total = mb_total * 1048576;
            }

            if (track > recorder->priv->current_track_num) {
                  recorder->priv->current_track_num = track;
                  recorder->priv->current_track_end_pos += mb_total * 1048576;
            }

            this_remain = (mb_total - mb_written) * 1048576;
            bytes = (total - recorder->priv->current_track_end_pos) + this_remain;

            secs = -1;
            if (speed > 0) {
                  gdouble ave_rate;
                  gdouble rate;

                  rate = (double)speed * CDR_SPEED;

                  ave_rate = get_average_rate (&process->rates, rate);
                  secs = compute_time_remaining (bytes, ave_rate);
            }

            if (!process->changed_text) {
                  g_signal_emit (recorder,
                               nautilus_burn_recorder_table_signals [ACTION_CHANGED], 0,
                               NAUTILUS_BURN_RECORDER_ACTION_WRITING,
                               NAUTILUS_BURN_RECORDER_MEDIA_CD);
            }

            if (recorder->priv->tracks_total_bytes > 0) {
                  percent = 0.98 * (1.0 - (double)bytes / (double)recorder->priv->tracks_total_bytes);
            } else {
                  percent = 0.98 * ((double)((track - 1) / (double)recorder->priv->track_count)
                                + ((double)mb_written / mb_total) / (double)recorder->priv->track_count);
            }

            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [PROGRESS_CHANGED], 0,
                         percent, secs);
      } else if (sscanf (line, "Track %*d: %*s %d MB ", &mb_total)) {
            /* %s can be either "data" or "audio" */
            if (mb_total > 0) {
                  recorder->priv->tracks_total_bytes += mb_total * 1048576;
            }
      } else if (g_str_has_prefix (line, "Re-load disk and hit <CR>") ||
               g_str_has_prefix (line, "send SIGUSR1 to continue")) {
            gboolean  res;

            /* This is not supposed to happen since we checked for the cd
               before starting, but we try to handle it anyway, since mmc
               profiling can fail. */

            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [INSERT_MEDIA_REQUEST],
                         0, TRUE, recorder->priv->can_rewrite,
                         FALSE, &res);

            process->expect_process_to_die = TRUE;

            insert_cd_retry (process, !res, TRUE, (*line == 'R'));

      } else if (g_str_has_prefix (line, "Fixating...")) {
            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [ACTION_CHANGED], 0,
                         NAUTILUS_BURN_RECORDER_ACTION_FIXATING,
                         NAUTILUS_BURN_RECORDER_MEDIA_CD);
      } else if (g_str_has_prefix (line, "Fixating time:")) {
            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [PROGRESS_CHANGED], 0,
                         1.0, (long)-1);
            process->result = NAUTILUS_BURN_RECORDER_RESULT_FINISHED;
      } else if (g_str_has_prefix (line, "Last chance to quit, ")) {
            process->dangerous = TRUE;
      } else if (g_str_has_prefix (line, "Blanking PMA, TOC, pregap")) {
            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [ACTION_CHANGED], 0,
                         NAUTILUS_BURN_RECORDER_ACTION_BLANKING,
                         NAUTILUS_BURN_RECORDER_MEDIA_CD);
      }

      return TRUE;
}

static gboolean
cdrecord_blank_stdout_line (NautilusBurnProcess   *process,
                      const char            *line,
                      gpointer               data)
{
      NautilusBurnRecorder *recorder = data;

      if (line && process->debug) {
            g_print ("cdrecord blank stdout: %s", line);
      }

      if (g_str_has_prefix (line, "Re-load disk and hit <CR>") ||
          g_str_has_prefix (line, "send SIGUSR1 to continue")) {
            gboolean  res;

            /* This is not supposed to happen since we checked for the cd
               before starting, but we try to handle it anyway, since mmc
               profiling can fail. */

            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [INSERT_MEDIA_REQUEST],
                         0, TRUE, recorder->priv->can_rewrite,
                         FALSE, &res);

            process->expect_process_to_die = TRUE;

            insert_cd_retry (process, !res, TRUE, (*line == 'R'));
      } else if (g_str_has_prefix (line, "Blanking time:")) {
            process->result = NAUTILUS_BURN_RECORDER_RESULT_FINISHED;
      } else if (g_str_has_prefix (line, "Last chance to quit, ")) {
            process->dangerous = TRUE;
      } else if (g_str_has_prefix (line, "Blanking PMA, TOC, pregap")) {
            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [ACTION_CHANGED], 0,
                         NAUTILUS_BURN_RECORDER_ACTION_BLANKING,
                         NAUTILUS_BURN_RECORDER_MEDIA_CD);
      }

      return TRUE;
}

static gboolean
dvdrw_format_stderr_line (NautilusBurnProcess   *process,
                    const char            *line,
                    gpointer               data)
{
      NautilusBurnRecorder *recorder = data;
      float                 percent;

      if (line && process->debug) {
            g_print ("dvdrw format stderr: %s", line);
      }

      if ((sscanf (line, "* blanking %f%%,", &percent) == 1)
          || (sscanf (line, "* formatting %f%%,", &percent) == 1)
          || (sscanf (line, "* relocating lead-out %f%%,", &percent) == 1)) {

            process->dangerous = TRUE;

            if (percent > 1) {
                  if (!process->changed_text) {
                        g_signal_emit (recorder,
                                     nautilus_burn_recorder_table_signals [ACTION_CHANGED], 0,
                                     NAUTILUS_BURN_RECORDER_ACTION_BLANKING,
                                     NAUTILUS_BURN_RECORDER_MEDIA_DVD);
                  }

                  g_signal_emit (recorder,
                               nautilus_burn_recorder_table_signals [PROGRESS_CHANGED], 0,
                               percent, (long)-1);
            }

      } else {

      }

      return TRUE;
}

static gboolean
growisofs_stderr_line (NautilusBurnProcess   *process,
                   const char            *line,
                   gpointer               data)
{
      NautilusBurnRecorder *recorder = data;

      if (line && process->debug) {
            g_print ("growisofs stderr: %s", line);
      }

      if (strstr (line, "unsupported MMC profile") != NULL
          || (strstr (line, "already carries isofs") != NULL
            && strstr (line, "FATAL:") != NULL)) {
            gboolean  res;

            /* This is not supposed to happen since we checked for the cd
               type before starting, but we try to handle it anyway, since mmc
               profiling can fail. */

            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [INSERT_MEDIA_REQUEST],
                         0, TRUE, recorder->priv->can_rewrite,
                         FALSE, &res);

            process->expect_process_to_die = TRUE;

            insert_cd_retry (process, !res, FALSE, FALSE);
      } else if (strstr (line, "unable to open") != NULL || strstr (line, "unable to stat") != NULL) {
            /* This fits the "open64" and "open"-like messages */
            process->last_error = g_strdup (_("The recorder could not be accessed"));
            process->result = NAUTILUS_BURN_RECORDER_RESULT_ERROR;
      } else if (strstr (line, "not enough space available") != NULL) {
            process->last_error = g_strdup (_("Not enough space available on the disc"));
            process->result = NAUTILUS_BURN_RECORDER_RESULT_ERROR;
      } else if (strstr (line, "end of user area encountered on this track") != NULL) {
            process->last_error = g_strdup (_("The files selected did not fit on the CD"));
            process->result = NAUTILUS_BURN_RECORDER_RESULT_ERROR;
      } else if (strstr (line, "blocks are free") != NULL) {
            process->last_error = g_strdup (_("The files selected did not fit on the CD"));
            process->result = NAUTILUS_BURN_RECORDER_RESULT_ERROR;
      } else if (strstr (line, "flushing cache") != NULL) {
            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [ACTION_CHANGED], 0,
                         NAUTILUS_BURN_RECORDER_ACTION_FIXATING,
                         NAUTILUS_BURN_RECORDER_MEDIA_DVD);
            process->result = NAUTILUS_BURN_RECORDER_RESULT_FINISHED;
      } else if (strstr (line, ":-(") != NULL || strstr (line, "FATAL") != NULL) {
            process->last_error = g_strdup (_("Unhandled error, aborting"));
            process->result = NAUTILUS_BURN_RECORDER_RESULT_ERROR;
      }

      return TRUE;
}

static gboolean
cdrecord_stderr_line (NautilusBurnProcess   *process,
                  const char            *line,
                  gpointer               data)
{
      NautilusBurnRecorder *recorder = data;

      if (line && process->debug) {
            g_print ("cdrecord stderr: %s", line);
      }

      if (strstr (line, "No disk / Wrong disk!") != NULL) {
            gboolean  res;

            /* This is not supposed to happen since we checked for the cd
               before starting, but we try to handle it anyway, since mmc
               profiling can fail. */

            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [INSERT_MEDIA_REQUEST],
                         0, TRUE, recorder->priv->can_rewrite,
                         FALSE, &res);

            process->expect_process_to_die = TRUE;

            insert_cd_retry (process, !res, FALSE, FALSE);
      } else if (strstr (line, "This means that we are checking recorded media.") != NULL) {
            process->last_error = g_strdup (_("The CD has already been recorded"));
            process->result = NAUTILUS_BURN_RECORDER_RESULT_ERROR;
      } else if (strstr (line, "Cannot blank disk, aborting.") != NULL) {
            gboolean  res;

            /* This is not supposed to happen since we checked for the cd
               type before starting, but we try to handle it anyway, since
               mmc profiling can fail. */

            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [INSERT_MEDIA_REQUEST],
                         0, TRUE, TRUE,
                         FALSE, &res);

            process->expect_process_to_die = TRUE;

            insert_cd_retry (process, !res, FALSE, FALSE);
      } else if (strstr (line, "Data may not fit on current disk") != NULL) {
            process->last_error = g_strdup (_("The files selected did not fit on the CD"));
            /* FIXME should we error out in that case?
               process->result = NAUTILUS_BURN_RECORDER_RESULT_ERROR; */
      } else if (strstr (line, "Inappropriate audio coding") != NULL) {
            process->last_error = g_strdup (_("All audio files must be stereo, 16-bit digital audio with 44100Hz samples"));
            process->result = NAUTILUS_BURN_RECORDER_RESULT_ERROR;
      } else if (strstr (line, "cannot write medium - incompatible format") != NULL) {
            gboolean  res;

            /* This is not supposed to happen since we checked for the cd
               type before starting, but we try to handle it anyway, since
               mmc profiling can fail. */

            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [INSERT_MEDIA_REQUEST],
                         0, TRUE, recorder->priv->can_rewrite,
                         FALSE, &res);

            process->expect_process_to_die = TRUE;

            insert_cd_retry (process, !res, FALSE, FALSE);
      } else if (strstr (line, "DMA speed too slow") != NULL) {
            process->last_error = g_strdup (_("The system is too slow to write the CD at this speed. Try a lower speed."));
      }

      return TRUE;
}

static NautilusBurnMediaType
nautilus_burn_recorder_wait_for_insertion (NautilusBurnRecorder *recorder,
                                 NautilusBurnDrive    *drive,
                                 gboolean             *can_rewrite,
                                 gboolean             *blank)
{
      NautilusBurnMediaType type;
      gint64                media_size;
      gboolean              reload;
      gboolean              is_rewritable;
      gboolean              can_write;
      gboolean              is_blank;
      gboolean              has_data;
      gboolean              has_audio;

      reload = FALSE;

 again:

      /* if drive is mounted then unmount before checking anything */
      if (nautilus_burn_drive_is_mounted (drive)) {
            gboolean res;
            res = nautilus_burn_drive_unmount (drive);
            if (! res) {
                  g_warning ("Couldn't unmount volume in drive: %s", drive->device);
            }
      }

      media_size = nautilus_burn_drive_get_media_size (drive);
      type = nautilus_burn_drive_get_media_type_full (drive, &is_rewritable, &is_blank, &has_data, &has_audio);
      can_write = nautilus_burn_drive_media_type_is_writable (type, is_blank);

      if (can_rewrite) {
            *can_rewrite = is_rewritable;
      }

      if (blank) {
            *blank = is_blank;
      }

      if (! can_write) {
            gboolean is_mounted;
            int      res;

            reload = (media_size > 0);
            if (type == NAUTILUS_BURN_MEDIA_TYPE_ERROR) {
                  reload = FALSE;
            }

            /* check one more time */
            is_mounted = nautilus_burn_drive_is_mounted (drive);

            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [INSERT_MEDIA_REQUEST],
                         0,
                         reload,
                         recorder->priv->can_rewrite,
                         is_mounted,
                         &res);

            if (res == FALSE) {
                  return NAUTILUS_BURN_MEDIA_TYPE_ERROR;
            }

            goto again;
      }

      if (! is_blank) {
            int res;

            res = 0;

            /* We need to warn the user that it's going to erase their stuff */
            g_signal_emit (G_OBJECT (recorder),
                         nautilus_burn_recorder_table_signals [WARN_DATA_LOSS],
                         0, &res);

            if (res == NAUTILUS_BURN_RECORDER_RESPONSE_RETRY) {
                  goto again;
            } else if (res == NAUTILUS_BURN_RECORDER_RESPONSE_CANCEL) {
                  type = NAUTILUS_BURN_MEDIA_TYPE_ERROR;
            }
      }

      return type;
}

/**
 * nautilus_burn_recorder_write_tracks:
 * @recorder: #NautilusBurnRecorder
 * @drive: #NautilusBurnDrive to write to
 * @tracks: list of tracks to write
 * @speed: speed at which to write data
 * @flags: #NautilusBurnRecorderWriteFlags
 * @error: return location for errors
 *
 * Write tracks to the specified drive.
 *
 * Return value: #NautilusBurnRecorderResult
 **/
int
nautilus_burn_recorder_write_tracks (NautilusBurnRecorder          *recorder,
                             NautilusBurnDrive             *drive,
                             GList                         *tracks,
                             int                            speed,
                             NautilusBurnRecorderWriteFlags flags,
                             GError                       **error)
{
      NautilusBurnMediaType type;
      gboolean              can_rewrite;
      gboolean              is_blank;
      gboolean              is_locked;
      int                   ret;

      g_return_val_if_fail (recorder != NULL, NAUTILUS_BURN_RECORDER_RESULT_ERROR);
      g_return_val_if_fail (tracks != NULL, NAUTILUS_BURN_RECORDER_RESULT_ERROR);

      recorder->priv->tracks = tracks;
      recorder->priv->track_count = g_list_length (tracks);
      recorder->priv->debug = (flags & NAUTILUS_BURN_RECORDER_WRITE_DEBUG);

      recorder->priv->can_rewrite = nautilus_burn_drive_can_rewrite (drive);

      if (recorder->priv->track_count > 99) {
            g_set_error (error,
                       NAUTILUS_BURN_RECORDER_ERROR,
                       NAUTILUS_BURN_RECORDER_ERROR_GENERAL,
                       _("You can only burn 99 tracks on one disc"));

            return NAUTILUS_BURN_RECORDER_RESULT_ERROR;
      } else if (recorder->priv->track_count < 1) {
            g_set_error (error,
                       NAUTILUS_BURN_RECORDER_ERROR,
                       NAUTILUS_BURN_RECORDER_ERROR_GENERAL,
                       _("No tracks given to write"));

            return NAUTILUS_BURN_RECORDER_RESULT_ERROR;
      }

      is_locked = nautilus_burn_drive_lock (drive, _("Burning CD"), NULL);

      type = nautilus_burn_recorder_wait_for_insertion (recorder,
                                            drive,
                                            &can_rewrite,
                                            &is_blank);

      if (type == NAUTILUS_BURN_MEDIA_TYPE_ERROR) {
            if (is_locked)
                  nautilus_burn_drive_unlock (drive);

            return NAUTILUS_BURN_RECORDER_RESULT_CANCEL;
      }

      if (can_rewrite != FALSE) {
            flags |= NAUTILUS_BURN_RECORDER_WRITE_BLANK;
      }

      if (cd_write_needs_growisofs (drive, type, tracks)) {
            ret = nautilus_burn_recorder_write_growisofs (recorder,
                                                drive,
                                                tracks,
                                                speed,
                                                flags,
                                                error);
      } else {
            ret = nautilus_burn_recorder_write_cdrecord (recorder,
                                               drive,
                                               tracks,
                                               speed,
                                               flags,
                                               error);
      }

      if (is_locked) {
            nautilus_burn_drive_unlock (drive);
      }

      return ret;
}

static gboolean
start_async_with_watch (char        **args,
                  GPid         *ppid,
                  GIOFunc       out_watch_func,
                  GIOFunc       err_watch_func,
                  gpointer      user_data,
                  guint        *out_watch_id,
                  guint        *err_watch_id,
                  int          *input_pipe,
                  GError      **error)
{
      gboolean    ret;
      int       stdin_pipe;
      int       stdout_pipe;
      int       stderr_pipe;
      GPid      pid = 0;

      g_return_val_if_fail (args != NULL, FALSE);

      ret = g_spawn_async_with_pipes (NULL,
                              args,
                              NULL,
                              G_SPAWN_SEARCH_PATH,
                              NULL,
                              NULL,
                              &pid,
                              &stdin_pipe,
                              &stdout_pipe,
                              &stderr_pipe,
                              error);

      if (!ret)
            return FALSE;

      if (ppid)
            *ppid = pid;

      if (input_pipe)
            *input_pipe = stdin_pipe;

      fcntl (stdout_pipe, F_SETFL, O_NONBLOCK);
      fcntl (stderr_pipe, F_SETFL, O_NONBLOCK);

      if (out_watch_func) {
            GIOChannel *channel;
            guint     id;

            channel = g_io_channel_unix_new (stdout_pipe);
            g_io_channel_set_flags (channel,
                              g_io_channel_get_flags (channel) | G_IO_FLAG_NONBLOCK,
                              NULL);
            g_io_channel_set_encoding (channel, NULL, NULL);

            id = g_io_add_watch (channel,
                             G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
                             out_watch_func,
                             user_data);

            g_io_channel_unref (channel);

            if (out_watch_id)
                  *out_watch_id = id;
      } else {
            if (out_watch_id)
                  *out_watch_id = -1;
      }

      if (err_watch_func) {
            GIOChannel *channel;
            guint     id;

            channel = g_io_channel_unix_new (stderr_pipe);
            g_io_channel_set_flags (channel,
                              g_io_channel_get_flags (channel) | G_IO_FLAG_NONBLOCK,
                              NULL);
            g_io_channel_set_encoding (channel, NULL, NULL);

            id = g_io_add_watch (channel,
                             G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
                             err_watch_func,
                             user_data);

            g_io_channel_unref (channel);

            if (err_watch_id)
                  *err_watch_id = id;
      } else {
            if (err_watch_id)
                  *err_watch_id = -1;
      }

      return ret;
}

static gboolean
nautilus_burn_process_stdout_read (GIOChannel   *source,
                           GIOCondition  condition,
                           gpointer      data)
{
      NautilusBurnRecorder  *recorder = data;
      NautilusBurnProcess   *process;
      gboolean               res      = TRUE;

      process = recorder->priv->process;

      if (condition & G_IO_IN) {
            char     *line;
            char      buf [1];
            GIOStatus status;

            status = g_io_channel_read_line (source,
                                     &line, NULL, NULL, NULL);

            if (status == G_IO_STATUS_NORMAL) {
                  if (process->line) {
                        g_string_append (process->line, line);
                        g_free (line);
                        line = g_string_free (process->line, FALSE);
                        process->line = NULL;
                  }

                  if (process->funcs->out_line)
                        res = process->funcs->out_line (process, line, recorder);
                  
                  g_free (line);
            } else if (status == G_IO_STATUS_AGAIN) {
                  /* A non-terminated line was read, read the data into the buffer. */
                  status = g_io_channel_read_chars (source, buf, 1, NULL, NULL);

                  if (status == G_IO_STATUS_NORMAL) {
                        char *line2;

                        if (process->line == NULL) {
                              process->line = g_string_new (NULL);
                        }
                        g_string_append_c (process->line, buf [0]);

                        switch (buf [0]) {
                        case '\n':
                        case '\r':
                        case '\xe2':
                        case '\0':
                              line2 = g_string_free (process->line, FALSE);
                              process->line = NULL;

                              if (process->funcs->out_line)
                                    res = process->funcs->out_line (process, line2, recorder);

                              g_free (line2);
                              break;
                        default:
                              break;
                        }
                  }
            } else if (status == G_IO_STATUS_EOF) {
                  if (g_main_loop_is_running (process->loop))
                        g_main_loop_quit (process->loop);

                  res = FALSE;
            }
      } else if (condition & G_IO_HUP) {
            /* only handle the HUP when we have read all available lines of output */
            if (process->debug)
                  g_print ("process stdout: HUP\n");
            if (g_main_loop_is_running (process->loop))
                  g_main_loop_quit (process->loop);

            res = FALSE;
      }

      return res;
}

static gboolean
nautilus_burn_process_stderr_read (GIOChannel   *source,
                           GIOCondition  condition,
                           gpointer      data)
{
      NautilusBurnRecorder  *recorder = data;
      NautilusBurnProcess   *process;
      gboolean               res      = TRUE;

      process = recorder->priv->process;

      if (condition & G_IO_IN) {
            char     *line;
            char      buf [1];
            GIOStatus status;

            status = g_io_channel_read_line (source,
                                     &line, NULL, NULL, NULL);

            if (status == G_IO_STATUS_NORMAL) {
                  if (process->line_stderr) {
                        g_string_append (process->line_stderr, line);
                        g_free (line);
                        line = g_string_free (process->line_stderr, FALSE);
                        process->line_stderr = NULL;
                  }

                  if (process->funcs->err_line)
                        res = process->funcs->err_line (process, line, recorder);
                  
                  g_free (line);
            } else if (status == G_IO_STATUS_AGAIN) {
                  /* A non-terminated line was read, read the data into the buffer. */
                  status = g_io_channel_read_chars (source, buf, 1, NULL, NULL);

                  if (status == G_IO_STATUS_NORMAL) {
                        char *line2;

                        if (process->line_stderr == NULL) {
                              process->line_stderr = g_string_new (NULL);
                        }
                        g_string_append_c (process->line_stderr, buf [0]);

                        switch (buf [0]) {
                        case '\n':
                        case '\r':
                        case '\xe2':
                        case '\0':
                              line2 = g_string_free (process->line_stderr, FALSE);
                              process->line_stderr = NULL;

                              if (process->funcs->err_line)
                                    res = process->funcs->err_line (process, line2, recorder);

                              g_free (line2);
                              break;
                        default:
                              break;
                        }
                  }
            } else if (status == G_IO_STATUS_EOF) {
                  if (g_main_loop_is_running (process->loop))
                        g_main_loop_quit (process->loop);

                  res = FALSE;
            }
      } else if (condition & G_IO_HUP) {
            /* only handle the HUP when we have read all available lines of output */
            if (process->debug)
                  g_print ("process stderr: HUP\n");
            if (g_main_loop_is_running (process->loop))
                  g_main_loop_quit (process->loop);

            res = FALSE;
      }

      return res;
}

static int
nautilus_burn_recorder_run_process (NautilusBurnRecorder       *recorder,
                            NautilusBurnDrive          *drive,
                            NautilusBurnRecorderMedia   media_type,
                            NautilusBurnProcessLineFunc out_line_func,
                            NautilusBurnProcessLineFunc err_line_func,
                            GPtrArray                  *argv,
                            GError                    **error)
{
      int                  res;
      int                  result;
      guint                stdout_tag;
      guint                stderr_tag;
      GError              *local_error = NULL;
      NautilusBurnProcess *process;

 retry:
      
      process = nautilus_burn_process_new ();
      process->debug = recorder->priv->debug;
      process->funcs->out_line = out_line_func;
      process->funcs->err_line = err_line_func;

      nautilus_burn_process_free (recorder->priv->process);
      recorder->priv->process = process;

      process->result = NAUTILUS_BURN_RECORDER_RESULT_ERROR;

      g_signal_emit (G_OBJECT (recorder),
                   nautilus_burn_recorder_table_signals [ACTION_CHANGED], 0,
                   NAUTILUS_BURN_RECORDER_ACTION_PREPARING_WRITE,
                   media_type);
      g_signal_emit (G_OBJECT (recorder),
                   nautilus_burn_recorder_table_signals [PROGRESS_CHANGED], 0,
                   0.0, (long)-1);
      g_signal_emit (G_OBJECT (recorder),
                   nautilus_burn_recorder_table_signals [ANIMATION_CHANGED], 0,
                   TRUE);

      if (process->debug) {
            guint i;
            g_print ("launching command: ");
            for (i = 0; i < argv->len - 1; i++) {
                  g_print ("%s ", (char *) g_ptr_array_index (argv, i));
            }
            g_print ("\n");
      }

      local_error = NULL;

      stdout_tag = 0;
      stderr_tag = 0;
      res = start_async_with_watch ((char **)argv->pdata,
                              &process->pid,
                              nautilus_burn_process_stdout_read,
                              nautilus_burn_process_stderr_read,
                              recorder,
                              &stdout_tag,
                              &stderr_tag,
                              &process->pipe_stdin,
                              &local_error);

      if (! res) {
            g_warning ("command failed: %s\n", local_error->message);
            g_set_error (error,
                       NAUTILUS_BURN_RECORDER_ERROR,
                       NAUTILUS_BURN_RECORDER_ERROR_GENERAL,
                       _("Could not run the necessary command: %s"),
                       local_error->message);
            g_error_free (local_error);

            if (stdout_tag > 0)
                  g_source_remove (stdout_tag);
            if (stderr_tag > 0)
                  g_source_remove (stderr_tag);
      } else {
            process->loop = g_main_loop_new (NULL, FALSE);

            process->dangerous = FALSE;

            g_main_loop_run (process->loop);
            g_main_loop_unref (process->loop);
            
            if (stdout_tag > 0)
                  g_source_remove (stdout_tag);
            if (stderr_tag > 0)
                  g_source_remove (stderr_tag);

            if (process->result == NAUTILUS_BURN_RECORDER_RESULT_RETRY) {
                  goto retry;
            }

            if (process->last_error) {
                  g_set_error (error,
                             NAUTILUS_BURN_RECORDER_ERROR,
                             NAUTILUS_BURN_RECORDER_ERROR_GENERAL,
                             process->last_error);
            }
      }

      result = process->result;
      nautilus_burn_process_free (process);
      recorder->priv->process = NULL;

      g_signal_emit (G_OBJECT (recorder),
                   nautilus_burn_recorder_table_signals [ANIMATION_CHANGED], 0,
                   FALSE);

      return result;
}

static int
nautilus_burn_recorder_write_growisofs (NautilusBurnRecorder          *recorder,
                              NautilusBurnDrive             *drive,
                              GList                         *tracks,
                              int                            speed,
                              NautilusBurnRecorderWriteFlags flags,
                              GError                       **error)
{
      GPtrArray                 *argv;
      char                      *speed_str, *dev_str;
      NautilusBurnRecorderTrack *t;
      int                        result;

      if (g_list_length (tracks) != 1) {
            g_warning ("Can only use growisofs on a single track");
            return NAUTILUS_BURN_RECORDER_RESULT_ERROR;
      }

      t = (NautilusBurnRecorderTrack*)tracks->data;
      if (t->type != NAUTILUS_BURN_RECORDER_TRACK_TYPE_DATA) {
            g_warning ("Can only use growisofs on a data track");
            return NAUTILUS_BURN_RECORDER_RESULT_ERROR;
      }

      argv = g_ptr_array_new ();

      g_ptr_array_add (argv, "growisofs");
      speed_str = g_strdup_printf ("-speed=%d", speed);
      if (speed != 0) {
            g_ptr_array_add (argv, speed_str);
      }

      g_ptr_array_add (argv, "-dvd-compat");

      /* Weird, innit? We tell growisofs we have a tty so it ignores
       * the fact that the DVD+ has an ISO fs already */
      if (flags & NAUTILUS_BURN_RECORDER_WRITE_BLANK) {
            g_ptr_array_add (argv, "-use-the-force-luke=tty");
      }

      g_ptr_array_add (argv, "-Z");

      dev_str = g_strdup_printf ("%s=%s", drive->device, t->contents.data.filename);
      g_ptr_array_add (argv, dev_str);

      g_ptr_array_add (argv, NULL);

      result = nautilus_burn_recorder_run_process (recorder,
                                         drive,
                                         NAUTILUS_BURN_RECORDER_MEDIA_DVD,
                                         growisofs_stdout_line,
                                         growisofs_stderr_line,
                                         argv,
                                         error);

      g_free (speed_str);
      g_free (dev_str);
      g_ptr_array_free (argv, TRUE);
      argv = NULL;
      
      g_signal_emit (recorder,
                   nautilus_burn_recorder_table_signals [ANIMATION_CHANGED], 0,
                   FALSE);

      if (flags & NAUTILUS_BURN_RECORDER_WRITE_EJECT && result == NAUTILUS_BURN_RECORDER_RESULT_FINISHED) {
            nautilus_burn_drive_eject (drive);
      }

      return result;
}

static gboolean
cdrdao_stderr_line (NautilusBurnProcess   *process,
                const char            *line,
                gpointer               data)
{
      NautilusBurnRecorder *recorder = data;
      unsigned int          written, total;

      if (line && process->debug) {
            g_print ("cdrdao stderr: %s", line);
      }

      if (sscanf (line, "Wrote %u of %u", &written, &total) == 2) {
            long    secs = -1;
            gdouble fraction;

            process->dangerous = TRUE;

            fraction = (gdouble) written / total;

            if (process->start_time == 0
                && written > 2) {
                  /* let the speed regulate before keeping track */
                  process->start_time = time (NULL);
                  process->start_num = (gint64)written;
            }

            if (process->start_time > 0) {
                  guint64 elapsed;
                  gdouble rate;

                  elapsed = time (NULL) - process->start_time;
                  rate = (gdouble)(written - process->start_num) / (gdouble)elapsed;

                  if (rate > 0)
                        secs = (total - written) / rate;
            }

            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [ACTION_CHANGED], 0,
                         NAUTILUS_BURN_RECORDER_ACTION_WRITING,
                         NAUTILUS_BURN_RECORDER_MEDIA_CD);
            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [PROGRESS_CHANGED], 0,
                         fraction, secs);
      }

      if (strstr (line, "Writing track 01")) {
            process->dangerous = TRUE;
            process->start_time = 0;
            process->result = NAUTILUS_BURN_RECORDER_RESULT_ERROR;
            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [ACTION_CHANGED], 0,
                         NAUTILUS_BURN_RECORDER_ACTION_WRITING,
                         NAUTILUS_BURN_RECORDER_MEDIA_CD);
            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [PROGRESS_CHANGED], 0,
                         0.0, (long)-1);

      }

      if (strstr (line, "Writing finished successfully")) {
            process->dangerous = FALSE;
            process->result = NAUTILUS_BURN_RECORDER_RESULT_FINISHED;
            g_signal_emit (recorder,
                         nautilus_burn_recorder_table_signals [PROGRESS_CHANGED], 0,
                         1.0, (long)-1);

      }

      return TRUE;
}

static int
nautilus_burn_recorder_write_cdrecord (NautilusBurnRecorder          *recorder,
                               NautilusBurnDrive             *drive,
                               GList                         *tracks,
                               int                            speed,
                               NautilusBurnRecorderWriteFlags flags,
                               GError                       **error)
{
      NautilusBurnRecorderTrack  *track;
      GPtrArray                  *argv      = NULL;
      char                       *speed_str = NULL;
      char                       *dev_str   = NULL;
      char                       *cue_str   = NULL;
      GList                      *l;
      NautilusBurnProcessLineFunc out_line_func;
      NautilusBurnProcessLineFunc err_line_func;
      int                         result    = NAUTILUS_BURN_RECORDER_RESULT_ERROR;

      g_return_val_if_fail (tracks != NULL, NAUTILUS_BURN_RECORDER_RESULT_ERROR);

      track = tracks->data;

      if (flags & NAUTILUS_BURN_RECORDER_WRITE_BLANK) {
            NautilusBurnRecorderBlankFlags blank_flags = 0;
            int                            res;
            GError                        *err = NULL;

            if (flags & NAUTILUS_BURN_RECORDER_WRITE_DEBUG)
                  blank_flags |= NAUTILUS_BURN_RECORDER_BLANK_DEBUG;
            if (flags & NAUTILUS_BURN_RECORDER_WRITE_DUMMY_WRITE)
                  blank_flags |= NAUTILUS_BURN_RECORDER_BLANK_DUMMY_WRITE;
            /* cdrdao doesn't seem to have on-the-fly blank */
            /* cdrecord doesn't seem to correctly detect disc size when blanking */
            res = nautilus_burn_recorder_blank_disc (recorder,
                                           drive,
                                           NAUTILUS_BURN_RECORDER_BLANK_FAST,
                                           blank_flags,
                                           &err);
            if (res != NAUTILUS_BURN_RECORDER_RESULT_FINISHED)
                  return res;
      }


      if (track->type ==  NAUTILUS_BURN_RECORDER_TRACK_TYPE_CUE) {
            argv = g_ptr_array_new ();
            g_ptr_array_add (argv, "cdrdao");
            g_ptr_array_add (argv, "write");
            g_ptr_array_add (argv, "--device");
            g_ptr_array_add (argv, drive->cdrecord_id);
            g_ptr_array_add (argv, "--speed");
            speed_str = g_strdup_printf ("%d", speed);
            g_ptr_array_add (argv, speed_str);

            if (flags & NAUTILUS_BURN_RECORDER_WRITE_DUMMY_WRITE) {
                  g_ptr_array_add (argv, "--simulate");
            }
            if (flags & NAUTILUS_BURN_RECORDER_WRITE_EJECT) {
                  g_ptr_array_add (argv, "--eject");
            }
            if (flags & NAUTILUS_BURN_RECORDER_WRITE_OVERBURN) {
                  g_ptr_array_add (argv, "--overburn");
            }
            g_ptr_array_add (argv, "-v");
            g_ptr_array_add (argv, "2");

            g_ptr_array_add (argv, track->contents.cue.filename);
            g_ptr_array_add (argv, NULL);

            out_line_func = NULL;
            err_line_func = cdrdao_stderr_line;
      } else {
            argv = g_ptr_array_new ();
            g_ptr_array_add (argv, "cdrecord");

            g_ptr_array_add (argv, "fs=16m");

            speed_str = g_strdup_printf ("speed=%d", speed);
            if (speed != 0) {
                  g_ptr_array_add (argv, speed_str);
            }
            dev_str = g_strdup_printf ("dev=%s", drive->cdrecord_id);
            g_ptr_array_add (argv, dev_str);
            if (flags & NAUTILUS_BURN_RECORDER_WRITE_DUMMY_WRITE) {
                  g_ptr_array_add (argv, "-dummy");
            }
            if (flags & NAUTILUS_BURN_RECORDER_WRITE_EJECT) {
                  g_ptr_array_add (argv, "-eject");
            }
            if (flags & NAUTILUS_BURN_RECORDER_WRITE_DISC_AT_ONCE) {
                  g_ptr_array_add (argv, "-dao");
            }
            if (flags & NAUTILUS_BURN_RECORDER_WRITE_OVERBURN) {
                  g_ptr_array_add (argv, "-overburn");
            }
            if (flags & NAUTILUS_BURN_RECORDER_WRITE_BURNPROOF) {
                  g_ptr_array_add (argv, "driveropts=burnfree");
            }
            g_ptr_array_add (argv, "-v");

            l = tracks;
            while (l != NULL && l->data != NULL) {

                  NautilusBurnRecorderTrack *track = l->data;

                  switch (track->type) {
                  case NAUTILUS_BURN_RECORDER_TRACK_TYPE_DATA:
                        g_ptr_array_add (argv, "-data");
                        g_ptr_array_add (argv, "-nopad");
                        g_ptr_array_add (argv, track->contents.data.filename);
                        break;
                  case NAUTILUS_BURN_RECORDER_TRACK_TYPE_AUDIO:
                        g_ptr_array_add (argv, "-copy");
                        g_ptr_array_add (argv, "-audio");
                        g_ptr_array_add (argv, "-pad");
                        g_ptr_array_add (argv, track->contents.audio.filename);
                        /* TODO: handle CD-TEXT somehow */
                        break;
                  case NAUTILUS_BURN_RECORDER_TRACK_TYPE_CUE:
                        /* skip silently if not the first track */
                        break;
                  default:
                        g_warning ("Unknown track type %d", track->type);
                  }

                  l = g_list_next (l);
            }

            g_ptr_array_add (argv, NULL);

            out_line_func = cdrecord_stdout_line;
            err_line_func = cdrecord_stderr_line;
      }

      if (argv) {
            result = nautilus_burn_recorder_run_process (recorder,
                                               drive,
                                               NAUTILUS_BURN_RECORDER_MEDIA_CD,
                                               out_line_func,
                                               err_line_func,
                                               argv,
                                               error);
      }

      g_free (cue_str);
      g_free (speed_str);
      g_free (dev_str);
      g_ptr_array_free (argv, TRUE);
      argv = NULL;

      return result;
}

static int
nautilus_burn_recorder_blank_disc_cdrecord (NautilusBurnRecorder          *recorder,
                                  NautilusBurnDrive             *drive,
                                  NautilusBurnRecorderBlankType  type,
                                  NautilusBurnRecorderBlankFlags flags,
                                  GError                       **error)
{
      GPtrArray            *argv;
      NautilusBurnMediaType media_type;
      char                 *blank_str, *dev_str;
      gboolean              is_locked;
      gboolean              can_rewrite;
      gboolean              is_blank;
      int                   result = NAUTILUS_BURN_RECORDER_RESULT_ERROR;

      recorder->priv->can_rewrite = (drive->type & NAUTILUS_BURN_DRIVE_TYPE_CDRW_RECORDER);

      if (!recorder->priv->can_rewrite) {
            return NAUTILUS_BURN_RECORDER_RESULT_CANCEL;
      }

      is_locked = nautilus_burn_drive_lock (drive, _("Blanking CD"), NULL);

      media_type = nautilus_burn_drive_get_media_type_full (drive, &can_rewrite, &is_blank, NULL, NULL);

      if (media_type == NAUTILUS_BURN_MEDIA_TYPE_ERROR || can_rewrite == FALSE) {
            if (is_locked)
                  nautilus_burn_drive_unlock (drive);

            return NAUTILUS_BURN_RECORDER_RESULT_CANCEL;
      }

      argv = g_ptr_array_new ();
      g_ptr_array_add (argv, "cdrecord");

      dev_str = g_strdup_printf ("dev=%s", drive->cdrecord_id);
      g_ptr_array_add (argv, dev_str);
      g_ptr_array_add (argv, "-v");

      blank_str = g_strdup_printf ("blank=%s",
                             (type == NAUTILUS_BURN_RECORDER_BLANK_FAST) ? "fast" : "all");
      g_ptr_array_add (argv, blank_str);

      if (flags & NAUTILUS_BURN_RECORDER_BLANK_DUMMY_WRITE) {
            g_ptr_array_add (argv, "-dummy");
      }

      g_ptr_array_add (argv, NULL);

      result = nautilus_burn_recorder_run_process (recorder,
                                         drive,
                                         NAUTILUS_BURN_RECORDER_MEDIA_CD,
                                         cdrecord_blank_stdout_line,
                                         cdrecord_stderr_line,
                                         argv,
                                         error);

      if (is_locked) {
            nautilus_burn_drive_unlock (drive);
      }

      g_free (dev_str);
      g_free (blank_str);
      g_ptr_array_free (argv, TRUE);
      
      return result;
}

static int
nautilus_burn_recorder_blank_disc_dvdrw_format (NautilusBurnRecorder          *recorder,
                                    NautilusBurnDrive             *drive,
                                    NautilusBurnRecorderBlankType  type,
                                    NautilusBurnRecorderBlankFlags flags,
                                    GError                       **error)
{
      NautilusBurnMediaType       media_type;
      NautilusBurnProcessLineFunc out_line_func = NULL;
      NautilusBurnProcessLineFunc err_line_func = NULL;
      GPtrArray                  *argv           = NULL;
      char                       *blank_str      = NULL;
      char                       *dev_str        = NULL;
      gboolean                    is_locked;
      gboolean                    can_rewrite;
      gboolean                    is_blank;
      int                         result         = NAUTILUS_BURN_RECORDER_RESULT_ERROR;

      recorder->priv->can_rewrite = (drive->type & NAUTILUS_BURN_DRIVE_TYPE_CDRW_RECORDER);

      if (!recorder->priv->can_rewrite) {
            return NAUTILUS_BURN_RECORDER_RESULT_CANCEL;
      }

      is_locked = nautilus_burn_drive_lock (drive, _("Blanking DVD"), NULL);

      media_type = nautilus_burn_drive_get_media_type_full (drive, &can_rewrite, &is_blank, NULL, NULL);

      if (media_type == NAUTILUS_BURN_MEDIA_TYPE_ERROR || can_rewrite == FALSE) {
            if (is_locked)
                  nautilus_burn_drive_unlock (drive);

            return NAUTILUS_BURN_RECORDER_RESULT_CANCEL;
      }

      if (media_type == NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_RW
          && type == NAUTILUS_BURN_RECORDER_BLANK_FULL) {
            argv = g_ptr_array_new ();

            g_ptr_array_add (argv, "growisofs");
            g_ptr_array_add (argv, "-Z");

            dev_str = g_strdup_printf ("%s=%s", drive->device, "/dev/zero");
            g_ptr_array_add (argv, dev_str);
            g_ptr_array_add (argv, NULL);

            out_line_func = growisofs_blank_stdout_line;
            err_line_func = growisofs_stderr_line;
      } else if (!is_blank && media_type == NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_RW) {
            if (recorder->priv->debug) {
                  g_print ("Skipping fast blank for already formatted DVD+RW media\n");
            }
      } else {
            argv = g_ptr_array_new ();

            g_ptr_array_add (argv, "dvd+rw-format");

            /* undocumented option to show progress */
            g_ptr_array_add (argv, "-gui");

            if (media_type != NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_RW) {
                  blank_str = g_strdup_printf ("-blank%s", (type == NAUTILUS_BURN_RECORDER_BLANK_FAST) ? "" : "=full");
                  g_ptr_array_add (argv, blank_str);
            }

            dev_str = g_strdup_printf ("%s", drive->device);
            g_ptr_array_add (argv, dev_str);
            g_ptr_array_add (argv, NULL);

            err_line_func = dvdrw_format_stderr_line;
      }

      if (argv) {
            result = nautilus_burn_recorder_run_process (recorder,
                                               drive,
                                               NAUTILUS_BURN_RECORDER_MEDIA_DVD,
                                               out_line_func,
                                               err_line_func,
                                               argv,
                                               error);
            g_free (dev_str);
            g_free (blank_str);
            g_ptr_array_free (argv, TRUE);
      }

      if (is_locked) {
            nautilus_burn_drive_unlock (drive);
      }
      
      return result;
}


static gboolean
nautilus_burn_drive_format_needs_growisofs (NautilusBurnDrive    *drive,
                                  NautilusBurnMediaType type)
{
      if (can_burn_dvds (drive) == FALSE) {
            return FALSE;
      }

      if (type == NAUTILUS_BURN_MEDIA_TYPE_DVDRW
          || type == NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_RW) {
            return TRUE;
      }

      return FALSE;
}

/**
 * nautilus_burn_recorder_blank_disc:
 * @recorder: #NautilusBurnRecorder
 * @drive: #NautilusBurnDrive to use
 * @type: #NautilusBurnRecorderBlankType
 * @flags: #NautilusBurnRecorderBlankFlags
 * @error: return location for errors
 *
 * Blank the media in the specified drive.
 *
 * Return value: #NautilusBurnRecorderResult
 **/
int
nautilus_burn_recorder_blank_disc (NautilusBurnRecorder          *recorder,
                           NautilusBurnDrive             *drive,
                           NautilusBurnRecorderBlankType  type,
                           NautilusBurnRecorderBlankFlags flags,
                           GError                       **error)
{
      NautilusBurnMediaType media_type;
      gboolean              can_rewrite;
      gboolean              is_blank;

      g_return_val_if_fail (recorder != NULL, NAUTILUS_BURN_RECORDER_RESULT_ERROR);
      g_return_val_if_fail (drive != NULL, NAUTILUS_BURN_RECORDER_RESULT_ERROR);

      media_type = nautilus_burn_drive_get_media_type_full (drive, &can_rewrite, &is_blank, NULL, NULL);

      if (media_type == NAUTILUS_BURN_MEDIA_TYPE_ERROR || media_type == NAUTILUS_BURN_MEDIA_TYPE_BUSY) {
            return NAUTILUS_BURN_RECORDER_RESULT_ERROR;
      } else if (can_rewrite == FALSE) {
            return NAUTILUS_BURN_RECORDER_RESULT_ERROR;
      }

      if (nautilus_burn_drive_format_needs_growisofs (drive, media_type)) {
            return nautilus_burn_recorder_blank_disc_dvdrw_format (recorder, drive, type, flags, error);
      } else {
            return nautilus_burn_recorder_blank_disc_cdrecord (recorder, drive, type, flags, error);
      }
}

static void
nautilus_burn_recorder_finalize (GObject *object)
{
      NautilusBurnRecorder *recorder = NAUTILUS_BURN_RECORDER (object);

      g_return_if_fail (object != NULL);

      if (recorder->priv->process) {
            nautilus_burn_process_free (recorder->priv->process);
      }

      if (G_OBJECT_CLASS (parent_class)->finalize != NULL) {
            (* G_OBJECT_CLASS (parent_class)->finalize) (object);
      }
}

static void
nautilus_burn_recorder_init (NautilusBurnRecorder *recorder)
{
      recorder->priv = NAUTILUS_BURN_RECORDER_GET_PRIVATE (recorder);
}

/**
 * nautilus_burn_recorder_new:
 *
 * Create a new #NautilusBurnRecorder.
 *
 * Return value: The new recorder.
 **/
NautilusBurnRecorder *
nautilus_burn_recorder_new (void)
{
      return g_object_new (NAUTILUS_BURN_TYPE_RECORDER, NULL);
}

static void
nautilus_burn_recorder_class_init (NautilusBurnRecorderClass *klass)
{
      GObjectClass *object_class;

      object_class = (GObjectClass *) klass;

      parent_class = g_type_class_peek_parent (klass);

      object_class->finalize = nautilus_burn_recorder_finalize;

      g_type_class_add_private (klass, sizeof (NautilusBurnRecorderPrivate));

      /* Signals */
      nautilus_burn_recorder_table_signals [PROGRESS_CHANGED] =
            g_signal_new ("progress-changed",
                        G_TYPE_FROM_CLASS (klass),
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (NautilusBurnRecorderClass,
                                     progress_changed),
                        NULL, NULL,
                        nautilus_burn_recorder_marshal_VOID__DOUBLE_LONG,
                        G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_LONG);
      nautilus_burn_recorder_table_signals [ACTION_CHANGED] =
            g_signal_new ("action-changed",
                        G_TYPE_FROM_CLASS (klass),
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (NautilusBurnRecorderClass,
                                     action_changed),
                        NULL, NULL,
                        nautilus_burn_recorder_marshal_VOID__INT_INT,
                        G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
      nautilus_burn_recorder_table_signals [ANIMATION_CHANGED] =
            g_signal_new ("animation-changed",
                        G_TYPE_FROM_CLASS (klass),
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (NautilusBurnRecorderClass,
                                     animation_changed),
                        NULL, NULL,
                        g_cclosure_marshal_VOID__BOOLEAN,
                        G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
      nautilus_burn_recorder_table_signals [INSERT_MEDIA_REQUEST] =
            g_signal_new ("insert-media-request",
                        G_TYPE_FROM_CLASS (klass),
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (NautilusBurnRecorderClass,
                                     insert_cd_request),
                        NULL, NULL,
                        nautilus_burn_recorder_marshal_BOOLEAN__BOOLEAN_BOOLEAN_BOOLEAN,
                        G_TYPE_BOOLEAN, 3, G_TYPE_BOOLEAN,
                        G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
      nautilus_burn_recorder_table_signals [WARN_DATA_LOSS] =
            g_signal_new ("warn-data-loss",
                        G_TYPE_FROM_CLASS (klass),
                        G_SIGNAL_RUN_LAST,
                        G_STRUCT_OFFSET (NautilusBurnRecorderClass,
                                     warn_data_loss),
                        NULL, NULL,
                        nautilus_burn_recorder_marshal_INT__VOID,
                        G_TYPE_INT, 0);
}

Generated by  Doxygen 1.6.0   Back to index