gnome-control-center/panels/user-accounts/um-fingerprint-dialog.c
Robert Ancell ec529e8566 user-accounts: Use GtkTemplate
Switch from GtkBuilder to using GtkTemplate.
Rename .[ch] and .ui files to standard names.
Rename widget IDs to be more readable.
Drop widget IDs that are not used.
Move code into the .ui file that can be.
Connect signals in swapped form.
2018-10-25 11:28:53 +00:00

751 lines
27 KiB
C

/* gnome-about-me-fingerprint.h
* Copyright (C) 2008 Bastien Nocera <hadess@hadess.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <stdlib.h>
#include <glib/gi18n.h>
#include <gio/gio.h>
#include <gtk/gtk.h>
#include "um-fingerprint-dialog.h"
/* Retrieve a widget from the UI object */
#define WID(s) GTK_WIDGET (gtk_builder_get_object (dialog, s))
/* Translate fprintd strings */
#define TR(s) dgettext("fprintd", s)
#include "fingerprint-strings.h"
/* This must match the number of images on the 2nd page in the UI file */
#define MAX_ENROLL_STAGES 5
static GDBusProxy *manager = NULL;
static GDBusConnection *connection = NULL;
static gboolean is_disable = FALSE;
enum {
STATE_NONE,
STATE_CLAIMED,
STATE_ENROLLING
};
typedef struct {
GtkButton *editable_button;
GtkWidget *ass;
GtkBuilder *dialog;
GDBusProxy *device;
gboolean is_swipe;
int num_enroll_stages;
int num_stages_done;
char *name;
const char *finger;
gint state;
} EnrollData;
static void
ensure_manager (void)
{
GError *error = NULL;
if (manager != NULL)
return;
connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (connection == NULL) {
g_warning ("Failed to connect to session bus: %s", error->message);
g_error_free (error);
return;
}
manager = g_dbus_proxy_new_sync (connection,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
"net.reactivated.Fprint",
"/net/reactivated/Fprint/Manager",
"net.reactivated.Fprint.Manager",
NULL,
&error);
if (manager == NULL) {
g_warning ("Failed to create fingerprint manager proxy: %s", error->message);
g_error_free (error);
}
}
static GDBusProxy *
get_first_device (void)
{
GDBusProxy *device;
GVariant *result;
char *device_str = NULL;
GError *error = NULL;
result = g_dbus_proxy_call_sync (manager,
"GetDefaultDevice",
g_variant_new ("()"),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
NULL);
if (result == NULL)
return NULL;
if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(o)")))
g_warning ("net.reactivated.Fprint.Manager.GetDefaultDevice returns unknown result %s", g_variant_get_type_string (result));
else
g_variant_get (result, "(o)", &device_str);
g_variant_unref (result);
if (device_str == NULL)
return NULL;
device = g_dbus_proxy_new_sync (connection,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
"net.reactivated.Fprint",
device_str,
"net.reactivated.Fprint.Device",
NULL,
&error);
if (device == NULL) {
g_warning ("Failed to create fingerprint device proxy: %s", error->message);
g_error_free (error);
}
g_free (device_str);
return device;
}
static const char *
get_reason_for_error (const char *dbus_error)
{
if (g_str_equal (dbus_error, "net.reactivated.Fprint.Error.PermissionDenied"))
return N_("You are not allowed to access the device. Contact your system administrator.");
if (g_str_equal (dbus_error, "net.reactivated.Fprint.Error.AlreadyInUse"))
return N_("The device is already in use.");
if (g_str_equal (dbus_error, "net.reactivated.Fprint.Error.Internal"))
return N_("An internal error occurred.");
return NULL;
}
static GtkWidget *
get_error_dialog (const char *title,
const char *dbus_error,
GtkWindow *parent)
{
GtkWidget *error_dialog;
const char *reason;
if (dbus_error == NULL)
g_warning ("get_error_dialog called with reason == NULL");
error_dialog =
gtk_message_dialog_new (parent,
GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_OK,
"%s", title);
reason = get_reason_for_error (dbus_error);
gtk_message_dialog_format_secondary_text
(GTK_MESSAGE_DIALOG (error_dialog), "%s", reason ? _(reason) : _(dbus_error));
gtk_window_set_title (GTK_WINDOW (error_dialog), ""); /* as per HIG */
gtk_container_set_border_width (GTK_CONTAINER (error_dialog), 5);
gtk_dialog_set_default_response (GTK_DIALOG (error_dialog),
GTK_RESPONSE_OK);
gtk_window_set_modal (GTK_WINDOW (error_dialog), TRUE);
gtk_window_set_position (GTK_WINDOW (error_dialog), GTK_WIN_POS_CENTER_ON_PARENT);
return error_dialog;
}
gboolean
set_fingerprint_label (GtkButton *editable_button)
{
GDBusProxy *device;
GVariant *result;
GVariantIter *fingers;
GError *error = NULL;
ensure_manager ();
if (manager == NULL)
return FALSE;
device = get_first_device ();
if (device == NULL)
return FALSE;
result = g_dbus_proxy_call_sync (device, "ListEnrolledFingers", g_variant_new ("(s)", ""), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
if (!result) {
if (!g_dbus_error_is_remote_error (error) ||
strcmp (g_dbus_error_get_remote_error(error), "net.reactivated.Fprint.Error.NoEnrolledPrints") != 0) {
g_object_unref (device);
return FALSE;
}
}
if (result && g_variant_is_of_type (result, G_VARIANT_TYPE ("(as)")))
g_variant_get (result, "(as)", &fingers);
else
fingers = NULL;
if (fingers == NULL || g_variant_iter_n_children (fingers) == 0) {
is_disable = FALSE;
gtk_button_set_label (editable_button, _("Disabled"));
} else {
is_disable = TRUE;
gtk_button_set_label (editable_button, _("Enabled"));
}
gtk_widget_set_halign (gtk_bin_get_child (GTK_BIN (editable_button)), GTK_ALIGN_START);
if (result != NULL)
g_variant_unref (result);
if (fingers != NULL)
g_variant_iter_free (fingers);
g_object_unref (device);
return TRUE;
}
static void
delete_fingerprints (void)
{
GDBusProxy *device;
GVariant *result;
ensure_manager ();
if (manager == NULL)
return;
device = get_first_device ();
if (device == NULL)
return;
result = g_dbus_proxy_call_sync (device, "DeleteEnrolledFingers", g_variant_new ("(s)", ""), G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL);
if (result)
g_variant_unref (result);
g_object_unref (device);
}
static void
delete_fingerprints_question (GtkWindow *parent,
GtkButton *editable_button,
ActUser *user)
{
GtkWidget *question;
GtkWidget *button;
question = gtk_message_dialog_new (parent,
GTK_DIALOG_MODAL,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_NONE,
_("Delete registered fingerprints?"));
gtk_dialog_add_button (GTK_DIALOG (question), _("_Cancel"), GTK_RESPONSE_CANCEL);
gtk_window_set_modal (GTK_WINDOW (question), TRUE);
button = gtk_button_new_with_mnemonic (_("_Delete Fingerprints"));
gtk_widget_set_can_default (button, TRUE);
gtk_widget_show (button);
gtk_dialog_add_action_widget (GTK_DIALOG (question), button, GTK_RESPONSE_OK);
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (question),
_("Do you want to delete your registered fingerprints so fingerprint login is disabled?"));
gtk_dialog_set_default_response (GTK_DIALOG (question), GTK_RESPONSE_OK);
if (gtk_dialog_run (GTK_DIALOG (question)) == GTK_RESPONSE_OK) {
delete_fingerprints ();
set_fingerprint_label (editable_button);
}
gtk_widget_destroy (question);
}
static gboolean
enroll_start (EnrollData *data, GError **error)
{
GVariant *result;
result = g_dbus_proxy_call_sync (data->device, "EnrollStart", g_variant_new ("(s)", data->finger), G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
if (result == NULL)
return FALSE;
g_variant_unref (result);
return TRUE;
}
static gboolean
enroll_stop (EnrollData *data, GError **error)
{
GVariant *result;
result = g_dbus_proxy_call_sync (data->device, "EnrollStop", g_variant_new ("()"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
if (result == NULL)
return FALSE;
g_variant_unref (result);
return TRUE;
}
static gboolean
claim (EnrollData *data, GError **error)
{
GVariant *result;
result = g_dbus_proxy_call_sync (data->device, "Claim", g_variant_new ("(s)", ""), G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
if (result == NULL)
return FALSE;
g_variant_unref (result);
return TRUE;
}
static gboolean
release (EnrollData *data, GError **error)
{
GVariant *result;
result = g_dbus_proxy_call_sync (data->device, "Release", g_variant_new ("()"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
if (result == NULL)
return FALSE;
g_variant_unref (result);
return TRUE;
}
static void
enroll_data_destroy (EnrollData *data)
{
switch (data->state) {
case STATE_ENROLLING:
enroll_stop (data, NULL);
/* fall-through */
case STATE_CLAIMED:
release (data, NULL);
/* fall-through */
case STATE_NONE:
g_free (data->name);
g_object_unref (data->device);
g_object_unref (data->dialog);
gtk_widget_destroy (data->ass);
g_free (data);
}
}
static const char *
selected_finger (GtkBuilder *dialog)
{
int index;
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (WID ("radiobutton1")))) {
gtk_widget_set_sensitive (WID ("finger_combobox"), FALSE);
return "right-index-finger";
}
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (WID ("radiobutton2")))) {
gtk_widget_set_sensitive (WID ("finger_combobox"), FALSE);
return "left-index-finger";
}
gtk_widget_set_sensitive (WID ("finger_combobox"), TRUE);
index = gtk_combo_box_get_active (GTK_COMBO_BOX (WID ("finger_combobox")));
switch (index) {
case 0:
return "left-thumb";
case 1:
return "left-middle-finger";
case 2:
return "left-ring-finger";
case 3:
return "left-little-finger";
case 4:
return "right-thumb";
case 5:
return "right-middle-finger";
case 6:
return "right-ring-finger";
case 7:
return "right-little-finger";
default:
g_assert_not_reached ();
}
return NULL;
}
static void
finger_radio_button_toggled (GtkToggleButton *button, EnrollData *data)
{
GtkBuilder *dialog = data->dialog;
char *msg;
data->finger = selected_finger (data->dialog);
msg = finger_str_to_msg (data->finger, data->name, data->is_swipe);
gtk_label_set_text (GTK_LABEL (WID("enroll-label")), msg);
g_free (msg);
}
static void
finger_combobox_changed (GtkComboBox *combobox, EnrollData *data)
{
GtkBuilder *dialog = data->dialog;
char *msg;
data->finger = selected_finger (data->dialog);
msg = finger_str_to_msg (data->finger, data->name, data->is_swipe);
gtk_label_set_text (GTK_LABEL (WID("enroll-label")), msg);
g_free (msg);
}
static void
assistant_cancelled (GtkAssistant *ass, EnrollData *data)
{
GtkButton *editable_button = data->editable_button;
enroll_data_destroy (data);
set_fingerprint_label (editable_button);
}
static void
enroll_result (EnrollData *data, const char *result, gboolean done)
{
GtkBuilder *dialog = data->dialog;
char *msg;
if (g_str_equal (result, "enroll-completed") || g_str_equal (result, "enroll-stage-passed")) {
char *name, *path;
data->num_stages_done++;
name = g_strdup_printf ("image%d", data->num_stages_done);
path = g_strdup_printf ("/org/gnome/control-center/user-accounts/print_ok.png");
gtk_image_set_from_resource (GTK_IMAGE (WID (name)), path);
g_free (name);
g_free (path);
}
if (g_str_equal (result, "enroll-completed")) {
gtk_label_set_text (GTK_LABEL (WID ("status-label")), _("Done!"));
gtk_label_set_text (GTK_LABEL (WID("enroll-label")), "");
gtk_assistant_set_page_complete (GTK_ASSISTANT (data->ass), WID ("page2"), TRUE);
}
if (done != FALSE) {
enroll_stop (data, NULL);
data->state = STATE_CLAIMED;
if (g_str_equal (result, "enroll-completed") == FALSE) {
/* The enrollment failed, restart it */
enroll_start (data, NULL);
data->state = STATE_ENROLLING;
result = "enroll-retry-scan";
} else {
return;
}
}
msg = g_strdup_printf (TR(enroll_result_str_to_msg (result, data->is_swipe)), data->name);
gtk_label_set_text (GTK_LABEL (WID ("status-label")), msg);
g_free (msg);
}
static void
device_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, EnrollData *data)
{
if (strcmp (signal_name, "EnrollStatus") == 0) {
if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sb)"))) {
gchar *result;
gboolean done;
g_variant_get (parameters, "(&sb)", &result, &done);
enroll_result (data, result, done);
}
}
}
static void
assistant_prepare (GtkAssistant *ass, GtkWidget *page, EnrollData *data)
{
const char *name;
name = g_object_get_data (G_OBJECT (page), "name");
if (name == NULL)
return;
if (g_str_equal (name, "enroll")) {
GError *error = NULL;
GtkBuilder *dialog = data->dialog;
char *path;
guint i;
GVariant *result;
gint num_enroll_stages;
if (!claim (data, &error)) {
GtkWidget *d;
char *msg;
/* translators:
* The variable is the name of the device, for example:
* "Could you not access "Digital Persona U.are.U 4000/4000B" device */
msg = g_strdup_printf (_("Could not access “%s” device"), data->name);
d = get_error_dialog (msg, error->message, GTK_WINDOW (data->ass));
g_error_free (error);
gtk_dialog_run (GTK_DIALOG (d));
gtk_widget_destroy (d);
g_free (msg);
enroll_data_destroy (data);
return;
}
data->state = STATE_CLAIMED;
result = g_dbus_connection_call_sync (connection,
"net.reactivated.Fprint",
g_dbus_proxy_get_object_path (data->device),
"org.freedesktop.DBus.Properties",
"Get",
g_variant_new ("(ss)", "net.reactivated.Fprint.Device", "num-enroll-stages"),
G_VARIANT_TYPE ("(v)"),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
num_enroll_stages = 0;
if (result) {
GVariant *v;
g_variant_get (result, "(v)", &v);
num_enroll_stages = g_variant_get_int32 (v);
g_variant_unref (result);
g_variant_unref (v);
}
if (num_enroll_stages < 1) {
GtkWidget *d;
char *msg;
/* translators:
* The variable is the name of the device, for example:
* "Could you not access "Digital Persona U.are.U 4000/4000B" device */
msg = g_strdup_printf (_("Could not access “%s” device"), data->name);
d = get_error_dialog (msg, "net.reactivated.Fprint.Error.Internal", GTK_WINDOW (data->ass));
gtk_dialog_run (GTK_DIALOG (d));
gtk_widget_destroy (d);
g_free (msg);
enroll_data_destroy (data);
return;
}
data->num_enroll_stages = num_enroll_stages;
/* Hide the extra "bulbs" if not needed */
for (i = MAX_ENROLL_STAGES; i > data->num_enroll_stages; i--) {
char *name;
name = g_strdup_printf ("image%d", i);
gtk_widget_hide (WID (name));
g_free (name);
}
/* And set the right image */
{
path = g_strdup_printf ("/org/gnome/control-center/user-accounts/%s.png", data->finger);
}
for (i = 1; i <= data->num_enroll_stages; i++) {
char *name;
name = g_strdup_printf ("image%d", i);
gtk_image_set_from_resource (GTK_IMAGE (WID (name)), path);
g_free (name);
}
g_free (path);
g_signal_connect (data->device, "g-signal", G_CALLBACK (device_signal_cb), data);
if (!enroll_start (data, &error)) {
GtkWidget *d;
char *msg;
/* translators:
* The variable is the name of the device, for example:
* "Could you not access "Digital Persona U.are.U 4000/4000B" device */
msg = g_strdup_printf (_("Could not start finger capture on “%s” device"), data->name);
d = get_error_dialog (msg, error->message, GTK_WINDOW (data->ass));
g_error_free (error);
gtk_dialog_run (GTK_DIALOG (d));
gtk_widget_destroy (d);
g_free (msg);
enroll_data_destroy (data);
return;
}
data->state = STATE_ENROLLING;;
} else {
if (data->state == STATE_ENROLLING) {
enroll_stop (data, NULL);
data->state = STATE_CLAIMED;
}
if (data->state == STATE_CLAIMED) {
release (data, NULL);
data->state = STATE_NONE;
}
}
}
static void
enroll_fingerprints (GtkWindow *parent,
GtkButton *editable_button,
ActUser *user)
{
GDBusProxy *device = NULL;
GtkBuilder *dialog;
EnrollData *data;
GtkWidget *ass;
char *msg;
GVariant *result;
GError *error = NULL;
ensure_manager ();
if (manager != NULL)
device = get_first_device ();
if (manager == NULL || device == NULL) {
GtkWidget *d;
d = get_error_dialog (_("Could not access any fingerprint readers"),
_("Please contact your system administrator for help."),
parent);
gtk_dialog_run (GTK_DIALOG (d));
gtk_widget_destroy (d);
return;
}
data = g_new0 (EnrollData, 1);
data->device = device;
data->editable_button = editable_button;
/* Get some details about the device */
result = g_dbus_connection_call_sync (connection,
"net.reactivated.Fprint",
g_dbus_proxy_get_object_path (data->device),
"org.freedesktop.DBus.Properties",
"GetAll",
g_variant_new ("(s)", "net.reactivated.Fprint.Device"),
G_VARIANT_TYPE ("(a{sv})"),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
NULL);
if (result) {
GVariant *props;
gchar *scan_type;
g_variant_get (result, "(@a{sv})", &props);
g_variant_lookup (props, "name", "s", &data->name);
g_variant_lookup (props, "scan-type", "s", &scan_type);
if (g_strcmp0 (scan_type, "swipe") == 0)
data->is_swipe = TRUE;
g_free (scan_type);
g_variant_unref (props);
g_variant_unref (result);
}
dialog = gtk_builder_new ();
if (!gtk_builder_add_from_resource (dialog,
"/org/gnome/control-center/user-accounts/account-fingerprint.ui",
&error)) {
g_error ("%s", error->message);
g_error_free (error);
return;
}
data->dialog = dialog;
ass = WID ("assistant");
gtk_window_set_title (GTK_WINDOW (ass), _("Enable Fingerprint Login"));
gtk_window_set_transient_for (GTK_WINDOW (ass), parent);
gtk_window_set_modal (GTK_WINDOW (ass), TRUE);
gtk_window_set_resizable (GTK_WINDOW (ass), FALSE);
gtk_window_set_type_hint (GTK_WINDOW (ass), GDK_WINDOW_TYPE_HINT_DIALOG);
g_signal_connect (G_OBJECT (ass), "cancel",
G_CALLBACK (assistant_cancelled), data);
g_signal_connect (G_OBJECT (ass), "close",
G_CALLBACK (assistant_cancelled), data);
g_signal_connect (G_OBJECT (ass), "prepare",
G_CALLBACK (assistant_prepare), data);
/* Page 1 */
gtk_combo_box_set_active (GTK_COMBO_BOX (WID ("finger_combobox")), 0);
g_signal_connect (G_OBJECT (WID ("radiobutton1")), "toggled",
G_CALLBACK (finger_radio_button_toggled), data);
g_signal_connect (G_OBJECT (WID ("radiobutton2")), "toggled",
G_CALLBACK (finger_radio_button_toggled), data);
g_signal_connect (G_OBJECT (WID ("radiobutton3")), "toggled",
G_CALLBACK (finger_radio_button_toggled), data);
g_signal_connect (G_OBJECT (WID ("finger_combobox")), "changed",
G_CALLBACK (finger_combobox_changed), data);
data->finger = selected_finger (dialog);
g_object_set_data (G_OBJECT (WID("page1")), "name", "intro");
/* translators:
* The variable is the name of the device, for example:
* "To enable fingerprint login, you need to save one of your fingerprints, using the
* 'Digital Persona U.are.U 4000/4000B' device."
*/
msg = g_strdup_printf (_("To enable fingerprint login, you need to save one of your fingerprints, using the “%s” device."),
data->name);
gtk_label_set_text (GTK_LABEL (WID("intro-label")), msg);
g_free (msg);
gtk_assistant_set_page_complete (GTK_ASSISTANT (ass), WID("page1"), TRUE);
gtk_assistant_set_page_title (GTK_ASSISTANT (ass), WID("page1"), _("Selecting finger"));
gtk_assistant_set_page_title (GTK_ASSISTANT (ass), WID("page2"), _("Enrolling fingerprints"));
gtk_assistant_set_page_title (GTK_ASSISTANT (ass), WID("page3"), _("Summary"));
/* Page 2 */
g_object_set_data (G_OBJECT (WID("page2")), "name", "enroll");
msg = finger_str_to_msg (data->finger, data->name, data->is_swipe);
gtk_label_set_text (GTK_LABEL (WID("enroll-label")), msg);
g_free (msg);
/* Page 3 */
g_object_set_data (G_OBJECT (WID("page3")), "name", "summary");
data->ass = ass;
gtk_widget_show (ass);
}
void
fingerprint_button_clicked (GtkWindow *parent,
GtkButton *editable_button,
ActUser *user)
{
bindtextdomain ("fprintd", GNOMELOCALEDIR);
bind_textdomain_codeset ("fprintd", "UTF-8");
if (is_disable != FALSE) {
delete_fingerprints_question (parent, editable_button, user);
} else {
enroll_fingerprints (parent, editable_button, user);
}
}
#pragma GCC diagnostic pop