summaryrefslogtreecommitdiffstats
path: root/panels/thunderbolt/cc-bolt-device-dialog.c
diff options
context:
space:
mode:
Diffstat (limited to 'panels/thunderbolt/cc-bolt-device-dialog.c')
-rw-r--r--panels/thunderbolt/cc-bolt-device-dialog.c522
1 files changed, 522 insertions, 0 deletions
diff --git a/panels/thunderbolt/cc-bolt-device-dialog.c b/panels/thunderbolt/cc-bolt-device-dialog.c
new file mode 100644
index 0000000..a1683c4
--- /dev/null
+++ b/panels/thunderbolt/cc-bolt-device-dialog.c
@@ -0,0 +1,522 @@
+/* Copyright (C) 2018 Red Hat, Inc
+ *
+ * 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 of the License, 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/>.
+ *
+ * Authors: Christian J. Kellner <ckellner@redhat.com>
+ *
+ */
+
+#include <config.h>
+
+#include <list-box-helper.h>
+
+#include <glib/gi18n.h>
+
+#include "bolt-device.h"
+#include "bolt-error.h"
+#include "bolt-time.h"
+
+#include "cc-thunderbolt-resources.h"
+
+#include "cc-bolt-device-dialog.h"
+#include "cc-bolt-device-entry.h"
+
+struct _CcBoltDeviceDialog
+{
+ GtkDialog parent;
+
+ BoltClient *client;
+ BoltDevice *device;
+ GCancellable *cancel;
+
+ /* main ui */
+ GtkHeaderBar *header_bar;
+
+ /* notifications */
+ GtkLabel *notify_label;
+ GtkRevealer *notify_revealer;
+
+ /* device details */
+ GtkLabel *name_label;
+ GtkLabel *status_label;
+ GtkLabel *uuid_label;
+
+ GtkLabel *time_title;
+ GtkLabel *time_label;
+
+ /* parents */
+ GtkExpander *parents_expander;
+ GtkLabel *parents_label;
+ GtkListBox *parents_devices;
+
+ /* actions */
+ GtkWidget *button_box;
+ GtkSpinner *spinner;
+ GtkButton *connect_button;
+ GtkButton *forget_button;
+};
+
+static void on_notify_button_clicked_cb (GtkButton *button,
+ CcBoltDeviceDialog *panel);
+
+static void on_forget_button_clicked_cb (CcBoltDeviceDialog *dialog);
+static void on_connect_button_clicked_cb (CcBoltDeviceDialog *dialog);
+
+G_DEFINE_TYPE (CcBoltDeviceDialog, cc_bolt_device_dialog, GTK_TYPE_DIALOG);
+
+#define RESOURCE_UI "/org/gnome/control-center/thunderbolt/cc-bolt-device-dialog.ui"
+
+static const char *
+status_to_string_for_ui (BoltDevice *dev)
+{
+ BoltStatus status;
+ BoltAuthFlags aflags;
+ gboolean nopcie;
+
+ status = bolt_device_get_status (dev);
+ aflags = bolt_device_get_authflags(dev);
+ nopcie = bolt_flag_isset (aflags, BOLT_AUTH_NOPCIE);
+
+ switch (status)
+ {
+ case BOLT_STATUS_DISCONNECTED:
+ return C_("Thunderbolt Device Status", "Disconnected");
+
+ case BOLT_STATUS_CONNECTING:
+ return C_("Thunderbolt Device Status", "Connecting");
+
+ case BOLT_STATUS_CONNECTED:
+ return C_("Thunderbolt Device Status", "Connected");
+
+ case BOLT_STATUS_AUTH_ERROR:
+ return C_("Thunderbolt Device Status", "Authorization Error");
+
+ case BOLT_STATUS_AUTHORIZING:
+ return C_("Thunderbolt Device Status", "Authorizing");
+
+ case BOLT_STATUS_AUTHORIZED:
+ case BOLT_STATUS_AUTHORIZED_NEWKEY:
+ case BOLT_STATUS_AUTHORIZED_SECURE:
+ case BOLT_STATUS_AUTHORIZED_DPONLY:
+ if (nopcie)
+ return C_("Thunderbolt Device Status", "Reduced Functionality");
+ else
+ return C_("Thunderbolt Device Status", "Connected & Authorized");
+
+ case BOLT_STATUS_UNKNOWN:
+ break; /* use default return value, i.e. Unknown */
+ }
+
+ return C_("Thunderbolt Device Status", "Unknown");
+}
+
+static void
+dialog_update_from_device (CcBoltDeviceDialog *dialog)
+{
+ g_autofree char *generated = NULL;
+ g_autofree char *timestr = NULL;
+ const char *label;
+ const char *uuid;
+ const char *status_brief;
+ BoltStatus status;
+ gboolean stored;
+ BoltDevice *dev;
+ guint timestamp;
+
+ if (gtk_widget_in_destruction (GTK_WIDGET (dialog)))
+ return;
+
+ dev = dialog->device;
+
+ uuid = bolt_device_get_uid (dev);
+ label = bolt_device_get_label (dev);
+
+ stored = bolt_device_is_stored (dev);
+ status = bolt_device_get_status (dev);
+
+ if (label == NULL)
+ {
+ const char *name = bolt_device_get_name (dev);
+ const char *vendor = bolt_device_get_vendor (dev);
+
+ generated = g_strdup_printf ("%s %s", name, vendor);
+ label = generated;
+ }
+
+ gtk_label_set_label (dialog->name_label, label);
+ gtk_header_bar_set_title (dialog->header_bar, label);
+
+ status_brief = status_to_string_for_ui (dev);
+ gtk_label_set_label (dialog->status_label, status_brief);
+ gtk_widget_set_visible (GTK_WIDGET (dialog->forget_button), stored);
+
+ /* while we are having an ongoing operation we are setting the buttons
+ * to be in-sensitive. In that case, if the button was visible
+ * before it will be hidden when the operation is finished by the
+ * dialog_operation_done() function */
+ if (gtk_widget_is_sensitive (GTK_WIDGET (dialog->connect_button)))
+ gtk_widget_set_visible (GTK_WIDGET (dialog->connect_button),
+ status == BOLT_STATUS_CONNECTED);
+
+ gtk_label_set_label (dialog->uuid_label, uuid);
+
+ if (bolt_status_is_authorized (status))
+ {
+ /* Translators: The time point the device was authorized. */
+ gtk_label_set_label (dialog->time_title, _("Authorized at:"));
+ timestamp = bolt_device_get_authtime (dev);
+ }
+ else if (bolt_status_is_connected (status))
+ {
+ /* Translators: The time point the device was connected. */
+ gtk_label_set_label (dialog->time_title, _("Connected at:"));
+ timestamp = bolt_device_get_conntime (dev);
+ }
+ else
+ {
+ /* Translators: The time point the device was enrolled,
+ * i.e. authorized and stored in the device database. */
+ gtk_label_set_label (dialog->time_title, _("Enrolled at:"));
+ timestamp = bolt_device_get_storetime (dev);
+ }
+
+ timestr = bolt_epoch_format (timestamp, "%c");
+ gtk_label_set_label (dialog->time_label, timestr);
+
+}
+
+static void
+on_device_notify_cb (GObject *gobject,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ CcBoltDeviceDialog *dialog = CC_BOLT_DEVICE_DIALOG (user_data);
+
+ dialog_update_from_device (dialog);
+}
+
+static void
+dialog_operation_start (CcBoltDeviceDialog *dialog)
+{
+ gtk_widget_set_sensitive (GTK_WIDGET (dialog->connect_button), FALSE);
+ gtk_widget_set_sensitive (GTK_WIDGET (dialog->forget_button), FALSE);
+ gtk_spinner_start (dialog->spinner);
+}
+
+static void
+dialog_operation_done (CcBoltDeviceDialog *dialog,
+ GtkWidget *sender,
+ GError *error)
+{
+ GtkWidget *cb = GTK_WIDGET (dialog->connect_button);
+ GtkWidget *fb = GTK_WIDGET (dialog->forget_button);
+
+ /* don' do anything if we are being destroyed */
+ if (gtk_widget_in_destruction (GTK_WIDGET (dialog)))
+ return;
+
+ /* also don't do anything if the op was canceled */
+ if (error != NULL && bolt_err_cancelled (error))
+ return;
+
+ gtk_spinner_stop (dialog->spinner);
+
+ if (error != NULL)
+ {
+ gtk_label_set_label (dialog->notify_label, error->message);
+ gtk_revealer_set_reveal_child (dialog->notify_revealer, TRUE);
+
+ /* set the *other* button to sensitive */
+ gtk_widget_set_sensitive (cb, cb != sender);
+ gtk_widget_set_sensitive (fb, fb != sender);
+ }
+ else
+ {
+ gtk_widget_set_visible (sender, FALSE);
+ gtk_widget_set_sensitive (cb, TRUE);
+ gtk_widget_set_sensitive (fb, TRUE);
+ }
+}
+
+static void
+on_connect_all_done (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ g_autoptr(GError) err = NULL;
+ CcBoltDeviceDialog *dialog = CC_BOLT_DEVICE_DIALOG (user_data);
+ gboolean ok;
+
+ ok = bolt_client_connect_all_finish (dialog->client, res, &err);
+
+ if (!ok)
+ g_prefix_error (&err, _("Failed to authorize device: "));
+
+ dialog_operation_done (dialog, GTK_WIDGET (dialog->connect_button), err);
+}
+
+static void
+on_connect_button_clicked_cb (CcBoltDeviceDialog *dialog)
+{
+ g_autoptr(GPtrArray) devices = NULL;
+ g_autoptr(GList) entries = NULL;
+ BoltDevice *device = dialog->device;
+ GList *iter;
+
+ g_return_if_fail (device != NULL);
+
+ dialog_operation_start (dialog);
+
+ entries = gtk_container_get_children (GTK_CONTAINER (dialog->parents_devices));
+ devices = g_ptr_array_new ();
+
+ /* reverse the order, so to start with the devices closest to the host */
+ entries = g_list_reverse (entries);
+
+ for (iter = entries; iter; iter = iter->next)
+ {
+ CcBoltDeviceEntry *entry;
+ BoltDevice *dev;
+ BoltStatus status;
+
+ entry = (CcBoltDeviceEntry *) iter->data;
+ dev = cc_bolt_device_entry_get_device (entry);
+ status = bolt_device_get_status (dev);
+
+ /* skip any devices down in the chain that are already authorized
+ * NB: it is not possible to have gaps of non-authorized devices
+ * in the chain, i.e. once we encounter a non-authorized device,
+ * all following device (down the chain, towards the target) will
+ * also be not authorized. */
+ if (!bolt_status_is_pending (status))
+ continue;
+
+ /* device is now either !stored || pending */
+ g_ptr_array_add (devices, dev);
+ }
+
+ /* finally the actual device of the dialog */
+ g_ptr_array_add (devices, device);
+
+ bolt_client_connect_all_async (dialog->client,
+ devices,
+ BOLT_POLICY_DEFAULT,
+ BOLT_AUTHCTRL_NONE,
+ dialog->cancel,
+ on_connect_all_done,
+ dialog);
+}
+
+static void
+on_forget_device_done (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ g_autoptr(GError) err = NULL;
+ CcBoltDeviceDialog *dialog = CC_BOLT_DEVICE_DIALOG (user_data);
+ gboolean ok;
+
+ ok = bolt_client_forget_device_finish (dialog->client, res, &err);
+
+ if (!ok)
+ g_prefix_error (&err, _("Failed to forget device: "));
+
+ dialog_operation_done (dialog, GTK_WIDGET (dialog->forget_button), err);
+}
+
+static void
+on_forget_button_clicked_cb (CcBoltDeviceDialog *dialog)
+{
+ const char *uid = NULL;
+
+ g_return_if_fail (dialog->device != NULL);
+
+ uid = bolt_device_get_uid (dialog->device);
+ dialog_operation_start (dialog);
+
+ bolt_client_forget_device_async (dialog->client,
+ uid,
+ dialog->cancel,
+ on_forget_device_done,
+ dialog);
+}
+
+static void
+on_notify_button_clicked_cb (GtkButton *button,
+ CcBoltDeviceDialog *dialog)
+{
+ gtk_revealer_set_reveal_child (dialog->notify_revealer, FALSE);
+}
+
+
+static void
+cc_bolt_device_dialog_finalize (GObject *object)
+{
+ CcBoltDeviceDialog *dialog = CC_BOLT_DEVICE_DIALOG (object);
+
+ g_clear_object (&dialog->device);
+ g_cancellable_cancel (dialog->cancel);
+ g_clear_object (&dialog->cancel);
+ g_clear_object (&dialog->client);
+
+ G_OBJECT_CLASS (cc_bolt_device_dialog_parent_class)->finalize (object);
+}
+
+static void
+cc_bolt_device_dialog_class_init (CcBoltDeviceDialogClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = cc_bolt_device_dialog_finalize;
+
+ gtk_widget_class_set_template_from_resource (widget_class, RESOURCE_UI);
+ gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceDialog, header_bar);
+
+ gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceDialog, notify_label);
+ gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceDialog, notify_revealer);
+
+ gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceDialog, name_label);
+ gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceDialog, status_label);
+
+ gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceDialog, uuid_label);
+ gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceDialog, time_title);
+ gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceDialog, time_label);
+
+ gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceDialog, parents_expander);
+ gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceDialog, parents_label);
+ gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceDialog, parents_devices);
+
+ gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceDialog, button_box);
+ gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceDialog, spinner);
+ gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceDialog, connect_button);
+ gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceDialog, forget_button);
+
+ gtk_widget_class_bind_template_callback (widget_class, on_notify_button_clicked_cb);
+ gtk_widget_class_bind_template_callback (widget_class, on_connect_button_clicked_cb);
+ gtk_widget_class_bind_template_callback (widget_class, on_forget_button_clicked_cb);
+}
+
+static void
+cc_bolt_device_dialog_init (CcBoltDeviceDialog *dialog)
+{
+ g_resources_register (cc_thunderbolt_get_resource ());
+ gtk_widget_init_template (GTK_WIDGET (dialog));
+
+ gtk_list_box_set_header_func (dialog->parents_devices,
+ cc_list_box_update_header_func,
+ NULL,
+ NULL);
+}
+
+/* public functions */
+CcBoltDeviceDialog *
+cc_bolt_device_dialog_new (void)
+{
+ CcBoltDeviceDialog *dialog;
+
+ dialog = g_object_new (CC_TYPE_BOLT_DEVICE_DIALOG,
+ "use-header-bar", TRUE,
+ NULL);
+ return dialog;
+}
+
+void
+cc_bolt_device_dialog_set_client (CcBoltDeviceDialog *dialog,
+ BoltClient *client)
+{
+ g_clear_object (&dialog->client);
+ dialog->client = g_object_ref (client);
+}
+
+void
+cc_bolt_device_dialog_set_device (CcBoltDeviceDialog *dialog,
+ BoltDevice *device,
+ GPtrArray *parents)
+{
+ g_autofree char *msg = NULL;
+ guint i;
+
+ if (device == dialog->device)
+ return;
+
+ if (dialog->device)
+ {
+ g_cancellable_cancel (dialog->cancel);
+ g_clear_object (&dialog->cancel);
+ dialog->cancel = g_cancellable_new ();
+
+ g_signal_handlers_disconnect_by_func (dialog->device,
+ G_CALLBACK (on_device_notify_cb),
+ dialog);
+ g_clear_object (&dialog->device);
+
+ gtk_container_foreach (GTK_CONTAINER (dialog->parents_devices),
+ (GtkCallback) gtk_widget_destroy, NULL);
+ gtk_widget_hide (GTK_WIDGET (dialog->parents_expander));
+ }
+
+ if (device == NULL)
+ return;
+
+ dialog->device = g_object_ref (device);
+ g_signal_connect_object (dialog->device,
+ "notify",
+ G_CALLBACK (on_device_notify_cb),
+ dialog,
+ 0);
+
+ /* reset the sensitivity of the buttons, because
+ * dialog_update_from_device, because it can't know */
+ gtk_widget_set_sensitive (GTK_WIDGET (dialog->connect_button), TRUE);
+ gtk_widget_set_sensitive (GTK_WIDGET (dialog->forget_button), TRUE);
+
+ dialog_update_from_device (dialog);
+
+ /* no parents, we are done here */
+ if (!parents || parents->len == 0)
+ return;
+
+ msg = g_strdup_printf (ngettext ("Depends on %u other device",
+ "Depends on %u other devices",
+ parents->len), parents->len);
+
+ gtk_label_set_label (dialog->parents_label, msg);
+ gtk_widget_show (GTK_WIDGET (dialog->parents_expander));
+
+ for (i = 0; i < parents->len; i++)
+ {
+ CcBoltDeviceEntry *entry;
+ BoltDevice *parent;
+
+ parent = g_ptr_array_index (parents, i);
+
+ entry = cc_bolt_device_entry_new (parent, TRUE);
+ gtk_widget_show (GTK_WIDGET (entry));
+ gtk_container_add (GTK_CONTAINER (dialog->parents_devices), GTK_WIDGET (entry));
+ }
+}
+
+BoltDevice *
+cc_bolt_device_dialog_peek_device (CcBoltDeviceDialog *dialog)
+{
+ return dialog->device;
+}
+
+gboolean
+cc_bolt_device_dialog_device_equal (CcBoltDeviceDialog *dialog,
+ BoltDevice *device)
+{
+ return dialog->device != NULL && device == dialog->device;
+}