diff options
Diffstat (limited to 'panels/thunderbolt/cc-bolt-panel.c')
-rw-r--r-- | panels/thunderbolt/cc-bolt-panel.c | 994 |
1 files changed, 994 insertions, 0 deletions
diff --git a/panels/thunderbolt/cc-bolt-panel.c b/panels/thunderbolt/cc-bolt-panel.c new file mode 100644 index 0000000..9004c3c --- /dev/null +++ b/panels/thunderbolt/cc-bolt-panel.c @@ -0,0 +1,994 @@ +/* Copyright © 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 <shell/cc-panel.h> +#include <list-box-helper.h> + +#include <glib/gi18n.h> +#include <polkit/polkit.h> + +#include "cc-bolt-device-dialog.h" +#include "cc-bolt-device-entry.h" + +#include "bolt-client.h" +#include "bolt-names.h" +#include "bolt-str.h" + +#include "cc-bolt-panel.h" +#include "cc-thunderbolt-resources.h" + +struct _CcBoltPanel +{ + CcPanel parent; + + BoltClient *client; + + /* headerbar menu */ + GtkBox *headerbar_box; + GtkLockButton *lock_button; + + /* main ui */ + GtkStack *container; + + /* empty state */ + GtkLabel *notb_caption; + GtkLabel *notb_details; + + /* notifications */ + GtkLabel *notification_label; + GtkRevealer *notification_revealer; + + /* authmode */ + GtkSwitch *authmode_switch; + GtkSpinner *authmode_spinner; + GtkStack *authmode_mode; + + /* device list */ + GHashTable *devices; + + GtkStack *devices_stack; + GtkBox *devices_box; + GtkBox *pending_box; + + GtkListBox *devices_list; + GtkListBox *pending_list; + + /* device details dialog */ + CcBoltDeviceDialog *device_dialog; + + /* polkit integration */ + GPermission *permission; +}; + +/* initialization */ +static void bolt_client_ready (GObject *source, + GAsyncResult *res, + gpointer user_data); + +/* panel functions */ +static void cc_bolt_panel_set_no_thunderbolt (CcBoltPanel *panel, + const char *custom_msg); + +static void cc_bolt_panel_name_owner_changed (CcBoltPanel *panel); + +static CcBoltDeviceEntry * cc_bolt_panel_add_device (CcBoltPanel *panel, + BoltDevice *dev); + +static void cc_bolt_panel_del_device_entry (CcBoltPanel *panel, + CcBoltDeviceEntry *entry); + +static void cc_bolt_panel_authmode_sync (CcBoltPanel *panel); + +static void cc_panel_list_box_migrate (CcBoltPanel *panel, + GtkListBox *from, + GtkListBox *to, + CcBoltDeviceEntry *entry); + +/* bolt client signals */ +static void on_bolt_name_owner_changed_cb (GObject *object, + GParamSpec *pspec, + gpointer user_data); + +static void on_bolt_device_added_cb (BoltClient *cli, + const char *path, + CcBoltPanel *panel); + +static void on_bolt_device_removed_cb (BoltClient *cli, + const char *opath, + CcBoltPanel *panel); + +static void on_bolt_notify_authmode_cb (GObject *gobject, + GParamSpec *pspec, + gpointer user_data); + +/* panel signals */ +static gboolean on_authmode_state_set_cb (CcBoltPanel *panel, + gboolean state, + GtkSwitch *toggle); + +static void on_device_entry_row_activated_cb (CcBoltPanel *panel, + GtkListBoxRow *row); + +static gboolean on_device_dialog_delete_event_cb (GtkWidget *widget, + GdkEvent *event, + CcBoltPanel *panel); + +static void on_device_entry_status_changed_cb (CcBoltDeviceEntry *entry, + BoltStatus new_status, + CcBoltPanel *panel); + +static void on_notification_button_clicked_cb (GtkButton *button, + CcBoltPanel *panel); + + +/* polkit */ +static void on_permission_ready (GObject *source_object, + GAsyncResult *res, + gpointer user_data); + +static void on_permission_notify_cb (GPermission *permission, + GParamSpec *pspec, + CcBoltPanel *panel); + +CC_PANEL_REGISTER (CcBoltPanel, cc_bolt_panel); + +static void +bolt_client_ready (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GError) err = NULL; + g_autoptr(CcBoltPanel) panel = NULL; + BoltClient *client; + + panel = CC_BOLT_PANEL (user_data); + client = bolt_client_new_finish (res, &err); + + if (client == NULL) + { + const char *text; + + /* operation got cancelled because the panel got destroyed */ + if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED) || + g_error_matches (err, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED)) + return; + + g_warning ("Could not create client: %s", err->message); + text = _("The Thunderbolt subsystem (boltd) is not installed or " + "not set up properly."); + + gtk_label_set_label (panel->notb_details, text); + gtk_stack_set_visible_child_name (panel->container, "no-thunderbolt"); + + return; + } + + g_signal_connect_object (client, + "notify::g-name-owner", + G_CALLBACK (on_bolt_name_owner_changed_cb), + panel, + 0); + + g_signal_connect_object (client, + "device-added", + G_CALLBACK (on_bolt_device_added_cb), + panel, + 0); + + g_signal_connect_object (client, + "device-removed", + G_CALLBACK (on_bolt_device_removed_cb), + panel, + 0); + + g_signal_connect_object (client, + "notify::auth-mode", + G_CALLBACK (on_bolt_notify_authmode_cb), + panel, + 0); + + /* Treat security-level changes, which should rarely happen, as + * if the name owner changed, i.e. as if boltd got restarted */ + g_signal_connect_object (client, + "notify::security-level", + G_CALLBACK (on_bolt_name_owner_changed_cb), + panel, + 0); + + panel->client = client; + + cc_bolt_device_dialog_set_client (panel->device_dialog, client); + + cc_bolt_panel_authmode_sync (panel); + + g_object_bind_property (panel->authmode_switch, + "active", + panel->devices_box, + "sensitive", + G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); + + g_object_bind_property (panel->authmode_switch, + "active", + panel->pending_box, + "sensitive", + G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); + + gtk_stack_set_visible_child_name (panel->devices_stack, "no-devices"); + cc_bolt_panel_name_owner_changed (panel); +} + +static gboolean +devices_table_transfer_entry (GHashTable *from, + GHashTable *to, + gconstpointer key) +{ + gpointer k, v; + gboolean found; + + found = g_hash_table_lookup_extended (from, key, &k, &v); + + if (found) + { + g_hash_table_steal (from, key); + g_hash_table_insert (to, k, v); + } + + return found; +} + +static void +devices_table_clear_entries (GHashTable *table, + CcBoltPanel *panel) +{ + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init (&iter, table); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + CcBoltDeviceEntry *entry = value; + + cc_bolt_panel_del_device_entry (panel, entry); + g_hash_table_iter_remove (&iter); + } +} + +static void +devices_table_synchronize (CcBoltPanel *panel) +{ + g_autoptr(GHashTable) old = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GError) err = NULL; + guint i; + + devices = bolt_client_list_devices (panel->client, cc_panel_get_cancellable (CC_PANEL (panel)), &err); + + if (!devices) + { + g_warning ("Could not list devices: %s", err->message); + devices = g_ptr_array_new_with_free_func (g_object_unref); + } + + old = panel->devices; + panel->devices = g_hash_table_new (g_str_hash, g_str_equal); + + for (i = 0; i < devices->len; i++) + { + BoltDevice *dev = g_ptr_array_index (devices, i); + const char *path; + gboolean found; + + path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (dev)); + found = devices_table_transfer_entry (old, panel->devices, path); + + if (found) + continue; + + cc_bolt_panel_add_device (panel, dev); + } + + devices_table_clear_entries (old, panel); + gtk_stack_set_visible_child_name (panel->container, "devices-listing"); +} + +static gboolean +list_box_sync_visible (GtkListBox *lstbox) +{ + g_autoptr(GList) children = NULL; + gboolean show; + + children = gtk_container_get_children (GTK_CONTAINER (lstbox)); + show = g_list_length (children) > 0; + + gtk_widget_set_visible (GTK_WIDGET (lstbox), show); + + return show; +} + +static GtkWidget * +cc_bolt_panel_box_for_listbox (CcBoltPanel *panel, + GtkListBox *lstbox) +{ + if ((gpointer) lstbox == panel->devices_list) + return GTK_WIDGET (panel->devices_box); + else if ((gpointer) lstbox == panel->pending_list) + return GTK_WIDGET (panel->pending_box); + + g_return_val_if_reached (NULL); +} + +static CcBoltDeviceEntry * +cc_bolt_panel_add_device (CcBoltPanel *panel, + BoltDevice *dev) +{ + CcBoltDeviceEntry *entry; + BoltDeviceType type; + BoltStatus status; + const char *path; + + type = bolt_device_get_device_type (dev); + + if (type != BOLT_DEVICE_PERIPHERAL) + return FALSE; + + entry = cc_bolt_device_entry_new (dev, FALSE); + path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (dev)); + + /* add to the list box */ + gtk_widget_show (GTK_WIDGET (entry)); + + status = bolt_device_get_status (dev); + + if (bolt_status_is_pending (status)) + { + gtk_container_add (GTK_CONTAINER (panel->pending_list), GTK_WIDGET (entry)); + gtk_widget_show (GTK_WIDGET (panel->pending_list)); + gtk_widget_show (GTK_WIDGET (panel->pending_box)); + } + else + { + gtk_container_add (GTK_CONTAINER (panel->devices_list), GTK_WIDGET (entry)); + gtk_widget_show (GTK_WIDGET (panel->devices_list)); + gtk_widget_show (GTK_WIDGET (panel->devices_box)); + } + + g_signal_connect_object (entry, + "status-changed", + G_CALLBACK (on_device_entry_status_changed_cb), + panel, + 0); + + gtk_stack_set_visible_child_name (panel->devices_stack, "have-devices"); + g_hash_table_insert (panel->devices, (gpointer) path, entry); + + return entry; +} + +static void +cc_bolt_panel_del_device_entry (CcBoltPanel *panel, + CcBoltDeviceEntry *entry) +{ + BoltDevice *dev; + GtkWidget *box; + GtkWidget *p; + gboolean show; + + dev = cc_bolt_device_entry_get_device (entry); + if (cc_bolt_device_dialog_device_equal (panel->device_dialog, dev)) + { + gtk_widget_hide (GTK_WIDGET (panel->device_dialog)); + cc_bolt_device_dialog_set_device (panel->device_dialog, NULL, NULL); + } + + p = gtk_widget_get_parent (GTK_WIDGET (entry)); + gtk_widget_destroy (GTK_WIDGET (entry)); + + box = cc_bolt_panel_box_for_listbox (panel, GTK_LIST_BOX (p)); + show = list_box_sync_visible (GTK_LIST_BOX (p)); + gtk_widget_set_visible (box, show); + + if (!gtk_widget_is_visible (GTK_WIDGET (panel->pending_list)) && + !gtk_widget_is_visible (GTK_WIDGET (panel->devices_list))) + { + gtk_stack_set_visible_child_name (panel->devices_stack, "no-devices"); + } +} + +static void +cc_bolt_panel_authmode_sync (CcBoltPanel *panel) +{ + BoltClient *client = panel->client; + BoltAuthMode mode; + gboolean enabled; + const char *name; + + mode = bolt_client_get_authmode (client); + enabled = (mode & BOLT_AUTH_ENABLED) != 0; + + g_signal_handlers_block_by_func (panel->authmode_switch, on_authmode_state_set_cb, panel); + + gtk_switch_set_state (panel->authmode_switch, enabled); + + g_signal_handlers_unblock_by_func (panel->authmode_switch, on_authmode_state_set_cb, panel); + + name = enabled ? "enabled" : "disabled"; + gtk_stack_set_visible_child_name (panel->authmode_mode, name); +} + +static void +cc_panel_list_box_migrate (CcBoltPanel *panel, + GtkListBox *from, + GtkListBox *to, + CcBoltDeviceEntry *entry) +{ + GtkWidget *from_box; + GtkWidget *to_box; + gboolean show; + GtkWidget *target; + + target = GTK_WIDGET (entry); + + gtk_container_remove (GTK_CONTAINER (from), target); + gtk_container_add (GTK_CONTAINER (to), target); + gtk_widget_show (GTK_WIDGET (to)); + + from_box = cc_bolt_panel_box_for_listbox (panel, from); + to_box = cc_bolt_panel_box_for_listbox (panel, to); + + show = list_box_sync_visible (from); + gtk_widget_set_visible (from_box, show); + gtk_widget_set_visible (to_box, TRUE); +} + +/* bolt client signals */ +static void +cc_bolt_panel_set_no_thunderbolt (CcBoltPanel *panel, + const char *msg) +{ + if (!msg) + { + msg = _("Thunderbolt could not be detected.\n" + "Either the system lacks Thunderbolt support, " + "it has been disabled in the BIOS or is set to " + "an unsupported security level in the BIOS."); + } + + gtk_label_set_label (panel->notb_details, msg); + gtk_stack_set_visible_child_name (panel->container, "no-thunderbolt"); +} + +static void +cc_bolt_panel_name_owner_changed (CcBoltPanel *panel) +{ + g_autofree char *name_owner = NULL; + BoltClient *client = panel->client; + BoltSecurity sl; + gboolean notb = TRUE; + const char *text = NULL; + + name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (panel->client)); + + if (name_owner == NULL) + { + cc_bolt_panel_set_no_thunderbolt (panel, NULL); + devices_table_clear_entries (panel->devices, panel); + gtk_widget_hide (GTK_WIDGET (panel->headerbar_box)); + return; + } + + gtk_stack_set_visible_child_name (panel->container, "loading"); + + sl = bolt_client_get_security (client); + + switch (sl) + { + case BOLT_SECURITY_NONE: + case BOLT_SECURITY_SECURE: + case BOLT_SECURITY_USER: + /* we fetch the device list and show them here */ + notb = FALSE; + break; + + case BOLT_SECURITY_DPONLY: + case BOLT_SECURITY_USBONLY: + text = _("Thunderbolt support has been disabled in the BIOS."); + break; + + case BOLT_SECURITY_UNKNOWN: + text = _("Thunderbolt security level could not be determined.");; + break; + } + + if (notb) + { + /* security level is unknown or un-handled */ + cc_bolt_panel_set_no_thunderbolt (panel, text); + return; + } + + if (panel->permission) + { + gtk_widget_show (GTK_WIDGET (panel->headerbar_box)); + } + else + { + polkit_permission_new ("org.freedesktop.bolt.manage", + NULL, + cc_panel_get_cancellable (CC_PANEL (panel)), + on_permission_ready, + g_object_ref (panel)); + } + + devices_table_synchronize (panel); +} + +/* bolt client signals */ +static void +on_bolt_name_owner_changed_cb (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + cc_bolt_panel_name_owner_changed (CC_BOLT_PANEL (user_data)); +} + +static void +on_bolt_device_added_cb (BoltClient *cli, + const char *path, + CcBoltPanel *panel) +{ + g_autoptr(GError) err = NULL; + GDBusConnection *bus; + BoltDevice *dev; + gboolean found; + + found = g_hash_table_contains (panel->devices, path); + + if (found) + return; + + bus = g_dbus_proxy_get_connection (G_DBUS_PROXY (panel->client)); + dev = bolt_device_new_for_object_path (bus, path, cc_panel_get_cancellable (CC_PANEL (panel)), &err); + + if (!dev) + { + g_warning ("Could not create proxy for %s", path); + return; + } + + cc_bolt_panel_add_device (panel, dev); +} + +static void +on_bolt_device_removed_cb (BoltClient *cli, + const char *path, + CcBoltPanel *panel) +{ + CcBoltDeviceEntry *entry; + + entry = g_hash_table_lookup (panel->devices, path); + + if (!entry) + return; + + cc_bolt_panel_del_device_entry (panel, entry); + g_hash_table_remove (panel->devices, path); +} + +static void +on_bolt_notify_authmode_cb (GObject *gobject, + GParamSpec *pspec, + gpointer user_data) +{ + cc_bolt_panel_authmode_sync (CC_BOLT_PANEL (user_data)); +} + +/* panel signals */ + +static void +on_authmode_ready (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GError) error = NULL; + CcBoltPanel *panel = CC_BOLT_PANEL (user_data); + gboolean ok; + + ok = bolt_client_set_authmode_finish (BOLT_CLIENT (source_object), res, &error); + if (!ok) + { + g_autofree char *text = NULL; + + g_warning ("Could not set authmode: %s", error->message); + + text = g_strdup_printf (_("Error switching direct mode: %s"), error->message); + gtk_label_set_markup (panel->notification_label, text); + gtk_revealer_set_reveal_child (panel->notification_revealer, TRUE); + + /* make sure we are reflecting the correct state */ + cc_bolt_panel_authmode_sync (panel); + } + + gtk_spinner_stop (panel->authmode_spinner); + gtk_widget_set_sensitive (GTK_WIDGET (panel->authmode_switch), TRUE); +} + +static gboolean +on_authmode_state_set_cb (CcBoltPanel *panel, + gboolean enable, + GtkSwitch *toggle) +{ + BoltClient *client = panel->client; + BoltAuthMode mode; + + gtk_widget_set_sensitive (GTK_WIDGET (panel->authmode_switch), FALSE); + gtk_spinner_start (panel->authmode_spinner); + + mode = bolt_client_get_authmode (client); + + if (enable) + mode = mode | BOLT_AUTH_ENABLED; + else + mode = mode & ~BOLT_AUTH_ENABLED; + + bolt_client_set_authmode_async (client, mode, NULL, on_authmode_ready, panel); + + return TRUE; +} + +static void +on_device_entry_row_activated_cb (CcBoltPanel *panel, + GtkListBoxRow *row) +{ + g_autoptr(GPtrArray) parents = NULL; + CcBoltDeviceEntry *entry; + BoltDevice *device; + BoltDevice *iter; + const char *parent; + + if (!CC_IS_BOLT_DEVICE_ENTRY (row)) + return; + + entry = CC_BOLT_DEVICE_ENTRY (row); + device = cc_bolt_device_entry_get_device (entry); + + /* walk up the chain and collect all parents */ + parents = g_ptr_array_new_with_free_func (g_object_unref); + iter = device; + + parent = bolt_device_get_parent (iter); + while (parent != NULL) + { + g_autofree char *path = NULL; + CcBoltDeviceEntry *child; + BoltDevice *dev; + + path = bolt_gen_object_path (BOLT_DBUS_PATH_DEVICES, parent); + + /* NB: the host device is not a peripheral and thus not + * in the hash table; therefore when get a NULL back, we + * should have reached the end of the chain */ + child = g_hash_table_lookup (panel->devices, path); + if (!child) + break; + + dev = cc_bolt_device_entry_get_device (child); + g_ptr_array_add (parents, g_object_ref (dev)); + iter = dev; + + parent = bolt_device_get_parent (iter); + } + + cc_bolt_device_dialog_set_device (panel->device_dialog, device, parents); + + gtk_window_resize (GTK_WINDOW (panel->device_dialog), 1, 1); + gtk_widget_show (GTK_WIDGET (panel->device_dialog)); +} + +static gboolean +on_device_dialog_delete_event_cb (GtkWidget *widget, + GdkEvent *event, + CcBoltPanel *panel) +{ + CcBoltDeviceDialog *dialog; + + dialog = CC_BOLT_DEVICE_DIALOG (widget); + + cc_bolt_device_dialog_set_device (dialog, NULL, NULL); + gtk_widget_hide (widget); + + return TRUE; +} + +static void +on_device_entry_status_changed_cb (CcBoltDeviceEntry *entry, + BoltStatus new_status, + CcBoltPanel *panel) +{ + GtkListBox *from = NULL; + GtkListBox *to = NULL; + GtkWidget *p; + gboolean is_pending; + gboolean parent_pending; + + /* if we are doing some active work, then lets not change + * the list the entry is in; otherwise we might just hop + * from one box to the other and back again. + */ + if (new_status == BOLT_STATUS_CONNECTING || new_status == BOLT_STATUS_AUTHORIZING) + return; + + is_pending = bolt_status_is_pending (new_status); + + p = gtk_widget_get_parent (GTK_WIDGET (entry)); + parent_pending = (gpointer) p == panel->pending_list; + + /* */ + if (is_pending && !parent_pending) + { + from = panel->devices_list; + to = panel->pending_list; + } + else if (!is_pending && parent_pending) + { + from = panel->pending_list; + to = panel->devices_list; + } + + if (from && to) + cc_panel_list_box_migrate (panel, from, to, entry); +} + + +static void +on_notification_button_clicked_cb (GtkButton *button, + CcBoltPanel *panel) +{ + gtk_revealer_set_reveal_child (panel->notification_revealer, FALSE); +} + +/* polkit */ + +static void +on_permission_ready (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(CcBoltPanel) panel = user_data; + g_autoptr(GError) err = NULL; + GPermission *permission; + gboolean is_allowed; + const char *name; + + permission = polkit_permission_new_finish (res, &err); + panel->permission = permission; + + if (!panel->permission) + { + g_warning ("Could not get polkit permissions: %s", err->message); + return; + } + + g_signal_connect_object (permission, + "notify", + G_CALLBACK (on_permission_notify_cb), + panel, + G_CONNECT_AFTER); + + is_allowed = g_permission_get_allowed (permission); + gtk_widget_set_sensitive (GTK_WIDGET (panel->authmode_switch), is_allowed); + gtk_lock_button_set_permission (panel->lock_button, permission); + + name = gtk_stack_get_visible_child_name (panel->container); + + gtk_widget_set_visible (GTK_WIDGET (panel->headerbar_box), + bolt_streq (name, "devices-listing")); +} + +static void +on_permission_notify_cb (GPermission *permission, + GParamSpec *pspec, + CcBoltPanel *panel) +{ + gboolean is_allowed = g_permission_get_allowed (permission); + + gtk_widget_set_sensitive (GTK_WIDGET (panel->authmode_switch), is_allowed); +} + +static gint +device_entries_sort_by_recency_cb (GtkListBoxRow *a_row, + GtkListBoxRow *b_row, + gpointer user_data) +{ + CcBoltDeviceEntry *a_entry = CC_BOLT_DEVICE_ENTRY (a_row); + CcBoltDeviceEntry *b_entry = CC_BOLT_DEVICE_ENTRY (b_row); + BoltDevice *a = cc_bolt_device_entry_get_device (a_entry); + BoltDevice *b = cc_bolt_device_entry_get_device (b_entry); + BoltStatus status; + gint64 a_ts, b_ts; + gint64 score; + + a_ts = (gint64) bolt_device_get_timestamp (a); + b_ts = (gint64) bolt_device_get_timestamp (b); + + score = b_ts - a_ts; + + if (score != 0) + return score; + + status = bolt_device_get_status (a); + + if (bolt_status_is_connected (status)) + { + const char *a_path; + const char *b_path; + + a_path = bolt_device_get_syspath (a); + b_path = bolt_device_get_syspath (b); + + return g_strcmp0 (a_path, b_path); + } + else + { + const char *a_name; + const char *b_name; + + a_name = bolt_device_get_name (a); + b_name = bolt_device_get_name (b); + + return g_strcmp0 (a_name, b_name); + } + + return 0; +} + +static gint +device_entries_sort_by_syspath_cb (GtkListBoxRow *a_row, + GtkListBoxRow *b_row, + gpointer user_data) +{ + CcBoltDeviceEntry *a_entry = CC_BOLT_DEVICE_ENTRY (a_row); + CcBoltDeviceEntry *b_entry = CC_BOLT_DEVICE_ENTRY (b_row); + BoltDevice *a = cc_bolt_device_entry_get_device (a_entry); + BoltDevice *b = cc_bolt_device_entry_get_device (b_entry); + + const char *a_path; + const char *b_path; + + a_path = bolt_device_get_syspath (a); + b_path = bolt_device_get_syspath (b); + + return g_strcmp0 (a_path, b_path); +} + +/* GObject overrides */ + +static void +cc_bolt_panel_finalize (GObject *object) +{ + CcBoltPanel *panel = CC_BOLT_PANEL (object); + + g_clear_object (&panel->client); + g_clear_pointer (&panel->devices, g_hash_table_unref); + g_clear_object (&panel->permission); + + G_OBJECT_CLASS (cc_bolt_panel_parent_class)->finalize (object); +} + +static void +cc_bolt_panel_dispose (GObject *object) +{ + CcBoltPanel *panel = CC_BOLT_PANEL (object); + + /* Must be destroyed in dispose, not finalize. */ + g_clear_pointer ((GtkWidget **) &panel->device_dialog, gtk_widget_destroy); + + G_OBJECT_CLASS (cc_bolt_panel_parent_class)->dispose (object); +} + +static void +cc_bolt_panel_constructed (GObject *object) +{ + CcBoltPanel *panel = CC_BOLT_PANEL (object); + GtkWindow *parent; + CcShell *shell; + + parent = GTK_WINDOW (cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (panel)))); + gtk_window_set_transient_for (GTK_WINDOW (panel->device_dialog), parent); + + G_OBJECT_CLASS (cc_bolt_panel_parent_class)->constructed (object); + + shell = cc_panel_get_shell (CC_PANEL (panel)); + cc_shell_embed_widget_in_header (shell, GTK_WIDGET (panel->headerbar_box), GTK_POS_RIGHT); +} + +static void +cc_bolt_panel_class_init (CcBoltPanelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->constructed = cc_bolt_panel_constructed; + object_class->dispose = cc_bolt_panel_dispose; + object_class->finalize = cc_bolt_panel_finalize; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/thunderbolt/cc-bolt-panel.ui"); + + gtk_widget_class_bind_template_child (widget_class, CcBoltPanel, authmode_mode); + gtk_widget_class_bind_template_child (widget_class, CcBoltPanel, authmode_spinner); + gtk_widget_class_bind_template_child (widget_class, CcBoltPanel, authmode_switch); + gtk_widget_class_bind_template_child (widget_class, CcBoltPanel, container); + gtk_widget_class_bind_template_child (widget_class, CcBoltPanel, devices_list); + gtk_widget_class_bind_template_child (widget_class, CcBoltPanel, devices_box); + gtk_widget_class_bind_template_child (widget_class, CcBoltPanel, devices_stack); + gtk_widget_class_bind_template_child (widget_class, CcBoltPanel, headerbar_box); + gtk_widget_class_bind_template_child (widget_class, CcBoltPanel, lock_button); + gtk_widget_class_bind_template_child (widget_class, CcBoltPanel, notb_caption); + gtk_widget_class_bind_template_child (widget_class, CcBoltPanel, notb_details); + gtk_widget_class_bind_template_child (widget_class, CcBoltPanel, notification_label); + gtk_widget_class_bind_template_child (widget_class, CcBoltPanel, notification_revealer); + gtk_widget_class_bind_template_child (widget_class, CcBoltPanel, pending_box); + gtk_widget_class_bind_template_child (widget_class, CcBoltPanel, pending_list); + + gtk_widget_class_bind_template_callback (widget_class, on_notification_button_clicked_cb); + gtk_widget_class_bind_template_callback (widget_class, on_authmode_state_set_cb); + gtk_widget_class_bind_template_callback (widget_class, on_device_entry_row_activated_cb); +} + +static void +cc_bolt_panel_init (CcBoltPanel *panel) +{ + g_resources_register (cc_thunderbolt_get_resource ()); + + gtk_widget_init_template (GTK_WIDGET (panel)); + + gtk_stack_set_visible_child_name (panel->container, "loading"); + + gtk_list_box_set_header_func (panel->devices_list, + cc_list_box_update_header_func, + NULL, + NULL); + + gtk_list_box_set_header_func (panel->pending_list, + cc_list_box_update_header_func, + NULL, + NULL); + + gtk_list_box_set_sort_func (panel->devices_list, + device_entries_sort_by_recency_cb, + panel, + NULL); + + gtk_list_box_set_sort_func (panel->pending_list, + device_entries_sort_by_syspath_cb, + panel, + NULL); + + panel->devices = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + + panel->device_dialog = cc_bolt_device_dialog_new (); + g_signal_connect_object (panel->device_dialog, + "delete-event", + G_CALLBACK (on_device_dialog_delete_event_cb), + panel, 0); + + bolt_client_new_async (cc_panel_get_cancellable (CC_PANEL (panel)), bolt_client_ready, g_object_ref (panel)); + +} |