libfprint/SOURCES/0105-fp-device-Move-fpi-code-into-its-own-unit-that-can-b.patch

2530 lines
74 KiB
Diff
Raw Normal View History

2020-04-23 23:55:43 +00:00
From 3b368f67de150199ba2dfb083ab22e7fd2e4204f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= <mail@3v1n0.net>
Date: Tue, 10 Dec 2019 20:24:04 +0100
Subject: [PATCH 105/181] fp-device: Move fpi code into its own unit that can
be compiled a part
In order to be able to test the private device code (used by drivers) we
need to have that split a part in a different .c file so that we can compile
it alone and link with it both the shared library and the test executables.
Redefine fp_device_get_instance_private for private usage, not to move
the private struct as part of FpDevice.
---
libfprint/fp-device-private.h | 65 ++
libfprint/fp-device.c | 1186 +--------------------------------
libfprint/fpi-device.c | 1177 ++++++++++++++++++++++++++++++++
libfprint/meson.build | 1 +
4 files changed, 1245 insertions(+), 1184 deletions(-)
create mode 100644 libfprint/fp-device-private.h
create mode 100644 libfprint/fpi-device.c
diff --git a/libfprint/fp-device-private.h b/libfprint/fp-device-private.h
new file mode 100644
index 0000000..65fb1cb
--- /dev/null
+++ b/libfprint/fp-device-private.h
@@ -0,0 +1,65 @@
+/*
+ * FpDevice - A fingerprint reader device
+ * Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
+ * Copyright (C) 2019 Marco Trevisan <marco.trevisan@canonical.com>
+ *
+ * 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.1 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#pragma once
+
+#include "fpi-device.h"
+
+typedef struct
+{
+ FpDeviceType type;
+
+ GUsbDevice *usb_device;
+ const gchar *virtual_env;
+
+ gboolean is_open;
+
+ gchar *device_id;
+ gchar *device_name;
+ FpScanType scan_type;
+
+ guint64 driver_data;
+
+ gint nr_enroll_stages;
+ GSList *sources;
+
+ /* We always make sure that only one task is run at a time. */
+ FpDeviceAction current_action;
+ GTask *current_task;
+ GAsyncReadyCallback current_user_cb;
+ gulong current_cancellable_id;
+ GSource *current_idle_cancel_source;
+ GSource *current_task_idle_return_source;
+
+ /* State for tasks */
+ gboolean wait_for_finger;
+} FpDevicePrivate;
+
+
+typedef struct
+{
+ FpPrint *print;
+
+ FpEnrollProgress enroll_progress_cb;
+ gpointer enroll_progress_data;
+ GDestroyNotify enroll_progress_destroy;
+} FpEnrollData;
+
+void enroll_data_free (FpEnrollData *enroll_data);
diff --git a/libfprint/fp-device.c b/libfprint/fp-device.c
index 91a3aae..c49e5a9 100644
--- a/libfprint/fp-device.c
+++ b/libfprint/fp-device.c
@@ -21,17 +21,7 @@
#define FP_COMPONENT "device"
#include "fpi-log.h"
-#include "fpi-device.h"
-
-/**
- * SECTION: fp-device
- * @title: FpDevice
- * @short_description: Fingerprint device handling
- *
- * The #FpDevice object allows you to interact with fingerprint readers.
- * Befor doing any other operation you need to fp_device_open() the device
- * and after you are done you need to fp_device_close() it again.
- */
+#include "fp-device-private.h"
/**
* SECTION: fpi-device
@@ -46,36 +36,6 @@
* Also see the public #FpDevice routines.
*/
-typedef struct
-{
- FpDeviceType type;
-
- GUsbDevice *usb_device;
- const gchar *virtual_env;
-
- gboolean is_open;
-
- gchar *device_id;
- gchar *device_name;
- FpScanType scan_type;
-
- guint64 driver_data;
-
- gint nr_enroll_stages;
- GSList *sources;
-
- /* We always make sure that only one task is run at a time. */
- FpDeviceAction current_action;
- GTask *current_task;
- GAsyncReadyCallback current_user_cb;
- gulong current_cancellable_id;
- GSource *current_idle_cancel_source;
- GSource *current_task_idle_return_source;
-
- /* State for tasks */
- gboolean wait_for_finger;
-} FpDevicePrivate;
-
static void fp_device_async_initable_iface_init (GAsyncInitableIface *iface);
G_DEFINE_TYPE_EXTENDED (FpDevice, fp_device, G_TYPE_OBJECT, G_TYPE_FLAG_ABSTRACT,
@@ -99,27 +59,6 @@ enum {
static GParamSpec *properties[N_PROPS];
-typedef struct
-{
- FpPrint *print;
-
- FpEnrollProgress enroll_progress_cb;
- gpointer enroll_progress_data;
- GDestroyNotify enroll_progress_destroy;
-} FpEnrollData;
-
-static void
-enroll_data_free (gpointer free_data)
-{
- FpEnrollData *data = free_data;
-
- if (data->enroll_progress_destroy)
- data->enroll_progress_destroy (data->enroll_progress_data);
- data->enroll_progress_data = NULL;
- g_clear_object (&data->print);
- g_free (data);
-}
-
/**
* fp_device_retry_quark:
*
@@ -134,150 +73,6 @@ G_DEFINE_QUARK (fp - device - retry - quark, fp_device_retry)
**/
G_DEFINE_QUARK (fp - device - error - quark, fp_device_error)
-/**
- * fpi_device_retry_new:
- * @error: The #FpDeviceRetry error value describing the issue
- *
- * Create a new retry error code for use with fpi_device_verify_complete()
- * and similar calls.
- */
-GError *
-fpi_device_retry_new (FpDeviceRetry error)
-{
- const gchar *msg;
-
- switch (error)
- {
- case FP_DEVICE_RETRY_GENERAL:
- msg = "Please try again.";
- break;
-
- case FP_DEVICE_RETRY_TOO_SHORT:
- msg = "The swipe was too short, please try again.";
- break;
-
- case FP_DEVICE_RETRY_CENTER_FINGER:
- msg = "The finger was not centered properly, please try again.";
- break;
-
- case FP_DEVICE_RETRY_REMOVE_FINGER:
- msg = "Please try again after removing the finger first.";
- break;
-
- default:
- g_warning ("Unsupported error, returning general error instead!");
- error = FP_DEVICE_RETRY_GENERAL;
- msg = "Please try again.";
- }
-
- return g_error_new_literal (FP_DEVICE_RETRY, error, msg);
-}
-
-/**
- * fpi_device_error_new:
- * @error: The #FpDeviceRetry error value describing the issue
- *
- * Create a new error code for use with fpi_device_verify_complete() and
- * similar calls.
- */
-GError *
-fpi_device_error_new (FpDeviceError error)
-{
- const gchar *msg;
-
- switch (error)
- {
- case FP_DEVICE_ERROR_GENERAL:
- msg = "An unspecified error occured!";
- break;
-
- case FP_DEVICE_ERROR_NOT_SUPPORTED:
- msg = "The operation is not supported on this device!";
- break;
-
- case FP_DEVICE_ERROR_NOT_OPEN:
- msg = "The device needs to be opened first!";
- break;
-
- case FP_DEVICE_ERROR_ALREADY_OPEN:
- msg = "The device has already been opened!";
- break;
-
- case FP_DEVICE_ERROR_BUSY:
- msg = "The device is still busy with another operation, please try again later.";
- break;
-
- case FP_DEVICE_ERROR_PROTO:
- msg = "The driver encountered a protocol error with the device.";
- break;
-
- case FP_DEVICE_ERROR_DATA_INVALID:
- msg = "Passed (print) data is not valid.";
- break;
-
- case FP_DEVICE_ERROR_DATA_FULL:
- msg = "On device storage space is full.";
- break;
-
- case FP_DEVICE_ERROR_DATA_NOT_FOUND:
- msg = "Print was not found on the devices storage.";
- break;
-
- default:
- g_warning ("Unsupported error, returning general error instead!");
- error = FP_DEVICE_ERROR_GENERAL;
- msg = "An unspecified error occured!";
- }
-
- return g_error_new_literal (FP_DEVICE_ERROR, error, msg);
-}
-
-/**
- * fpi_device_retry_new_msg:
- * @error: The #FpDeviceRetry error value describing the issue
- * @msg: Custom message to use
- *
- * Create a new retry error code for use with fpi_device_verify_complete()
- * and similar calls.
- */
-GError *
-fpi_device_retry_new_msg (FpDeviceRetry device_error,
- const gchar *msg,
- ...)
-{
- GError *error;
- va_list args;
-
- va_start (args, msg);
- error = g_error_new_valist (FP_DEVICE_RETRY, device_error, msg, args);
- va_end (args);
-
- return error;
-}
-
-/**
- * fpi_device_error_new_msg:
- * @error: The #FpDeviceRetry error value describing the issue
- * @msg: Custom message to use
- *
- * Create a new error code for use with fpi_device_verify_complete()
- * and similar calls.
- */
-GError *
-fpi_device_error_new_msg (FpDeviceError device_error,
- const gchar *msg,
- ...)
-{
- GError *error;
- va_list args;
-
- va_start (args, msg);
- error = g_error_new_valist (FP_DEVICE_ERROR, device_error, msg, args);
- va_end (args);
-
- return error;
-}
-
static gboolean
fp_device_cancel_in_idle_cb (gpointer user_data)
{
@@ -330,21 +125,6 @@ maybe_cancel_on_cancelled (FpDevice *device,
NULL);
}
-static void
-clear_device_cancel_action (FpDevice *device)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
-
- g_clear_pointer (&priv->current_idle_cancel_source, g_source_destroy);
-
- if (priv->current_cancellable_id)
- {
- g_cancellable_disconnect (g_task_get_cancellable (priv->current_task),
- priv->current_cancellable_id);
- priv->current_cancellable_id = 0;
- }
-}
-
static void
fp_device_constructed (GObject *object)
{
@@ -976,7 +756,7 @@ fp_device_enroll (FpDevice *device,
data->enroll_progress_data = progress_data;
// Attach the progress data as task data so that it is destroyed
- g_task_set_task_data (priv->current_task, data, enroll_data_free);
+ g_task_set_task_data (priv->current_task, data, (GDestroyNotify) enroll_data_free);
FP_DEVICE_GET_CLASS (device)->enroll (device);
}
@@ -1406,968 +1186,6 @@ fp_device_list_prints_finish (FpDevice *device,
return g_task_propagate_pointer (G_TASK (result), error);
}
-typedef struct
-{
- GSource source;
- FpDevice *device;
-} FpDeviceTimeoutSource;
-
-static void
-timeout_finalize (GSource *source)
-{
- FpDeviceTimeoutSource *timeout_source = (FpDeviceTimeoutSource *) source;
- FpDevicePrivate *priv;
-
- priv = fp_device_get_instance_private (timeout_source->device);
- priv->sources = g_slist_remove (priv->sources, source);
-}
-
-static gboolean
-timeout_dispatch (GSource *source, GSourceFunc gsource_func, gpointer user_data)
-{
- FpDeviceTimeoutSource *timeout_source = (FpDeviceTimeoutSource *) source;
- FpTimeoutFunc callback = (FpTimeoutFunc) gsource_func;
-
- callback (timeout_source->device, user_data);
-
- return G_SOURCE_REMOVE;
-}
-
-static GSourceFuncs timeout_funcs = {
- NULL, /* prepare */
- NULL, /* check */
- timeout_dispatch,
- timeout_finalize,
- NULL, NULL
-};
-
-/* Private API functions */
-
-/**
- * fpi_device_set_nr_enroll_stages:
- * @device: The #FpDevice
- * @enroll_stages: The number of enroll stages
- *
- * Updates the reported number of enroll stages that the device needs.
- * If all supported devices have the same number of stages, then the
- * value can simply be set in the class.
- */
-void
-fpi_device_set_nr_enroll_stages (FpDevice *device,
- gint enroll_stages)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
-
- g_return_if_fail (FP_IS_DEVICE (device));
-
- priv->nr_enroll_stages = enroll_stages;
- g_object_notify_by_pspec (G_OBJECT (device), properties[PROP_NR_ENROLL_STAGES]);
-}
-
-/**
- * fpi_device_set_scan_type:
- * @device: The #FpDevice
- * @scan_type: The scan type of the device
- *
- * Updates the the scan type of the device from the default.
- * If all supported devices have the same scan type, then the
- * value can simply be set in the class.
- */
-void
-fpi_device_set_scan_type (FpDevice *device,
- FpScanType scan_type)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
-
- g_return_if_fail (FP_IS_DEVICE (device));
-
- priv->scan_type = scan_type;
- g_object_notify_by_pspec (G_OBJECT (device), properties[PROP_SCAN_TYPE]);
-}
-
-/**
- * fpi_device_add_timeout:
- * @device: The #FpDevice
- * @interval: The interval in milliseconds
- * @func: The #FpTimeoutFunc to call on timeout
- * @user_data: (nullable): User data to pass to the callback
- * @destroy_notify: (nullable): #GDestroyNotify for @user_data
- *
- * Register a timeout to run. Drivers should always make sure that timers are
- * cancelled when appropriate.
- *
- * Returns: (transfer none): A newly created and attached #GSource
- */
-GSource *
-fpi_device_add_timeout (FpDevice *device,
- gint interval,
- FpTimeoutFunc func,
- gpointer user_data,
- GDestroyNotify destroy_notify)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
- FpDeviceTimeoutSource *source;
-
- source = (FpDeviceTimeoutSource *) g_source_new (&timeout_funcs,
- sizeof (FpDeviceTimeoutSource));
- source->device = device;
-
- g_source_attach (&source->source, NULL);
- g_source_set_callback (&source->source, (GSourceFunc) func, user_data, destroy_notify);
- g_source_set_ready_time (&source->source,
- g_source_get_time (&source->source) + interval * (guint64) 1000);
- priv->sources = g_slist_prepend (priv->sources, source);
- g_source_unref (&source->source);
-
- return &source->source;
-}
-
-/**
- * fpi_device_get_usb_device:
- * @device: The #FpDevice
- *
- * Get the #GUsbDevice for this #FpDevice. Only permissible to call if the
- * #FpDevice is of type %FP_DEVICE_TYPE_USB.
- *
- * Returns: The #GUsbDevice
- */
-GUsbDevice *
-fpi_device_get_usb_device (FpDevice *device)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
-
- g_return_val_if_fail (FP_IS_DEVICE (device), NULL);
- g_return_val_if_fail (priv->type == FP_DEVICE_TYPE_USB, NULL);
-
- return priv->usb_device;
-}
-
-/**
- * fpi_device_get_virtual_env:
- * @device: The #FpDevice
- *
- * Get the value of the environment variable that caused the virtual #FpDevice to be
- * generated. Only permissible to call if the #FpDevice is of type %FP_DEVICE_TYPE_VIRTUAL.
- *
- * Returns: The value of the environment variable
- */
-const gchar *
-fpi_device_get_virtual_env (FpDevice *device)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
-
- g_return_val_if_fail (FP_IS_DEVICE (device), NULL);
- g_return_val_if_fail (priv->type == FP_DEVICE_TYPE_VIRTUAL, NULL);
-
- return priv->virtual_env;
-}
-
-/**
- * fpi_device_get_current_action:
- * @device: The #FpDevice
- *
- * Get the currently ongoing action or %FP_DEVICE_ACTION_NONE if there
- * is no operation at this time.
- *
- * This is useful for drivers that might share code paths between different
- * actions (e.g. verify and identify) and want to find out again later which
- * action was started in the beginning.
- *
- * Returns: The ongoing #FpDeviceAction
- */
-FpDeviceAction
-fpi_device_get_current_action (FpDevice *device)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
-
- g_return_val_if_fail (FP_IS_DEVICE (device), FP_DEVICE_ACTION_NONE);
-
- return priv->current_action;
-}
-
-/**
- * fpi_device_action_is_cancelled:
- * @device: The #FpDevice
- *
- * Checks whether the current action has been cancelled by the user.
- * This is equivalent to first getting the cancellable using
- * fpi_device_get_cancellable() and then checking whether it has been
- * cancelled (if it is non-NULL).
- *
- * Returns: %TRUE if action should be cancelled
- */
-gboolean
-fpi_device_action_is_cancelled (FpDevice *device)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
- GCancellable *cancellable;
-
- g_return_val_if_fail (FP_IS_DEVICE (device), TRUE);
- g_return_val_if_fail (priv->current_action != FP_DEVICE_ACTION_NONE, TRUE);
-
- cancellable = g_task_get_cancellable (priv->current_task);
-
- return cancellable ? g_cancellable_is_cancelled (cancellable) : FALSE;
-}
-
-/**
- * fpi_device_get_driver_data:
- * @device: The #FpDevice
- *
- * Returns: The driver data from the #FpIdEntry table entry
- */
-guint64
-fpi_device_get_driver_data (FpDevice *device)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
-
- g_return_val_if_fail (FP_IS_DEVICE (device), 0);
-
- return priv->driver_data;
-}
-
-/**
- * fpi_device_get_enroll_data:
- * @device: The #FpDevice
- * @print: (out) (transfer none): The user provided template print
- *
- * Get data for enrollment.
- */
-void
-fpi_device_get_enroll_data (FpDevice *device,
- FpPrint **print)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
- FpEnrollData *data;
-
- g_return_if_fail (FP_IS_DEVICE (device));
- g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_ENROLL);
-
- data = g_task_get_task_data (priv->current_task);
- g_assert (data);
-
- if (print)
- *print = data->print;
-}
-
-/**
- * fpi_device_get_capture_data:
- * @device: The #FpDevice
- * @wait_for_finger: (out): Whether to wait for finger or not
- *
- * Get data for capture.
- */
-void
-fpi_device_get_capture_data (FpDevice *device,
- gboolean *wait_for_finger)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
-
- g_return_if_fail (FP_IS_DEVICE (device));
- g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_CAPTURE);
-
- if (wait_for_finger)
- *wait_for_finger = priv->wait_for_finger;
-}
-
-/**
- * fpi_device_get_verify_data:
- * @device: The #FpDevice
- * @print: (out) (transfer none): The enrolled print
- *
- * Get data for verify.
- */
-void
-fpi_device_get_verify_data (FpDevice *device,
- FpPrint **print)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
-
- g_return_if_fail (FP_IS_DEVICE (device));
- g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_VERIFY);
-
- if (print)
- *print = g_task_get_task_data (priv->current_task);
-}
-
-/**
- * fpi_device_get_identify_data:
- * @device: The #FpDevice
- * @prints: (out) (transfer none) (element-type FpPrint): The gallery of prints
- *
- * Get data for identify.
- */
-void
-fpi_device_get_identify_data (FpDevice *device,
- GPtrArray **prints)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
-
- g_return_if_fail (FP_IS_DEVICE (device));
- g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_IDENTIFY);
-
- if (prints)
- *prints = g_task_get_task_data (priv->current_task);
-}
-
-/**
- * fpi_device_get_delete_data:
- * @device: The #FpDevice
- * @print: (out) (transfer none): The print to delete
- *
- * Get data for delete.
- */
-void
-fpi_device_get_delete_data (FpDevice *device,
- FpPrint **print)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
-
- g_return_if_fail (FP_IS_DEVICE (device));
- g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_DELETE);
-
- if (print)
- *print = g_task_get_task_data (priv->current_task);
-}
-
-/**
- * fpi_device_get_cancellable:
- * @device: The #FpDevice
- *
- * Retrieve the #GCancellable that may cancel the currently ongoing operation. This
- * is primarily useful to pass directly to e.g. fpi_usb_transfer_submit() for cancellable
- * transfers.
- * In many cases the cancel vfunc may be more convenient to react to cancellation in some
- * way.
- *
- * Returns: (transfer none): The #GCancellable for the current action.
- */
-GCancellable *
-fpi_device_get_cancellable (FpDevice *device)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
-
- g_return_val_if_fail (FP_IS_DEVICE (device), NULL);
- g_return_val_if_fail (priv->current_action != FP_DEVICE_ACTION_NONE, NULL);
-
- return g_task_get_cancellable (priv->current_task);
-}
-
-/**
- * fpi_device_action_error:
- * @device: The #FpDevice
- * @error: The #GError to return
- *
- * Finish an ongoing action with an error. This is the same as calling
- * the corresponding complete function such as fpi_device_open_complete()
- * with an error set. If possible, use the correct complete function as
- * that results in improved error detection.
- */
-void
-fpi_device_action_error (FpDevice *device,
- GError *error)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
-
- g_return_if_fail (FP_IS_DEVICE (device));
- g_return_if_fail (priv->current_action != FP_DEVICE_ACTION_NONE);
-
- if (error != NULL)
- {
- g_debug ("Device reported generic error during action; action was: %i", priv->current_action);
- }
- else
- {
- g_warning ("Device failed to pass an error to generic action error function");
- error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, "Device reported error but did not provide an error condition");
- }
-
-
- switch (priv->current_action)
- {
- case FP_DEVICE_ACTION_PROBE:
- fpi_device_probe_complete (device, NULL, NULL, error);
- break;
-
- case FP_DEVICE_ACTION_OPEN:
- fpi_device_open_complete (device, error);
- break;
-
- case FP_DEVICE_ACTION_CLOSE:
- fpi_device_close_complete (device, error);
- break;
-
- case FP_DEVICE_ACTION_ENROLL:
- fpi_device_enroll_complete (device, NULL, error);
- break;
-
- case FP_DEVICE_ACTION_VERIFY:
- fpi_device_verify_complete (device, FPI_MATCH_ERROR, NULL, error);
- break;
-
- case FP_DEVICE_ACTION_IDENTIFY:
- fpi_device_identify_complete (device, NULL, NULL, error);
- break;
-
- case FP_DEVICE_ACTION_CAPTURE:
- fpi_device_capture_complete (device, NULL, error);
- break;
-
- case FP_DEVICE_ACTION_DELETE:
- fpi_device_delete_complete (device, error);
- break;
-
- case FP_DEVICE_ACTION_LIST:
- fpi_device_list_complete (device, NULL, error);
- break;
-
- default:
- case FP_DEVICE_ACTION_NONE:
- g_return_if_reached ();
- break;
- }
-}
-
-typedef enum _FpDeviceTaskReturnType {
- FP_DEVICE_TASK_RETURN_INT,
- FP_DEVICE_TASK_RETURN_BOOL,
- FP_DEVICE_TASK_RETURN_OBJECT,
- FP_DEVICE_TASK_RETURN_PTR_ARRAY,
- FP_DEVICE_TASK_RETURN_ERROR,
-} FpDeviceTaskReturnType;
-
-typedef struct _FpDeviceTaskReturnData
-{
- FpDevice *device;
- FpDeviceTaskReturnType type;
- gpointer result;
-} FpDeviceTaskReturnData;
-
-static gboolean
-fp_device_task_return_in_idle_cb (gpointer user_data)
-{
- FpDeviceTaskReturnData *data = user_data;
- FpDevicePrivate *priv = fp_device_get_instance_private (data->device);
-
- g_autoptr(GTask) task = NULL;
-
- g_debug ("Completing action %d in idle!", priv->current_action);
-
- task = g_steal_pointer (&priv->current_task);
- priv->current_action = FP_DEVICE_ACTION_NONE;
- priv->current_task_idle_return_source = NULL;
-
- switch (data->type)
- {
- case FP_DEVICE_TASK_RETURN_INT:
- g_task_return_int (task, GPOINTER_TO_INT (data->result));
- break;
-
- case FP_DEVICE_TASK_RETURN_BOOL:
- g_task_return_boolean (task, GPOINTER_TO_UINT (data->result));
- break;
-
- case FP_DEVICE_TASK_RETURN_OBJECT:
- g_task_return_pointer (task, data->result, g_object_unref);
- break;
-
- case FP_DEVICE_TASK_RETURN_PTR_ARRAY:
- g_task_return_pointer (task, data->result,
- (GDestroyNotify) g_ptr_array_unref);
- break;
-
- case FP_DEVICE_TASK_RETURN_ERROR:
- g_task_return_error (task, data->result);
- break;
-
- default:
- g_assert_not_reached ();
- }
-
- return G_SOURCE_REMOVE;
-}
-
-static void
-fp_device_task_return_data_free (FpDeviceTaskReturnData *data)
-{
- g_object_unref (data->device);
- g_free (data);
-}
-
-static void
-fp_device_return_task_in_idle (FpDevice *device,
- FpDeviceTaskReturnType return_type,
- gpointer return_data)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
- FpDeviceTaskReturnData *data;
-
- data = g_new0 (FpDeviceTaskReturnData, 1);
- data->device = g_object_ref (device);
- data->type = return_type;
- data->result = return_data;
-
- priv->current_task_idle_return_source = g_idle_source_new ();
- g_source_set_priority (priv->current_task_idle_return_source,
- g_task_get_priority (priv->current_task));
- g_source_set_callback (priv->current_task_idle_return_source,
- fp_device_task_return_in_idle_cb,
- data,
- (GDestroyNotify) fp_device_task_return_data_free);
-
- g_source_attach (priv->current_task_idle_return_source, NULL);
- g_source_unref (priv->current_task_idle_return_source);
-}
-
-/**
- * fpi_device_probe_complete:
- * @device: The #FpDevice
- * @device_id: Unique ID for the device or %NULL
- * @device_name: Human readable name or %NULL for driver name
- * @error: The #GError or %NULL on success
- *
- * Finish an ongoing probe operation. If error is %NULL success is assumed.
- */
-void
-fpi_device_probe_complete (FpDevice *device,
- const gchar *device_id,
- const gchar *device_name,
- GError *error)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
-
- g_return_if_fail (FP_IS_DEVICE (device));
- g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_PROBE);
-
- g_debug ("Device reported probe completion");
-
- clear_device_cancel_action (device);
-
- if (!error)
- {
- if (device_id)
- {
- g_clear_pointer (&priv->device_id, g_free);
- priv->device_id = g_strdup (device_id);
- g_object_notify_by_pspec (G_OBJECT (device), properties[PROP_DEVICE_ID]);
- }
- if (device_name)
- {
- g_clear_pointer (&priv->device_name, g_free);
- priv->device_name = g_strdup (device_name);
- g_object_notify_by_pspec (G_OBJECT (device), properties[PROP_NAME]);
- }
- fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL,
- GUINT_TO_POINTER (TRUE));
- }
- else
- {
- fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
- }
-}
-
-/**
- * fpi_device_open_complete:
- * @device: The #FpDevice
- * @error: The #GError or %NULL on success
- *
- * Finish an ongoing open operation. If error is %NULL success is assumed.
- */
-void
-fpi_device_open_complete (FpDevice *device, GError *error)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
-
- g_return_if_fail (FP_IS_DEVICE (device));
- g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_OPEN);
-
- g_debug ("Device reported open completion");
-
- clear_device_cancel_action (device);
-
- if (!error)
- {
- priv->is_open = TRUE;
- g_object_notify_by_pspec (G_OBJECT (device), properties[PROP_OPEN]);
- }
-
- if (!error)
- fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL,
- GUINT_TO_POINTER (TRUE));
- else
- fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
-}
-
-/**
- * fpi_device_close_complete:
- * @device: The #FpDevice
- * @error: The #GError or %NULL on success
- *
- * Finish an ongoing close operation. If error is %NULL success is assumed.
- */
-void
-fpi_device_close_complete (FpDevice *device, GError *error)
-{
- GError *nested_error = NULL;
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
-
- g_return_if_fail (FP_IS_DEVICE (device));
- g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_CLOSE);
-
- g_debug ("Device reported close completion");
-
- clear_device_cancel_action (device);
- priv->is_open = FALSE;
- g_object_notify_by_pspec (G_OBJECT (device), properties[PROP_OPEN]);
-
- switch (priv->type)
- {
- case FP_DEVICE_TYPE_USB:
- if (!g_usb_device_close (priv->usb_device, &nested_error))
- {
- if (error == NULL)
- error = nested_error;
- fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
- return;
- }
- break;
-
- case FP_DEVICE_TYPE_VIRTUAL:
- break;
-
- default:
- g_assert_not_reached ();
- fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR,
- fpi_device_error_new (FP_DEVICE_ERROR_GENERAL));
- return;
- }
-
- if (!error)
- fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL,
- GUINT_TO_POINTER (TRUE));
- else
- fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
-}
-
-/**
- * fpi_device_enroll_complete:
- * @device: The #FpDevice
- * @print: (nullable) (transfer full): The #FpPrint or %NULL on failure
- * @error: The #GError or %NULL on success
- *
- * Finish an ongoing enroll operation. The #FpPrint can be stored by the
- * caller for later verification.
- */
-void
-fpi_device_enroll_complete (FpDevice *device, FpPrint *print, GError *error)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
-
- g_return_if_fail (FP_IS_DEVICE (device));
- g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_ENROLL);
-
- g_debug ("Device reported enroll completion");
-
- clear_device_cancel_action (device);
-
- if (!error)
- {
- if (FP_IS_PRINT (print))
- {
- fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_OBJECT, print);
- }
- else
- {
- g_warning ("Driver did not provide a valid print and failed to provide an error!");
- error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
- "Driver failed to provide print data!");
- fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
- }
- }
- else
- {
- fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
- if (FP_IS_PRINT (print))
- {
- g_warning ("Driver passed an error but also provided a print, returning error!");
- g_object_unref (print);
- }
- }
-}
-
-/**
- * fpi_device_verify_complete:
- * @device: The #FpDevice
- * @result: The #FpiMatchResult of the operation
- * @print: The scanned #FpPrint
- * @error: A #GError if result is %FPI_MATCH_ERROR
- *
- * Finish an ongoing verify operation. The returned print should be
- * representing the new scan and not the one passed for verification.
- */
-void
-fpi_device_verify_complete (FpDevice *device,
- FpiMatchResult result,
- FpPrint *print,
- GError *error)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
-
- g_return_if_fail (FP_IS_DEVICE (device));
- g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_VERIFY);
-
- g_debug ("Device reported verify completion");
-
- clear_device_cancel_action (device);
-
- g_object_set_data_full (G_OBJECT (priv->current_task),
- "print",
- print,
- g_object_unref);
-
- if (!error)
- {
- if (result != FPI_MATCH_ERROR)
- {
- fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_INT,
- GINT_TO_POINTER (result));
- }
- else
- {
- g_warning ("Driver did not provide an error for a failed verify operation!");
- error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
- "Driver failed to provide an error!");
- fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
- }
- }
- else
- {
- fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
- if (result != FPI_MATCH_ERROR)
- {
- g_warning ("Driver passed an error but also provided a match result, returning error!");
- g_object_unref (print);
- }
- }
-}
-
-/**
- * fpi_device_identify_complete:
- * @device: The #FpDevice
- * @match: The matching #FpPrint from the passed gallery, or %NULL if none matched
- * @print: The scanned #FpPrint, may be %NULL
- * @error: The #GError or %NULL on success
- *
- * Finish an ongoing identify operation. The match that was identified is
- * returned in @match. The @print parameter returns the newly created scan
- * that was used for matching.
- */
-void
-fpi_device_identify_complete (FpDevice *device,
- FpPrint *match,
- FpPrint *print,
- GError *error)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
-
- g_return_if_fail (FP_IS_DEVICE (device));
- g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_IDENTIFY);
-
- g_debug ("Device reported identify completion");
-
- clear_device_cancel_action (device);
-
- g_object_set_data_full (G_OBJECT (priv->current_task),
- "print",
- print,
- g_object_unref);
- g_object_set_data_full (G_OBJECT (priv->current_task),
- "match",
- match,
- g_object_unref);
- if (!error)
- {
- fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL,
- GUINT_TO_POINTER (TRUE));
- }
- else
- {
- fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
- if (match)
- {
- g_warning ("Driver passed an error but also provided a match result, returning error!");
- g_clear_object (&match);
- }
- }
-}
-
-
-/**
- * fpi_device_capture_complete:
- * @device: The #FpDevice
- * @image: The #FpImage, or %NULL on error
- * @error: The #GError or %NULL on success
- *
- * Finish an ongoing capture operation.
- */
-void
-fpi_device_capture_complete (FpDevice *device,
- FpImage *image,
- GError *error)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
-
- g_return_if_fail (FP_IS_DEVICE (device));
- g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_CAPTURE);
-
- g_debug ("Device reported capture completion");
-
- clear_device_cancel_action (device);
-
- if (!error)
- {
- if (image)
- {
- fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_OBJECT, image);
- }
- else
- {
- g_warning ("Driver did not provide an error for a failed capture operation!");
- error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
- "Driver failed to provide an error!");
- fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
- }
- }
- else
- {
- fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
- if (image)
- {
- g_warning ("Driver passed an error but also provided an image, returning error!");
- g_clear_object (&image);
- }
- }
-}
-
-/**
- * fpi_device_delete_complete:
- * @device: The #FpDevice
- * @error: The #GError or %NULL on success
- *
- * Finish an ongoing delete operation.
- */
-void
-fpi_device_delete_complete (FpDevice *device,
- GError *error)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
-
- g_return_if_fail (FP_IS_DEVICE (device));
- g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_DELETE);
-
- g_debug ("Device reported deletion completion");
-
- clear_device_cancel_action (device);
-
- if (!error)
- fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL,
- GUINT_TO_POINTER (TRUE));
- else
- fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
-}
-
-/**
- * fpi_device_list_complete:
- * @device: The #FpDevice
- * @prints: (element-type FpPrint) (transfer container): Possibly empty array of prints or %NULL on error
- * @error: The #GError or %NULL on success
- *
- * Finish an ongoing list operation.
- *
- * Please note that the @prints array will be free'ed using
- * g_ptr_array_unref() and the elements are destroyed automatically.
- * As such, you must use g_ptr_array_new_with_free_func() with
- * g_object_unref() as free func to create the array.
- */
-void
-fpi_device_list_complete (FpDevice *device,
- GPtrArray *prints,
- GError *error)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
-
- g_return_if_fail (FP_IS_DEVICE (device));
- g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_LIST);
-
- g_debug ("Device reported listing completion");
-
- clear_device_cancel_action (device);
-
- if (prints && error)
- {
- g_warning ("Driver reported back prints and error, ignoring prints");
- g_clear_pointer (&prints, g_ptr_array_unref);
- }
- else if (!prints && !error)
- {
- g_warning ("Driver did not pass array but failed to provide an error");
- error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
- "Driver failed to provide a list of prints");
- }
-
- if (!error)
- fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_PTR_ARRAY, prints);
- else
- fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
-}
-
-/**
- * fpi_device_enroll_progress:
- * @device: The #FpDevice
- * @completed_stages: The number of stages that are completed at this point
- * @print: The #FpPrint for the newly completed stage or %NULL on failure
- * @error: The #GError or %NULL on success
- *
- * Notify about the progress of the enroll operation. This is important for UI interaction.
- * The passed error may be used if a scan needs to be retried, use fpi_device_retry_new().
- */
-void
-fpi_device_enroll_progress (FpDevice *device,
- gint completed_stages,
- FpPrint *print,
- GError *error)
-{
- FpDevicePrivate *priv = fp_device_get_instance_private (device);
- FpEnrollData *data;
-
- g_return_if_fail (FP_IS_DEVICE (device));
- g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_ENROLL);
- g_return_if_fail (error == NULL || error->domain == FP_DEVICE_RETRY);
-
- g_debug ("Device reported enroll progress, reported %i of %i have been completed", completed_stages, priv->nr_enroll_stages);
-
- if (error && print)
- {
- g_warning ("Driver passed an error and also provided a print, returning error!");
- g_clear_object (&print);
- }
-
- data = g_task_get_task_data (priv->current_task);
-
- if (data->enroll_progress_cb)
- {
- data->enroll_progress_cb (device,
- completed_stages,
- print,
- data->enroll_progress_data,
- error);
- }
-
- g_clear_error (&error);
- g_clear_object (&print);
-}
-
-
static void
async_result_ready (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
diff --git a/libfprint/fpi-device.c b/libfprint/fpi-device.c
new file mode 100644
index 0000000..3eee062
--- /dev/null
+++ b/libfprint/fpi-device.c
@@ -0,0 +1,1177 @@
+/*
+ * FpDevice - A fingerprint reader device - Private APIs
+ * Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
+ * Copyright (C) 2019 Marco Trevisan <marco.trevisan@canonical.com>
+ *
+ * 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.1 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define FP_COMPONENT "device"
+#include "fpi-log.h"
+
+#include "fp-device-private.h"
+
+/**
+ * SECTION: fpi-device
+ * @title: Internal FpDevice
+ * @short_description: Internal device routines
+ *
+ * The methods that are availabe for drivers to manipulate a device. See
+ * #FpDeviceClass for more information. Also note that most of these are
+ * not relevant for image based devices, see #FpImageDeviceClass in that
+ * case.
+ *
+ * Also see the public #FpDevice routines.
+ */
+
+/* Manually redefine what G_DEFINE_* macro does */
+static inline gpointer
+fp_device_get_instance_private (FpDevice *self)
+{
+ FpDeviceClass *dev_class = g_type_class_peek_static (FP_TYPE_DEVICE);
+
+ return G_STRUCT_MEMBER_P (self,
+ g_type_class_get_instance_private_offset (dev_class));
+}
+
+/**
+ * fpi_device_retry_new:
+ * @error: The #FpDeviceRetry error value describing the issue
+ *
+ * Create a new retry error code for use with fpi_device_verify_complete()
+ * and similar calls.
+ */
+GError *
+fpi_device_retry_new (FpDeviceRetry error)
+{
+ const gchar *msg;
+
+ switch (error)
+ {
+ case FP_DEVICE_RETRY_GENERAL:
+ msg = "Please try again.";
+ break;
+
+ case FP_DEVICE_RETRY_TOO_SHORT:
+ msg = "The swipe was too short, please try again.";
+ break;
+
+ case FP_DEVICE_RETRY_CENTER_FINGER:
+ msg = "The finger was not centered properly, please try again.";
+ break;
+
+ case FP_DEVICE_RETRY_REMOVE_FINGER:
+ msg = "Please try again after removing the finger first.";
+ break;
+
+ default:
+ g_warning ("Unsupported error, returning general error instead!");
+ error = FP_DEVICE_RETRY_GENERAL;
+ msg = "Please try again.";
+ }
+
+ return g_error_new_literal (FP_DEVICE_RETRY, error, msg);
+}
+
+/**
+ * fpi_device_error_new:
+ * @error: The #FpDeviceRetry error value describing the issue
+ *
+ * Create a new error code for use with fpi_device_verify_complete() and
+ * similar calls.
+ */
+GError *
+fpi_device_error_new (FpDeviceError error)
+{
+ const gchar *msg;
+
+ switch (error)
+ {
+ case FP_DEVICE_ERROR_GENERAL:
+ msg = "An unspecified error occured!";
+ break;
+
+ case FP_DEVICE_ERROR_NOT_SUPPORTED:
+ msg = "The operation is not supported on this device!";
+ break;
+
+ case FP_DEVICE_ERROR_NOT_OPEN:
+ msg = "The device needs to be opened first!";
+ break;
+
+ case FP_DEVICE_ERROR_ALREADY_OPEN:
+ msg = "The device has already been opened!";
+ break;
+
+ case FP_DEVICE_ERROR_BUSY:
+ msg = "The device is still busy with another operation, please try again later.";
+ break;
+
+ case FP_DEVICE_ERROR_PROTO:
+ msg = "The driver encountered a protocol error with the device.";
+ break;
+
+ case FP_DEVICE_ERROR_DATA_INVALID:
+ msg = "Passed (print) data is not valid.";
+ break;
+
+ case FP_DEVICE_ERROR_DATA_FULL:
+ msg = "On device storage space is full.";
+ break;
+
+ case FP_DEVICE_ERROR_DATA_NOT_FOUND:
+ msg = "Print was not found on the devices storage.";
+ break;
+
+ default:
+ g_warning ("Unsupported error, returning general error instead!");
+ error = FP_DEVICE_ERROR_GENERAL;
+ msg = "An unspecified error occured!";
+ }
+
+ return g_error_new_literal (FP_DEVICE_ERROR, error, msg);
+}
+
+/**
+ * fpi_device_retry_new_msg:
+ * @error: The #FpDeviceRetry error value describing the issue
+ * @msg: Custom message to use with printf-style formatting
+ * @...: args for @msg
+ *
+ * Create a new retry error code for use with fpi_device_verify_complete()
+ * and similar calls.
+ */
+GError *
+fpi_device_retry_new_msg (FpDeviceRetry device_error,
+ const gchar *msg,
+ ...)
+{
+ GError *error;
+ va_list args;
+
+ va_start (args, msg);
+ error = g_error_new_valist (FP_DEVICE_RETRY, device_error, msg, args);
+ va_end (args);
+
+ return error;
+}
+
+/**
+ * fpi_device_error_new_msg:
+ * @error: The #FpDeviceRetry error value describing the issue
+ * @msg: Custom message to use with printf-style formatting
+ * @...: args for @msg
+ *
+ * Create a new error code for use with fpi_device_verify_complete()
+ * and similar calls.
+ */
+GError *
+fpi_device_error_new_msg (FpDeviceError device_error,
+ const gchar *msg,
+ ...)
+{
+ GError *error;
+ va_list args;
+
+ va_start (args, msg);
+ error = g_error_new_valist (FP_DEVICE_ERROR, device_error, msg, args);
+ va_end (args);
+
+ return error;
+}
+
+/**
+ * fpi_device_set_nr_enroll_stages:
+ * @device: The #FpDevice
+ * @enroll_stages: The number of enroll stages
+ *
+ * Updates the reported number of enroll stages that the device needs.
+ * If all supported devices have the same number of stages, then the
+ * value can simply be set in the class.
+ */
+void
+fpi_device_set_nr_enroll_stages (FpDevice *device,
+ gint enroll_stages)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+
+ priv->nr_enroll_stages = enroll_stages;
+ g_object_notify (G_OBJECT (device), "nr-enroll-stages");
+}
+
+/**
+ * fpi_device_set_scan_type:
+ * @device: The #FpDevice
+ * @scan_type: The scan type of the device
+ *
+ * Updates the the scan type of the device from the default.
+ * If all supported devices have the same scan type, then the
+ * value can simply be set in the class.
+ */
+void
+fpi_device_set_scan_type (FpDevice *device,
+ FpScanType scan_type)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+
+ priv->scan_type = scan_type;
+ g_object_notify (G_OBJECT (device), "scan-type");
+}
+
+typedef struct
+{
+ GSource source;
+ FpDevice *device;
+} FpDeviceTimeoutSource;
+
+static void
+timeout_finalize (GSource *source)
+{
+ FpDeviceTimeoutSource *timeout_source = (FpDeviceTimeoutSource *) source;
+ FpDevicePrivate *priv;
+
+ priv = fp_device_get_instance_private (timeout_source->device);
+ priv->sources = g_slist_remove (priv->sources, source);
+}
+
+static gboolean
+timeout_dispatch (GSource *source, GSourceFunc gsource_func, gpointer user_data)
+{
+ FpDeviceTimeoutSource *timeout_source = (FpDeviceTimeoutSource *) source;
+ FpTimeoutFunc callback = (FpTimeoutFunc) gsource_func;
+
+ callback (timeout_source->device, user_data);
+
+ return G_SOURCE_REMOVE;
+}
+
+static GSourceFuncs timeout_funcs = {
+ NULL, /* prepare */
+ NULL, /* check */
+ timeout_dispatch,
+ timeout_finalize,
+ NULL, NULL
+};
+
+/**
+ * fpi_device_add_timeout:
+ * @device: The #FpDevice
+ * @interval: The interval in milliseconds
+ * @func: The #FpTimeoutFunc to call on timeout
+ * @user_data: (nullable): User data to pass to the callback
+ * @destroy_notify: (nullable): #GDestroyNotify for @user_data
+ *
+ * Register a timeout to run. Drivers should always make sure that timers are
+ * cancelled when appropriate.
+ *
+ * Returns: (transfer none): A newly created and attached #GSource
+ */
+GSource *
+fpi_device_add_timeout (FpDevice *device,
+ gint interval,
+ FpTimeoutFunc func,
+ gpointer user_data,
+ GDestroyNotify destroy_notify)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+ FpDeviceTimeoutSource *source;
+
+ source = (FpDeviceTimeoutSource *) g_source_new (&timeout_funcs,
+ sizeof (FpDeviceTimeoutSource));
+ source->device = device;
+
+ g_source_attach (&source->source, NULL);
+ g_source_set_callback (&source->source, (GSourceFunc) func, user_data, destroy_notify);
+ g_source_set_ready_time (&source->source,
+ g_source_get_time (&source->source) + interval * (guint64) 1000);
+ priv->sources = g_slist_prepend (priv->sources, source);
+ g_source_unref (&source->source);
+
+ return &source->source;
+}
+
+/**
+ * fpi_device_get_usb_device:
+ * @device: The #FpDevice
+ *
+ * Get the #GUsbDevice for this #FpDevice. Only permissible to call if the
+ * #FpDevice is of type %FP_DEVICE_TYPE_USB.
+ *
+ * Returns: The #GUsbDevice
+ */
+GUsbDevice *
+fpi_device_get_usb_device (FpDevice *device)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), NULL);
+ g_return_val_if_fail (priv->type == FP_DEVICE_TYPE_USB, NULL);
+
+ return priv->usb_device;
+}
+
+/**
+ * fpi_device_get_virtual_env:
+ * @device: The #FpDevice
+ *
+ * Get the value of the environment variable that caused the virtual #FpDevice to be
+ * generated. Only permissible to call if the #FpDevice is of type %FP_DEVICE_TYPE_VIRTUAL.
+ *
+ * Returns: The value of the environment variable
+ */
+const gchar *
+fpi_device_get_virtual_env (FpDevice *device)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), NULL);
+ g_return_val_if_fail (priv->type == FP_DEVICE_TYPE_VIRTUAL, NULL);
+
+ return priv->virtual_env;
+}
+
+/**
+ * fpi_device_get_current_action:
+ * @device: The #FpDevice
+ *
+ * Get the currently ongoing action or %FP_DEVICE_ACTION_NONE if there
+ * is no operation at this time.
+ *
+ * This is useful for drivers that might share code paths between different
+ * actions (e.g. verify and identify) and want to find out again later which
+ * action was started in the beginning.
+ *
+ * Returns: The ongoing #FpDeviceAction
+ */
+FpDeviceAction
+fpi_device_get_current_action (FpDevice *device)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), FP_DEVICE_ACTION_NONE);
+
+ return priv->current_action;
+}
+
+/**
+ * fpi_device_action_is_cancelled:
+ * @device: The #FpDevice
+ *
+ * Checks whether the current action has been cancelled by the user.
+ * This is equivalent to first getting the cancellable using
+ * fpi_device_get_cancellable() and then checking whether it has been
+ * cancelled (if it is non-NULL).
+ *
+ * Returns: %TRUE if action should be cancelled
+ */
+gboolean
+fpi_device_action_is_cancelled (FpDevice *device)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+ GCancellable *cancellable;
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), TRUE);
+ g_return_val_if_fail (priv->current_action != FP_DEVICE_ACTION_NONE, TRUE);
+
+ cancellable = g_task_get_cancellable (priv->current_task);
+
+ return cancellable ? g_cancellable_is_cancelled (cancellable) : FALSE;
+}
+
+/**
+ * fpi_device_get_driver_data:
+ * @device: The #FpDevice
+ *
+ * Returns: The driver data from the #FpIdEntry table entry
+ */
+guint64
+fpi_device_get_driver_data (FpDevice *device)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), 0);
+
+ return priv->driver_data;
+}
+
+void
+enroll_data_free (FpEnrollData *data)
+{
+ if (data->enroll_progress_destroy)
+ data->enroll_progress_destroy (data->enroll_progress_data);
+ data->enroll_progress_data = NULL;
+ g_clear_object (&data->print);
+ g_free (data);
+}
+
+/**
+ * fpi_device_get_enroll_data:
+ * @device: The #FpDevice
+ * @print: (out) (transfer none): The user provided template print
+ *
+ * Get data for enrollment.
+ */
+void
+fpi_device_get_enroll_data (FpDevice *device,
+ FpPrint **print)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+ FpEnrollData *data;
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_ENROLL);
+
+ data = g_task_get_task_data (priv->current_task);
+ g_assert (data);
+
+ if (print)
+ *print = data->print;
+}
+
+/**
+ * fpi_device_get_capture_data:
+ * @device: The #FpDevice
+ * @wait_for_finger: (out): Whether to wait for finger or not
+ *
+ * Get data for capture.
+ */
+void
+fpi_device_get_capture_data (FpDevice *device,
+ gboolean *wait_for_finger)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_CAPTURE);
+
+ if (wait_for_finger)
+ *wait_for_finger = priv->wait_for_finger;
+}
+
+/**
+ * fpi_device_get_verify_data:
+ * @device: The #FpDevice
+ * @print: (out) (transfer none): The enrolled print
+ *
+ * Get data for verify.
+ */
+void
+fpi_device_get_verify_data (FpDevice *device,
+ FpPrint **print)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_VERIFY);
+
+ if (print)
+ *print = g_task_get_task_data (priv->current_task);
+}
+
+/**
+ * fpi_device_get_identify_data:
+ * @device: The #FpDevice
+ * @prints: (out) (transfer none) (element-type FpPrint): The gallery of prints
+ *
+ * Get data for identify.
+ */
+void
+fpi_device_get_identify_data (FpDevice *device,
+ GPtrArray **prints)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_IDENTIFY);
+
+ if (prints)
+ *prints = g_task_get_task_data (priv->current_task);
+}
+
+/**
+ * fpi_device_get_delete_data:
+ * @device: The #FpDevice
+ * @print: (out) (transfer none): The print to delete
+ *
+ * Get data for delete.
+ */
+void
+fpi_device_get_delete_data (FpDevice *device,
+ FpPrint **print)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_DELETE);
+
+ if (print)
+ *print = g_task_get_task_data (priv->current_task);
+}
+
+/**
+ * fpi_device_get_cancellable:
+ * @device: The #FpDevice
+ *
+ * Retrieve the #GCancellable that may cancel the currently ongoing operation. This
+ * is primarily useful to pass directly to e.g. fpi_usb_transfer_submit() for cancellable
+ * transfers.
+ * In many cases the cancel vfunc may be more convenient to react to cancellation in some
+ * way.
+ *
+ * Returns: (transfer none): The #GCancellable for the current action.
+ */
+GCancellable *
+fpi_device_get_cancellable (FpDevice *device)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), NULL);
+ g_return_val_if_fail (priv->current_action != FP_DEVICE_ACTION_NONE, NULL);
+
+ return g_task_get_cancellable (priv->current_task);
+}
+
+/**
+ * fpi_device_action_error:
+ * @device: The #FpDevice
+ * @error: The #GError to return
+ *
+ * Finish an ongoing action with an error. This is the same as calling
+ * the corresponding complete function such as fpi_device_open_complete()
+ * with an error set. If possible, use the correct complete function as
+ * that results in improved error detection.
+ */
+void
+fpi_device_action_error (FpDevice *device,
+ GError *error)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action != FP_DEVICE_ACTION_NONE);
+
+ if (error != NULL)
+ {
+ g_debug ("Device reported generic error during action; action was: %i", priv->current_action);
+ }
+ else
+ {
+ g_warning ("Device failed to pass an error to generic action error function");
+ error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, "Device reported error but did not provide an error condition");
+ }
+
+
+ switch (priv->current_action)
+ {
+ case FP_DEVICE_ACTION_PROBE:
+ fpi_device_probe_complete (device, NULL, NULL, error);
+ break;
+
+ case FP_DEVICE_ACTION_OPEN:
+ fpi_device_open_complete (device, error);
+ break;
+
+ case FP_DEVICE_ACTION_CLOSE:
+ fpi_device_close_complete (device, error);
+ break;
+
+ case FP_DEVICE_ACTION_ENROLL:
+ fpi_device_enroll_complete (device, NULL, error);
+ break;
+
+ case FP_DEVICE_ACTION_VERIFY:
+ fpi_device_verify_complete (device, FPI_MATCH_ERROR, NULL, error);
+ break;
+
+ case FP_DEVICE_ACTION_IDENTIFY:
+ fpi_device_identify_complete (device, NULL, NULL, error);
+ break;
+
+ case FP_DEVICE_ACTION_CAPTURE:
+ fpi_device_capture_complete (device, NULL, error);
+ break;
+
+ case FP_DEVICE_ACTION_DELETE:
+ fpi_device_delete_complete (device, error);
+ break;
+
+ case FP_DEVICE_ACTION_LIST:
+ fpi_device_list_complete (device, NULL, error);
+ break;
+
+ default:
+ case FP_DEVICE_ACTION_NONE:
+ g_return_if_reached ();
+ break;
+ }
+}
+
+static void
+clear_device_cancel_action (FpDevice *device)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_clear_pointer (&priv->current_idle_cancel_source, g_source_destroy);
+
+ if (priv->current_cancellable_id)
+ {
+ g_cancellable_disconnect (g_task_get_cancellable (priv->current_task),
+ priv->current_cancellable_id);
+ priv->current_cancellable_id = 0;
+ }
+}
+
+typedef enum _FpDeviceTaskReturnType {
+ FP_DEVICE_TASK_RETURN_INT,
+ FP_DEVICE_TASK_RETURN_BOOL,
+ FP_DEVICE_TASK_RETURN_OBJECT,
+ FP_DEVICE_TASK_RETURN_PTR_ARRAY,
+ FP_DEVICE_TASK_RETURN_ERROR,
+} FpDeviceTaskReturnType;
+
+typedef struct _FpDeviceTaskReturnData
+{
+ FpDevice *device;
+ FpDeviceTaskReturnType type;
+ gpointer result;
+} FpDeviceTaskReturnData;
+
+static gboolean
+fp_device_task_return_in_idle_cb (gpointer user_data)
+{
+ FpDeviceTaskReturnData *data = user_data;
+ FpDevicePrivate *priv = fp_device_get_instance_private (data->device);
+
+ g_autoptr(GTask) task = NULL;
+
+ g_debug ("Completing action %d in idle!", priv->current_action);
+
+ task = g_steal_pointer (&priv->current_task);
+ priv->current_action = FP_DEVICE_ACTION_NONE;
+ priv->current_task_idle_return_source = NULL;
+
+ switch (data->type)
+ {
+ case FP_DEVICE_TASK_RETURN_INT:
+ g_task_return_int (task, GPOINTER_TO_INT (data->result));
+ break;
+
+ case FP_DEVICE_TASK_RETURN_BOOL:
+ g_task_return_boolean (task, GPOINTER_TO_UINT (data->result));
+ break;
+
+ case FP_DEVICE_TASK_RETURN_OBJECT:
+ g_task_return_pointer (task, data->result, g_object_unref);
+ break;
+
+ case FP_DEVICE_TASK_RETURN_PTR_ARRAY:
+ g_task_return_pointer (task, data->result,
+ (GDestroyNotify) g_ptr_array_unref);
+ break;
+
+ case FP_DEVICE_TASK_RETURN_ERROR:
+ g_task_return_error (task, data->result);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+fpi_device_task_return_data_free (FpDeviceTaskReturnData *data)
+{
+ g_object_unref (data->device);
+ g_free (data);
+}
+
+static void
+fpi_device_return_task_in_idle (FpDevice *device,
+ FpDeviceTaskReturnType return_type,
+ gpointer return_data)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+ FpDeviceTaskReturnData *data;
+
+ data = g_new0 (FpDeviceTaskReturnData, 1);
+ data->device = g_object_ref (device);
+ data->type = return_type;
+ data->result = return_data;
+
+ priv->current_task_idle_return_source = g_idle_source_new ();
+ g_source_set_priority (priv->current_task_idle_return_source,
+ g_task_get_priority (priv->current_task));
+ g_source_set_callback (priv->current_task_idle_return_source,
+ fp_device_task_return_in_idle_cb,
+ data,
+ (GDestroyNotify) fpi_device_task_return_data_free);
+
+ g_source_attach (priv->current_task_idle_return_source, NULL);
+ g_source_unref (priv->current_task_idle_return_source);
+}
+
+/**
+ * fpi_device_probe_complete:
+ * @device: The #FpDevice
+ * @device_id: Unique ID for the device or %NULL
+ * @device_name: Human readable name or %NULL for driver name
+ * @error: The #GError or %NULL on success
+ *
+ * Finish an ongoing probe operation. If error is %NULL success is assumed.
+ */
+void
+fpi_device_probe_complete (FpDevice *device,
+ const gchar *device_id,
+ const gchar *device_name,
+ GError *error)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_PROBE);
+
+ g_debug ("Device reported probe completion");
+
+ clear_device_cancel_action (device);
+
+ if (!error)
+ {
+ if (device_id)
+ {
+ g_clear_pointer (&priv->device_id, g_free);
+ priv->device_id = g_strdup (device_id);
+ g_object_notify (G_OBJECT (device), "device-id");
+ }
+ if (device_name)
+ {
+ g_clear_pointer (&priv->device_name, g_free);
+ priv->device_name = g_strdup (device_name);
+ g_object_notify (G_OBJECT (device), "name");
+ }
+ fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL,
+ GUINT_TO_POINTER (TRUE));
+ }
+ else
+ {
+ fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
+ }
+}
+
+/**
+ * fpi_device_open_complete:
+ * @device: The #FpDevice
+ * @error: The #GError or %NULL on success
+ *
+ * Finish an ongoing open operation. If error is %NULL success is assumed.
+ */
+void
+fpi_device_open_complete (FpDevice *device, GError *error)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_OPEN);
+
+ g_debug ("Device reported open completion");
+
+ clear_device_cancel_action (device);
+
+ if (!error)
+ {
+ priv->is_open = TRUE;
+ g_object_notify (G_OBJECT (device), "open");
+ }
+
+ if (!error)
+ fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL,
+ GUINT_TO_POINTER (TRUE));
+ else
+ fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
+}
+
+/**
+ * fpi_device_close_complete:
+ * @device: The #FpDevice
+ * @error: The #GError or %NULL on success
+ *
+ * Finish an ongoing close operation. If error is %NULL success is assumed.
+ */
+void
+fpi_device_close_complete (FpDevice *device, GError *error)
+{
+ GError *nested_error = NULL;
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_CLOSE);
+
+ g_debug ("Device reported close completion");
+
+ clear_device_cancel_action (device);
+ priv->is_open = FALSE;
+ g_object_notify (G_OBJECT (device), "open");
+
+ switch (priv->type)
+ {
+ case FP_DEVICE_TYPE_USB:
+ if (!g_usb_device_close (priv->usb_device, &nested_error))
+ {
+ if (error == NULL)
+ error = nested_error;
+ fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
+ return;
+ }
+ break;
+
+ case FP_DEVICE_TYPE_VIRTUAL:
+ break;
+
+ default:
+ g_assert_not_reached ();
+ fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR,
+ fpi_device_error_new (FP_DEVICE_ERROR_GENERAL));
+ return;
+ }
+
+ if (!error)
+ fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL,
+ GUINT_TO_POINTER (TRUE));
+ else
+ fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
+}
+
+/**
+ * fpi_device_enroll_complete:
+ * @device: The #FpDevice
+ * @print: (nullable) (transfer full): The #FpPrint or %NULL on failure
+ * @error: The #GError or %NULL on success
+ *
+ * Finish an ongoing enroll operation. The #FpPrint can be stored by the
+ * caller for later verification.
+ */
+void
+fpi_device_enroll_complete (FpDevice *device, FpPrint *print, GError *error)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_ENROLL);
+
+ g_debug ("Device reported enroll completion");
+
+ clear_device_cancel_action (device);
+
+ if (!error)
+ {
+ if (FP_IS_PRINT (print))
+ {
+ fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_OBJECT, print);
+ }
+ else
+ {
+ g_warning ("Driver did not provide a valid print and failed to provide an error!");
+ error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
+ "Driver failed to provide print data!");
+ fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
+ }
+ }
+ else
+ {
+ fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
+ if (FP_IS_PRINT (print))
+ {
+ g_warning ("Driver passed an error but also provided a print, returning error!");
+ g_object_unref (print);
+ }
+ }
+}
+
+/**
+ * fpi_device_verify_complete:
+ * @device: The #FpDevice
+ * @result: The #FpiMatchResult of the operation
+ * @print: The scanned #FpPrint
+ * @error: A #GError if result is %FPI_MATCH_ERROR
+ *
+ * Finish an ongoing verify operation. The returned print should be
+ * representing the new scan and not the one passed for verification.
+ */
+void
+fpi_device_verify_complete (FpDevice *device,
+ FpiMatchResult result,
+ FpPrint *print,
+ GError *error)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_VERIFY);
+
+ g_debug ("Device reported verify completion");
+
+ clear_device_cancel_action (device);
+
+ g_object_set_data_full (G_OBJECT (priv->current_task),
+ "print",
+ print,
+ g_object_unref);
+
+ if (!error)
+ {
+ if (result != FPI_MATCH_ERROR)
+ {
+ fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_INT,
+ GINT_TO_POINTER (result));
+ }
+ else
+ {
+ g_warning ("Driver did not provide an error for a failed verify operation!");
+ error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
+ "Driver failed to provide an error!");
+ fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
+ }
+ }
+ else
+ {
+ fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
+ if (result != FPI_MATCH_ERROR)
+ {
+ g_warning ("Driver passed an error but also provided a match result, returning error!");
+ g_object_unref (print);
+ }
+ }
+}
+
+/**
+ * fpi_device_identify_complete:
+ * @device: The #FpDevice
+ * @match: The matching #FpPrint from the passed gallery, or %NULL if none matched
+ * @print: The scanned #FpPrint, may be %NULL
+ * @error: The #GError or %NULL on success
+ *
+ * Finish an ongoing identify operation. The match that was identified is
+ * returned in @match. The @print parameter returns the newly created scan
+ * that was used for matching.
+ */
+void
+fpi_device_identify_complete (FpDevice *device,
+ FpPrint *match,
+ FpPrint *print,
+ GError *error)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_IDENTIFY);
+
+ g_debug ("Device reported identify completion");
+
+ clear_device_cancel_action (device);
+
+ g_object_set_data_full (G_OBJECT (priv->current_task),
+ "print",
+ print,
+ g_object_unref);
+ g_object_set_data_full (G_OBJECT (priv->current_task),
+ "match",
+ match,
+ g_object_unref);
+ if (!error)
+ {
+ fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL,
+ GUINT_TO_POINTER (TRUE));
+ }
+ else
+ {
+ fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
+ if (match)
+ {
+ g_warning ("Driver passed an error but also provided a match result, returning error!");
+ g_clear_object (&match);
+ }
+ }
+}
+
+
+/**
+ * fpi_device_capture_complete:
+ * @device: The #FpDevice
+ * @image: The #FpImage, or %NULL on error
+ * @error: The #GError or %NULL on success
+ *
+ * Finish an ongoing capture operation.
+ */
+void
+fpi_device_capture_complete (FpDevice *device,
+ FpImage *image,
+ GError *error)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_CAPTURE);
+
+ g_debug ("Device reported capture completion");
+
+ clear_device_cancel_action (device);
+
+ if (!error)
+ {
+ if (image)
+ {
+ fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_OBJECT, image);
+ }
+ else
+ {
+ g_warning ("Driver did not provide an error for a failed capture operation!");
+ error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
+ "Driver failed to provide an error!");
+ fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
+ }
+ }
+ else
+ {
+ fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
+ if (image)
+ {
+ g_warning ("Driver passed an error but also provided an image, returning error!");
+ g_clear_object (&image);
+ }
+ }
+}
+
+/**
+ * fpi_device_delete_complete:
+ * @device: The #FpDevice
+ * @error: The #GError or %NULL on success
+ *
+ * Finish an ongoing delete operation.
+ */
+void
+fpi_device_delete_complete (FpDevice *device,
+ GError *error)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_DELETE);
+
+ g_debug ("Device reported deletion completion");
+
+ clear_device_cancel_action (device);
+
+ if (!error)
+ fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL,
+ GUINT_TO_POINTER (TRUE));
+ else
+ fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
+}
+
+/**
+ * fpi_device_list_complete:
+ * @device: The #FpDevice
+ * @prints: (element-type FpPrint) (transfer container): Possibly empty array of prints or %NULL on error
+ * @error: The #GError or %NULL on success
+ *
+ * Finish an ongoing list operation.
+ *
+ * Please note that the @prints array will be free'ed using
+ * g_ptr_array_unref() and the elements are destroyed automatically.
+ * As such, you must use g_ptr_array_new_with_free_func() with
+ * g_object_unref() as free func to create the array.
+ */
+void
+fpi_device_list_complete (FpDevice *device,
+ GPtrArray *prints,
+ GError *error)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_LIST);
+
+ g_debug ("Device reported listing completion");
+
+ clear_device_cancel_action (device);
+
+ if (prints && error)
+ {
+ g_warning ("Driver reported back prints and error, ignoring prints");
+ g_clear_pointer (&prints, g_ptr_array_unref);
+ }
+ else if (!prints && !error)
+ {
+ g_warning ("Driver did not pass array but failed to provide an error");
+ error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
+ "Driver failed to provide a list of prints");
+ }
+
+ if (!error)
+ fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_PTR_ARRAY, prints);
+ else
+ fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
+}
+
+/**
+ * fpi_device_enroll_progress:
+ * @device: The #FpDevice
+ * @completed_stages: The number of stages that are completed at this point
+ * @print: The #FpPrint for the newly completed stage or %NULL on failure
+ * @error: The #GError or %NULL on success
+ *
+ * Notify about the progress of the enroll operation. This is important for UI interaction.
+ * The passed error may be used if a scan needs to be retried, use fpi_device_retry_new().
+ */
+void
+fpi_device_enroll_progress (FpDevice *device,
+ gint completed_stages,
+ FpPrint *print,
+ GError *error)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+ FpEnrollData *data;
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_ENROLL);
+ g_return_if_fail (error == NULL || error->domain == FP_DEVICE_RETRY);
+
+ g_debug ("Device reported enroll progress, reported %i of %i have been completed", completed_stages, priv->nr_enroll_stages);
+
+ if (error && print)
+ {
+ g_warning ("Driver passed an error and also provided a print, returning error!");
+ g_clear_object (&print);
+ }
+
+ data = g_task_get_task_data (priv->current_task);
+
+ if (data->enroll_progress_cb)
+ {
+ data->enroll_progress_cb (device,
+ completed_stages,
+ print,
+ data->enroll_progress_data,
+ error);
+ }
+
+ g_clear_error (&error);
+ g_clear_object (&print);
+}
diff --git a/libfprint/meson.build b/libfprint/meson.build
index 1e98e2d..4a34cbd 100644
--- a/libfprint/meson.build
+++ b/libfprint/meson.build
@@ -8,6 +8,7 @@ libfprint_sources = [
libfprint_private_sources = [
'fpi-assembling.c',
+ 'fpi-device.c',
'fpi-ssm.c',
'fpi-usb-transfer.c',
'fpi-byte-reader.c',
--
2.24.1