fwupd/libfwupdplugin/fu-progress.c
Richard Hughes f302bbcb03 trivial: Fix a potential division by zero
Spotted by Coverity.
2021-09-24 19:03:52 +01:00

924 lines
22 KiB
C

/*
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#define G_LOG_DOMAIN "FuProgress"
#include "config.h"
#include <math.h>
#include "fu-progress.h"
/**
* FuProgress:
*
* Objects can use fu_progress_set_percentage() if the absolute percentage
* is known. Percentages should always go up, not down.
*
* Modules usually set the number of steps that are expected using
* fu_progress_set_steps() and then after each section is completed,
* the fu_progress_step_done() function should be called. This will automatically
* call fu_progress_set_percentage() with the correct values.
*
* #FuProgress allows sub-modules to be "chained up" to the parent module
* so that as the sub-module progresses, so does the parent.
* The child can be reused for each section, and chains can be deep.
*
* To get a child object, you should use fu_progress_get_child() and then
* use the result in any sub-process. You should ensure that the child
* is not re-used without calling fu_progress_step_done().
*
* There are a few nice touches in this module, so that if a module only has
* one progress step, the child progress is used for parent updates.
*
* static void
* _do_something(FuProgress *self)
* {
* // setup correct number of steps
* fu_progress_set_steps(self, 2);
*
* // run a sub function
* _do_something_else1(fu_progress_get_child(self));
*
* // this section done
* fu_progress_step_done(self);
*
* // run another sub function
* _do_something_else2(fu_progress_get_child(self));
*
* // this progress done (all complete)
* fu_progress_step_done(self);
* }
*
* See also: [class@FuDevice]
*/
typedef struct {
gchar *id;
FuProgressFlags flags;
guint percentage;
FwupdStatus status;
GPtrArray *steps;
gboolean profile;
GTimer *timer;
guint step_now;
guint step_max;
gulong percentage_child_id;
gulong status_child_id;
FuProgress *child;
FuProgress *parent; /* no-ref */
} FuProgressPrivate;
typedef struct {
FwupdStatus status;
guint value;
gdouble profile;
} FuProgressStep;
enum { SIGNAL_PERCENTAGE_CHANGED, SIGNAL_STATUS_CHANGED, SIGNAL_LAST };
static guint signals[SIGNAL_LAST] = {0};
G_DEFINE_TYPE_WITH_PRIVATE(FuProgress, fu_progress, G_TYPE_OBJECT)
#define GET_PRIVATE(o) (fu_progress_get_instance_private(o))
/**
* fu_progress_get_id:
* @self: a #FuProgress
*
* Return the id of the progress, which is normally set by the caller.
*
* Returns: progress ID
*
* Since: 1.7.0
**/
const gchar *
fu_progress_get_id(FuProgress *self)
{
FuProgressPrivate *priv = GET_PRIVATE(self);
g_return_val_if_fail(FU_IS_PROGRESS(self), NULL);
return priv->id;
}
/**
* fu_progress_set_id:
* @self: a #FuProgress
* @id: progress ID, normally `G_STRLOC`
*
* Sets the id of the progress.
*
* Since: 1.7.0
**/
void
fu_progress_set_id(FuProgress *self, const gchar *id)
{
FuProgressPrivate *priv = GET_PRIVATE(self);
g_return_if_fail(FU_IS_PROGRESS(self));
g_return_if_fail(id != NULL);
/* not changed */
if (g_strcmp0(priv->id, id) == 0)
return;
/* set id */
g_free(priv->id);
priv->id = g_strdup(id);
}
/**
* fu_progress_get_status:
* @self: a #FuProgress
*
* Return the status of the progress, which is normally indirectly by fu_progress_add_step().
*
* Returns: status
*
* Since: 1.7.0
**/
FwupdStatus
fu_progress_get_status(FuProgress *self)
{
FuProgressPrivate *priv = GET_PRIVATE(self);
g_return_val_if_fail(FU_IS_PROGRESS(self), FWUPD_STATUS_UNKNOWN);
return priv->status;
}
/**
* fu_progress_flag_to_string:
* @flag: an internal progress flag, e.g. %FU_PROGRESS_FLAG_GUESSED
*
* Converts an progress flag to a string.
*
* Returns: identifier string
*
* Since: 1.7.0
**/
const gchar *
fu_progress_flag_to_string(FuProgressFlags flag)
{
if (flag == FU_PROGRESS_FLAG_GUESSED)
return "guessed";
if (flag == FU_PROGRESS_FLAG_NO_PROFILE)
return "no-profile";
return NULL;
}
/**
* fu_progress_flag_from_string:
* @flag: a string, e.g. `guessed`
*
* Converts a string to an progress flag.
*
* Returns: enumerated value
*
* Since: 1.7.0
**/
FuProgressFlags
fu_progress_flag_from_string(const gchar *flag)
{
if (g_strcmp0(flag, "guessed") == 0)
return FU_PROGRESS_FLAG_GUESSED;
if (g_strcmp0(flag, "no-profile") == 0)
return FU_PROGRESS_FLAG_NO_PROFILE;
return FU_PROGRESS_FLAG_UNKNOWN;
}
/**
* fu_progress_add_flag:
* @self: a #FuProgress
* @flag: an internal progress flag, e.g. %FU_PROGRESS_FLAG_GUESSED
*
* Adds a flag.
*
* Since: 1.7.0
**/
void
fu_progress_add_flag(FuProgress *self, FuProgressFlags flag)
{
FuProgressPrivate *priv = GET_PRIVATE(self);
g_return_if_fail(FU_IS_PROGRESS(self));
priv->flags |= flag;
}
/**
* fu_progress_remove_flag:
* @self: a #FuProgress
* @flag: an internal progress flag, e.g. %FU_PROGRESS_FLAG_GUESSED
*
* Removes a flag.
*
* Since: 1.7.0
**/
void
fu_progress_remove_flag(FuProgress *self, FuProgressFlags flag)
{
FuProgressPrivate *priv = GET_PRIVATE(self);
g_return_if_fail(FU_IS_PROGRESS(self));
priv->flags &= ~flag;
}
/**
* fu_progress_has_flag:
* @self: a #FuProgress
* @flag: an internal progress flag, e.g. %FU_PROGRESS_FLAG_GUESSED
*
* Tests for a flag.
*
* Since: 1.7.0
**/
gboolean
fu_progress_has_flag(FuProgress *self, FuProgressFlags flag)
{
FuProgressPrivate *priv = GET_PRIVATE(self);
g_return_val_if_fail(FU_IS_PROGRESS(self), FALSE);
return (priv->flags & flag) > 0;
}
/**
* fu_progress_set_status:
* @self: a #FuProgress
* @status: device status
*
* Sets the status of the progress.
*
* Since: 1.7.0
**/
void
fu_progress_set_status(FuProgress *self, FwupdStatus status)
{
FuProgressPrivate *priv = GET_PRIVATE(self);
g_return_if_fail(FU_IS_PROGRESS(self));
/* not changed */
if (priv->status == status)
return;
/* save */
priv->status = status;
g_signal_emit(self, signals[SIGNAL_STATUS_CHANGED], 0, status);
}
/**
* fu_progress_get_percentage:
* @self: a #FuProgress
*
* Get the last set progress percentage.
*
* Return value: The percentage value, or %G_MAXUINT for error
*
* Since: 1.7.0
**/
guint
fu_progress_get_percentage(FuProgress *self)
{
FuProgressPrivate *priv = GET_PRIVATE(self);
g_return_val_if_fail(FU_IS_PROGRESS(self), G_MAXUINT);
return priv->percentage;
}
static void
fu_progress_build_parent_chain(FuProgress *self, GString *str, guint level)
{
FuProgressPrivate *priv = GET_PRIVATE(self);
if (priv->parent != NULL)
fu_progress_build_parent_chain(priv->parent, str, level + 1);
g_string_append_printf(str,
"%u) %s (%u/%u)\n",
level,
priv->id,
priv->step_now,
priv->step_max);
}
/**
* fu_progress_set_percentage:
* @self: a #FuProgress
* @percentage: value between 0% and 100%
*
* Sets the progress percentage complete.
*
* NOTE: this must be above what was previously set, or it will be rejected.
*
* Since: 1.7.0
**/
void
fu_progress_set_percentage(FuProgress *self, guint percentage)
{
FuProgressPrivate *priv = GET_PRIVATE(self);
g_return_if_fail(FU_IS_PROGRESS(self));
g_return_if_fail(percentage <= 100);
/* is it the same */
if (percentage == priv->percentage)
return;
/* is it less */
if (percentage < priv->percentage) {
if (priv->profile) {
g_autoptr(GString) str = g_string_new(NULL);
fu_progress_build_parent_chain(self, str, 0);
g_warning("percentage should not go down from %u to %u: %s",
priv->percentage,
percentage,
str->str);
}
return;
}
/* save */
priv->percentage = percentage;
g_signal_emit(self, signals[SIGNAL_PERCENTAGE_CHANGED], 0, percentage);
}
/**
* fu_progress_set_percentage_full:
* @self: a #FuDevice
* @progress_done: the bytes already done
* @progress_total: the total number of bytes
*
* Sets the progress completion using the raw progress values.
*
* Since: 1.7.0
**/
void
fu_progress_set_percentage_full(FuProgress *self, gsize progress_done, gsize progress_total)
{
gdouble percentage = 0.f;
g_return_if_fail(FU_IS_PROGRESS(self));
g_return_if_fail(progress_done <= progress_total);
if (progress_total > 0)
percentage = (100.f * (gdouble)progress_done) / (gdouble)progress_total;
fu_progress_set_percentage(self, (guint)percentage);
}
/**
* fu_progress_set_profile:
* @self: A #FuProgress
* @profile: if profiling should be enabled
*
* This enables profiling of FuProgress. This may be useful in development,
* but be warned; enabling profiling makes #FuProgress very slow.
*
* Since: 1.7.0
**/
void
fu_progress_set_profile(FuProgress *self, gboolean profile)
{
FuProgressPrivate *priv = GET_PRIVATE(self);
g_return_if_fail(FU_IS_PROGRESS(self));
priv->profile = profile;
}
/**
* fu_progress_get_profile:
* @self: A #FuProgress
* @profile:
*
* Returns if the profile is enabled for this progress.
*
* Return value: if profiling should be enabled
*
* Since: 1.7.0
**/
static gboolean
fu_progress_get_profile(FuProgress *self)
{
FuProgressPrivate *priv = GET_PRIVATE(self);
g_return_val_if_fail(FU_IS_PROGRESS(self), FALSE);
return priv->profile;
}
/**
* fu_progress_reset:
* @self: A #FuProgress
*
* Resets the #FuProgress object to unset
*
* Since: 1.7.0
**/
void
fu_progress_reset(FuProgress *self)
{
FuProgressPrivate *priv = GET_PRIVATE(self);
g_return_if_fail(FU_IS_PROGRESS(self));
/* reset values */
priv->step_max = 0;
priv->step_now = 0;
priv->percentage = 0;
/* only use the timer if profiling; it's expensive */
if (priv->profile)
g_timer_start(priv->timer);
/* disconnect client */
if (priv->percentage_child_id != 0) {
g_signal_handler_disconnect(priv->child, priv->percentage_child_id);
priv->percentage_child_id = 0;
}
if (priv->status_child_id != 0) {
g_signal_handler_disconnect(priv->child, priv->status_child_id);
priv->status_child_id = 0;
}
g_clear_object(&priv->child);
/* no more step data */
g_ptr_array_set_size(priv->steps, 0);
}
/**
* fu_progress_set_steps:
* @self: A #FuProgress
* @step_max: The number of sub-tasks in this progress, can be 0
*
* Sets the number of sub-tasks, i.e. how many times the fu_progress_step_done()
* function will be called in the loop.
*
* The progress ID must be set fu_progress_set_id() before this method is used.
*
* Since: 1.7.0
**/
void
fu_progress_set_steps(FuProgress *self, guint step_max)
{
FuProgressPrivate *priv = GET_PRIVATE(self);
g_return_if_fail(FU_IS_PROGRESS(self));
g_return_if_fail(priv->id != NULL);
/* only use the timer if profiling; it's expensive */
if (priv->profile)
g_timer_start(priv->timer);
/* set step_max */
priv->step_max = step_max;
}
/**
* fu_progress_get_steps:
* @self: A #FuProgress
*
* Gets the number of sub-tasks, i.e. how many times the fu_progress_step_done()
* function will be called in the loop.
*
* Return value: number of sub-tasks in this progress
*
* Since: 1.7.0
**/
guint
fu_progress_get_steps(FuProgress *self)
{
FuProgressPrivate *priv = GET_PRIVATE(self);
g_return_val_if_fail(FU_IS_PROGRESS(self), G_MAXUINT);
return priv->step_max;
}
/**
* fu_progress_add_step:
* @self: A #FuProgress
* @status: status value to use for this phase
* @value: A step weighting variable argument array
*
* This sets the step weighting, which you will want to do if one action
* will take a bigger chunk of time than another.
*
* The progress ID must be set fu_progress_set_id() before this method is used.
*
* Since: 1.7.0
**/
void
fu_progress_add_step(FuProgress *self, FwupdStatus status, guint value)
{
FuProgressPrivate *priv = GET_PRIVATE(self);
FuProgressStep *step;
g_return_if_fail(FU_IS_PROGRESS(self));
g_return_if_fail(priv->id != NULL);
/* current status */
if (priv->steps->len == 0)
fu_progress_set_status(self, status);
/* save data */
step = g_new0(FuProgressStep, 1);
step->status = status;
step->value = value;
step->profile = .0;
g_ptr_array_add(priv->steps, step);
/* in case anything is not using ->steps */
fu_progress_set_steps(self, priv->steps->len);
}
/**
* fu_progress_finished:
* @self: A #FuProgress
*
* Called when the step_now sub-task wants to finish early and still complete.
*
* Since: 1.7.0
**/
void
fu_progress_finished(FuProgress *self)
{
FuProgressPrivate *priv = GET_PRIVATE(self);
g_return_if_fail(FU_IS_PROGRESS(self));
g_return_if_fail(priv->id != NULL);
/* is already at 100%? */
if (priv->step_now == priv->step_max)
return;
/* all done */
priv->step_now = priv->step_max;
fu_progress_set_percentage(self, 100);
fu_progress_set_status(self, FWUPD_STATUS_UNKNOWN);
}
static gdouble
fu_progress_discrete_to_percent(guint discrete, guint step_max)
{
/* check we are in range */
if (discrete > step_max)
return 100;
if (step_max == 0) {
g_warning("step_max is 0!");
return 0;
}
return ((gdouble)discrete * (100.0f / (gdouble)(step_max)));
}
static gdouble
fu_progress_get_step_percentage(FuProgress *self, guint idx)
{
FuProgressPrivate *priv = GET_PRIVATE(self);
guint current = 0;
guint total = 0;
for (guint i = 0; i < priv->steps->len; i++) {
FuProgressStep *step = g_ptr_array_index(priv->steps, i);
if (i <= idx)
current += step->value;
total += step->value;
}
if (total == 0)
return 0;
return ((gdouble)current * 100.f) / (gdouble)total;
}
static void
fu_progress_child_status_changed_cb(FuProgress *child, FwupdStatus status, FuProgress *self)
{
fu_progress_set_status(self, status);
}
static void
fu_progress_child_percentage_changed_cb(FuProgress *child, guint percentage, FuProgress *self)
{
FuProgressPrivate *priv = GET_PRIVATE(self);
gdouble offset;
gdouble range;
gdouble extra;
guint parent_percentage;
/* propagate up the stack if FuProgress has only one step */
if (priv->step_max == 1) {
fu_progress_set_percentage(self, percentage);
return;
}
/* did we call done on a self that did not have a size set? */
if (priv->step_max == 0)
return;
/* already at >= 100% */
if (priv->step_now >= priv->step_max) {
g_warning("already at %u/%u step_max", priv->step_now, priv->step_max);
return;
}
/* we have to deal with non-linear step_max */
if (priv->steps->len > 0) {
/* we don't store zero */
if (priv->step_now == 0) {
gdouble pc = fu_progress_get_step_percentage(self, 0);
parent_percentage = percentage * pc / 100;
} else {
gdouble pc1 = fu_progress_get_step_percentage(self, priv->step_now - 1);
gdouble pc2 = fu_progress_get_step_percentage(self, priv->step_now);
/* bi-linearly interpolate */
parent_percentage = (((100 - percentage) * pc1) + (percentage * pc2)) / 100;
}
goto out;
}
/* get the offset */
offset = fu_progress_discrete_to_percent(priv->step_now, priv->step_max);
/* get the range between the parent step and the next parent step */
range = fu_progress_discrete_to_percent(priv->step_now + 1, priv->step_max) - offset;
if (range < 0.01)
return;
/* get the extra contributed by the child */
extra = ((gdouble)percentage / 100.0f) * range;
/* emit from the parent */
parent_percentage = (guint)(offset + extra);
out:
fu_progress_set_percentage(self, parent_percentage);
}
static void
fu_progress_set_parent(FuProgress *self, FuProgress *parent)
{
FuProgressPrivate *priv = GET_PRIVATE(self);
g_return_if_fail(FU_IS_PROGRESS(self));
priv->parent = parent; /* no ref! */
priv->profile = fu_progress_get_profile(parent);
}
/**
* fu_progress_get_child:
* @self: A #FuProgress
*
* Monitor a child and proxy back up to the parent with the correct percentage.
*
* Return value: (transfer none): A new %FuProgress or %NULL for failure
*
* Since: 1.7.0
**/
FuProgress *
fu_progress_get_child(FuProgress *self)
{
FuProgressPrivate *priv = GET_PRIVATE(self);
g_return_val_if_fail(FU_IS_PROGRESS(self), NULL);
g_return_val_if_fail(priv->id != NULL, NULL);
/* already created child */
if (priv->child != NULL)
return priv->child;
/* connect up signals */
priv->child = fu_progress_new(NULL);
priv->percentage_child_id =
g_signal_connect(priv->child,
"percentage-changed",
G_CALLBACK(fu_progress_child_percentage_changed_cb),
self);
priv->status_child_id = g_signal_connect(priv->child,
"status-changed",
G_CALLBACK(fu_progress_child_status_changed_cb),
self);
fu_progress_set_parent(priv->child, self);
return priv->child;
}
static void
fu_progress_show_profile(FuProgress *self)
{
FuProgressPrivate *priv = GET_PRIVATE(self);
gdouble division;
gdouble total_time = 0.0f;
gboolean close_enough = TRUE;
g_autoptr(GString) str = NULL;
/* not accurate enough for a profile result */
if (priv->flags & FU_PROGRESS_FLAG_NO_PROFILE)
return;
/* get the total time so we can work out the divisor */
str = g_string_new("raw timing data was { ");
for (guint i = 0; i < priv->step_max; i++) {
FuProgressStep *step = g_ptr_array_index(priv->steps, i);
g_string_append_printf(str, "%.3f, ", step->profile);
}
if (priv->step_max > 0)
g_string_set_size(str, str->len - 2);
g_string_append(str, " } -- ");
/* get the total time so we can work out the divisor */
for (guint i = 0; i < priv->step_max; i++) {
FuProgressStep *step = g_ptr_array_index(priv->steps, i);
total_time += step->profile;
}
if (total_time < 0.001)
return;
division = total_time / 100.0f;
/* what we set */
g_string_append(str, "steps were set as [ ");
for (guint i = 0; i < priv->step_max; i++) {
FuProgressStep *step = g_ptr_array_index(priv->steps, i);
g_string_append_printf(str, "%u ", step->value);
}
/* what we _should_ have set */
g_string_append_printf(str, "] but should have been [ ");
for (guint i = 0; i < priv->step_max; i++) {
FuProgressStep *step = g_ptr_array_index(priv->steps, i);
g_string_append_printf(str, "%.0f ", step->profile / division);
/* this is sufficiently different to what we guessed */
if (fabs((gdouble)step->value - step->profile / division) > 5)
close_enough = FALSE;
}
g_string_append(str, "]");
if (priv->flags & FU_PROGRESS_FLAG_GUESSED) {
#ifdef SUPPORTED_BUILD
g_debug("%s at %s", str->str, priv->id);
#else
g_warning("%s at %s", str->str, priv->id);
g_warning("Please see "
"https://github.com/fwupd/fwupd/wiki/Daemon-Warning:-FuProgress-steps");
#endif
} else if (!close_enough) {
g_debug("%s at %s", str->str, priv->id);
}
}
/**
* fu_progress_step_done:
* @self: A #FuProgress
*
* Called when the step_now sub-task has finished.
*
* Since: 1.7.0
**/
void
fu_progress_step_done(FuProgress *self)
{
FuProgressPrivate *priv = GET_PRIVATE(self);
gdouble percentage;
g_return_if_fail(FU_IS_PROGRESS(self));
g_return_if_fail(priv->id != NULL);
/* did we call done on a self that did not have a size set? */
if (priv->step_max == 0) {
g_autoptr(GString) str = g_string_new(NULL);
fu_progress_build_parent_chain(self, str, 0);
g_warning("progress done when no size set! [%s]: %s", priv->id, str->str);
return;
}
/* save the duration in the array */
if (priv->profile) {
if (priv->steps->len > 0) {
FuProgressStep *step = g_ptr_array_index(priv->steps, priv->step_now);
step->profile = g_timer_elapsed(priv->timer, NULL);
}
g_timer_start(priv->timer);
}
/* is already at 100%? */
if (priv->step_now >= priv->step_max) {
g_autoptr(GString) str = g_string_new(NULL);
fu_progress_build_parent_chain(self, str, 0);
g_warning("already at 100%% [%s]: %s", priv->id, str->str);
return;
}
/* is child not at 100%? */
if (priv->child != NULL) {
FuProgressPrivate *child_priv = GET_PRIVATE(priv->child);
if (child_priv->step_now != child_priv->step_max) {
g_autoptr(GString) str = g_string_new(NULL);
fu_progress_build_parent_chain(priv->child, str, 0);
g_warning("child is at %u/%u step_max and parent done [%s]\n%s",
child_priv->step_now,
child_priv->step_max,
priv->id,
str->str);
/* do not abort, as we want to clean this up */
}
}
/* another */
priv->step_now++;
/* update status */
if (priv->steps->len > 0) {
if (priv->step_now == priv->step_max) {
fu_progress_set_status(self, FWUPD_STATUS_UNKNOWN);
} else {
FuProgressStep *step = g_ptr_array_index(priv->steps, priv->step_now);
fu_progress_set_status(self, step->status);
}
}
/* find new percentage */
if (priv->steps->len == 0) {
percentage = fu_progress_discrete_to_percent(priv->step_now, priv->step_max);
} else {
percentage = fu_progress_get_step_percentage(self, priv->step_now - 1);
}
fu_progress_set_percentage(self, (guint)percentage);
/* show any profiling stats */
if (priv->profile && priv->step_now == priv->step_max && priv->steps->len > 0)
fu_progress_show_profile(self);
/* reset child if it exists */
if (priv->child != NULL)
fu_progress_reset(priv->child);
}
/**
* fu_progress_sleep:
* @self: a #FuProgress
* @delay_ms: the delay in milliseconds
*
* Sleeps, setting the device progress from 0..100% as time continues.
*
* Since: 1.7.0
**/
void
fu_progress_sleep(FuProgress *self, guint delay_ms)
{
gulong delay_us_pc = (delay_ms * 1000) / 100;
g_return_if_fail(FU_IS_PROGRESS(self));
g_return_if_fail(delay_ms > 0);
fu_progress_set_percentage(self, 0);
for (guint i = 0; i < 100; i++) {
g_usleep(delay_us_pc);
fu_progress_set_percentage(self, i + 1);
}
}
static void
fu_progress_init(FuProgress *self)
{
FuProgressPrivate *priv = GET_PRIVATE(self);
priv->timer = g_timer_new();
priv->steps = g_ptr_array_new_with_free_func(g_free);
}
static void
fu_progress_finalize(GObject *object)
{
FuProgress *self = FU_PROGRESS(object);
FuProgressPrivate *priv = GET_PRIVATE(self);
fu_progress_reset(self);
g_free(priv->id);
g_ptr_array_unref(priv->steps);
g_timer_destroy(priv->timer);
G_OBJECT_CLASS(fu_progress_parent_class)->finalize(object);
}
static void
fu_progress_class_init(FuProgressClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->finalize = fu_progress_finalize;
signals[SIGNAL_PERCENTAGE_CHANGED] =
g_signal_new("percentage-changed",
G_TYPE_FROM_CLASS(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(FuProgressClass, percentage_changed),
NULL,
NULL,
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE,
1,
G_TYPE_UINT);
signals[SIGNAL_STATUS_CHANGED] =
g_signal_new("status-changed",
G_TYPE_FROM_CLASS(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(FuProgressClass, status_changed),
NULL,
NULL,
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE,
1,
G_TYPE_UINT);
}
/**
* fu_progress_new:
* @id: (nullable): progress ID, normally `G_STRLOC`
*
* Return value: A new #FuProgress instance.
*
* Since: 1.7.0
**/
FuProgress *
fu_progress_new(const gchar *id)
{
FuProgress *self;
self = g_object_new(FU_TYPE_PROGRESS, NULL);
if (id != NULL)
fu_progress_set_id(self, id);
return FU_PROGRESS(self);
}