[gnome-disk-utility/udisks2-port] Estimate speed and duration when creating/restoring disk images
- From: David Zeuthen <davidz src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-disk-utility/udisks2-port] Estimate speed and duration when creating/restoring disk images
- Date: Tue, 6 Dec 2011 21:59:15 +0000 (UTC)
commit a00ab1bee946a8929b838b7b1726a3015ca5d687
Author: David Zeuthen <davidz redhat com>
Date: Tue Dec 6 16:58:21 2011 -0500
Estimate speed and duration when creating/restoring disk images
It's very frustrating to not have this, so here goes
http://people.freedesktop.org/~david/gdu2-create-disk-image-progress.png
http://people.freedesktop.org/~david/gdu2-restore-disk-image-progress.png
Signed-off-by: David Zeuthen <davidz redhat com>
data/ui/create-disk-image-dialog.ui | 26 +++-
data/ui/restore-disk-image-dialog.ui | 141 +++++----------
src/palimpsest/Makefile.am | 1 +
src/palimpsest/gduatasmartdialog.c | 43 +----
src/palimpsest/gducreatediskimagedialog.c | 47 +++++-
src/palimpsest/gduestimator.c | 270 ++++++++++++++++++++++++++++
src/palimpsest/gduestimator.h | 47 +++++
src/palimpsest/gdurestorediskimagedialog.c | 50 +++++-
src/palimpsest/gdutypes.h | 3 +
src/palimpsest/gduutils.c | 42 +++++
src/palimpsest/gduutils.h | 2 +
11 files changed, 526 insertions(+), 146 deletions(-)
---
diff --git a/data/ui/create-disk-image-dialog.ui b/data/ui/create-disk-image-dialog.ui
index d0575a9..8a55fbd 100644
--- a/data/ui/create-disk-image-dialog.ui
+++ b/data/ui/create-disk-image-dialog.ui
@@ -193,9 +193,33 @@
</packing>
</child>
<child>
- <object class="GtkProgressBar" id="copying_progressbar">
+ <object class="GtkVBox" id="vbox2">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkProgressBar" id="copying_progressbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="copying_progress_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
</object>
<packing>
<property name="expand">False</property>
diff --git a/data/ui/restore-disk-image-dialog.ui b/data/ui/restore-disk-image-dialog.ui
index e1fb547..0323d15 100644
--- a/data/ui/restore-disk-image-dialog.ui
+++ b/data/ui/restore-disk-image-dialog.ui
@@ -86,100 +86,6 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
- <child>
- <object class="GtkInfoBar" id="error_infobar">
- <property name="message_type">error</property>
- <property name="no_show_all">True</property>
- <child internal-child="content_area">
- <object class="GtkBox" id="vbox3">
- <property name="can_focus">False</property>
- <property name="orientation">horizontal</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkImage" id="image1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="xalign">1.0</property>
- <property name="stock">gtk-dialog-error</property>
- <property name="icon-size">1</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="error_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Error: Disk image is too big.</property>
- <property name="use_markup">True</property>
- <property name="xalign">0.0</property>
- <property name="selectable">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkInfoBar" id="warning_infobar">
- <property name="message_type">warning</property>
- <property name="no_show_all">True</property>
- <child internal-child="content_area">
- <object class="GtkBox" id="vbox4">
- <property name="can_focus">False</property>
- <property name="orientation">horizontal</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkImage" id="image2">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="xalign">1.0</property>
- <property name="stock">gtk-dialog-warning</property>
- <property name="icon-size">1</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="warning_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Warning: Disk image is too small.</property>
- <property name="use_markup">True</property>
- <property name="xalign">0.0</property>
- <property name="selectable">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
</object>
<packing>
<property name="expand">False</property>
@@ -227,6 +133,27 @@
<property name="height">1</property>
</packing>
</child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
</object>
<packing>
<property name="expand">False</property>
@@ -259,9 +186,33 @@
</packing>
</child>
<child>
- <object class="GtkProgressBar" id="copying_progressbar">
+ <object class="GtkVBox" id="vbox3">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkProgressBar" id="copying_progressbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="copying_progress_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
</object>
<packing>
<property name="expand">False</property>
diff --git a/src/palimpsest/Makefile.am b/src/palimpsest/Makefile.am
index d15096e..8b5ca93 100644
--- a/src/palimpsest/Makefile.am
+++ b/src/palimpsest/Makefile.am
@@ -44,6 +44,7 @@ palimpsest_SOURCES = \
gducreatediskimagedialog.h gducreatediskimagedialog.c \
gdurestorediskimagedialog.h gdurestorediskimagedialog.c \
gdupasswordstrengthwidget.h gdupasswordstrengthwidget.c \
+ gduestimator.h gduestimator.c \
$(enum_built_sources) \
$(NULL)
diff --git a/src/palimpsest/gduatasmartdialog.c b/src/palimpsest/gduatasmartdialog.c
index d699b24..b099db0 100644
--- a/src/palimpsest/gduatasmartdialog.c
+++ b/src/palimpsest/gduatasmartdialog.c
@@ -27,6 +27,7 @@
#include "gduapplication.h"
#include "gduwindow.h"
#include "gduatasmartdialog.h"
+#include "gduutils.h"
enum
{
@@ -121,41 +122,6 @@ dialog_data_free (DialogData *data)
/* ---------------------------------------------------------------------------------------------------- */
-
-static gchar *
-age_to_string (guint age_seconds)
-{
- gchar *s;
-
- if (age_seconds < 60)
- {
- s = g_strdup_printf (_("Less than a minute ago"));
- //next_update = 60 - age_seconds;
- }
- else if (age_seconds < 60 * 60)
- {
- s = g_strdup_printf (dngettext (GETTEXT_PACKAGE,
- N_("%d minute ago"),
- N_("%d minutes ago"),
- age_seconds / 60),
- age_seconds / 60);
- //next_update = 60*(age_seconds/60 + 1) - age_seconds;
- }
- else
- {
- s = g_strdup_printf (dngettext (GETTEXT_PACKAGE,
- N_("%d hour ago"),
- N_("%d hours ago"),
- age_seconds / 60 / 60),
- age_seconds / 60 / 60);
- //next_update = 60*60*(age_seconds/(60*60) + 1) - age_seconds;
- }
- return s;
-}
-
-
-/* ---------------------------------------------------------------------------------------------------- */
-
typedef struct {
const gchar *name;
const gchar *pretty_name;
@@ -1020,11 +986,14 @@ update_updated_label (DialogData *data)
time_t now;
time_t updated;
gchar *s;
+ gchar *s2;
now = time (NULL);
updated = udisks_drive_ata_get_smart_updated (data->ata);
- s = age_to_string (now - updated);
- gtk_label_set_text (GTK_LABEL (data->updated_label), s);
+ s = gdu_utils_duration_to_string (now - updated, FALSE);
+ s2 = g_strdup_printf (_("%s ago"), s);
+ gtk_label_set_text (GTK_LABEL (data->updated_label), s2);
+ g_free (s2);
g_free (s);
}
diff --git a/src/palimpsest/gducreatediskimagedialog.c b/src/palimpsest/gducreatediskimagedialog.c
index f026144..0b1cff1 100644
--- a/src/palimpsest/gducreatediskimagedialog.c
+++ b/src/palimpsest/gducreatediskimagedialog.c
@@ -32,10 +32,12 @@
#include "gduvolumegrid.h"
#include "gduutils.h"
#include "gducreatefilesystemwidget.h"
+#include "gduestimator.h"
/* ---------------------------------------------------------------------------------------------------- */
-#define BUFFER_SIZE (1024*1024)
+/* TODO: make dynamic? */
+#define BUFFER_SIZE (1*1024*1024)
typedef struct
{
@@ -53,8 +55,10 @@ typedef struct
GtkWidget *start_copying_button;
GtkWidget *destination_name_entry;
GtkWidget *destination_name_fcbutton;
- GtkWidget *copying_progressbar;
+
GtkWidget *copying_label;
+ GtkWidget *copying_progressbar;
+ GtkWidget *copying_progress_label;
GCancellable *cancellable;
GInputStream *block_stream;
@@ -67,6 +71,8 @@ typedef struct
guint64 total_bytes_read;
guint64 buffer_bytes_written;
guint64 buffer_bytes_to_write;
+
+ GduEstimator *estimator;
} CreateDiskImageData;
static CreateDiskImageData *
@@ -106,6 +112,7 @@ create_disk_image_data_unref (CreateDiskImageData *data)
if (data->builder != NULL)
g_object_unref (data->builder);
g_free (data->buffer);
+ g_clear_object (&data->estimator);
g_free (data);
}
}
@@ -198,6 +205,9 @@ write_cb (GOutputStream *output_stream,
CreateDiskImageData *data = user_data;
GError *error;
gssize bytes_written;
+ guint64 bytes_per_sec;
+ guint64 usec_remaining;
+ gchar *s, *s2, *s3, *s4, *s5;
error = NULL;
bytes_written = g_output_stream_write_finish (output_stream, res, &error);
@@ -214,11 +224,36 @@ write_cb (GOutputStream *output_stream,
data->buffer_bytes_written += bytes_written;
data->buffer_bytes_to_write -= bytes_written;
- /* update progress bar */
+ /* update progress bar and estimator */
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (data->copying_progressbar),
((gdouble) data->total_bytes_read) / ((gdouble) data->block_size));
- /* TODO: we should do fancy stuff like printing the estimated speed and time remaining */
+ gdu_estimator_add_sample (data->estimator, data->total_bytes_read);
+ bytes_per_sec = gdu_estimator_get_bytes_per_sec (data->estimator);
+ usec_remaining = gdu_estimator_get_usec_remaining (data->estimator);
+ if (bytes_per_sec > 0 && usec_remaining > 0)
+ {
+ s2 = g_format_size (data->total_bytes_read);
+ s3 = g_format_size (data->block_size);
+ s4 = gdu_utils_duration_to_string (usec_remaining / G_USEC_PER_SEC, TRUE);
+ s5 = g_format_size (bytes_per_sec);
+ s = g_strdup_printf ("%s of %s copied â %s remaining (%s/sec)", s2, s3, s4, s5);
+ g_free (s5);
+ g_free (s4);
+ g_free (s3);
+ g_free (s2);
+ }
+ else
+ {
+ s2 = g_format_size (data->total_bytes_read);
+ s3 = g_format_size (data->block_size);
+ s = g_strdup_printf ("%s of %s copied", s2, s3);
+ g_free (s2);
+ g_free (s3);
+ }
+ s2 = g_strconcat ("<small>", s, "</small>", NULL);
+ gtk_label_set_markup (GTK_LABEL (data->copying_progress_label), s2);
+ g_free (s);
write_more (data);
@@ -340,6 +375,7 @@ open_cb (UDisksBlock *block,
/* Alright, time to start copying! */
data->cancellable = g_cancellable_new ();
data->buffer = g_new0 (guchar, BUFFER_SIZE);
+ data->estimator = gdu_estimator_new (data->block_size);
copy_more (data);
out:
@@ -474,8 +510,9 @@ gdu_create_disk_image_dialog_show (GduWindow *window,
data->destination_name_entry = GTK_WIDGET (gtk_builder_get_object (data->builder, "destination_name_entry"));
g_signal_connect (data->destination_name_entry, "notify::text", G_CALLBACK (on_notify), data);
data->destination_name_fcbutton = GTK_WIDGET (gtk_builder_get_object (data->builder, "destination_folder_fcbutton"));
- data->copying_progressbar = GTK_WIDGET (gtk_builder_get_object (data->builder, "copying_progressbar"));
data->copying_label = GTK_WIDGET (gtk_builder_get_object (data->builder, "copying_label"));
+ data->copying_progressbar = GTK_WIDGET (gtk_builder_get_object (data->builder, "copying_progressbar"));
+ data->copying_progress_label = GTK_WIDGET (gtk_builder_get_object (data->builder, "copying_progress_label"));
create_disk_image_populate (data);
create_disk_image_update (data);
diff --git a/src/palimpsest/gduestimator.c b/src/palimpsest/gduestimator.c
new file mode 100644
index 0000000..824bf75
--- /dev/null
+++ b/src/palimpsest/gduestimator.c
@@ -0,0 +1,270 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2008-2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+#include <math.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdkx.h>
+#include <stdlib.h>
+
+#include "gduestimator.h"
+
+#define MAX_SAMPLES 100
+
+typedef struct
+{
+ gint64 time_usec;
+ guint64 value;
+} Sample;
+
+typedef struct _GduEstimatorClass GduEstimatorClass;
+struct _GduEstimator
+{
+ GObject parent;
+
+ guint64 target_bytes;
+ guint64 completed_bytes;
+ guint64 bytes_per_sec;
+ guint64 usec_remaining;
+
+ Sample samples[MAX_SAMPLES];
+ guint num_samples;
+};
+
+struct _GduEstimatorClass
+{
+ GObjectClass parent_class;
+};
+
+enum
+{
+ PROP_0,
+ PROP_TARGET_BYTES,
+ PROP_COMPLETED_BYTES,
+ PROP_BYTES_PER_SEC,
+ PROP_USEC_REMAINING,
+};
+
+G_DEFINE_TYPE (GduEstimator, gdu_estimator, G_TYPE_OBJECT)
+
+static void
+gdu_estimator_finalize (GObject *object)
+{
+ //GduEstimator *estimator = GDU_ESTIMATOR (object);
+
+ G_OBJECT_CLASS (gdu_estimator_parent_class)->finalize (object);
+}
+
+static void
+gdu_estimator_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GduEstimator *estimator = GDU_ESTIMATOR (object);
+
+ switch (property_id)
+ {
+ case PROP_TARGET_BYTES:
+ g_value_set_uint64 (value, gdu_estimator_get_target_bytes (estimator));
+ break;
+
+ case PROP_COMPLETED_BYTES:
+ g_value_set_uint64 (value, gdu_estimator_get_completed_bytes (estimator));
+ break;
+
+ case PROP_BYTES_PER_SEC:
+ g_value_set_uint64 (value, gdu_estimator_get_bytes_per_sec (estimator));
+ break;
+
+ case PROP_USEC_REMAINING:
+ g_value_set_uint64 (value, gdu_estimator_get_usec_remaining (estimator));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gdu_estimator_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GduEstimator *estimator = GDU_ESTIMATOR (object);
+
+ switch (property_id)
+ {
+ case PROP_TARGET_BYTES:
+ estimator->target_bytes = g_value_get_uint64 (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+update (GduEstimator *estimator)
+{
+ guint n;
+ gdouble sum_of_speeds;
+ guint num_speeds;
+ gdouble speed;
+
+ num_speeds = 0;
+ sum_of_speeds = 0.0;
+ for (n = 1; n < estimator->num_samples; n++)
+ {
+ Sample *a = &estimator->samples[n-1];
+ Sample *b = &estimator->samples[n];
+ gdouble speed;
+ speed = (b->value - a->value) / (((gdouble) (b->time_usec - a->time_usec)) / G_USEC_PER_SEC);
+ sum_of_speeds += speed;
+ num_speeds++;
+ }
+ estimator->bytes_per_sec = 0;
+ estimator->usec_remaining = 0;
+ if (num_speeds > 0)
+ {
+ speed = sum_of_speeds / num_speeds;
+ estimator->bytes_per_sec = speed;
+ if (estimator->bytes_per_sec > 0)
+ {
+ guint64 remaining_bytes = estimator->target_bytes - estimator->completed_bytes;
+ estimator->usec_remaining = G_USEC_PER_SEC * remaining_bytes / estimator->bytes_per_sec;
+ }
+ }
+
+ g_object_freeze_notify (G_OBJECT (estimator));
+ g_object_notify (G_OBJECT (estimator), "bytes-per-sec");
+ g_object_notify (G_OBJECT (estimator), "usec-remaining");
+ g_object_thaw_notify (G_OBJECT (estimator));
+}
+
+static void
+gdu_estimator_class_init (GduEstimatorClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->get_property = gdu_estimator_get_property;
+ gobject_class->set_property = gdu_estimator_set_property;
+ gobject_class->finalize = gdu_estimator_finalize;
+
+ g_object_class_install_property (gobject_class, PROP_TARGET_BYTES,
+ g_param_spec_uint64 ("target-bytes", NULL, NULL,
+ 0, G_MAXUINT64, 0,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_COMPLETED_BYTES,
+ g_param_spec_uint64 ("completed-bytes", NULL, NULL,
+ 0, G_MAXUINT64, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_BYTES_PER_SEC,
+ g_param_spec_uint64 ("bytes-per-sec", NULL, NULL,
+ 0, G_MAXUINT64, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_USEC_REMAINING,
+ g_param_spec_uint64 ("usec-remaining", NULL, NULL,
+ 0, G_MAXUINT64, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gdu_estimator_init (GduEstimator *estimator)
+{
+}
+
+GduEstimator *
+gdu_estimator_new (guint64 target_bytes)
+{
+ return GDU_ESTIMATOR (g_object_new (GDU_TYPE_ESTIMATOR,
+ "target-bytes", target_bytes,
+ NULL));
+}
+
+guint64
+gdu_estimator_get_target_bytes (GduEstimator *estimator)
+{
+ g_return_val_if_fail (GDU_IS_ESTIMATOR (estimator), 0);
+ return estimator->target_bytes;
+}
+
+guint64
+gdu_estimator_get_completed_bytes (GduEstimator *estimator)
+{
+ g_return_val_if_fail (GDU_IS_ESTIMATOR (estimator), 0);
+ return estimator->completed_bytes;
+}
+
+guint64
+gdu_estimator_get_bytes_per_sec (GduEstimator *estimator)
+{
+ g_return_val_if_fail (GDU_IS_ESTIMATOR (estimator), 0);
+ return estimator->bytes_per_sec;
+}
+
+guint64
+gdu_estimator_get_usec_remaining (GduEstimator *estimator)
+{
+ g_return_val_if_fail (GDU_IS_ESTIMATOR (estimator), 0);
+ return estimator->usec_remaining;
+}
+
+void
+gdu_estimator_add_sample (GduEstimator *estimator,
+ guint64 completed_bytes)
+{
+ Sample *sample;
+ g_return_if_fail (GDU_IS_ESTIMATOR (estimator));
+ g_return_if_fail (completed_bytes >= estimator->completed_bytes);
+
+ estimator->completed_bytes = completed_bytes;
+
+ if (estimator->num_samples == MAX_SAMPLES)
+ {
+ memmove (estimator->samples, estimator->samples + 1, sizeof (Sample) * (MAX_SAMPLES - 1));
+ estimator->num_samples -= 1;
+ }
+ sample = &estimator->samples[estimator->num_samples++];
+
+ sample->time_usec = g_get_real_time ();
+ sample->value = completed_bytes;
+
+ update (estimator);
+}
diff --git a/src/palimpsest/gduestimator.h b/src/palimpsest/gduestimator.h
new file mode 100644
index 0000000..48d802b
--- /dev/null
+++ b/src/palimpsest/gduestimator.h
@@ -0,0 +1,47 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2008-2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#ifndef __GDU_ESTIMATOR_H__
+#define __GDU_ESTIMATOR_H__
+
+#include <gtk/gtk.h>
+#include "gdutypes.h"
+
+G_BEGIN_DECLS
+
+#define GDU_TYPE_ESTIMATOR gdu_estimator_get_type()
+#define GDU_ESTIMATOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDU_TYPE_ESTIMATOR, GduEstimator))
+#define GDU_IS_ESTIMATOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDU_TYPE_ESTIMATOR))
+
+GType gdu_estimator_get_type (void) G_GNUC_CONST;
+GduEstimator *gdu_estimator_new (guint64 target_bytes);
+void gdu_estimator_add_sample (GduEstimator *estimator,
+ guint64 completed_bytes);
+guint64 gdu_estimator_get_target_bytes (GduEstimator *estimator);
+guint64 gdu_estimator_get_completed_bytes (GduEstimator *estimator);
+
+guint64 gdu_estimator_get_bytes_per_sec (GduEstimator *estimator);
+guint64 gdu_estimator_get_usec_remaining (GduEstimator *estimator);
+
+G_END_DECLS
+
+#endif /* __GDU_ESTIMATOR_H__ */
diff --git a/src/palimpsest/gdurestorediskimagedialog.c b/src/palimpsest/gdurestorediskimagedialog.c
index 8ccef48..ed71235 100644
--- a/src/palimpsest/gdurestorediskimagedialog.c
+++ b/src/palimpsest/gdurestorediskimagedialog.c
@@ -31,10 +31,12 @@
#include "gdurestorediskimagedialog.h"
#include "gduvolumegrid.h"
#include "gduutils.h"
+#include "gduestimator.h"
/* ---------------------------------------------------------------------------------------------------- */
-#define BUFFER_SIZE (1024*1024)
+/* TODO: make dynamic? */
+#define BUFFER_SIZE (1*1024*1024)
typedef struct
{
@@ -56,8 +58,9 @@ typedef struct
GtkWidget *error_infobar;
GtkWidget *error_label;
- GtkWidget *copying_progressbar;
GtkWidget *copying_label;
+ GtkWidget *copying_progressbar;
+ GtkWidget *copying_progress_label;
GCancellable *cancellable;
GOutputStream *block_stream;
@@ -68,6 +71,8 @@ typedef struct
guint64 total_bytes_read;
guint64 buffer_bytes_written;
guint64 buffer_bytes_to_write;
+
+ GduEstimator *estimator;
} RestoreDiskImageData;
static RestoreDiskImageData *
@@ -97,6 +102,7 @@ restore_disk_image_data_unref (RestoreDiskImageData *data)
if (data->builder != NULL)
g_object_unref (data->builder);
g_free (data->buffer);
+ g_clear_object (&data->estimator);
g_free (data);
}
}
@@ -226,6 +232,9 @@ write_cb (GOutputStream *output_stream,
RestoreDiskImageData *data = user_data;
GError *error;
gssize bytes_written;
+ guint64 bytes_per_sec;
+ guint64 usec_remaining;
+ gchar *s, *s2, *s3, *s4, *s5;
error = NULL;
bytes_written = g_output_stream_write_finish (output_stream, res, &error);
@@ -242,11 +251,35 @@ write_cb (GOutputStream *output_stream,
data->buffer_bytes_written += bytes_written;
data->buffer_bytes_to_write -= bytes_written;
- /* update progress bar */
+ /* update progress bar and estimator */
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (data->copying_progressbar),
((gdouble) data->total_bytes_read) / ((gdouble) data->file_size));
-
- /* TODO: we should do fancy stuff like printing the estimated speed and time remaining */
+ gdu_estimator_add_sample (data->estimator, data->total_bytes_read);
+ bytes_per_sec = gdu_estimator_get_bytes_per_sec (data->estimator);
+ usec_remaining = gdu_estimator_get_usec_remaining (data->estimator);
+ if (bytes_per_sec > 0 && usec_remaining > 0)
+ {
+ s2 = g_format_size (data->total_bytes_read);
+ s3 = g_format_size (data->file_size);
+ s4 = gdu_utils_duration_to_string (usec_remaining / G_USEC_PER_SEC, TRUE);
+ s5 = g_format_size (bytes_per_sec);
+ s = g_strdup_printf ("%s of %s copied â %s remaining (%s/sec)", s2, s3, s4, s5);
+ g_free (s5);
+ g_free (s4);
+ g_free (s3);
+ g_free (s2);
+ }
+ else
+ {
+ s2 = g_format_size (data->total_bytes_read);
+ s3 = g_format_size (data->file_size);
+ s = g_strdup_printf ("%s of %s copied", s2, s3);
+ g_free (s2);
+ g_free (s3);
+ }
+ s2 = g_strconcat ("<small>", s, "</small>", NULL);
+ gtk_label_set_markup (GTK_LABEL (data->copying_progress_label), s2);
+ g_free (s);
write_more (data);
@@ -263,7 +296,6 @@ write_more (RestoreDiskImageData *data)
}
else
{
- g_debug ("Writing %d bytes", (gint) data->buffer_bytes_to_write);
g_output_stream_write_async (data->block_stream,
data->buffer + data->buffer_bytes_written,
data->buffer_bytes_to_write,
@@ -367,6 +399,7 @@ open_cb (UDisksBlock *block,
/* Alright, time to start copying! */
data->cancellable = g_cancellable_new ();
data->buffer = g_new0 (guchar, BUFFER_SIZE);
+ data->estimator = gdu_estimator_new (data->file_size);
copy_more (data);
out:
@@ -451,12 +484,13 @@ gdu_restore_disk_image_dialog_show (GduWindow *window,
data->source_file_fcbutton = GTK_WIDGET (gtk_builder_get_object (data->builder, "source_file_fcbutton"));
g_signal_connect (data->source_file_fcbutton, "file-set",
G_CALLBACK (on_file_set), data);
- data->copying_progressbar = GTK_WIDGET (gtk_builder_get_object (data->builder, "copying_progressbar"));
- data->copying_label = GTK_WIDGET (gtk_builder_get_object (data->builder, "copying_label"));
data->warning_infobar = GTK_WIDGET (gtk_builder_get_object (data->builder, "warning_infobar"));
data->warning_label = GTK_WIDGET (gtk_builder_get_object (data->builder, "warning_label"));
data->error_infobar = GTK_WIDGET (gtk_builder_get_object (data->builder, "error_infobar"));
data->error_label = GTK_WIDGET (gtk_builder_get_object (data->builder, "error_label"));
+ data->copying_label = GTK_WIDGET (gtk_builder_get_object (data->builder, "copying_label"));
+ data->copying_progressbar = GTK_WIDGET (gtk_builder_get_object (data->builder, "copying_progressbar"));
+ data->copying_progress_label = GTK_WIDGET (gtk_builder_get_object (data->builder, "copying_progress_label"));
restore_disk_image_populate (data);
restore_disk_image_update (data);
diff --git a/src/palimpsest/gdutypes.h b/src/palimpsest/gdutypes.h
index 588b206..917908a 100644
--- a/src/palimpsest/gdutypes.h
+++ b/src/palimpsest/gdutypes.h
@@ -49,6 +49,9 @@ typedef struct _GduCreateFilesystemWidget GduCreateFilesystemWidget;
struct _GduPasswordStrengthWidget;
typedef struct _GduPasswordStrengthWidget GduPasswordStrengthWidget;
+struct _GduEstimator;
+typedef struct _GduEstimator GduEstimator;
+
G_END_DECLS
#endif /* __GDU_TYPES_H__ */
diff --git a/src/palimpsest/gduutils.c b/src/palimpsest/gduutils.c
index c27e9f0..c7530ad 100644
--- a/src/palimpsest/gduutils.c
+++ b/src/palimpsest/gduutils.c
@@ -88,3 +88,45 @@ gdu_utils_configure_file_chooser_for_disk_images (GtkFileChooser *file_chooser)
gtk_file_chooser_add_filter (file_chooser, filter); /* adopts filter */
gtk_file_chooser_set_filter (file_chooser, filter);
}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+gchar *
+gdu_utils_duration_to_string (guint duration_sec,
+ gboolean include_second_precision)
+{
+ gchar *s;
+
+ if (duration_sec < 60)
+ {
+ if (include_second_precision)
+ {
+ s = g_strdup_printf (dngettext (GETTEXT_PACKAGE,
+ N_("%d second"),
+ N_("%d seconds"),
+ duration_sec),
+ duration_sec);
+ }
+ else
+ {
+ s = g_strdup (_("Less than a minute"));
+ }
+ }
+ else if (duration_sec < 3600)
+ {
+ s = g_strdup_printf (dngettext (GETTEXT_PACKAGE,
+ N_("%d minute"),
+ N_("%d minutes"),
+ duration_sec / 60),
+ duration_sec / 60);
+ }
+ else
+ {
+ s = g_strdup_printf (dngettext (GETTEXT_PACKAGE,
+ N_("%d hour"),
+ N_("%d hours"),
+ duration_sec / 3600),
+ duration_sec / 3600);
+ }
+ return s;
+}
diff --git a/src/palimpsest/gduutils.h b/src/palimpsest/gduutils.h
index 4f1c9c1..ce9df52 100644
--- a/src/palimpsest/gduutils.h
+++ b/src/palimpsest/gduutils.h
@@ -34,6 +34,8 @@ gboolean gdu_utils_has_configuration (UDisksBlock *block,
void gdu_utils_configure_file_chooser_for_disk_images (GtkFileChooser *file_chooser);
+gchar *gdu_utils_duration_to_string (guint duration_sec,
+ gboolean include_second_precision);
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]