# HG changeset patch
# Node ID 4aa81685f474
# Parent  1fab141c84ff
diff --git a/traversity/meson.build b/traversity/meson.build
--- a/traversity/meson.build
+++ b/traversity/meson.build
@@ -10,13 +10,26 @@
 	'traversityupnpdiscoverer.h',
 ]
 
+TRAVERSITY_ENUM_HEADERS = [
+	'traversitydiscoverer.h',
+]
+
 TRAVERSITY_BUILT_SOURCES = []
 TRAVERSITY_BUILT_HEADERS = []
 
 traversity_filebase = 'traversity-1'
+traversity_include_base = traversity_filebase / 'traversity'
+
+TRAVERSITY_ENUMS = gnome.mkenums_simple('traversityenums',
+    sources : TRAVERSITY_ENUM_HEADERS,
+    install_header : true,
+    install_dir : get_option('includedir') / traversity_include_base)
+TRAVERSITY_BUILT_SOURCES += TRAVERSITY_ENUMS[0]
+TRAVERSITY_BUILT_HEADERS += TRAVERSITY_ENUMS[1]
+
 
 TRAVERSITY_H_INCLUDES = []
-foreach header : TRAVERSITY_HEADERS
+foreach header : TRAVERSITY_HEADERS + ['traversityenums.h']
 	TRAVERSITY_H_INCLUDES += '#include <traversity/@0@>'.format(header)
 endforeach
 traversity_h_conf = configuration_data()
@@ -34,7 +47,8 @@
 	'resources/traversity.gresource.xml',
 	source_dir : 'resources',
 	c_name : 'traversity')
-TRAVERSITY_BUILT_SOURCES += traversity_resources
+TRAVERSITY_BUILT_SOURCES += traversity_resources[0]
+TRAVERSITY_BUILT_HEADERS += traversity_resources[1]
 
 traversity_inc = include_directories('.')
 
@@ -55,13 +69,15 @@
 
 traversity_dep = declare_dependency(
     # Ensure headers built before any dependencies:
-    sources : TRAVERSITY_BUILT_SOURCES,
+    sources : TRAVERSITY_BUILT_HEADERS,
     include_directories : [toplevel_inc, traversity_inc],
     link_with : libtraversity,
     dependencies : [GLIB, GOBJECT, TEMPLATE_GLIB])
 
 meson.override_dependency(traversity_filebase, traversity_dep)
 
+install_headers(TRAVERSITY_HEADERS, subdir : traversity_include_base)
+
 pkgconfig.generate(
     libtraversity,
     name : 'libtraversity',
diff --git a/traversity/traversitydiscoverer.h b/traversity/traversitydiscoverer.h
--- a/traversity/traversitydiscoverer.h
+++ b/traversity/traversitydiscoverer.h
@@ -13,6 +13,28 @@
 G_BEGIN_DECLS
 
 /**
+ * TraversityDiscovererStatus:
+ * @TRAVERSITY_DISCOVERER_STATUS_UNDISCOVERED: A discovery attempt has not yet
+ *                                             been made.
+ * @TRAVERSITY_DISCOVERER_STATUS_UNABLE_TO_DISCOVER: The most recent discovery
+ *                                                   attempt failed.
+ * @TRAVERSITY_DISCOVERER_STATUS_DISCOVERING: A discovery attempt is in
+ *                                            progress.
+ * @TRAVERSITY_DISCOVERER_STATUS_DISCOVERED: The most recent discovery attempt
+ *                                           was successful.
+ *
+ * A status that represents the state of a [class@Traversity.Discoverer].
+ *
+ * Since: 1.0.0
+ */
+typedef enum {
+	TRAVERSITY_DISCOVERER_STATUS_UNDISCOVERED = -1,
+	TRAVERSITY_DISCOVERER_STATUS_UNABLE_TO_DISCOVER,
+	TRAVERSITY_DISCOVERER_STATUS_DISCOVERING,
+	TRAVERSITY_DISCOVERER_STATUS_DISCOVERED
+} TraversityDiscovererStatus;
+
+/**
  * TraversityDiscoverer:
  *
  * A discoverer is an abstract class that defines the interface for discovering
@@ -29,52 +51,71 @@
 	/*< private >*/
 	GObjectClass parent;
 
+	/*< public >*/
 	void (*discover_async)(TraversityDiscoverer *discoverer, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data);
-    gboolean (*discover_finish)(TraversityDiscoverer *discoverer, GAsyncResult *result, GError **error);
+	gboolean (*discover_finish)(TraversityDiscoverer *discoverer, GAsyncResult *result, GError **error);
+
+	void (*discover)(TraversityDiscoverer *discoverer);
 
 	/*< private >*/
 	gpointer reserved[8];
 };
 
 /**
- * traversity_discoverer_discover_async:
+ * traversity_discoverer_discover:
  * @discoverer: The instance.
- * @cancellable: (nullable): A [class@Gio.Cancellable] or %NULL.
- * @callback: The callback to call when discovery is done.
- * @data: User data to pass to @callback.
+ *
+ * Starts the discovery process asynchronously.
+ *
+ * To know when this process is finished, you may connect to the
+ * [signal@GObject.Object::notify] signal for this instance. You can either watch for
+ * the [property@Traversity.Discoverer:status] or the specific IP address that
+ * you are interested in.
  *
- * Starts the discovery process asynchronously. When the process has been
- * completed, @callback will be called with @data and you should call
- * [method@Traversity.Discoverer.discover_finish] to get the results of the
- * discovery.
+ * Since: 1.0.0
+ */
+void traversity_discoverer_discover(TraversityDiscoverer *discoverer);
+
+/**
+ * traversity_discoverer_discover_finished:
+ * @discoverer: The instance.
+ * @status: The new status.
+ * @error: (nullable): The [type@GLib.Error] if there was an error.
+ *
+ * Sets the status of the most recent call to
+ * [method@Traversity.Discoverer.discover].
+ *
+ * This method is only ment to be called by subclasses.
  *
  * Since: 1.0.0
  */
