gnome-control-center/panels/thunderbolt/cc-bolt-panel.c
Christian Kellner 05f2c5ef7c thunderbolt: react to security level changes
Listen for security level changes, which in theory should not really
happen at all with the exception that on hardware where force power
is not support and boltd has not yet seen the thunderbolt controller
because it was powered down. Then we should get an initial change
from 'unknown' to the real level. Handle it in the same way that
as if boltd was restarted, i.e. the dbus name owner changed
2019-01-30 13:54:14 +00:00

967 lines
29 KiB
C

/* 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-str.h"
#include "cc-bolt-panel.h"
#include "cc-thunderbolt-resources.h"
struct _CcBoltPanel
{
CcPanel parent;
BoltClient *client;
GCancellable *cancel;
/* 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, panel->cancel, &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);
path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (dev));
/* add to the list box */
gtk_widget_show_all (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_all (GTK_WIDGET (panel->pending_list));
gtk_widget_show_all (GTK_WIDGET (panel->pending_box));
}
else
{
gtk_container_add (GTK_CONTAINER (panel->devices_list), GTK_WIDGET (entry));
gtk_widget_show_all (GTK_WIDGET (panel->devices_list));
gtk_widget_show_all (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);
}
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_all (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,
panel->cancel,
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, panel->cancel, &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;
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)
{
CcBoltDeviceEntry *entry;
BoltDevice *device;
if (!CC_IS_BOLT_DEVICE_ENTRY (row))
return;
entry = CC_BOLT_DEVICE_ENTRY (row);
device = cc_bolt_device_entry_get_device (entry);
cc_bolt_device_dialog_set_device (panel->device_dialog, device);
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);
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);
/* cancel any ongoing operation */
g_cancellable_cancel (panel->cancel);
/* 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));
}
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);
panel->cancel = g_cancellable_new ();
bolt_client_new_async (panel->cancel, bolt_client_ready, g_object_ref (panel));
}