diff --git a/libfprint/drivers/virtual-device-listener.c b/libfprint/drivers/virtual-device-listener.c new file mode 100644 index 0000000..50fe95f --- /dev/null +++ b/libfprint/drivers/virtual-device-listener.c @@ -0,0 +1,355 @@ +/* + * Socket utilities for "simple" device debugging + * + * Copyright (C) 2019 Benjamin Berg + * Copyright (C) 2020 Marco Trevisan + * + * 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 "virtual_device_connection" + +#include "fpi-log.h" + +#include +#include + +#include "virtual-device-private.h" + +struct _FpDeviceVirtualListener +{ + GSocketListener parent_instance; + + GSocketConnection *connection; + GCancellable *cancellable; + guint cancellable_id; + + FpDeviceVirtualListenerConnectionCb ready_cb; + gpointer ready_cb_data; + + gint socket_fd; + gint client_fd; +}; + +G_DEFINE_TYPE (FpDeviceVirtualListener, fp_device_virtual_listener, G_TYPE_SOCKET_LISTENER) + +static void start_listen (FpDeviceVirtualListener *self); + +FpDeviceVirtualListener * +fp_device_virtual_listener_new (void) +{ + return g_object_new (fp_device_virtual_listener_get_type (), NULL); +} + +static void +fp_device_virtual_listener_dispose (GObject *object) +{ + FpDeviceVirtualListener *self = FP_DEVICE_VIRTUAL_LISTENER (object); + + if (self->cancellable_id) + { + g_cancellable_disconnect (self->cancellable, self->cancellable_id); + self->cancellable_id = 0; + } + + g_cancellable_cancel (self->cancellable); + g_clear_object (&self->cancellable); + g_clear_object (&self->connection); + + self->ready_cb = NULL; + + G_OBJECT_CLASS (fp_device_virtual_listener_parent_class)->dispose (object); +} + +static void +fp_device_virtual_listener_class_init (FpDeviceVirtualListenerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = fp_device_virtual_listener_dispose; +} + +static void +fp_device_virtual_listener_init (FpDeviceVirtualListener *self) +{ +} + +static void +new_connection_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GError) error = NULL; + FpDeviceVirtualListener *self = user_data; + GSocketConnection *connection; + + connection = g_socket_listener_accept_finish (G_SOCKET_LISTENER (source_object), + res, + NULL, + &error); + if (!connection) + { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + g_warning ("Error accepting a new connection: %s", error->message); + start_listen (self); + return; + } + + /* Always allow further connections. + * If we get a new one, we generally just close the old connection. */ + start_listen (self); + if (self->connection) + { + g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL); + g_clear_object (&self->connection); + } + + self->connection = connection; + fp_dbg ("Got a new connection!"); + + self->ready_cb (self, self->ready_cb_data); +} + +static void +start_listen (FpDeviceVirtualListener *self) +{ + g_socket_listener_accept_async (G_SOCKET_LISTENER (self), + self->cancellable, + new_connection_cb, + self); +} + +static void +on_cancelled (GCancellable *cancellable, + FpDeviceVirtualListener *self) +{ + fp_device_virtual_listener_connection_close (self); + g_socket_listener_close (G_SOCKET_LISTENER (self)); + g_clear_object (&self->cancellable); + self->ready_cb = NULL; +} + +gboolean +fp_device_virtual_listener_start (FpDeviceVirtualListener *self, + const char *address, + GCancellable *cancellable, + FpDeviceVirtualListenerConnectionCb cb, + gpointer user_data, + GError **error) +{ + g_autoptr(GSocketAddress) addr = NULL; + G_DEBUG_HERE (); + + g_return_val_if_fail (FP_IS_DEVICE_VIRTUAL_LISTENER (self), FALSE); + g_return_val_if_fail (cb != NULL, FALSE); + g_return_val_if_fail (self->ready_cb == NULL, FALSE); + + self->client_fd = -1; + + g_socket_listener_set_backlog (G_SOCKET_LISTENER (self), 1); + + /* Remove any left over socket. */ + g_unlink (address); + + addr = g_unix_socket_address_new (address); + + if (!g_socket_listener_add_address (G_SOCKET_LISTENER (self), + addr, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT, + NULL, + NULL, + error)) + { + g_warning ("Could not listen on unix socket: %s", (*error)->message); + return FALSE; + } + + self->ready_cb = cb; + self->ready_cb_data = user_data; + self->cancellable = cancellable ? g_object_ref (cancellable) : NULL; + + if (self->cancellable) + self->cancellable_id = g_cancellable_connect (self->cancellable, + G_CALLBACK (on_cancelled), self, NULL); + + start_listen (self); + + return TRUE; +} + +gboolean +fp_device_virtual_listener_connection_close (FpDeviceVirtualListener *self) +{ + g_return_val_if_fail (FP_IS_DEVICE_VIRTUAL_LISTENER (self), FALSE); + + if (!self->connection) + return FALSE; + + g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL); + g_clear_object (&self->connection); + + return TRUE; +} + +static void +on_stream_read_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = user_data; + FpDeviceVirtualListener *self = g_task_get_source_object (task); + gboolean all; + gboolean success; + gsize bytes; + + all = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (task), "all")); + + if (all) + { + success = g_input_stream_read_all_finish (G_INPUT_STREAM (source_object), res, &bytes, &error); + } + else + { + gssize sbytes; + + sbytes = g_input_stream_read_finish (G_INPUT_STREAM (source_object), res, &error); + bytes = sbytes; + success = (sbytes >= 0); + } + + if (g_task_return_error_if_cancelled (task)) + return; + + /* If we are cancelled, just return immediately. */ + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED)) + { + g_task_return_int (task, 0); + return; + } + + /* If this error is for an old connection (that should be closed already), + * then just give up immediately with a CLOSED error. + */ + if (self->connection && + g_io_stream_get_input_stream (G_IO_STREAM (self->connection)) != G_INPUT_STREAM (source_object)) + { + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_CLOSED, + "Error on old connection, ignoring."); + return; + } + + if (!success || bytes == 0) + { + /* We accept it if someone tries to read twice and just return that error. */ + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PENDING)) + { + if (self->connection) + { + g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL); + g_clear_object (&self->connection); + } + } + + if (error) + { + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + else + { + // g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Got empty data"); + return; + } + } + + g_task_return_int (task, bytes); +} + +void +fp_device_virtual_listener_read (FpDeviceVirtualListener *self, + gboolean all, + void *buffer, + gsize count, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = NULL; + GInputStream *stream; + + g_return_if_fail (FP_IS_DEVICE_VIRTUAL_LISTENER (self)); + + task = g_task_new (self, self->cancellable, callback, user_data); + g_object_set_data (G_OBJECT (task), "all", GINT_TO_POINTER (all)); + + if (!self->connection || g_io_stream_is_closed (G_IO_STREAM (self->connection))) + { + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED, + "Listener not connected to any stream"); + return; + } + + stream = g_io_stream_get_input_stream (G_IO_STREAM (self->connection)); + if (all) + { + g_input_stream_read_all_async (stream, buffer, count, + G_PRIORITY_DEFAULT, + self->cancellable, + on_stream_read_cb, + g_steal_pointer (&task)); + } + else + { + g_input_stream_read_async (stream, buffer, count, + G_PRIORITY_DEFAULT, + self->cancellable, + on_stream_read_cb, + g_steal_pointer (&task)); + } +} + +gsize +fp_device_virtual_listener_read_finish (FpDeviceVirtualListener *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, self), 0); + + return g_task_propagate_int (G_TASK (result), error); +} + +gboolean +fp_device_virtual_listener_write_sync (FpDeviceVirtualListener *self, + const char *buffer, + gsize count, + GError **error) +{ + if (!self->connection || g_io_stream_is_closed (G_IO_STREAM (self->connection))) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED, + "Listener not connected to any stream"); + return FALSE; + } + + return g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (self->connection)), + buffer, + count, + NULL, + self->cancellable, + error); +} diff --git a/libfprint/drivers/virtual-device-private.h b/libfprint/drivers/virtual-device-private.h new file mode 100644 index 0000000..6049d66 --- /dev/null +++ b/libfprint/drivers/virtual-device-private.h @@ -0,0 +1,93 @@ +/* + * Virtual driver for "simple" device debugging + * + * Copyright (C) 2019 Benjamin Berg + * Copyright (C) 2020 Bastien Nocera + * + * 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 + */ + +/* + * This is a virtual driver to debug the non-image based drivers. A small + * python script is provided to connect to it via a socket, allowing + * prints to registered programmatically. + * Using this, it is possible to test libfprint and fprintd. + */ + +#include + +#include "fpi-device.h" + +#define MAX_LINE_LEN 1024 + +G_DECLARE_FINAL_TYPE (FpDeviceVirtualListener, fp_device_virtual_listener, FP, DEVICE_VIRTUAL_LISTENER, GSocketListener) + +typedef void (*FpDeviceVirtualListenerConnectionCb) (FpDeviceVirtualListener *listener, + gpointer user_data); + +FpDeviceVirtualListener * fp_device_virtual_listener_new (void); + +gboolean fp_device_virtual_listener_start (FpDeviceVirtualListener *listener, + const char *address, + GCancellable *cancellable, + FpDeviceVirtualListenerConnectionCb cb, + gpointer user_data, + GError **error); + +gboolean fp_device_virtual_listener_connection_close (FpDeviceVirtualListener *listener); + +void fp_device_virtual_listener_read (FpDeviceVirtualListener *listener, + gboolean all, + void *buffer, + gsize count, + GAsyncReadyCallback callback, + gpointer user_data); +gsize fp_device_virtual_listener_read_finish (FpDeviceVirtualListener *listener, + GAsyncResult *result, + GError **error); + +gboolean fp_device_virtual_listener_write_sync (FpDeviceVirtualListener *self, + const char *buffer, + gsize count, + GError **error); + + +struct _FpDeviceVirtualDevice +{ + FpDevice parent; + + FpDeviceVirtualListener *listener; + GCancellable *cancellable; + + char recv_buf[MAX_LINE_LEN]; + + GPtrArray *pending_commands; + + GHashTable *prints_storage; +}; + +/* Not really final here, but we can do this to share the FpDeviceVirtualDevice + * contents without having to use a shared private struct instead. */ +G_DECLARE_FINAL_TYPE (FpDeviceVirtualDevice, fpi_device_virtual_device, FP, DEVICE_VIRTUAL_DEVICE, FpDevice) + +struct _FpDeviceVirtualDeviceStorage +{ + FpDeviceVirtualDevice parent; +}; + +G_DECLARE_FINAL_TYPE (FpDeviceVirtualDeviceStorage, fpi_device_virtual_device_storage, FP, DEVICE_VIRTUAL_DEVICE_STORAGE, FpDeviceVirtualDevice) + + +char * process_cmds (FpDeviceVirtualDevice * self, gboolean scan, GError **error); diff --git a/libfprint/drivers/virtual-image.c b/libfprint/drivers/virtual-image.c index d4c7cc5..6b0043f 100644 --- a/libfprint/drivers/virtual-image.c +++ b/libfprint/drivers/virtual-image.c @@ -29,36 +29,27 @@ #include "fpi-log.h" +#include "virtual-device-private.h" + #include "../fpi-image.h" #include "../fpi-image-device.h" -#include -#include -#include - struct _FpDeviceVirtualImage { - FpImageDevice parent; + FpImageDevice parent; - GSocketListener *listener; - GSocketConnection *connection; - GCancellable *listen_cancellable; - GCancellable *cancellable; + FpDeviceVirtualListener *listener; + GCancellable *cancellable; - gint socket_fd; - gint client_fd; - - gboolean automatic_finger; - FpImage *recv_img; - gint recv_img_hdr[2]; + gboolean automatic_finger; + FpImage *recv_img; + gint recv_img_hdr[2]; }; G_DECLARE_FINAL_TYPE (FpDeviceVirtualImage, fpi_device_virtual_image, FPI, DEVICE_VIRTUAL_IMAGE, FpImageDevice) G_DEFINE_TYPE (FpDeviceVirtualImage, fpi_device_virtual_image, FP_TYPE_IMAGE_DEVICE) -static void start_listen (FpDeviceVirtualImage *dev); -static void recv_image (FpDeviceVirtualImage *dev, - GInputStream *stream); +static void recv_image (FpDeviceVirtualImage *self); static void recv_image_img_recv_cb (GObject *source_object, @@ -66,35 +57,20 @@ recv_image_img_recv_cb (GObject *source_object, gpointer user_data) { g_autoptr(GError) error = NULL; + FpDeviceVirtualListener *listener = FP_DEVICE_VIRTUAL_LISTENER (source_object); FpDeviceVirtualImage *self; FpImageDevice *device; - gboolean success; - gsize bytes = 0; + gsize bytes; - success = g_input_stream_read_all_finish (G_INPUT_STREAM (source_object), res, &bytes, &error); + bytes = fp_device_virtual_listener_read_finish (listener, res, &error); - /* Can't use self if the operation was cancelled. */ - if (!success && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + if (!bytes || g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) || + g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED)) return; self = FPI_DEVICE_VIRTUAL_IMAGE (user_data); device = FP_IMAGE_DEVICE (self); - /* Consider success if we received the right amount of data, otherwise - * an error must have happened. */ - if (bytes < self->recv_img->width * self->recv_img->height) - { - if (!success) - g_warning ("Error receiving image data: %s", error->message); - else - g_warning ("Error receiving image data: end of stream before all data was read"); - - self = FPI_DEVICE_VIRTUAL_IMAGE (user_data); - g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL); - g_clear_object (&self->connection); - return; - } - if (self->automatic_finger) fpi_image_device_report_finger_status (device, TRUE); fpi_image_device_image_captured (device, g_steal_pointer (&self->recv_img)); @@ -102,7 +78,7 @@ recv_image_img_recv_cb (GObject *source_object, fpi_image_device_report_finger_status (device, FALSE); /* And, listen for more images from the same client. */ - recv_image (self, G_INPUT_STREAM (source_object)); + recv_image (self); } static void @@ -112,37 +88,30 @@ recv_image_hdr_recv_cb (GObject *source_object, { g_autoptr(GError) error = NULL; FpDeviceVirtualImage *self; - gboolean success; + FpDeviceVirtualListener *listener = FP_DEVICE_VIRTUAL_LISTENER (source_object); gsize bytes; - success = g_input_stream_read_all_finish (G_INPUT_STREAM (source_object), res, &bytes, &error); + bytes = fp_device_virtual_listener_read_finish (listener, res, &error); - if (!success || bytes != sizeof (self->recv_img_hdr)) + if (error) { - if (!success) - { - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) || - g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED)) - return; - g_warning ("Error receiving header for image data: %s", error->message); - } - else if (bytes != 0) - { - g_warning ("Received incomplete header before end of stream."); - } + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) || + g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED) || + g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PENDING)) + return; - self = FPI_DEVICE_VIRTUAL_IMAGE (user_data); - g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL); - g_clear_object (&self->connection); + g_warning ("Error receiving header for image data: %s", error->message); return; } + if (!bytes) + return; + self = FPI_DEVICE_VIRTUAL_IMAGE (user_data); if (self->recv_img_hdr[0] > 5000 || self->recv_img_hdr[1] > 5000) { g_warning ("Image header suggests an unrealistically large image, disconnecting client."); - g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL); - g_clear_object (&self->connection); + fp_device_virtual_listener_connection_close (listener); } if (self->recv_img_hdr[0] < 0 || self->recv_img_hdr[1] < 0) @@ -178,153 +147,80 @@ recv_image_hdr_recv_cb (GObject *source_object, default: /* disconnect client, it didn't play fair */ - g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL); - g_clear_object (&self->connection); + fp_device_virtual_listener_connection_close (listener); } /* And, listen for more images from the same client. */ - recv_image (self, G_INPUT_STREAM (source_object)); + recv_image (self); return; } self->recv_img = fp_image_new (self->recv_img_hdr[0], self->recv_img_hdr[1]); g_debug ("image data: %p", self->recv_img->data); - g_input_stream_read_all_async (G_INPUT_STREAM (source_object), - (guint8 *) self->recv_img->data, - self->recv_img->width * self->recv_img->height, - G_PRIORITY_DEFAULT, - self->cancellable, - recv_image_img_recv_cb, - self); + fp_device_virtual_listener_read (listener, + TRUE, + (guint8 *) self->recv_img->data, + self->recv_img->width * self->recv_img->height, + recv_image_img_recv_cb, + self); } static void -recv_image (FpDeviceVirtualImage *self, GInputStream *stream) +recv_image (FpDeviceVirtualImage *self) { + fp_device_virtual_listener_read (self->listener, + TRUE, + self->recv_img_hdr, + sizeof (self->recv_img_hdr), + recv_image_hdr_recv_cb, + self); +} + +static void +on_listener_connected (FpDeviceVirtualListener *listener, + gpointer user_data) +{ + FpDeviceVirtualImage *self = FPI_DEVICE_VIRTUAL_IMAGE (user_data); FpiImageDeviceState state; - g_object_get (self, "fpi-image-device-state", &state, NULL); + self->automatic_finger = TRUE; - g_debug ("Starting image receive (if active), state is: %i", state); + g_object_get (self, + "fpi-image-device-state", &state, + NULL); + /* Only read if we are in AWAIT_FINGER_* or CAPTURE states */ + if (state <= FPI_IMAGE_DEVICE_STATE_DEACTIVATING) + return; - /* Only register if the state is active. */ - if (state >= FPI_IMAGE_DEVICE_STATE_IDLE) - { - g_input_stream_read_all_async (stream, - self->recv_img_hdr, - sizeof (self->recv_img_hdr), - G_PRIORITY_DEFAULT, - self->cancellable, - recv_image_hdr_recv_cb, - self); - } -} - -static void -new_connection_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) -{ - g_autoptr(GError) error = NULL; - GSocketConnection *connection; - GInputStream *stream; - FpDeviceVirtualImage *dev = user_data; - - connection = g_socket_listener_accept_finish (G_SOCKET_LISTENER (source_object), - res, - NULL, - &error); - if (!connection) - { - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - return; - - g_warning ("Error accepting a new connection: %s", error->message); - start_listen (dev); - } - - /* Always accept further connections (but we disconnect them immediately - * if we already have a connection). */ - start_listen (dev); - - if (dev->connection) - { - /* We may not have noticed that the stream was closed, - * if the device is deactivated. - * Cancel any ongoing operation on the old connection. */ - g_cancellable_cancel (dev->cancellable); - g_clear_object (&dev->cancellable); - dev->cancellable = g_cancellable_new (); - - g_clear_object (&dev->connection); - } - - if (dev->connection) - { - g_warning ("Rejecting new connection"); - g_io_stream_close (G_IO_STREAM (connection), NULL, NULL); - g_object_unref (connection); - return; - } - - dev->connection = connection; - dev->automatic_finger = TRUE; - stream = g_io_stream_get_input_stream (G_IO_STREAM (connection)); - - fp_dbg ("Got a new connection!"); - recv_image (dev, stream); -} - -static void -start_listen (FpDeviceVirtualImage *dev) -{ - g_socket_listener_accept_async (dev->listener, - dev->listen_cancellable, - new_connection_cb, - dev); + recv_image (self); } static void dev_init (FpImageDevice *dev) { g_autoptr(GError) error = NULL; - g_autoptr(GSocketListener) listener = NULL; + g_autoptr(FpDeviceVirtualListener) listener = NULL; + g_autoptr(GCancellable) cancellable = NULL; FpDeviceVirtualImage *self = FPI_DEVICE_VIRTUAL_IMAGE (dev); - const char *env; - g_autoptr(GSocketAddress) addr = NULL; G_DEBUG_HERE (); - self->client_fd = -1; + listener = fp_device_virtual_listener_new (); + cancellable = g_cancellable_new (); - env = fpi_device_get_virtual_env (FP_DEVICE (self)); - - listener = g_socket_listener_new (); - g_socket_listener_set_backlog (listener, 1); - - /* Remove any left over socket. */ - g_unlink (env); - - addr = g_unix_socket_address_new (env); - - if (!g_socket_listener_add_address (listener, - addr, - G_SOCKET_TYPE_STREAM, - G_SOCKET_PROTOCOL_DEFAULT, - NULL, - NULL, - &error)) + if (!fp_device_virtual_listener_start (listener, + fpi_device_get_virtual_env (FP_DEVICE (self)), + cancellable, + on_listener_connected, + self, + &error)) { - g_warning ("Could not listen on unix socket: %s", error->message); - - fpi_image_device_open_complete (FP_IMAGE_DEVICE (dev), g_steal_pointer (&error)); - + fpi_image_device_open_complete (dev, g_steal_pointer (&error)); return; } self->listener = g_steal_pointer (&listener); - self->cancellable = g_cancellable_new (); - self->listen_cancellable = g_cancellable_new (); - - start_listen (self); + self->cancellable = g_steal_pointer (&cancellable); /* Delay result to open up the possibility of testing race conditions. */ fpi_device_add_timeout (FP_DEVICE (dev), 100, (FpTimeoutFunc) fpi_image_device_open_complete, NULL, NULL); @@ -338,11 +234,8 @@ dev_deinit (FpImageDevice *dev) G_DEBUG_HERE (); g_cancellable_cancel (self->cancellable); - g_cancellable_cancel (self->listen_cancellable); g_clear_object (&self->cancellable); - g_clear_object (&self->listen_cancellable); g_clear_object (&self->listener); - g_clear_object (&self->connection); /* Delay result to open up the possibility of testing race conditions. */ fpi_device_add_timeout (FP_DEVICE (dev), 100, (FpTimeoutFunc) fpi_image_device_close_complete, NULL, NULL); @@ -353,23 +246,16 @@ dev_activate (FpImageDevice *dev) { FpDeviceVirtualImage *self = FPI_DEVICE_VIRTUAL_IMAGE (dev); - fpi_image_device_activate_complete (dev, NULL); + /* Start reading (again). */ + recv_image (self); - if (self->connection) - recv_image (self, g_io_stream_get_input_stream (G_IO_STREAM (self->connection))); + fpi_image_device_activate_complete (dev, NULL); } static void dev_deactivate (FpImageDevice *dev) { - FpDeviceVirtualImage *self = FPI_DEVICE_VIRTUAL_IMAGE (dev); - - g_cancellable_cancel (self->cancellable); - g_clear_object (&self->cancellable); - self->cancellable = g_cancellable_new (); - - /* XXX: Need to wait for the operation to be cancelled. */ - fpi_device_add_timeout (FP_DEVICE (dev), 10, (FpTimeoutFunc) fpi_image_device_deactivate_complete, NULL, NULL); + fpi_image_device_deactivate_complete (dev, NULL); } static void diff --git a/libfprint/meson.build b/libfprint/meson.build index 110c458..5309391 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -155,6 +155,9 @@ foreach driver: drivers if driver == 'virtual_image' drivers_sources += [ 'drivers/virtual-image.c' ] endif + if driver.startswith('virtual_') + drivers_sources += [ 'drivers/virtual-device-listener.c' ] + endif if driver == 'synaptics' drivers_sources += [ 'drivers/synaptics/synaptics.c',