-void traversity_discoverer_discover_async(TraversityDiscoverer *discoverer, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data);
+void traversity_discoverer_discover_finished(TraversityDiscoverer *discoverer, TraversityDiscovererStatus status, GError *error);
 
 /**
- * traversity_discoverer_discover_finish:
+ * traversity_discoverer_get_status:
  * @discoverer: The instance.
- * @result: The [iface@Gio.AsyncResult] that was passed to your callback.
- * @error: (out) (nullable): A return address for a [struct@Glib.Error].
- *
- * Retrieves the results from a previous call to
- * [method@Traversity.Discoverer.discover_async] which should only be called in
- * the callback function from the previous call.
  *
- * If a new address has been discovered, this will return %TRUE, otherwise
- * %FALSE will be returned.
+ * Gets the status of the discoverer.
  *
- * Use [method@Traversity.Discoverer.get_local_ipv4],
- * [method@Traversity.Discoverer.get_local_ipv6],
- * [method@Traversity.Discoverer.get_public_ipv4], and
- * [method@Traversity.Discoverer.get_public_ipv6] to get the new IP addresses.
- *
- * Returns: %TRUE if something new was discovered, otherwise %FALSE.
+ * Returns: The status of the discoverer.
  *
  * Since: 1.0.0
  */
-gboolean traversity_discoverer_discover_finish(TraversityDiscoverer *discoverer, GAsyncResult *result, GError **error);
+TraversityDiscovererStatus traversity_discoverer_get_status(TraversityDiscoverer *discoverer);
+
+/**
+ * traversity_discoverer_get_error:
+ * @discoverer: The instance.
+ *
+ * Gets the [type@GLib.Error] from the last discover run or %NULL if there was
+ * no error.
+ *
+ * Returns: (transfer none): The error from the last discover run or %NULL if
+ *          there was no error.
+ *
+ * Since: 1.0.0
+ */
+GError *traversity_discoverer_get_error(TraversityDiscoverer *discoverer);
 
 /**
  * traversity_discoverer_get_local_ipv4:
@@ -188,6 +229,9 @@
  */
 void traversity_discoverer_set_public_ipv6(TraversityDiscoverer *discoverer, GInetAddress *public_ipv6);
 
+void traversity_discoverer_discover_async(TraversityDiscoverer *discoverer, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data);
+gboolean traversity_discoverer_discover_finish(TraversityDiscoverer *discoverer, GAsyncResult *result, GError **error);
+
 G_END_DECLS
 
 #endif /* TRAVERSITY_DISCOVERER_H */
diff --git a/traversity/traversitydiscoverer.c b/traversity/traversitydiscoverer.c
--- a/traversity/traversitydiscoverer.c
+++ b/traversity/traversitydiscoverer.c
@@ -1,6 +1,11 @@
 #include "traversitydiscoverer.h"
 
+#include "traversityenums.h"
+
 typedef struct {
+	TraversityDiscovererStatus status;
+	GError *error;
+
 	GInetAddress *local_ipv4;
 	GInetAddress *local_ipv6;
 
@@ -10,6 +15,8 @@
 
 enum {
 	PROP_0,
+	PROP_STATUS,
+	PROP_ERROR,
 	PROP_LOCAL_IPV4,
 	PROP_LOCAL_IPV6,
 	PROP_PUBLIC_IPV4,
@@ -20,7 +27,6 @@
 
 G_DEFINE_TYPE_WITH_PRIVATE(TraversityDiscoverer, traversity_discoverer,
                            G_TYPE_OBJECT)
-
 /******************************************************************************
  * GObject Implementation
  *****************************************************************************/
@@ -31,6 +37,14 @@
 	TraversityDiscoverer *discoverer = TRAVERSITY_DISCOVERER(obj);
 
 	switch(param_id) {
+		case PROP_STATUS:
+			g_value_set_enum(value,
+			                 traversity_discoverer_get_status(discoverer));
+			break;
+		case PROP_ERROR:
+			g_value_set_boxed(value,
+			                  traversity_discoverer_get_error(discoverer));
+			break;
 		case PROP_LOCAL_IPV4:
 			g_value_set_object(value,
 			                   traversity_discoverer_get_local_ipv4(discoverer));
@@ -89,6 +103,8 @@
 
 	priv = traversity_discoverer_get_instance_private(discoverer);
 
+	g_clear_error(&priv->error);
+
 	g_clear_object(&priv->local_ipv4);
 	g_clear_object(&priv->local_ipv6);
 	g_clear_object(&priv->public_ipv4);
@@ -110,6 +126,34 @@
 	obj_class->dispose = traversity_discoverer_dispose;
 
 	/**
+	 * TraversityDiscoverer:status:
+	 *
+	 * The [enum@Traversity.DiscovererStatus] that represents the state of this
+	 * discoverer.
+	 *
+	 * Since: 1.0.0
+	 */
+	properties[PROP_STATUS] = g_param_spec_enum(
+		"status", "status",
+		"The status of this discoverer",
+		TRAVERSITY_TYPE_DISCOVERER_STATUS,
+		TRAVERSITY_DISCOVERER_STATUS_UNDISCOVERED,
+		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * TraversityDiscoverer:error:
+	 *
+	 * The error from the last discovery attempt.
+	 *
+	 * Since: 1.0.0
+	 */
+	properties[PROP_ERROR] = g_param_spec_boxed(
+		"error", "error",
+		"The error from the last discovery attempt",
+		G_TYPE_ERROR,
+		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+	/**
 	 * TraversityDiscoverer:local-ipv4:
 	 *
 	 * The local IPv4 address if found or %NULL.
@@ -168,6 +212,78 @@
  * Public API
  *****************************************************************************/
 void
+traversity_discoverer_discover(TraversityDiscoverer *discoverer) {
+	TraversityDiscovererClass *klass = NULL;
+	TraversityDiscovererPrivate *priv = NULL;
+
+	g_return_if_fail(TRAVERSITY_IS_DISCOVERER(discoverer));
+
+	priv = traversity_discoverer_get_instance_private(discoverer);
+	if(priv->status == TRAVERSITY_DISCOVERER_STATUS_DISCOVERING) {
+		return;
+	}
+
+	klass = TRAVERSITY_DISCOVERER_GET_CLASS(discoverer);
+	if(klass != NULL && klass->discover != NULL) {
+		/* This method is named for the way it's normally called, but we abuse
+		 * it here, to set the state to discovering and clear the error.
+		 */
+		traversity_discoverer_discover_finished(discoverer,
+		                                        TRAVERSITY_DISCOVERER_STATUS_DISCOVERING,
+		                                        NULL);
+
+		klass->discover(discoverer);
+	}
+}
+
+void
+traversity_discoverer_discover_finished(TraversityDiscoverer *discoverer,
+                                        TraversityDiscovererStatus status,
+                                        GError *error)
+{
+	TraversityDiscovererPrivate *priv = NULL;
+	GObject *obj = NULL;
+
+	g_return_if_fail(TRAVERSITY_IS_DISCOVERER(discoverer));
+
+	priv = traversity_discoverer_get_instance_private(discoverer);
+
+	priv->status = status;
+
+	g_clear_error(&priv->error);
+	priv->error = error;
+
+	obj = G_OBJECT(discoverer);
+	g_object_freeze_notify(obj);
+	g_object_notify_by_pspec(obj, properties[PROP_STATUS]);
+	g_object_notify_by_pspec(obj, properties[PROP_ERROR]);
+	g_object_thaw_notify(obj);
+}
+
+TraversityDiscovererStatus
+traversity_discoverer_get_status(TraversityDiscoverer *discoverer) {
+	TraversityDiscovererPrivate *priv = NULL;
+
+	g_return_val_if_fail(TRAVERSITY_IS_DISCOVERER(discoverer),
+	                     TRAVERSITY_DISCOVERER_STATUS_UNDISCOVERED);
+
+	priv = traversity_discoverer_get_instance_private(discoverer);
+
+	return priv->status;
+}
+
+GError *
+traversity_discoverer_get_error(TraversityDiscoverer *discoverer) {
+	TraversityDiscovererPrivate *priv = NULL;
+
+	g_return_val_if_fail(TRAVERSITY_IS_DISCOVERER(discoverer), NULL);
+
+	priv = traversity_discoverer_get_instance_private(discoverer);
+
+	return priv->error;
+}
+
+void
 traversity_discoverer_discover_async(TraversityDiscoverer *discoverer,
                                      GCancellable *cancellable,
                                      GAsyncReadyCallback callback,
