From 5aac29f877ba110ec2dc84cf1b88e9b1b08214cf Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Thu, 1 Aug 2019 17:51:53 -0400 Subject: [PATCH] import gnome-control-center-3.28.2-4.el8 --- .gitignore | 1 + .gnome-control-center.metadata | 1 + ...ettings-widget-for-gnome-remote-desk.patch | 383 + .../0001-shell-Don-t-set-per-panel-icon.patch | 47 + ...t-your-settings-button-sensitivity-o.patch | 72 + ...on-name-helper-returns-symbolic-name.patch | 78 + ...bolt-new-panel-for-device-management.patch | 6388 +++++++++++++++++ ...thunderbolt-move-to-the-Devices-page.patch | 51 + SOURCES/distro-logo.patch | 99 + SPECS/gnome-control-center.spec | 228 + 10 files changed, 7348 insertions(+) create mode 100644 .gitignore create mode 100644 .gnome-control-center.metadata create mode 100644 SOURCES/0001-sharing-Enable-settings-widget-for-gnome-remote-desk.patch create mode 100644 SOURCES/0001-shell-Don-t-set-per-panel-icon.patch create mode 100644 SOURCES/0001-wacom-Update-Test-your-settings-button-sensitivity-o.patch create mode 100644 SOURCES/0002-shell-Icon-name-helper-returns-symbolic-name.patch create mode 100644 SOURCES/0003-thunderbolt-new-panel-for-device-management.patch create mode 100644 SOURCES/0004-thunderbolt-move-to-the-Devices-page.patch create mode 100644 SOURCES/distro-logo.patch create mode 100644 SPECS/gnome-control-center.spec diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d1284af --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/gnome-control-center-3.28.2.tar.xz diff --git a/.gnome-control-center.metadata b/.gnome-control-center.metadata new file mode 100644 index 0000000..a7456aa --- /dev/null +++ b/.gnome-control-center.metadata @@ -0,0 +1 @@ +68f77d7fd2921025a65d0b0904e6db018ca7c1d0 SOURCES/gnome-control-center-3.28.2.tar.xz diff --git a/SOURCES/0001-sharing-Enable-settings-widget-for-gnome-remote-desk.patch b/SOURCES/0001-sharing-Enable-settings-widget-for-gnome-remote-desk.patch new file mode 100644 index 0000000..3d64836 --- /dev/null +++ b/SOURCES/0001-sharing-Enable-settings-widget-for-gnome-remote-desk.patch @@ -0,0 +1,383 @@ +From f135d985e80c85e1578cd60eeb79bd974788031f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 14 Feb 2018 20:52:37 +0800 +Subject: [PATCH] sharing: Enable settings widget for gnome-remote-desktop + +Enable support for manipulating GNOME Remote Desktop settings. Settings +are done via the org.gnome.desktop.remote-desktop.vnc schema. +Configuring the VNC password is done via libsecret, thus libsecret is +added as a dependency. +--- + meson.build | 1 + + panels/sharing/cc-gnome-remote-desktop.c | 171 +++++++++++++++++++++++ + panels/sharing/cc-gnome-remote-desktop.h | 49 +++++++ + panels/sharing/cc-sharing-panel.c | 62 +++++++- + panels/sharing/meson.build | 3 +- + 5 files changed, 282 insertions(+), 4 deletions(-) + create mode 100644 panels/sharing/cc-gnome-remote-desktop.c + create mode 100644 panels/sharing/cc-gnome-remote-desktop.h + +diff --git a/meson.build b/meson.build +index 84e04334c..3017b180a 100644 +--- a/meson.build ++++ b/meson.build +@@ -109,6 +109,7 @@ pulse_mainloop_dep = dependency('libpulse-mainloop-glib', version: pulse_req_ver + upower_glib_dep = dependency('upower-glib', version: '>= 0.99.6') + x11_dep = dependency('x11') + xi_dep = dependency('xi', version: '>= 1.2') ++libsecret_dep = dependency('libsecret-1') + + m_dep = cc.find_library('m') + +diff --git a/panels/sharing/cc-gnome-remote-desktop.c b/panels/sharing/cc-gnome-remote-desktop.c +new file mode 100644 +index 000000000..8420fddca +--- /dev/null ++++ b/panels/sharing/cc-gnome-remote-desktop.c +@@ -0,0 +1,171 @@ ++/* ++ * 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ */ ++ ++#include "config.h" ++ ++#include "cc-gnome-remote-desktop.h" ++ ++const SecretSchema * ++cc_grd_vnc_password_get_schema (void) ++{ ++ static const SecretSchema grd_vnc_password_schema = { ++ .name = "org.gnome.RemoteDesktop.VncPassword", ++ .flags = SECRET_SCHEMA_NONE, ++ .attributes = { ++ { "password", SECRET_SCHEMA_ATTRIBUTE_STRING }, ++ { "NULL", 0 }, ++ }, ++ }; ++ ++ return &grd_vnc_password_schema; ++} ++ ++gboolean ++cc_grd_get_is_auth_method_prompt (GValue *value, ++ GVariant *variant, ++ gpointer user_data) ++{ ++ const char * auth_method; ++ ++ auth_method = g_variant_get_string (variant, NULL); ++ ++ if (g_strcmp0 (auth_method, "prompt") == 0) ++ { ++ g_value_set_boolean (value, TRUE); ++ } ++ else if (g_strcmp0 (auth_method, "password") == 0) ++ { ++ g_value_set_boolean (value, FALSE); ++ } ++ else ++ { ++ g_warning ("Unhandled VNC auth method %s", auth_method); ++ g_value_set_boolean (value, FALSE); ++ } ++ ++ return TRUE; ++} ++ ++GVariant * ++cc_grd_set_is_auth_method_prompt (const GValue *value, ++ const GVariantType *type, ++ gpointer user_data) ++{ ++ char *auth_method; ++ ++ if (g_value_get_boolean (value)) ++ auth_method = "prompt"; ++ else ++ auth_method = "password"; ++ ++ return g_variant_new_string (auth_method); ++} ++ ++gboolean ++cc_grd_get_is_auth_method_password (GValue *value, ++ GVariant *variant, ++ gpointer user_data) ++{ ++ const char *auth_method; ++ ++ auth_method = g_variant_get_string (variant, NULL); ++ ++ if (g_strcmp0 (auth_method, "prompt") == 0) ++ { ++ g_value_set_boolean (value, FALSE); ++ } ++ else if (g_strcmp0 (auth_method, "password") == 0) ++ { ++ g_value_set_boolean (value, TRUE); ++ } ++ else ++ { ++ g_warning ("Unhandled VNC auth method %s", auth_method); ++ g_value_set_boolean (value, FALSE); ++ } ++ ++ return TRUE; ++} ++ ++GVariant * ++cc_grd_set_is_auth_method_password (const GValue *value, ++ const GVariantType *type, ++ gpointer user_data) ++{ ++ char *auth_method; ++ ++ if (g_value_get_boolean (value)) ++ auth_method = "password"; ++ else ++ auth_method = "prompt"; ++ ++ return g_variant_new_string (auth_method); ++} ++ ++static void ++on_password_stored (GObject *source, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ GtkEntry *entry = GTK_ENTRY (user_data); ++ GError *error = NULL; ++ ++ if (!secret_password_store_finish (result, &error)) ++ { ++ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) ++ { ++ g_warning ("Failed to store VNC password: %s", error->message); ++ g_object_set_data (G_OBJECT (entry), ++ "vnc-password-cancellable", NULL); ++ } ++ g_error_free (error); ++ } ++ else ++ { ++ g_object_set_data (G_OBJECT (entry), ++ "vnc-password-cancellable", NULL); ++ } ++} ++ ++void ++cc_grd_on_vnc_password_entry_notify_text (GtkEntry *entry, ++ GParamSpec *pspec, ++ gpointer user_data) ++{ ++ GCancellable *cancellable; ++ const char *password; ++ ++ cancellable = g_object_get_data (G_OBJECT (entry), "vnc-password-cancellable"); ++ if (cancellable) ++ g_cancellable_cancel (cancellable); ++ ++ cancellable = g_cancellable_new (); ++ g_object_set_data_full (G_OBJECT (entry), ++ "vnc-password-cancellable", ++ cancellable, g_object_unref); ++ ++ password = gtk_entry_get_text (entry); ++ ++ secret_password_store (CC_GRD_VNC_PASSWORD_SCHEMA, ++ SECRET_COLLECTION_DEFAULT, ++ "GNOME Remote Desktop VNC password", ++ password, ++ cancellable, on_password_stored, entry, ++ NULL); ++} +diff --git a/panels/sharing/cc-gnome-remote-desktop.h b/panels/sharing/cc-gnome-remote-desktop.h +new file mode 100644 +index 000000000..2a4819986 +--- /dev/null ++++ b/panels/sharing/cc-gnome-remote-desktop.h +@@ -0,0 +1,49 @@ ++/* ++ * 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ */ ++ ++#ifndef CC_GNOME_REMOTE_DESKTOP_H ++#define CC_GNOME_REMOTE_DESKTOP_H ++ ++#include ++#include ++ ++const SecretSchema * cc_grd_vnc_password_get_schema (void); ++#define CC_GRD_VNC_PASSWORD_SCHEMA cc_grd_vnc_password_get_schema () ++ ++gboolean cc_grd_get_is_auth_method_prompt (GValue *value, ++ GVariant *variant, ++ gpointer user_data); ++ ++GVariant * cc_grd_set_is_auth_method_prompt (const GValue *value, ++ const GVariantType *type, ++ gpointer user_data); ++ ++gboolean cc_grd_get_is_auth_method_password (GValue *value, ++ GVariant *variant, ++ gpointer user_data); ++ ++GVariant * cc_grd_set_is_auth_method_password (const GValue *value, ++ const GVariantType *type, ++ gpointer user_data); ++ ++void cc_grd_on_vnc_password_entry_notify_text (GtkEntry *entry, ++ GParamSpec *pspec, ++ gpointer user_data); ++ ++#endif /* CC_GNOME_REMOTE_DESKTOP_H */ +diff --git a/panels/sharing/cc-sharing-panel.c b/panels/sharing/cc-sharing-panel.c +index 8b35c9a31..adcbcdc86 100644 +--- a/panels/sharing/cc-sharing-panel.c ++++ b/panels/sharing/cc-sharing-panel.c +@@ -30,6 +30,7 @@ + #include "cc-media-sharing.h" + #include "cc-sharing-networks.h" + #include "cc-sharing-switch.h" ++#include "cc-gnome-remote-desktop.h" + #include "org.gnome.SettingsDaemon.Sharing.h" + + #ifdef GDK_WINDOWING_WAYLAND +@@ -66,6 +67,13 @@ _gtk_builder_get_widget (GtkBuilder *builder, + #define VINO_SCHEMA_ID "org.gnome.Vino" + #define FILE_SHARING_SCHEMA_ID "org.gnome.desktop.file-sharing" + #define GNOME_REMOTE_DESKTOP_SCHEMA_ID "org.gnome.desktop.remote-desktop" ++#define GNOME_REMOTE_DESKTOP_VNC_SCHEMA_ID "org.gnome.desktop.remote-desktop.vnc" ++ ++typedef enum ++{ ++ GRD_VNC_AUTH_METHOD_PROMPT, ++ GRD_VNC_AUTH_METHOD_PASSWORD ++} GrdVncAuthMethod; + + struct _CcSharingPanelPrivate + { +@@ -1077,11 +1085,56 @@ static void + cc_sharing_panel_setup_screen_sharing_dialog_gnome_remote_desktop (CcSharingPanel *self) + { + CcSharingPanelPrivate *priv = self->priv; +- GtkWidget *networks, *w; ++ GSettings *vnc_settings; ++ GtkWidget *networks, *box, *w; ++ ++ cc_sharing_panel_bind_switch_to_widgets (WID ("require-password-radiobutton"), ++ WID ("password-grid"), ++ NULL); ++ ++ cc_sharing_panel_setup_label_with_hostname (self, ++ WID ("screen-sharing-label")); ++ ++ g_object_bind_property (WID ("show-password-checkbutton"), "active", ++ WID ("remote-control-password-entry"), "visibility", ++ G_BINDING_SYNC_CREATE); ++ ++ /* make sure the password entry is hidden by default */ ++ g_signal_connect (priv->screen_sharing_dialog, "show", ++ G_CALLBACK (screen_sharing_show_cb), self); ++ ++ g_signal_connect (priv->screen_sharing_dialog, "hide", ++ G_CALLBACK (screen_sharing_hide_cb), self); ++ ++ /* accept at most 8 bytes in password entry */ ++ g_signal_connect (WID ("remote-control-password-entry"), "insert-text", ++ G_CALLBACK (screen_sharing_password_insert_text_cb), self); ++ ++ /* Bind settings to widgets */ ++ vnc_settings = g_settings_new (GNOME_REMOTE_DESKTOP_VNC_SCHEMA_ID); ++ g_settings_bind (vnc_settings, "view-only", ++ WID ("remote-control-checkbutton"), "active", ++ G_SETTINGS_BIND_DEFAULT | G_SETTINGS_BIND_INVERT_BOOLEAN); ++ g_settings_bind_with_mapping (vnc_settings, "auth-method", ++ WID ("approve-connections-radiobutton"), "active", ++ G_SETTINGS_BIND_DEFAULT, ++ cc_grd_get_is_auth_method_prompt, ++ cc_grd_set_is_auth_method_prompt, ++ NULL, NULL); ++ g_settings_bind_with_mapping (vnc_settings, "auth-method", ++ WID ("require-password-radiobutton"), "active", ++ G_SETTINGS_BIND_DEFAULT, ++ cc_grd_get_is_auth_method_password, ++ cc_grd_set_is_auth_method_password, ++ NULL, NULL); ++ g_signal_connect (WID ("remote-control-password-entry"), ++ "notify::text", ++ G_CALLBACK (cc_grd_on_vnc_password_entry_notify_text), ++ self); + + networks = cc_sharing_networks_new (self->priv->sharing_proxy, "gnome-remote-desktop"); +- gtk_widget_hide (WID ("remote-control-box")); +- gtk_grid_attach (GTK_GRID (WID ("grid3")), networks, 0, 1, 2, 1); ++ box = WID ("remote-control-box"); ++ gtk_box_pack_end (GTK_BOX (box), networks, TRUE, TRUE, 0); + gtk_widget_show (networks); + + w = cc_sharing_switch_new (networks); +@@ -1116,6 +1169,9 @@ check_remote_desktop_available (CcSharingPanel *self) + if (!cc_sharing_panel_check_schema_available (self, GNOME_REMOTE_DESKTOP_SCHEMA_ID)) + return; + ++ if (!cc_sharing_panel_check_schema_available (self, GNOME_REMOTE_DESKTOP_VNC_SCHEMA_ID)) ++ return; ++ + priv->remote_desktop_name_watch = g_bus_watch_name (G_BUS_TYPE_SESSION, + "org.gnome.Mutter.RemoteDesktop", + G_BUS_NAME_WATCHER_FLAGS_NONE, +diff --git a/panels/sharing/meson.build b/panels/sharing/meson.build +index 5caac36c0..1565a089a 100644 +--- a/panels/sharing/meson.build ++++ b/panels/sharing/meson.build +@@ -43,6 +43,7 @@ sources = files( + 'cc-remote-login.c', + 'cc-sharing-networks.c', + 'cc-sharing-switch.c', ++ 'cc-gnome-remote-desktop.c', + 'file-share-properties.c', + 'vino-preferences.c' + ) +@@ -79,7 +80,7 @@ panels_libs += static_library( + cappletname, + sources: sources, + include_directories: top_inc, +- dependencies: common_deps, ++ dependencies: [common_deps, libsecret_dep], + c_args: cflags + ) + +-- +2.17.1 + diff --git a/SOURCES/0001-shell-Don-t-set-per-panel-icon.patch b/SOURCES/0001-shell-Don-t-set-per-panel-icon.patch new file mode 100644 index 0000000..ff593da --- /dev/null +++ b/SOURCES/0001-shell-Don-t-set-per-panel-icon.patch @@ -0,0 +1,47 @@ +From ec695fae92ef7470ef05211160e431f5c3486299 Mon Sep 17 00:00:00 2001 +From: Christian Kellner +Date: Tue, 10 Apr 2018 09:43:22 +0200 +Subject: [PATCH 1/4] shell: Don't set per-panel icon + +The control center app is considered one single application with +a single icon to represent it. Therefore get rid of per-panel +icons. +--- + shell/cc-window.c | 7 +------ + 1 file changed, 1 insertion(+), 6 deletions(-) + +diff --git a/shell/cc-window.c b/shell/cc-window.c +index 557819e0c76c..33f1ddcad511 100644 +--- a/shell/cc-window.c ++++ b/shell/cc-window.c +@@ -118,7 +118,6 @@ activate_panel (CcWindow *self, + GIcon *gicon) + { + GtkWidget *box, *title_widget; +- const gchar *icon_name; + + if (!id) + return FALSE; +@@ -144,12 +143,8 @@ activate_panel (CcWindow *self, + gtk_stack_set_visible_child_name (GTK_STACK (self->stack), id); + + /* set the title of the window */ +- icon_name = get_icon_name_from_g_icon (gicon); +- + gtk_window_set_role (GTK_WINDOW (self), id); + gtk_header_bar_set_title (GTK_HEADER_BAR (self->panel_headerbar), name); +- gtk_window_set_default_icon_name (icon_name); +- gtk_window_set_icon_name (GTK_WINDOW (self), icon_name); + + title_widget = cc_panel_get_title_widget (CC_PANEL (self->current_panel)); + gtk_header_bar_set_custom_title (GTK_HEADER_BAR (self->panel_headerbar), title_widget); +@@ -778,4 +773,4 @@ cc_window_set_search_item (CcWindow *center, + gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (center->search_bar), TRUE); + gtk_entry_set_text (GTK_ENTRY (center->search_entry), search); + gtk_editable_set_position (GTK_EDITABLE (center->search_entry), -1); +-} +\ No newline at end of file ++} +-- +2.17.0 + diff --git a/SOURCES/0001-wacom-Update-Test-your-settings-button-sensitivity-o.patch b/SOURCES/0001-wacom-Update-Test-your-settings-button-sensitivity-o.patch new file mode 100644 index 0000000..9a681a1 --- /dev/null +++ b/SOURCES/0001-wacom-Update-Test-your-settings-button-sensitivity-o.patch @@ -0,0 +1,72 @@ +From 7a4532ff72a74ce74dee4b96b993ecd11f557acc Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Mon, 11 Feb 2019 20:48:23 +0100 +Subject: [PATCH] wacom: Update "Test your settings" button sensitivity on + device availability + +The button/popover are meaningless if there's no device to test with. Set +it inactive (so the popover hides if visible) and set insensitive if no +devices are found. + +https://gitlab.gnome.org/GNOME/gnome-control-center/merge_requests/390 +--- + panels/wacom/cc-wacom-panel.c | 22 ++++++++++++++++++++++ + 1 file changed, 22 insertions(+) + +diff --git a/panels/wacom/cc-wacom-panel.c b/panels/wacom/cc-wacom-panel.c +index 77a1e261f..e4f3ca7fc 100644 +--- a/panels/wacom/cc-wacom-panel.c ++++ b/panels/wacom/cc-wacom-panel.c +@@ -53,6 +53,7 @@ struct _CcWacomPanelPrivate + GtkWidget *stylus_notebook; + GtkWidget *test_popover; + GtkWidget *test_draw_area; ++ GtkWidget *test_button; + GHashTable *devices; /* key=GsdDevice, value=CcWacomDevice */ + GHashTable *pages; /* key=device name, value=GtkWidget */ + GHashTable *stylus_pages; /* key=CcWacomTool, value=GtkWidget */ +@@ -309,6 +310,22 @@ add_stylus (CcWacomPanel *self, + return TRUE; + } + ++static void ++update_test_button (CcWacomPanel *self) ++{ ++ CcWacomPanelPrivate *priv = self->priv;; ++ ++ if (!priv->test_button) ++ return; ++ ++ if (g_hash_table_size (priv->devices) == 0) { ++ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->test_button), FALSE); ++ gtk_widget_set_sensitive (priv->test_button, FALSE); ++ } else { ++ gtk_widget_set_sensitive (priv->test_button, TRUE); ++ } ++} ++ + static void + update_current_tool (CcWacomPanel *panel, + GdkDevice *device, +@@ -422,6 +439,9 @@ cc_wacom_panel_constructed (GObject *object) + + g_signal_connect_object (shell, "event", + G_CALLBACK (on_shell_event_cb), self, 0); ++ ++ priv->test_button = button; ++ update_test_button (self); + } + + static const char * +@@ -561,6 +581,8 @@ update_current_page (CcWacomPanel *self, + if (num_pages > 1) + gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->tablet_notebook), 1); + } ++ ++ update_test_button (self); + } + + static void +-- +2.20.1 + diff --git a/SOURCES/0002-shell-Icon-name-helper-returns-symbolic-name.patch b/SOURCES/0002-shell-Icon-name-helper-returns-symbolic-name.patch new file mode 100644 index 0000000..d9aa722 --- /dev/null +++ b/SOURCES/0002-shell-Icon-name-helper-returns-symbolic-name.patch @@ -0,0 +1,78 @@ +From b24a8e9aa82b64de970d8137181bf8a03b6f724a Mon Sep 17 00:00:00 2001 +From: Christian Kellner +Date: Tue, 10 Apr 2018 09:47:48 +0200 +Subject: [PATCH 2/4] shell: Icon name helper returns symbolic name + +The helper function to get the icon name from a GIcon directly +returns the symbolic icon now. This makes it in turn possible +to also directly check if the theme has the icon with the symbolic +name instead of checking of for the full colored one and then +deriving the symbolic name from that. The latter (old) practice +will fail if there is a symbolic icon in the theme that has no +full color icon (like e.g. thunderbolt). +--- + shell/cc-window.c | 19 ++++++++++--------- + 1 file changed, 10 insertions(+), 9 deletions(-) + +diff --git a/shell/cc-window.c b/shell/cc-window.c +index 33f1ddcad511..3af9cf0bd9fc 100644 +--- a/shell/cc-window.c ++++ b/shell/cc-window.c +@@ -88,8 +88,8 @@ enum + }; + + /* Auxiliary methods */ +-static const gchar * +-get_icon_name_from_g_icon (GIcon *gicon) ++static gchar * ++get_symbolic_icon_name_from_g_icon (GIcon *gicon) + { + const gchar * const *names; + GtkIconTheme *icon_theme; +@@ -103,8 +103,11 @@ get_icon_name_from_g_icon (GIcon *gicon) + + for (i = 0; names[i] != NULL; i++) + { +- if (gtk_icon_theme_has_icon (icon_theme, names[i])) +- return names[i]; ++ g_autofree gchar *name = NULL; ++ name = g_strdup_printf ("%s-symbolic", names[i]); ++ ++ if (gtk_icon_theme_has_icon (icon_theme, name)) ++ return g_steal_pointer (&name); + } + + return NULL; +@@ -248,9 +251,8 @@ setup_model (CcWindow *shell) + g_autofree gchar *name = NULL; + g_autofree gchar *description = NULL; + g_autofree gchar *id = NULL; +- g_autofree gchar *symbolic_icon = NULL; ++ g_autofree gchar *icon_name = NULL; + g_autofree GStrv keywords = NULL; +- const gchar *icon_name; + + gtk_tree_model_get (model, &iter, + COL_CATEGORY, &category, +@@ -261,8 +263,7 @@ setup_model (CcWindow *shell) + COL_KEYWORDS, &keywords, + -1); + +- icon_name = get_icon_name_from_g_icon (icon); +- symbolic_icon = g_strdup_printf ("%s-symbolic", icon_name); ++ icon_name = get_symbolic_icon_name_from_g_icon (icon); + + cc_panel_list_add_panel (CC_PANEL_LIST (shell->panel_list), + category, +@@ -270,7 +271,7 @@ setup_model (CcWindow *shell) + name, + description, + keywords, +- symbolic_icon); ++ icon_name); + + valid = gtk_tree_model_iter_next (model, &iter); + } +-- +2.17.0 + diff --git a/SOURCES/0003-thunderbolt-new-panel-for-device-management.patch b/SOURCES/0003-thunderbolt-new-panel-for-device-management.patch new file mode 100644 index 0000000..7aea82c --- /dev/null +++ b/SOURCES/0003-thunderbolt-new-panel-for-device-management.patch @@ -0,0 +1,6388 @@ +From 3d9ad5e5657059e054f011d65e3f81b3723b41a5 Mon Sep 17 00:00:00 2001 +From: Christian Kellner +Date: Mon, 26 Mar 2018 16:18:30 +0200 +Subject: [PATCH 3/4] thunderbolt: new panel for device management + +Thunderbolt devices need to be approved before they can be used. +This is done via the boltd system daemon and gnome-shell. The new +panel enables the user to manage thunderbolt devices, i.e.: + + - forget devices that have previously been authorized + - authorize currently unauthorize devices + +Additionally authorization of devices an be temporarily disabled +to ensure no evil device will gain access to the computers +resources. + +File starting with "bolt-" are copied from bolt's source tree +and currently correspond to the bolt upstream commit with the id +f22b1cd6104bdc2b33a95d9896b50f29a141b8d8 +They can be updated from bolt via the update-from-bolt.sh script. +--- + meson.build | 3 + + panels/meson.build | 1 + + panels/thunderbolt/bolt-client.c | 697 +++++++++++++ + panels/thunderbolt/bolt-client.h | 107 ++ + panels/thunderbolt/bolt-device.c | 604 +++++++++++ + panels/thunderbolt/bolt-device.h | 87 ++ + panels/thunderbolt/bolt-enums.c | 395 ++++++++ + panels/thunderbolt/bolt-enums.h | 249 +++++ + panels/thunderbolt/bolt-error.c | 99 ++ + panels/thunderbolt/bolt-error.h | 55 + + panels/thunderbolt/bolt-names.h | 50 + + panels/thunderbolt/bolt-proxy.c | 514 ++++++++++ + panels/thunderbolt/bolt-proxy.h | 97 ++ + panels/thunderbolt/bolt-str.c | 117 +++ + panels/thunderbolt/bolt-str.h | 43 + + panels/thunderbolt/bolt-time.c | 44 + + panels/thunderbolt/bolt-time.h | 32 + + panels/thunderbolt/cc-bolt-device-dialog.c | 476 +++++++++ + panels/thunderbolt/cc-bolt-device-dialog.h | 45 + + panels/thunderbolt/cc-bolt-device-dialog.ui | 359 +++++++ + panels/thunderbolt/cc-bolt-device-entry.c | 218 ++++ + panels/thunderbolt/cc-bolt-device-entry.h | 34 + + panels/thunderbolt/cc-bolt-device-entry.ui | 49 + + panels/thunderbolt/cc-bolt-panel.c | 958 ++++++++++++++++++ + panels/thunderbolt/cc-bolt-panel.ui | 594 +++++++++++ + .../gnome-thunderbolt-panel.desktop.in.in | 17 + + panels/thunderbolt/meson.build | 74 ++ + panels/thunderbolt/thunderbolt.gresource.xml | 9 + + panels/thunderbolt/update-from-bolt.sh | 50 + + shell/cc-panel-list.c | 1 + + shell/cc-panel-loader.c | 6 + + 31 files changed, 6084 insertions(+) + create mode 100644 panels/thunderbolt/bolt-client.c + create mode 100644 panels/thunderbolt/bolt-client.h + create mode 100644 panels/thunderbolt/bolt-device.c + create mode 100644 panels/thunderbolt/bolt-device.h + create mode 100644 panels/thunderbolt/bolt-enums.c + create mode 100644 panels/thunderbolt/bolt-enums.h + create mode 100644 panels/thunderbolt/bolt-error.c + create mode 100644 panels/thunderbolt/bolt-error.h + create mode 100644 panels/thunderbolt/bolt-names.h + create mode 100644 panels/thunderbolt/bolt-proxy.c + create mode 100644 panels/thunderbolt/bolt-proxy.h + create mode 100644 panels/thunderbolt/bolt-str.c + create mode 100644 panels/thunderbolt/bolt-str.h + create mode 100644 panels/thunderbolt/bolt-time.c + create mode 100644 panels/thunderbolt/bolt-time.h + create mode 100644 panels/thunderbolt/cc-bolt-device-dialog.c + create mode 100644 panels/thunderbolt/cc-bolt-device-dialog.h + create mode 100644 panels/thunderbolt/cc-bolt-device-dialog.ui + create mode 100644 panels/thunderbolt/cc-bolt-device-entry.c + create mode 100644 panels/thunderbolt/cc-bolt-device-entry.h + create mode 100644 panels/thunderbolt/cc-bolt-device-entry.ui + create mode 100644 panels/thunderbolt/cc-bolt-panel.c + create mode 100644 panels/thunderbolt/cc-bolt-panel.ui + create mode 100644 panels/thunderbolt/gnome-thunderbolt-panel.desktop.in.in + create mode 100644 panels/thunderbolt/meson.build + create mode 100644 panels/thunderbolt/thunderbolt.gresource.xml + create mode 100755 panels/thunderbolt/update-from-bolt.sh + +diff --git a/meson.build b/meson.build +index 90ee21cb0f39..ab0e91af627a 100644 +--- a/meson.build ++++ b/meson.build +@@ -203,6 +203,7 @@ if host_is_linux_not_s390 + description: 'Define to 1 if libwacom provides definition for 3D styli') + else + message('Bluetooth and Wacom panels will not be built (no USB support on this platform)') ++ message('Thunderbolt panel will not be built (not supported on this platform)') + endif + config_h.set('BUILD_BLUETOOTH', host_is_linux_not_s390, + description: 'Define to 1 to build the Bluetooth panel') +@@ -212,6 +213,8 @@ config_h.set('BUILD_WACOM', host_is_linux_not_s390, + description: 'Define to 1 to build the Wacom panel') + config_h.set('HAVE_WACOM', host_is_linux_not_s390, + description: 'Define to 1 if Wacom is supportted') ++config_h.set('BUILD_THUNDERBOLT', host_is_linux_not_s390, ++ description: 'Define to 1 to build the Thunderbolt panel') + + # Check for info panel + gnome_session_libexecdir = get_option('gnome_session_libexecdir') +diff --git a/panels/meson.build b/panels/meson.build +index d671c4775736..37a343642218 100644 +--- a/panels/meson.build ++++ b/panels/meson.build +@@ -28,6 +28,7 @@ endif + if host_is_linux_not_s390 + panels += [ + 'bluetooth', ++ 'thunderbolt', + 'wacom' + ] + endif +diff --git a/panels/thunderbolt/bolt-client.c b/panels/thunderbolt/bolt-client.c +new file mode 100644 +index 000000000000..0ebc360b18ff +--- /dev/null ++++ b/panels/thunderbolt/bolt-client.c +@@ -0,0 +1,697 @@ ++/* ++ * Copyright © 2017 Red Hat, Inc ++ * ++ * This program 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, see . ++ * ++ * Authors: ++ * Christian J. Kellner ++ */ ++ ++#include "bolt-client.h" ++ ++#include "bolt-device.h" ++#include "bolt-error.h" ++#include "bolt-names.h" ++ ++#include ++ ++static void handle_dbus_device_added (GObject *self, ++ GDBusProxy *bus_proxy, ++ GVariant *params); ++static void handle_dbus_device_removed (GObject *self, ++ GDBusProxy *bus_proxy, ++ GVariant *params); ++ ++struct _BoltClient ++{ ++ BoltProxy parent; ++}; ++ ++enum { ++ PROP_0, ++ ++ /* D-Bus Props */ ++ PROP_VERSION, ++ PROP_PROBING, ++ PROP_SECURITY, ++ PROP_AUTHMODE, ++ ++ PROP_LAST ++}; ++ ++static GParamSpec *props[PROP_LAST] = {NULL, }; ++ ++enum { ++ SIGNAL_DEVICE_ADDED, ++ SIGNAL_DEVICE_REMOVED, ++ SIGNAL_LAST ++}; ++ ++static guint signals[SIGNAL_LAST] = {0}; ++ ++ ++G_DEFINE_TYPE (BoltClient, ++ bolt_client, ++ BOLT_TYPE_PROXY); ++ ++ ++static void ++bolt_client_get_property (GObject *object, ++ guint prop_id, ++ GValue *value, ++ GParamSpec *pspec) ++{ ++ if (bolt_proxy_get_dbus_property (object, pspec, value)) ++ return; ++ ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++} ++ ++static const BoltProxySignal * ++bolt_client_get_dbus_signals (guint *n) ++{ ++ static BoltProxySignal dbus_signals[] = { ++ {"DeviceAdded", handle_dbus_device_added}, ++ {"DeviceRemoved", handle_dbus_device_removed}, ++ }; ++ ++ *n = G_N_ELEMENTS (dbus_signals); ++ ++ return dbus_signals; ++} ++ ++ ++static void ++bolt_client_class_init (BoltClientClass *klass) ++{ ++ GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ++ BoltProxyClass *proxy_class = BOLT_PROXY_CLASS (klass); ++ ++ gobject_class->get_property = bolt_client_get_property; ++ ++ proxy_class->get_dbus_signals = bolt_client_get_dbus_signals; ++ ++ props[PROP_VERSION] ++ = g_param_spec_uint ("version", ++ "Version", NULL, ++ 0, G_MAXUINT, 0, ++ G_PARAM_READABLE | ++ G_PARAM_STATIC_NAME); ++ ++ props[PROP_PROBING] ++ = g_param_spec_boolean ("probing", ++ "Probing", NULL, ++ FALSE, ++ G_PARAM_READABLE | ++ G_PARAM_STATIC_NAME); ++ ++ props[PROP_SECURITY] ++ = g_param_spec_enum ("security-level", ++ "SecurityLevel", NULL, ++ BOLT_TYPE_SECURITY, ++ BOLT_SECURITY_UNKNOWN, ++ G_PARAM_READABLE | ++ G_PARAM_STATIC_NAME); ++ ++ props[PROP_AUTHMODE] = ++ g_param_spec_flags ("auth-mode", "AuthMode", NULL, ++ BOLT_TYPE_AUTH_MODE, ++ BOLT_AUTH_ENABLED, ++ G_PARAM_READABLE | ++ G_PARAM_STATIC_STRINGS); ++ ++ g_object_class_install_properties (gobject_class, ++ PROP_LAST, ++ props); ++ ++ /* signals */ ++ signals[SIGNAL_DEVICE_ADDED] = ++ g_signal_new ("device-added", ++ G_TYPE_FROM_CLASS (gobject_class), ++ G_SIGNAL_RUN_LAST, ++ 0, ++ NULL, NULL, ++ NULL, ++ G_TYPE_NONE, ++ 1, G_TYPE_STRING); ++ ++ signals[SIGNAL_DEVICE_REMOVED] = ++ g_signal_new ("device-removed", ++ G_TYPE_FROM_CLASS (gobject_class), ++ G_SIGNAL_RUN_LAST, ++ 0, ++ NULL, NULL, ++ NULL, ++ G_TYPE_NONE, ++ 1, G_TYPE_STRING); ++} ++ ++ ++static void ++bolt_client_init (BoltClient *cli) ++{ ++} ++ ++/* dbus signals */ ++ ++static void ++handle_dbus_device_added (GObject *self, GDBusProxy *bus_proxy, GVariant *params) ++{ ++ BoltClient *cli = BOLT_CLIENT (self); ++ const char *opath = NULL; ++ ++ g_variant_get_child (params, 0, "&o", &opath); ++ g_signal_emit (cli, signals[SIGNAL_DEVICE_ADDED], 0, opath); ++} ++ ++static void ++handle_dbus_device_removed (GObject *self, GDBusProxy *bus_proxy, GVariant *params) ++{ ++ BoltClient *cli = BOLT_CLIENT (self); ++ const char *opath = NULL; ++ ++ g_variant_get_child (params, 0, "&o", &opath); ++ g_signal_emit (cli, signals[SIGNAL_DEVICE_REMOVED], 0, opath); ++} ++ ++/* public methods */ ++ ++BoltClient * ++bolt_client_new (GError **error) ++{ ++ BoltClient *cli; ++ GDBusConnection *bus; ++ ++ bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error); ++ if (bus == NULL) ++ { ++ g_prefix_error (error, "Error connecting to D-Bus: "); ++ return FALSE; ++ } ++ ++ cli = g_initable_new (BOLT_TYPE_CLIENT, ++ NULL, error, ++ "g-flags", G_DBUS_PROXY_FLAGS_NONE, ++ "g-connection", bus, ++ "g-name", BOLT_DBUS_NAME, ++ "g-object-path", BOLT_DBUS_PATH, ++ "g-interface-name", BOLT_DBUS_INTERFACE, ++ NULL); ++ ++ g_object_unref (bus); ++ ++ return cli; ++} ++ ++static void ++got_the_client (GObject *source, ++ GAsyncResult *res, ++ gpointer user_data) ++{ ++ g_autoptr(GError) error = NULL; ++ GTask *task = user_data; ++ GObject *obj; ++ ++ obj = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, &error); ++ ++ if (obj == NULL) ++ { ++ g_task_return_error (task, error); ++ return; ++ } ++ ++ g_task_return_pointer (task, obj, g_object_unref); ++ g_object_unref (task); ++} ++ ++static void ++got_the_bus (GObject *source, ++ GAsyncResult *res, ++ gpointer user_data) ++{ ++ g_autoptr(GError) error = NULL; ++ GTask *task = user_data; ++ GCancellable *cancellable; ++ GDBusConnection *bus; ++ ++ bus = g_bus_get_finish (res, &error); ++ if (bus == NULL) ++ { ++ g_prefix_error (&error, "could not connect to D-Bus: "); ++ g_task_return_error (task, error); ++ return; ++ } ++ ++ cancellable = g_task_get_cancellable (task); ++ g_async_initable_new_async (BOLT_TYPE_CLIENT, ++ G_PRIORITY_DEFAULT, ++ cancellable, ++ got_the_client, task, ++ "g-flags", G_DBUS_PROXY_FLAGS_NONE, ++ "g-connection", bus, ++ "g-name", BOLT_DBUS_NAME, ++ "g-object-path", BOLT_DBUS_PATH, ++ "g-interface-name", BOLT_DBUS_INTERFACE, ++ NULL); ++ g_object_unref (bus); ++} ++ ++void ++bolt_client_new_async (GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GTask *task; ++ ++ task = g_task_new (NULL, cancellable, callback, user_data); ++ g_bus_get (G_BUS_TYPE_SYSTEM, cancellable, got_the_bus, task); ++} ++ ++BoltClient * ++bolt_client_new_finish (GAsyncResult *res, ++ GError **error) ++{ ++ g_return_val_if_fail (G_IS_TASK (res), NULL); ++ ++ return g_task_propagate_pointer (G_TASK (res), error); ++} ++ ++GPtrArray * ++bolt_client_list_devices (BoltClient *client, ++ GCancellable *cancel, ++ GError **error) ++{ ++ g_autoptr(GVariant) val = NULL; ++ g_autoptr(GPtrArray) devices = NULL; ++ g_autoptr(GVariantIter) iter = NULL; ++ GDBusConnection *bus = NULL; ++ const char *d; ++ ++ g_return_val_if_fail (BOLT_IS_CLIENT (client), NULL); ++ ++ val = g_dbus_proxy_call_sync (G_DBUS_PROXY (client), ++ "ListDevices", ++ NULL, ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, ++ cancel, ++ error); ++ if (val == NULL) ++ return NULL; ++ ++ bus = g_dbus_proxy_get_connection (G_DBUS_PROXY (client)); ++ ++ devices = g_ptr_array_new_with_free_func (g_object_unref); ++ ++ g_variant_get (val, "(ao)", &iter); ++ while (g_variant_iter_loop (iter, "&o", &d, NULL)) ++ { ++ BoltDevice *dev; ++ ++ dev = bolt_device_new_for_object_path (bus, d, cancel, error); ++ if (dev == NULL) ++ return NULL; ++ ++ g_ptr_array_add (devices, dev); ++ } ++ ++ return g_steal_pointer (&devices); ++} ++ ++BoltDevice * ++bolt_client_get_device (BoltClient *client, ++ const char *uid, ++ GCancellable *cancel, ++ GError **error) ++{ ++ g_autoptr(GVariant) val = NULL; ++ g_autoptr(GError) err = NULL; ++ BoltDevice *dev = NULL; ++ GDBusConnection *bus = NULL; ++ const char *opath = NULL; ++ ++ g_return_val_if_fail (BOLT_IS_CLIENT (client), NULL); ++ ++ val = g_dbus_proxy_call_sync (G_DBUS_PROXY (client), ++ "DeviceByUid", ++ g_variant_new ("(s)", uid), ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, ++ cancel, ++ &err); ++ ++ if (val == NULL) ++ { ++ bolt_error_propagate_stripped (error, &err); ++ return NULL; ++ } ++ ++ bus = g_dbus_proxy_get_connection (G_DBUS_PROXY (client)); ++ g_variant_get (val, "(&o)", &opath); ++ ++ if (opath == NULL) ++ return NULL; ++ ++ dev = bolt_device_new_for_object_path (bus, opath, cancel, error); ++ return dev; ++} ++ ++BoltDevice * ++bolt_client_enroll_device (BoltClient *client, ++ const char *uid, ++ BoltPolicy policy, ++ BoltAuthCtrl flags, ++ GError **error) ++{ ++ g_autoptr(GVariant) val = NULL; ++ g_autoptr(GError) err = NULL; ++ g_autofree char *fstr = NULL; ++ BoltDevice *dev = NULL; ++ GDBusConnection *bus = NULL; ++ GVariant *params = NULL; ++ const char *opath = NULL; ++ const char *pstr; ++ ++ g_return_val_if_fail (BOLT_IS_CLIENT (client), NULL); ++ ++ pstr = bolt_enum_to_string (BOLT_TYPE_POLICY, policy, error); ++ if (pstr == NULL) ++ return NULL; ++ ++ fstr = bolt_flags_to_string (BOLT_TYPE_AUTH_CTRL, flags, error); ++ if (fstr == NULL) ++ return NULL; ++ ++ params = g_variant_new ("(sss)", uid, pstr, fstr); ++ val = g_dbus_proxy_call_sync (G_DBUS_PROXY (client), ++ "EnrollDevice", ++ params, ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, ++ NULL, ++ &err); ++ ++ if (val == NULL) ++ { ++ bolt_error_propagate_stripped (error, &err); ++ return NULL; ++ } ++ ++ bus = g_dbus_proxy_get_connection (G_DBUS_PROXY (client)); ++ g_variant_get (val, "(&o)", &opath); ++ ++ if (opath == NULL) ++ return NULL; ++ ++ dev = bolt_device_new_for_object_path (bus, opath, NULL, error); ++ return dev; ++} ++ ++void ++bolt_client_enroll_device_async (BoltClient *client, ++ const char *uid, ++ BoltPolicy policy, ++ BoltAuthCtrl flags, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ g_autofree char *fstr = NULL; ++ GError *err = NULL; ++ GVariant *params; ++ const char *pstr; ++ ++ g_return_if_fail (BOLT_IS_CLIENT (client)); ++ g_return_if_fail (uid != NULL); ++ ++ pstr = bolt_enum_to_string (BOLT_TYPE_POLICY, policy, &err); ++ if (pstr == NULL) ++ { ++ g_task_report_error (client, callback, user_data, NULL, err); ++ return; ++ } ++ ++ fstr = bolt_flags_to_string (BOLT_TYPE_AUTH_CTRL, flags, &err); ++ if (fstr == NULL) ++ { ++ g_task_report_error (client, callback, user_data, NULL, err); ++ return; ++ } ++ ++ params = g_variant_new ("(sss)", uid, pstr, fstr); ++ g_dbus_proxy_call (G_DBUS_PROXY (client), ++ "EnrollDevice", ++ params, ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, ++ cancellable, ++ callback, ++ user_data); ++} ++ ++gboolean ++bolt_client_enroll_device_finish (BoltClient *client, ++ GAsyncResult *res, ++ char **path, ++ GError **error) ++{ ++ GVariant *val = NULL; ++ ++ g_autoptr(GError) err = NULL; ++ ++ g_return_val_if_fail (BOLT_IS_CLIENT (client), FALSE); ++ ++ val = g_dbus_proxy_call_finish (G_DBUS_PROXY (client), res, &err); ++ if (val == NULL) ++ { ++ bolt_error_propagate_stripped (error, &err); ++ return FALSE; ++ } ++ ++ if (path != NULL) ++ g_variant_get (val, "(o)", path); ++ ++ return TRUE; ++} ++ ++gboolean ++bolt_client_forget_device (BoltClient *client, ++ const char *uid, ++ GError **error) ++{ ++ g_autoptr(GVariant) val = NULL; ++ g_autoptr(GError) err = NULL; ++ ++ g_return_val_if_fail (BOLT_IS_CLIENT (client), FALSE); ++ ++ val = g_dbus_proxy_call_sync (G_DBUS_PROXY (client), ++ "ForgetDevice", ++ g_variant_new ("(s)", uid), ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, ++ NULL, ++ &err); ++ ++ if (val == NULL) ++ { ++ bolt_error_propagate_stripped (error, &err); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++void ++bolt_client_forget_device_async (BoltClient *client, ++ const char *uid, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ g_return_if_fail (BOLT_IS_CLIENT (client)); ++ ++ g_dbus_proxy_call (G_DBUS_PROXY (client), ++ "ForgetDevice", ++ g_variant_new ("(s)", uid), ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, ++ cancellable, ++ callback, ++ user_data); ++} ++ ++gboolean ++bolt_client_forget_device_finish (BoltClient *client, ++ GAsyncResult *res, ++ GError **error) ++{ ++ g_autoptr(GVariant) val = NULL; ++ g_autoptr(GError) err = NULL; ++ ++ g_return_val_if_fail (BOLT_IS_CLIENT (client), FALSE); ++ ++ val = g_dbus_proxy_call_finish (G_DBUS_PROXY (client), res, &err); ++ if (val == NULL) ++ { ++ bolt_error_propagate_stripped (error, &err); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++/* getter */ ++guint ++bolt_client_get_version (BoltClient *client) ++{ ++ const char *key; ++ guint val = 0; ++ gboolean ok; ++ ++ g_return_val_if_fail (BOLT_IS_CLIENT (client), val); ++ ++ key = g_param_spec_get_name (props[PROP_VERSION]); ++ ok = bolt_proxy_get_property_uint32 (BOLT_PROXY (client), key, &val); ++ ++ if (!ok) ++ g_warning ("failed to get property '%s'", key); ++ ++ return val; ++} ++ ++gboolean ++bolt_client_is_probing (BoltClient *client) ++{ ++ const char *key; ++ gboolean val = FALSE; ++ gboolean ok; ++ ++ g_return_val_if_fail (BOLT_IS_CLIENT (client), val); ++ ++ key = g_param_spec_get_name (props[PROP_PROBING]); ++ ok = bolt_proxy_get_property_bool (BOLT_PROXY (client), key, &val); ++ ++ if (!ok) ++ g_warning ("failed to get enum property '%s'", key); ++ ++ return val; ++} ++ ++BoltSecurity ++bolt_client_get_security (BoltClient *client) ++{ ++ const char *key; ++ gboolean ok; ++ gint val = BOLT_SECURITY_UNKNOWN; ++ ++ g_return_val_if_fail (BOLT_IS_CLIENT (client), val); ++ ++ key = g_param_spec_get_name (props[PROP_SECURITY]); ++ ok = bolt_proxy_get_property_enum (BOLT_PROXY (client), key, &val); ++ ++ if (!ok) ++ g_warning ("failed to get enum property '%s'", key); ++ ++ return val; ++} ++ ++BoltAuthMode ++bolt_client_get_authmode (BoltClient *client) ++{ ++ const char *key; ++ gboolean ok; ++ guint val = BOLT_AUTH_DISABLED; ++ ++ g_return_val_if_fail (BOLT_IS_CLIENT (client), val); ++ ++ key = g_param_spec_get_name (props[PROP_AUTHMODE]); ++ ok = bolt_proxy_get_property_flags (BOLT_PROXY (client), key, &val); ++ ++ if (!ok) ++ g_warning ("failed to get enum property '%s'", key); ++ ++ return val; ++} ++ ++void ++bolt_client_set_authmode_async (BoltClient *client, ++ BoltAuthMode mode, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ g_autofree char *str = NULL; ++ GError *err = NULL; ++ GParamSpec *pspec; ++ GParamSpecFlags *flags_pspec; ++ GFlagsClass *flags_class; ++ ++ pspec = props[PROP_AUTHMODE]; ++ flags_pspec = G_PARAM_SPEC_FLAGS (pspec); ++ flags_class = flags_pspec->flags_class; ++ str = bolt_flags_class_to_string (flags_class, mode, &err); ++ ++ if (str == NULL) ++ { ++ g_task_report_error (client, callback, user_data, NULL, err); ++ return; ++ } ++ ++ bolt_proxy_set_property_async (BOLT_PROXY (client), ++ g_param_spec_get_nick (pspec), ++ g_variant_new ("s", str), ++ cancellable, ++ callback, ++ user_data); ++} ++ ++gboolean ++bolt_client_set_authmode_finish (BoltClient *client, ++ GAsyncResult *res, ++ GError **error) ++{ ++ return bolt_proxy_set_property_finish (res, error); ++} ++ ++/* utility functions */ ++static gint ++device_sort_by_syspath (gconstpointer ap, ++ gconstpointer bp, ++ gpointer data) ++{ ++ BoltDevice *a = BOLT_DEVICE (*((BoltDevice **) ap)); ++ BoltDevice *b = BOLT_DEVICE (*((BoltDevice **) bp)); ++ gint sort_order = GPOINTER_TO_INT (data); ++ const char *pa; ++ const char *pb; ++ ++ pa = bolt_device_get_syspath (a); ++ pb = bolt_device_get_syspath (b); ++ ++ return sort_order * g_strcmp0 (pa, pb); ++} ++ ++void ++bolt_devices_sort_by_syspath (GPtrArray *devices, ++ gboolean reverse) ++{ ++ gpointer sort_order = GINT_TO_POINTER (reverse ? -1 : 1); ++ ++ if (devices == NULL) ++ return; ++ ++ g_ptr_array_sort_with_data (devices, ++ device_sort_by_syspath, ++ sort_order); ++} +diff --git a/panels/thunderbolt/bolt-client.h b/panels/thunderbolt/bolt-client.h +new file mode 100644 +index 000000000000..85382301182b +--- /dev/null ++++ b/panels/thunderbolt/bolt-client.h +@@ -0,0 +1,107 @@ ++/* ++ * Copyright © 2017 Red Hat, Inc ++ * ++ * This program 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, see . ++ * ++ * Authors: ++ * Christian J. Kellner ++ */ ++ ++#pragma once ++ ++#include "bolt-enums.h" ++#include "bolt-device.h" ++#include "bolt-proxy.h" ++ ++G_BEGIN_DECLS ++ ++#define BOLT_TYPE_CLIENT bolt_client_get_type () ++G_DECLARE_FINAL_TYPE (BoltClient, bolt_client, BOLT, CLIENT, BoltProxy); ++ ++BoltClient * bolt_client_new (GError **error); ++ ++void bolt_client_new_async (GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data); ++BoltClient * bolt_client_new_finish (GAsyncResult *res, ++ GError **error); ++ ++GPtrArray * bolt_client_list_devices (BoltClient *client, ++ GCancellable *cancellable, ++ GError **error); ++ ++BoltDevice * bolt_client_get_device (BoltClient *client, ++ const char *uid, ++ GCancellable *cancellable, ++ GError **error); ++ ++BoltDevice * bolt_client_enroll_device (BoltClient *client, ++ const char *uid, ++ BoltPolicy policy, ++ BoltAuthCtrl flags, ++ GError **error); ++ ++void bolt_client_enroll_device_async (BoltClient *client, ++ const char *uid, ++ BoltPolicy policy, ++ BoltAuthCtrl flags, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data); ++ ++gboolean bolt_client_enroll_device_finish (BoltClient *client, ++ GAsyncResult *res, ++ char **path, ++ GError **error); ++ ++gboolean bolt_client_forget_device (BoltClient *client, ++ const char *uid, ++ GError **error); ++ ++void bolt_client_forget_device_async (BoltClient *client, ++ const char *uid, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data); ++ ++gboolean bolt_client_forget_device_finish (BoltClient *client, ++ GAsyncResult *res, ++ GError **error); ++ ++/* getter */ ++guint bolt_client_get_version (BoltClient *client); ++ ++gboolean bolt_client_is_probing (BoltClient *client); ++ ++BoltSecurity bolt_client_get_security (BoltClient *client); ++ ++BoltAuthMode bolt_client_get_authmode (BoltClient *client); ++ ++/* setter */ ++ ++void bolt_client_set_authmode_async (BoltClient *client, ++ BoltAuthMode mode, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data); ++ ++gboolean bolt_client_set_authmode_finish (BoltClient *client, ++ GAsyncResult *res, ++ GError **error); ++ ++/* utility functions */ ++void bolt_devices_sort_by_syspath (GPtrArray *devices, ++ gboolean reverse); ++ ++G_END_DECLS +diff --git a/panels/thunderbolt/bolt-device.c b/panels/thunderbolt/bolt-device.c +new file mode 100644 +index 000000000000..b316950d3b81 +--- /dev/null ++++ b/panels/thunderbolt/bolt-device.c +@@ -0,0 +1,604 @@ ++/* ++ * Copyright © 2017 Red Hat, Inc ++ * ++ * This program 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, see . ++ * ++ * Authors: ++ * Christian J. Kellner ++ */ ++ ++#include "config.h" ++ ++#include "bolt-device.h" ++ ++#include "bolt-enums.h" ++#include "bolt-error.h" ++#include "bolt-names.h" ++ ++#include ++ ++struct _BoltDevice ++{ ++ BoltProxy parent; ++}; ++ ++enum { ++ PROP_0, ++ ++ /* D-Bus Props */ ++ PROP_UID, ++ PROP_NAME, ++ PROP_VENDOR, ++ PROP_TYPE, ++ PROP_STATUS, ++ PROP_AUTHFLAGS, ++ PROP_PARENT, ++ PROP_SYSPATH, ++ PROP_CONNTIME, ++ PROP_AUTHTIME, ++ ++ PROP_STORED, ++ PROP_POLICY, ++ PROP_KEY, ++ PROP_STORETIME, ++ PROP_LABEL, ++ ++ PROP_LAST ++}; ++ ++static GParamSpec *props[PROP_LAST] = {NULL, }; ++ ++G_DEFINE_TYPE (BoltDevice, ++ bolt_device, ++ BOLT_TYPE_PROXY); ++ ++static void ++bolt_device_get_property (GObject *object, ++ guint prop_id, ++ GValue *value, ++ GParamSpec *pspec) ++{ ++ if (bolt_proxy_get_dbus_property (object, pspec, value)) ++ return; ++} ++ ++ ++ ++static void ++bolt_device_class_init (BoltDeviceClass *klass) ++{ ++ GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ++ ++ gobject_class->get_property = bolt_device_get_property; ++ ++ props[PROP_UID] = ++ g_param_spec_string ("uid", ++ "Uid", NULL, ++ "unknown", ++ G_PARAM_READABLE | ++ G_PARAM_STATIC_NICK); ++ ++ props[PROP_NAME] = ++ g_param_spec_string ("name", ++ "Name", NULL, ++ "unknown", ++ G_PARAM_READABLE | ++ G_PARAM_STATIC_NICK); ++ ++ props[PROP_VENDOR] = ++ g_param_spec_string ("vendor", ++ "Vendor", NULL, ++ "unknown", ++ G_PARAM_READABLE | ++ G_PARAM_STATIC_NICK); ++ ++ props[PROP_TYPE] = ++ g_param_spec_enum ("type", ++ "Type", NULL, ++ BOLT_TYPE_DEVICE_TYPE, ++ BOLT_DEVICE_PERIPHERAL, ++ G_PARAM_READABLE | ++ G_PARAM_STATIC_NICK); ++ ++ props[PROP_STATUS] = ++ g_param_spec_enum ("status", ++ "Status", NULL, ++ BOLT_TYPE_STATUS, ++ BOLT_STATUS_DISCONNECTED, ++ G_PARAM_READABLE | ++ G_PARAM_STATIC_NICK); ++ ++ props[PROP_AUTHFLAGS] = ++ g_param_spec_flags ("authflags", ++ "AuthFlags", NULL, ++ BOLT_TYPE_AUTH_FLAGS, ++ BOLT_AUTH_NONE, ++ G_PARAM_READABLE | ++ G_PARAM_STATIC_STRINGS); ++ ++ props[PROP_PARENT] = ++ g_param_spec_string ("parent", ++ "Parent", NULL, ++ "unknown", ++ G_PARAM_READABLE | ++ G_PARAM_STATIC_NICK); ++ ++ props[PROP_SYSPATH] = ++ g_param_spec_string ("syspath", ++ "SysfsPath", NULL, ++ "unknown", ++ G_PARAM_READABLE | ++ G_PARAM_STATIC_NICK); ++ ++ props[PROP_CONNTIME] = ++ g_param_spec_uint64 ("conntime", ++ "ConnectTime", NULL, ++ 0, G_MAXUINT64, 0, ++ G_PARAM_READABLE | ++ G_PARAM_STATIC_STRINGS); ++ ++ props[PROP_AUTHTIME] = ++ g_param_spec_uint64 ("authtime", ++ "AuthorizeTime", NULL, ++ 0, G_MAXUINT64, 0, ++ G_PARAM_READABLE | ++ G_PARAM_STATIC_STRINGS); ++ ++ props[PROP_STORED] = ++ g_param_spec_boolean ("stored", ++ "Stored", NULL, ++ FALSE, ++ G_PARAM_READABLE | ++ G_PARAM_STATIC_NICK); ++ ++ props[PROP_POLICY] = ++ g_param_spec_enum ("policy", ++ "Policy", NULL, ++ BOLT_TYPE_POLICY, ++ BOLT_POLICY_DEFAULT, ++ G_PARAM_READABLE | ++ G_PARAM_STATIC_NICK); ++ ++ props[PROP_KEY] = ++ g_param_spec_enum ("key", ++ "Key", NULL, ++ BOLT_TYPE_KEY_STATE, ++ BOLT_KEY_MISSING, ++ G_PARAM_READABLE | ++ G_PARAM_STATIC_NICK); ++ ++ props[PROP_STORETIME] = ++ g_param_spec_uint64 ("storetime", ++ "StoreTime", NULL, ++ 0, G_MAXUINT64, 0, ++ G_PARAM_READABLE | ++ G_PARAM_STATIC_STRINGS); ++ ++ props[PROP_LABEL] = ++ g_param_spec_string ("label", ++ "Label", NULL, ++ NULL, ++ G_PARAM_READABLE | ++ G_PARAM_STATIC_STRINGS); ++ ++ g_object_class_install_properties (gobject_class, ++ PROP_LAST, ++ props); ++ ++} ++ ++static void ++bolt_device_init (BoltDevice *mgr) ++{ ++} ++ ++/* public methods */ ++ ++BoltDevice * ++bolt_device_new_for_object_path (GDBusConnection *bus, ++ const char *path, ++ GCancellable *cancel, ++ GError **error) ++{ ++ BoltDevice *dev; ++ ++ dev = g_initable_new (BOLT_TYPE_DEVICE, ++ cancel, error, ++ "g-flags", G_DBUS_PROXY_FLAGS_NONE, ++ "g-connection", bus, ++ "g-name", BOLT_DBUS_NAME, ++ "g-object-path", path, ++ "g-interface-name", BOLT_DBUS_DEVICE_INTERFACE, ++ NULL); ++ ++ return dev; ++} ++ ++gboolean ++bolt_device_authorize (BoltDevice *dev, ++ BoltAuthCtrl flags, ++ GCancellable *cancel, ++ GError **error) ++{ ++ g_autoptr(GError) err = NULL; ++ g_autofree char *fstr = NULL; ++ ++ g_return_val_if_fail (BOLT_IS_DEVICE (dev), FALSE); ++ ++ fstr = bolt_flags_to_string (BOLT_TYPE_AUTH_CTRL, flags, error); ++ if (fstr == NULL) ++ return FALSE; ++ ++ g_dbus_proxy_call_sync (G_DBUS_PROXY (dev), ++ "Authorize", ++ g_variant_new ("(s)", fstr), ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, ++ cancel, ++ &err); ++ ++ if (err != NULL) ++ return bolt_error_propagate_stripped (error, &err); ++ ++ return TRUE; ++} ++ ++void ++bolt_device_authorize_async (BoltDevice *dev, ++ BoltAuthCtrl flags, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GError *err = NULL; ++ g_autofree char *fstr = NULL; ++ ++ g_return_if_fail (BOLT_IS_DEVICE (dev)); ++ ++ fstr = bolt_flags_to_string (BOLT_TYPE_AUTH_CTRL, flags, &err); ++ if (fstr == NULL) ++ { ++ g_task_report_error (dev, callback, user_data, NULL, err); ++ return; ++ } ++ ++ g_dbus_proxy_call (G_DBUS_PROXY (dev), ++ "Authorize", ++ g_variant_new ("(s)", fstr), ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, ++ cancellable, ++ callback, ++ user_data); ++} ++ ++gboolean ++bolt_device_authorize_finish (BoltDevice *dev, ++ GAsyncResult *res, ++ GError **error) ++{ ++ g_autoptr(GError) err = NULL; ++ g_autoptr(GVariant) val = NULL; ++ ++ g_return_val_if_fail (BOLT_IS_DEVICE (dev), FALSE); ++ ++ val = g_dbus_proxy_call_finish (G_DBUS_PROXY (dev), res, &err); ++ if (val == NULL) ++ { ++ bolt_error_propagate_stripped (error, &err); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++const char * ++bolt_device_get_uid (BoltDevice *dev) ++{ ++ const char *key; ++ const char *str; ++ ++ g_return_val_if_fail (BOLT_IS_DEVICE (dev), NULL); ++ ++ key = g_param_spec_get_name (props[PROP_UID]); ++ str = bolt_proxy_get_property_string (BOLT_PROXY (dev), key); ++ ++ return str; ++} ++ ++const char * ++bolt_device_get_name (BoltDevice *dev) ++{ ++ const char *key; ++ const char *str; ++ ++ g_return_val_if_fail (BOLT_IS_DEVICE (dev), NULL); ++ ++ key = g_param_spec_get_name (props[PROP_NAME]); ++ str = bolt_proxy_get_property_string (BOLT_PROXY (dev), key); ++ ++ return str; ++} ++ ++const char * ++bolt_device_get_vendor (BoltDevice *dev) ++{ ++ const char *key; ++ const char *str; ++ ++ g_return_val_if_fail (BOLT_IS_DEVICE (dev), NULL); ++ ++ key = g_param_spec_get_name (props[PROP_VENDOR]); ++ str = bolt_proxy_get_property_string (BOLT_PROXY (dev), key); ++ ++ return str; ++} ++ ++BoltDeviceType ++bolt_device_get_device_type (BoltDevice *dev) ++{ ++ const char *key; ++ gboolean ok; ++ gint val = BOLT_DEVICE_PERIPHERAL; ++ ++ g_return_val_if_fail (BOLT_IS_DEVICE (dev), val); ++ ++ key = g_param_spec_get_name (props[PROP_TYPE]); ++ ok = bolt_proxy_get_property_enum (BOLT_PROXY (dev), key, &val); ++ ++ if (!ok) ++ g_warning ("failed to get enum property '%s'", key); ++ ++ return val; ++} ++ ++BoltStatus ++bolt_device_get_status (BoltDevice *dev) ++{ ++ const char *key; ++ gboolean ok; ++ gint val = BOLT_STATUS_UNKNOWN; ++ ++ g_return_val_if_fail (BOLT_IS_DEVICE (dev), val); ++ ++ key = g_param_spec_get_name (props[PROP_STATUS]); ++ ok = bolt_proxy_get_property_enum (BOLT_PROXY (dev), key, &val); ++ ++ if (!ok) ++ g_warning ("failed to get enum property '%s'", key); ++ ++ return val; ++} ++ ++BoltAuthFlags ++bolt_device_get_authflags (BoltDevice *dev) ++{ ++ const char *key; ++ gboolean ok; ++ guint val = BOLT_AUTH_NONE; ++ ++ g_return_val_if_fail (BOLT_IS_DEVICE (dev), val); ++ ++ key = g_param_spec_get_name (props[PROP_AUTHFLAGS]); ++ ok = bolt_proxy_get_property_flags (BOLT_PROXY (dev), key, &val); ++ ++ if (!ok) ++ g_warning ("failed to get enum property '%s'", key); ++ ++ return val; ++} ++ ++const char * ++bolt_device_get_parent (BoltDevice *dev) ++{ ++ const char *key; ++ const char *str; ++ ++ g_return_val_if_fail (BOLT_IS_DEVICE (dev), NULL); ++ ++ key = g_param_spec_get_name (props[PROP_PARENT]); ++ str = bolt_proxy_get_property_string (BOLT_PROXY (dev), key); ++ ++ return str; ++} ++ ++const char * ++bolt_device_get_syspath (BoltDevice *dev) ++{ ++ const char *key; ++ const char *str; ++ ++ g_return_val_if_fail (BOLT_IS_DEVICE (dev), NULL); ++ ++ key = g_param_spec_get_name (props[PROP_SYSPATH]); ++ str = bolt_proxy_get_property_string (BOLT_PROXY (dev), key); ++ ++ return str; ++} ++ ++guint64 ++bolt_device_get_conntime (BoltDevice *dev) ++{ ++ const char *key; ++ guint64 val = 0; ++ gboolean ok; ++ ++ g_return_val_if_fail (BOLT_IS_DEVICE (dev), val); ++ ++ key = g_param_spec_get_name (props[PROP_CONNTIME]); ++ ok = bolt_proxy_get_property_uint64 (BOLT_PROXY (dev), key, &val); ++ ++ if (!ok) ++ g_warning ("failed to get enum property '%s'", key); ++ ++ return val; ++} ++ ++guint64 ++bolt_device_get_authtime (BoltDevice *dev) ++{ ++ const char *key; ++ guint64 val = 0; ++ gboolean ok; ++ ++ g_return_val_if_fail (BOLT_IS_DEVICE (dev), val); ++ ++ key = g_param_spec_get_name (props[PROP_AUTHTIME]); ++ ok = bolt_proxy_get_property_uint64 (BOLT_PROXY (dev), key, &val); ++ ++ if (!ok) ++ g_warning ("failed to get enum property '%s'", key); ++ ++ return val; ++} ++ ++gboolean ++bolt_device_is_stored (BoltDevice *dev) ++{ ++ const char *key; ++ gboolean val = FALSE; ++ gboolean ok; ++ ++ g_return_val_if_fail (BOLT_IS_DEVICE (dev), val); ++ ++ key = g_param_spec_get_name (props[PROP_STORED]); ++ ok = bolt_proxy_get_property_bool (BOLT_PROXY (dev), key, &val); ++ ++ if (!ok) ++ g_warning ("failed to get enum property '%s'", key); ++ ++ return val; ++} ++ ++BoltPolicy ++bolt_device_get_policy (BoltDevice *dev) ++{ ++ const char *key; ++ gboolean ok; ++ gint val = BOLT_POLICY_DEFAULT; ++ ++ g_return_val_if_fail (BOLT_IS_DEVICE (dev), val); ++ ++ key = g_param_spec_get_name (props[PROP_POLICY]); ++ ok = bolt_proxy_get_property_enum (BOLT_PROXY (dev), key, &val); ++ ++ if (!ok) ++ g_warning ("failed to get enum property '%s'", key); ++ ++ return val; ++} ++ ++BoltKeyState ++bolt_device_get_keystate (BoltDevice *dev) ++{ ++ const char *key; ++ gboolean ok; ++ gint val = BOLT_KEY_MISSING; ++ ++ g_return_val_if_fail (BOLT_IS_DEVICE (dev), val); ++ ++ key = g_param_spec_get_name (props[PROP_KEY]); ++ ok = bolt_proxy_get_property_enum (BOLT_PROXY (dev), key, &val); ++ ++ if (!ok) ++ g_warning ("failed to get enum property '%s'", key); ++ ++ return val; ++} ++ ++guint64 ++bolt_device_get_storetime (BoltDevice *dev) ++{ ++ const char *key; ++ guint64 val = 0; ++ gboolean ok; ++ ++ g_return_val_if_fail (BOLT_IS_DEVICE (dev), val); ++ ++ key = g_param_spec_get_name (props[PROP_STORETIME]); ++ ok = bolt_proxy_get_property_uint64 (BOLT_PROXY (dev), key, &val); ++ ++ if (!ok) ++ g_warning ("failed to get enum property '%s'", key); ++ ++ return val; ++} ++ ++const char * ++bolt_device_get_label (BoltDevice *dev) ++{ ++ const char *key; ++ const char *str; ++ ++ g_return_val_if_fail (BOLT_IS_DEVICE (dev), NULL); ++ ++ key = g_param_spec_get_name (props[PROP_LABEL]); ++ str = bolt_proxy_get_property_string (BOLT_PROXY (dev), key); ++ ++ return str; ++} ++ ++char * ++bolt_device_get_display_name (BoltDevice *dev) ++{ ++ const char *label; ++ const char *name; ++ const char *vendor; ++ ++ label = bolt_device_get_label (dev); ++ if (label != NULL) ++ return g_strdup (label); ++ ++ name = bolt_device_get_name (dev); ++ vendor = bolt_device_get_vendor (dev); ++ ++ return g_strdup_printf ("%s %s", vendor, name); ++} ++ ++guint64 ++bolt_device_get_timestamp (BoltDevice *dev) ++{ ++ BoltStatus status; ++ guint64 timestamp = 0; ++ ++ status = bolt_device_get_status (dev); ++ ++ switch (status) ++ { ++ case BOLT_STATUS_AUTHORIZING: ++ case BOLT_STATUS_AUTH_ERROR: ++ case BOLT_STATUS_CONNECTING: ++ case BOLT_STATUS_CONNECTED: ++ timestamp = bolt_device_get_conntime (dev); ++ break; ++ ++ case BOLT_STATUS_DISCONNECTED: ++ /* implicit: device is stored */ ++ timestamp = bolt_device_get_storetime (dev); ++ break; ++ ++ case BOLT_STATUS_AUTHORIZED: ++ case BOLT_STATUS_AUTHORIZED_DPONLY: ++ case BOLT_STATUS_AUTHORIZED_NEWKEY: ++ case BOLT_STATUS_AUTHORIZED_SECURE: ++ timestamp = bolt_device_get_authtime (dev); ++ break; ++ ++ case BOLT_STATUS_UNKNOWN: ++ timestamp = 0; ++ break; ++ } ++ ++ return timestamp; ++} +diff --git a/panels/thunderbolt/bolt-device.h b/panels/thunderbolt/bolt-device.h +new file mode 100644 +index 000000000000..ffd09f9a8ad7 +--- /dev/null ++++ b/panels/thunderbolt/bolt-device.h +@@ -0,0 +1,87 @@ ++/* ++ * Copyright © 2017 Red Hat, Inc ++ * ++ * This program 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, see . ++ * ++ * Authors: ++ * Christian J. Kellner ++ */ ++ ++#pragma once ++ ++#include "bolt-enums.h" ++#include "bolt-proxy.h" ++ ++G_BEGIN_DECLS ++ ++#define BOLT_TYPE_DEVICE bolt_device_get_type () ++G_DECLARE_FINAL_TYPE (BoltDevice, bolt_device, BOLT, DEVICE, BoltProxy); ++ ++BoltDevice * bolt_device_new_for_object_path (GDBusConnection *bus, ++ const char *path, ++ GCancellable *cancellable, ++ GError **error); ++ ++gboolean bolt_device_authorize (BoltDevice *dev, ++ BoltAuthCtrl flags, ++ GCancellable *cancellable, ++ GError **error); ++ ++void bolt_device_authorize_async (BoltDevice *dev, ++ BoltAuthCtrl flags, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data); ++ ++gboolean bolt_device_authorize_finish (BoltDevice *dev, ++ GAsyncResult *res, ++ GError **error); ++ ++/* getter */ ++const char * bolt_device_get_uid (BoltDevice *dev); ++ ++const char * bolt_device_get_name (BoltDevice *dev); ++ ++const char * bolt_device_get_vendor (BoltDevice *dev); ++ ++BoltDeviceType bolt_device_get_device_type (BoltDevice *dev); ++ ++BoltStatus bolt_device_get_status (BoltDevice *dev); ++ ++BoltAuthFlags bolt_device_get_authflags (BoltDevice *dev); ++ ++const char * bolt_device_get_parent (BoltDevice *dev); ++ ++const char * bolt_device_get_syspath (BoltDevice *dev); ++ ++guint64 bolt_device_get_conntime (BoltDevice *dev); ++ ++guint64 bolt_device_get_authtime (BoltDevice *dev); ++ ++gboolean bolt_device_is_stored (BoltDevice *dev); ++ ++BoltPolicy bolt_device_get_policy (BoltDevice *dev); ++ ++BoltKeyState bolt_device_get_keystate (BoltDevice *dev); ++ ++guint64 bolt_device_get_storetime (BoltDevice *dev); ++ ++const char * bolt_device_get_label (BoltDevice *dev); ++ ++/* derived getter */ ++char * bolt_device_get_display_name (BoltDevice *dev); ++ ++guint64 bolt_device_get_timestamp (BoltDevice *dev); ++ ++G_END_DECLS +diff --git a/panels/thunderbolt/bolt-enums.c b/panels/thunderbolt/bolt-enums.c +new file mode 100644 +index 000000000000..de77737f8088 +--- /dev/null ++++ b/panels/thunderbolt/bolt-enums.c +@@ -0,0 +1,395 @@ ++/* ++ * Copyright © 2017 Red Hat, Inc ++ * ++ * This program 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, see . ++ * ++ * Authors: ++ * Christian J. Kellner ++ */ ++ ++#include "config.h" ++ ++#include "bolt-enums.h" ++#include "bolt-error.h" ++ ++#include ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC (GEnumClass, g_type_class_unref); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC (GFlagsClass, g_type_class_unref); ++ ++gboolean ++bolt_enum_class_validate (GEnumClass *enum_class, ++ gint value, ++ GError **error) ++{ ++ const char *name; ++ gboolean oob; ++ ++ if (enum_class == NULL) ++ { ++ name = g_type_name_from_class ((GTypeClass *) enum_class); ++ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, ++ "could not determine enum class for '%s'", ++ name); ++ ++ return FALSE; ++ } ++ ++ oob = value < enum_class->minimum || value > enum_class->maximum; ++ ++ if (oob) ++ { ++ name = g_type_name_from_class ((GTypeClass *) enum_class); ++ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, ++ "enum value '%d' is out of bounds for '%s'", ++ value, name); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++gboolean ++bolt_enum_validate (GType enum_type, ++ gint value, ++ GError **error) ++{ ++ g_autoptr(GEnumClass) klass = g_type_class_ref (enum_type); ++ return bolt_enum_class_validate (klass, value, error); ++} ++ ++const char * ++bolt_enum_to_string (GType enum_type, ++ gint value, ++ GError **error) ++{ ++ g_autoptr(GEnumClass) klass = NULL; ++ GEnumValue *ev; ++ ++ klass = g_type_class_ref (enum_type); ++ ++ if (!bolt_enum_class_validate (klass, value, error)) ++ return NULL; ++ ++ ev = g_enum_get_value (klass, value); ++ return ev->value_nick; ++} ++ ++gint ++bolt_enum_from_string (GType enum_type, ++ const char *string, ++ GError **error) ++{ ++ g_autoptr(GEnumClass) klass = NULL; ++ const char *name; ++ GEnumValue *ev; ++ ++ klass = g_type_class_ref (enum_type); ++ ++ if (klass == NULL) ++ { ++ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, ++ "could not determine enum class"); ++ return -1; ++ } ++ ++ if (string == NULL) ++ { ++ name = g_type_name_from_class ((GTypeClass *) klass); ++ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, ++ "empty string passed for enum class for '%s'", ++ name); ++ return -1; ++ } ++ ++ ev = g_enum_get_value_by_nick (klass, string); ++ ++ if (ev == NULL) ++ { ++ name = g_type_name (enum_type); ++ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, ++ "invalid string '%s' for enum '%s'", string, name); ++ return -1; ++ } ++ ++ return ev->value; ++} ++ ++char * ++bolt_flags_class_to_string (GFlagsClass *flags_class, ++ guint value, ++ GError **error) ++{ ++ g_autoptr(GString) str = NULL; ++ const char *name; ++ GFlagsValue *fv; ++ ++ if (flags_class == NULL) ++ { ++ name = g_type_name_from_class ((GTypeClass *) flags_class); ++ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, ++ "could not determine flags class for '%s'", ++ name); ++ ++ return FALSE; ++ } ++ ++ fv = g_flags_get_first_value (flags_class, value); ++ if (fv == NULL) ++ { ++ if (value == 0) ++ return g_strdup (""); ++ ++ name = g_type_name_from_class ((GTypeClass *) flags_class); ++ ++ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, ++ "invalid value '%u' for flags '%s'", value, name); ++ return NULL; ++ } ++ ++ value &= ~fv->value; ++ str = g_string_new (fv->value_nick); ++ ++ while (value != 0 && ++ (fv = g_flags_get_first_value (flags_class, value)) != NULL) ++ { ++ g_string_append (str, " | "); ++ g_string_append (str, fv->value_nick); ++ ++ value &= ~fv->value; ++ } ++ ++ if (value != 0) ++ { ++ name = g_type_name_from_class ((GTypeClass *) flags_class); ++ ++ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, ++ "unhandled value '%u' for flags '%s'", value, name); ++ return NULL; ++ } ++ ++ return g_string_free (g_steal_pointer (&str), FALSE); ++} ++ ++gboolean ++bolt_flags_class_from_string (GFlagsClass *flags_class, ++ const char *string, ++ guint *flags_out, ++ GError **error) ++{ ++ g_auto(GStrv) vals = NULL; ++ const char *name; ++ guint flags = 0; ++ ++ if (flags_class == NULL) ++ { ++ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, ++ "could not determine flags class"); ++ ++ return FALSE; ++ } ++ ++ if (string == NULL) ++ { ++ name = g_type_name_from_class ((GTypeClass *) flags_class); ++ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, ++ "empty string passed for flags class for '%s'", ++ name); ++ return FALSE; ++ } ++ ++ vals = g_strsplit (string, "|", -1); ++ ++ for (guint i = 0; vals[i]; i++) ++ { ++ GFlagsValue *fv; ++ char *nick; ++ ++ nick = g_strstrip (vals[i]); ++ fv = g_flags_get_value_by_nick (flags_class, nick); ++ ++ if (fv == NULL) ++ { ++ name = g_type_name_from_class ((GTypeClass *) flags_class); ++ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, ++ "invalid flag '%s' for flags '%s'", string, name); ++ ++ return FALSE; ++ } ++ ++ flags |= fv->value; ++ } ++ ++ if (flags_out != NULL) ++ *flags_out = flags; ++ ++ return TRUE; ++} ++ ++char * ++bolt_flags_to_string (GType flags_type, ++ guint value, ++ GError **error) ++{ ++ g_autoptr(GFlagsClass) klass = NULL; ++ ++ klass = g_type_class_ref (flags_type); ++ return bolt_flags_class_to_string (klass, value, error); ++} ++ ++gboolean ++bolt_flags_from_string (GType flags_type, ++ const char *string, ++ guint *flags_out, ++ GError **error) ++{ ++ g_autoptr(GFlagsClass) klass = NULL; ++ ++ klass = g_type_class_ref (flags_type); ++ return bolt_flags_class_from_string (klass, string, flags_out, error); ++} ++ ++gboolean ++bolt_flags_update (guint from, ++ guint *to, ++ guint mask) ++{ ++ guint val; ++ gboolean chg; ++ ++ g_return_val_if_fail (to != NULL, FALSE); ++ ++ val = *to & ~mask; /* clear all bits in mask */ ++ val = val | (from & mask); /* set all bits in from and mask */ ++ chg = *to != val; ++ *to = val; ++ ++ return chg; ++} ++ ++const char * ++bolt_status_to_string (BoltStatus status) ++{ ++ return bolt_enum_to_string (BOLT_TYPE_STATUS, status, NULL); ++} ++ ++gboolean ++bolt_status_is_authorized (BoltStatus status) ++{ ++ return status == BOLT_STATUS_AUTHORIZED || ++ status == BOLT_STATUS_AUTHORIZED_SECURE || ++ status == BOLT_STATUS_AUTHORIZED_NEWKEY; ++} ++ ++gboolean ++bolt_status_is_pending (BoltStatus status) ++{ ++ return status == BOLT_STATUS_AUTH_ERROR || ++ status == BOLT_STATUS_CONNECTED; ++} ++ ++gboolean ++bolt_status_validate (BoltStatus status) ++{ ++ return bolt_enum_validate (BOLT_TYPE_STATUS, status, NULL); ++} ++ ++gboolean ++bolt_status_is_connected (BoltStatus status) ++{ ++ return status > BOLT_STATUS_DISCONNECTED; ++} ++ ++BoltSecurity ++bolt_security_from_string (const char *str) ++{ ++ return bolt_enum_from_string (BOLT_TYPE_SECURITY, str, NULL); ++} ++ ++const char * ++bolt_security_to_string (BoltSecurity security) ++{ ++ return bolt_enum_to_string (BOLT_TYPE_SECURITY, security, NULL); ++} ++ ++gboolean ++bolt_security_validate (BoltSecurity security) ++{ ++ return bolt_enum_validate (BOLT_TYPE_SECURITY, security, NULL); ++} ++ ++gboolean ++bolt_security_allows_pcie (BoltSecurity security) ++{ ++ gboolean pcie = FALSE; ++ ++ switch (security) ++ { ++ case BOLT_SECURITY_NONE: ++ case BOLT_SECURITY_USER: ++ case BOLT_SECURITY_SECURE: ++ pcie = TRUE; ++ break; ++ ++ case BOLT_SECURITY_DPONLY: ++ case BOLT_SECURITY_USBONLY: ++ case BOLT_SECURITY_UNKNOWN: ++ pcie = FALSE; ++ break; ++ } ++ ++ return pcie; ++} ++ ++BoltPolicy ++bolt_policy_from_string (const char *str) ++{ ++ return bolt_enum_from_string (BOLT_TYPE_POLICY, str, NULL); ++} ++ ++const char * ++bolt_policy_to_string (BoltPolicy policy) ++{ ++ return bolt_enum_to_string (BOLT_TYPE_POLICY, policy, NULL); ++} ++ ++gboolean ++bolt_policy_validate (BoltPolicy policy) ++{ ++ return bolt_enum_validate (BOLT_TYPE_POLICY, policy, NULL); ++} ++ ++BoltDeviceType ++bolt_device_type_from_string (const char *str) ++{ ++ return bolt_enum_from_string (BOLT_TYPE_DEVICE_TYPE, str, NULL); ++} ++ ++const char * ++bolt_device_type_to_string (BoltDeviceType type) ++{ ++ return bolt_enum_to_string (BOLT_TYPE_DEVICE_TYPE, type, NULL); ++} ++ ++gboolean ++bolt_device_type_validate (BoltDeviceType type) ++{ ++ return bolt_enum_validate (BOLT_TYPE_DEVICE_TYPE, type, NULL); ++} ++ ++gboolean ++bolt_device_type_is_host (BoltDeviceType type) ++{ ++ return type == BOLT_DEVICE_HOST; ++} +diff --git a/panels/thunderbolt/bolt-enums.h b/panels/thunderbolt/bolt-enums.h +new file mode 100644 +index 000000000000..6e2953fa2fd2 +--- /dev/null ++++ b/panels/thunderbolt/bolt-enums.h +@@ -0,0 +1,249 @@ ++/* ++ * Copyright © 2017 Red Hat, Inc ++ * ++ * This program 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, see . ++ * ++ * Authors: ++ * Christian J. Kellner ++ */ ++ ++#pragma once ++ ++#include "bolt-names.h" ++#include "bolt-enum-types.h" ++ ++ ++gboolean bolt_enum_validate (GType enum_type, ++ gint value, ++ GError **error); ++ ++gboolean bolt_enum_class_validate (GEnumClass *enum_class, ++ gint value, ++ GError **error); ++ ++const char * bolt_enum_to_string (GType enum_type, ++ gint value, ++ GError **error); ++ ++gint bolt_enum_from_string (GType enum_type, ++ const char *string, ++ GError **error); ++ ++ ++char * bolt_flags_class_to_string (GFlagsClass *flags_class, ++ guint value, ++ GError **error); ++ ++gboolean bolt_flags_class_from_string (GFlagsClass *flags_class, ++ const char *string, ++ guint *flags_out, ++ GError **error); ++ ++char * bolt_flags_to_string (GType flags_type, ++ guint value, ++ GError **error); ++ ++gboolean bolt_flags_from_string (GType flags_type, ++ const char *string, ++ guint *flags_out, ++ GError **error); ++ ++gboolean bolt_flags_update (guint from, ++ guint *to, ++ guint mask); ++ ++#define bolt_flag_isset(flags_, flag_) (!!(flags_ & flag_)) ++#define bolt_flag_isclear(flags_, flag_) (!(flags_ & flag_)) ++ ++/** ++ * BoltStatus: ++ * @BOLT_STATUS_UNKNOWN: Device is in an unknown state (should normally not happen). ++ * @BOLT_STATUS_DISCONNECTED: Device is not connected. ++ * @BOLT_STATUS_CONNECTING: Device is currently being connected. ++ * @BOLT_STATUS_CONNECTED: Device is connected, but not authorized. ++ * @BOLT_STATUS_AUTHORIZING: Device is currently authorizing. ++ * @BOLT_STATUS_AUTH_ERROR: Failed to authorize a device via a key. ++ * @BOLT_STATUS_AUTHORIZED: Device connected and authorized. ++ * @BOLT_STATUS_AUTHORIZED_SECURE: Device connected and securely authorized via a key (deprecated). ++ * @BOLT_STATUS_AUTHORIZED_NEWKEY: Device connected and authorized via a new key (deprecated). ++ * @BOLT_STATUS_AUTHORIZED_DPONLY: Device authorized but with thunderbolt disabled (deprecated). ++ * ++ * The current status of the device. ++ */ ++typedef enum { ++ ++ BOLT_STATUS_UNKNOWN = -1, ++ BOLT_STATUS_DISCONNECTED = 0, ++ BOLT_STATUS_CONNECTING, ++ BOLT_STATUS_CONNECTED, ++ BOLT_STATUS_AUTHORIZING, ++ BOLT_STATUS_AUTH_ERROR, ++ BOLT_STATUS_AUTHORIZED, ++ ++ /* deprecated, do not use */ ++ BOLT_STATUS_AUTHORIZED_SECURE, ++ BOLT_STATUS_AUTHORIZED_NEWKEY, ++ BOLT_STATUS_AUTHORIZED_DPONLY ++ ++} BoltStatus; ++ ++const char * bolt_status_to_string (BoltStatus status); ++gboolean bolt_status_is_authorized (BoltStatus status); ++gboolean bolt_status_is_connected (BoltStatus status); ++gboolean bolt_status_is_pending (BoltStatus status); ++gboolean bolt_status_validate (BoltStatus status); ++ ++/** ++ * BoltAuthFlags: ++ * @BOLT_AUTH_NONE: No specific authorization. ++ * @BOLT_AUTH_NOPCIE: PCIe tunnels are *not* authorized. ++ * @BOLT_AUTH_SECURE: Device is securely authorized. ++ * @BOLT_AUTH_NOKEY: Device does *not* support key verification. ++ * @BOLT_AUTH_BOOT: Device was already authorized during pre-boot. ++ * ++ * More specific information about device authorization. ++ */ ++typedef enum { /*< flags >*/ ++ ++ BOLT_AUTH_NONE = 0, ++ BOLT_AUTH_NOPCIE = 1 << 0, ++ BOLT_AUTH_SECURE = 1 << 1, ++ BOLT_AUTH_NOKEY = 1 << 2, ++ BOLT_AUTH_BOOT = 1 << 3, ++ ++} BoltAuthFlags; ++ ++/** ++ * BoltKeyState: ++ * @BOLT_KEY_UNKNOWN: unknown key state ++ * @BOLT_KEY_MISSING: no key ++ * @BOLT_KEY_HAVE: key exists ++ * @BOLT_KEY_NEW: key is new ++ * ++ * The state of the key. ++ */ ++ ++typedef enum { ++ ++ BOLT_KEY_UNKNOWN = -1, ++ BOLT_KEY_MISSING = 0, ++ BOLT_KEY_HAVE = 1, ++ BOLT_KEY_NEW = 2 ++ ++} BoltKeyState; ++ ++/** ++ * BoltSecurity: ++ * @BOLT_SECURITY_UNKNOWN : Unknown security. ++ * @BOLT_SECURITY_NONE : No security, all devices are automatically connected. ++ * @BOLT_SECURITY_DPONLY : Display Port only devices only. ++ * @BOLT_SECURITY_USER : User needs to authorize devices. ++ * @BOLT_SECURITY_SECURE : User needs to authorize devices. Authorization can ++ * be done via key exchange to verify the device identity. ++ * @BOLT_SECURITY_USBONLY : Only create a PCIe tunnel to the USB controller in a ++ * connected thunderbolt dock, allowing no downstream PCIe tunnels. ++ * ++ * The security level of the thunderbolt domain. ++ */ ++typedef enum { ++ ++ BOLT_SECURITY_UNKNOWN = -1, ++ BOLT_SECURITY_NONE = 0, ++ BOLT_SECURITY_DPONLY = 1, ++ BOLT_SECURITY_USER = '1', ++ BOLT_SECURITY_SECURE = '2', ++ BOLT_SECURITY_USBONLY = 4, ++ ++} BoltSecurity; ++ ++ ++BoltSecurity bolt_security_from_string (const char *str); ++const char * bolt_security_to_string (BoltSecurity security); ++gboolean bolt_security_validate (BoltSecurity security); ++gboolean bolt_security_allows_pcie (BoltSecurity security); ++ ++/** ++ * BoltPolicy: ++ * @BOLT_POLICY_UNKNOWN: Unknown policy. ++ * @BOLT_POLICY_DEFAULT: Default policy. ++ * @BOLT_POLICY_MANUAL: Manual authorization of the device. ++ * @BOLT_POLICY_AUTO: Connect the device automatically, ++ * with the best possible security level supported ++ * by the domain controller. ++ * ++ * What do to for connected devices. ++ */ ++typedef enum { ++ ++ BOLT_POLICY_UNKNOWN = -1, ++ BOLT_POLICY_DEFAULT = 0, ++ BOLT_POLICY_MANUAL = 1, ++ BOLT_POLICY_AUTO = 2, ++ ++} BoltPolicy; ++ ++ ++BoltPolicy bolt_policy_from_string (const char *str); ++const char * bolt_policy_to_string (BoltPolicy policy); ++gboolean bolt_policy_validate (BoltPolicy policy); ++ ++/** ++ * BoltAuthCtrl: ++ * @BOLT_AUTHCTRL_NONE: No authorization flags. ++ * ++ * Control authorization. ++ */ ++typedef enum { /*< flags >*/ ++ ++ BOLT_AUTHCTRL_NONE = 0 ++ ++} BoltAuthCtrl; ++ ++/** ++ * BoltDeviceType: ++ * @BOLT_DEVICE_UNKNOWN_TYPE: Unknown device type ++ * @BOLT_DEVICE_HOST: The device representing the host ++ * @BOLT_DEVICE_PERIPHERAL: A generic thunderbolt peripheral ++ * ++ * The type of the device. ++ */ ++typedef enum { ++ ++ BOLT_DEVICE_UNKNOWN_TYPE = -1, ++ BOLT_DEVICE_HOST = 0, ++ BOLT_DEVICE_PERIPHERAL ++ ++} BoltDeviceType; ++ ++BoltDeviceType bolt_device_type_from_string (const char *str); ++const char * bolt_device_type_to_string (BoltDeviceType type); ++gboolean bolt_device_type_validate (BoltDeviceType type); ++gboolean bolt_device_type_is_host (BoltDeviceType type); ++ ++/** ++ * BoltAuthMode: ++ * @BOLT_AUTH_DISABLED: Authorization is disabled ++ * @BOLT_AUTH_ENABLED: Authorization is enabled. ++ * ++ * Control authorization. ++ */ ++typedef enum { /*< flags >*/ ++ ++ BOLT_AUTH_DISABLED = 0, ++ BOLT_AUTH_ENABLED = 1 ++ ++} BoltAuthMode; ++ ++#define bolt_auth_mode_is_enabled(auth) ((auth & BOLT_AUTH_ENABLED) != 0) ++#define bolt_auth_mode_is_disabled(auth) (!bolt_auth_mode_is_enabled (auth)) +diff --git a/panels/thunderbolt/bolt-error.c b/panels/thunderbolt/bolt-error.c +new file mode 100644 +index 000000000000..37d844e4a14d +--- /dev/null ++++ b/panels/thunderbolt/bolt-error.c +@@ -0,0 +1,99 @@ ++/* ++ * Copyright © 2017 Red Hat, Inc ++ * ++ * This program 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, see . ++ * ++ * Authors: ++ * Christian J. Kellner ++ */ ++ ++#include "config.h" ++ ++#include "bolt-error.h" ++ ++#include "bolt-names.h" ++ ++#include ++ ++/** ++ * SECTION:bolt-error ++ * @Title: Error codes ++ * ++ */ ++ ++static const GDBusErrorEntry bolt_error_entries[] = { ++ {BOLT_ERROR_FAILED, BOLT_DBUS_NAME ".Error.Failed"}, ++ {BOLT_ERROR_UDEV, BOLT_DBUS_NAME ".Error.UDev"}, ++}; ++ ++ ++GQuark ++bolt_error_quark (void) ++{ ++ static volatile gsize quark_volatile = 0; ++ ++ g_dbus_error_register_error_domain ("bolt-error-quark", ++ &quark_volatile, ++ bolt_error_entries, ++ G_N_ELEMENTS (bolt_error_entries)); ++ return (GQuark) quark_volatile; ++} ++ ++gboolean ++bolt_err_notfound (const GError *error) ++{ ++ return g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) || ++ g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT) || ++ g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND) || ++ g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND); ++} ++ ++gboolean ++bolt_err_exists (const GError *error) ++{ ++ return g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS) || ++ g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_EXIST); ++} ++ ++gboolean ++bolt_err_inval (const GError *error) ++{ ++ return g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE); ++} ++ ++gboolean ++bolt_err_cancelled (const GError *error) ++{ ++ return g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); ++} ++ ++gboolean ++bolt_error_propagate_stripped (GError **dest, ++ GError **source) ++{ ++ GError *src; ++ ++ g_return_val_if_fail (source != NULL, FALSE); ++ ++ src = *source; ++ ++ if (src == NULL) ++ return TRUE; ++ ++ if (g_dbus_error_is_remote_error (src)) ++ g_dbus_error_strip_remote_error (src); ++ ++ g_propagate_error (dest, g_steal_pointer (source)); ++ return FALSE; ++} +diff --git a/panels/thunderbolt/bolt-error.h b/panels/thunderbolt/bolt-error.h +new file mode 100644 +index 000000000000..39b3eee98917 +--- /dev/null ++++ b/panels/thunderbolt/bolt-error.h +@@ -0,0 +1,55 @@ ++/* ++ * Copyright © 2017 Red Hat, Inc ++ * ++ * This program 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, see . ++ * ++ * Authors: ++ * Christian J. Kellner ++ */ ++ ++#pragma once ++ ++#include ++ ++G_BEGIN_DECLS ++ ++/** ++ * BoltError: ++ * @BOLT_ERROR_FAILED: Generic error code ++ * @BOLT_ERROR_UDEV: UDev error ++ * ++ * Error codes used inside Bolt. ++ */ ++enum { ++ BOLT_ERROR_FAILED = 0, ++ BOLT_ERROR_UDEV, ++ BOLT_ERROR_NOKEY, ++ BOLT_ERROR_BADKEY, ++ BOLT_ERROR_CFG, ++} BoltError; ++ ++ ++GQuark bolt_error_quark (void); ++#define BOLT_ERROR (bolt_error_quark ()) ++ ++/* helper function to check for certain error types */ ++gboolean bolt_err_notfound (const GError *error); ++gboolean bolt_err_exists (const GError *error); ++gboolean bolt_err_inval (const GError *error); ++gboolean bolt_err_cancelled (const GError *error); ++ ++gboolean bolt_error_propagate_stripped (GError **dest, ++ GError **source); ++ ++G_END_DECLS +diff --git a/panels/thunderbolt/bolt-names.h b/panels/thunderbolt/bolt-names.h +new file mode 100644 +index 000000000000..2c0a97b24b49 +--- /dev/null ++++ b/panels/thunderbolt/bolt-names.h +@@ -0,0 +1,50 @@ ++/* ++ * Copyright © 2018 Red Hat, Inc ++ * ++ * This program 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, see . ++ * ++ * Authors: ++ * Christian J. Kellner ++ */ ++ ++#pragma once ++ ++/* D-Bus API revision (here for the lack of a better place) */ ++#define BOLT_DBUS_API_VERSION 1U ++ ++/* logging */ ++ ++#define BOLT_LOG_DEVICE_UID "BOLT_DEVICE_UID" ++#define BOLT_LOG_DEVICE_NAME "BOLT_DEVICE_NAME" ++#define BOLT_LOG_DEVICE_STATE "BOLT_DEVICE_STATE" ++ ++#define BOLT_LOG_ERROR_DOMAIN "ERROR_DOMAIN" ++#define BOLT_LOG_ERROR_CODE "ERROR_CODE" ++#define BOLT_LOG_ERROR_MESSAGE "ERROR_MESSAGE" ++ ++#define BOLT_LOG_TOPIC "BOLT_TOPIC" ++#define BOLT_LOG_VERSION "BOLT_VERSION" ++#define BOLT_LOG_CONTEXT "BOLT_LOG_CONTEXT" ++ ++/* logging - message ids */ ++#define BOLT_LOG_MSG_ID_STARTUP "dd11929c788e48bdbb6276fb5f26b08a" ++ ++ ++/* dbus */ ++ ++#define BOLT_DBUS_NAME "org.freedesktop.bolt" ++#define BOLT_DBUS_PATH "/org/freedesktop/bolt" ++#define BOLT_DBUS_INTERFACE "org.freedesktop.bolt1.Manager" ++ ++#define BOLT_DBUS_DEVICE_INTERFACE "org.freedesktop.bolt1.Device" +diff --git a/panels/thunderbolt/bolt-proxy.c b/panels/thunderbolt/bolt-proxy.c +new file mode 100644 +index 000000000000..e044c871f747 +--- /dev/null ++++ b/panels/thunderbolt/bolt-proxy.c +@@ -0,0 +1,514 @@ ++/* ++ * Copyright © 2017 Red Hat, Inc ++ * ++ * This program 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, see . ++ * ++ * Authors: ++ * Christian J. Kellner ++ */ ++ ++#include "bolt-proxy.h" ++ ++#include "bolt-enums.h" ++#include "bolt-error.h" ++#include "bolt-names.h" ++#include "bolt-str.h" ++ ++static void bolt_proxy_handle_props_changed (GDBusProxy *proxy, ++ GVariant *changed_properties, ++ GStrv invalidated_properties, ++ gpointer user_data); ++ ++static void bolt_proxy_handle_dbus_signal (GDBusProxy *proxy, ++ const gchar *sender_name, ++ const gchar *signal_name, ++ GVariant *params, ++ gpointer user_data); ++ ++G_DEFINE_TYPE (BoltProxy, bolt_proxy, G_TYPE_DBUS_PROXY); ++ ++ ++static void ++bolt_proxy_constructed (GObject *object) ++{ ++ G_OBJECT_CLASS (bolt_proxy_parent_class)->constructed (object); ++ ++ g_signal_connect (object, "g-properties-changed", ++ G_CALLBACK (bolt_proxy_handle_props_changed), object); ++ ++ g_signal_connect (object, "g-signal", ++ G_CALLBACK (bolt_proxy_handle_dbus_signal), object); ++} ++ ++static const BoltProxySignal * ++bolt_proxy_get_dbus_signals (guint *n) ++{ ++ *n = 0; ++ return NULL; ++} ++ ++static void ++bolt_proxy_class_init (BoltProxyClass *klass) ++{ ++ GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ++ ++ gobject_class->constructed = bolt_proxy_constructed; ++ ++ klass->get_dbus_signals = bolt_proxy_get_dbus_signals; ++ ++} ++ ++static void ++bolt_proxy_init (BoltProxy *object) ++{ ++} ++ ++static void ++bolt_proxy_handle_props_changed (GDBusProxy *proxy, ++ GVariant *changed_properties, ++ GStrv invalidated_properties, ++ gpointer user_data) ++{ ++ g_autoptr(GVariantIter) iter = NULL; ++ gboolean handled; ++ GParamSpec **pp; ++ const char *key; ++ guint n; ++ ++ pp = g_object_class_list_properties (G_OBJECT_GET_CLASS (proxy), &n); ++ ++ g_variant_get (changed_properties, "a{sv}", &iter); ++ while (g_variant_iter_next (iter, "{&sv}", &key, NULL)) ++ { ++ handled = FALSE; ++ for (guint i = 0; !handled && i < n; i++) ++ { ++ GParamSpec *pspec = pp[i]; ++ const char *nick; ++ const char *name; ++ ++ nick = g_param_spec_get_nick (pspec); ++ name = g_param_spec_get_name (pspec); ++ ++ handled = bolt_streq (nick, key); ++ ++ if (handled) ++ g_object_notify (G_OBJECT (user_data), name); ++ } ++ } ++ ++ g_free (pp); ++} ++ ++static void ++bolt_proxy_handle_dbus_signal (GDBusProxy *proxy, ++ const gchar *sender_name, ++ const gchar *signal_name, ++ GVariant *params, ++ gpointer user_data) ++{ ++ const BoltProxySignal *ps; ++ guint n; ++ ++ if (signal_name == NULL) ++ return; ++ ++ ps = BOLT_PROXY_GET_CLASS (proxy)->get_dbus_signals (&n); ++ ++ for (guint i = 0; i < n; i++) ++ { ++ const BoltProxySignal *sig = &ps[i]; ++ ++ if (g_str_equal (sig->theirs, signal_name)) ++ { ++ sig->handle (G_OBJECT (proxy), proxy, params); ++ break; ++ } ++ } ++ ++} ++ ++/* public methods */ ++ ++gboolean ++bolt_proxy_get_dbus_property (GObject *proxy, ++ GParamSpec *spec, ++ GValue *value) ++{ ++ g_autoptr(GVariant) val = NULL; ++ const GVariantType *vt; ++ gboolean handled = FALSE; ++ const char *nick; ++ ++ nick = g_param_spec_get_nick (spec); ++ val = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), nick); ++ ++ if (val == NULL) ++ return FALSE; ++ ++ vt = g_variant_get_type (val); ++ ++ if (g_variant_type_equal (vt, G_VARIANT_TYPE_STRING) && ++ G_IS_PARAM_SPEC_ENUM (spec)) ++ { ++ GParamSpecEnum *enum_spec = G_PARAM_SPEC_ENUM (spec); ++ GEnumValue *ev; ++ const char *str; ++ ++ str = g_variant_get_string (val, NULL); ++ ev = g_enum_get_value_by_nick (enum_spec->enum_class, str); ++ ++ handled = ev != NULL; ++ ++ if (handled) ++ g_value_set_enum (value, ev->value); ++ else ++ g_value_set_enum (value, enum_spec->default_value); ++ } ++ else if (g_variant_type_equal (vt, G_VARIANT_TYPE_STRING) && ++ G_IS_PARAM_SPEC_FLAGS (spec)) ++ { ++ GParamSpecFlags *flags_spec = G_PARAM_SPEC_FLAGS (spec); ++ GFlagsClass *flags_class = flags_spec->flags_class; ++ const char *str; ++ guint v; ++ ++ str = g_variant_get_string (val, NULL); ++ handled = bolt_flags_class_from_string (flags_class, str, &v, NULL); ++ ++ if (handled) ++ g_value_set_flags (value, v); ++ else ++ g_value_set_flags (value, flags_spec->default_value); ++ } ++ else ++ { ++ g_dbus_gvariant_to_gvalue (val, value); ++ } ++ ++ return handled; ++} ++ ++gboolean ++bolt_proxy_has_name_owner (BoltProxy *proxy) ++{ ++ const char *name_owner; ++ ++ g_return_val_if_fail (proxy != NULL, FALSE); ++ g_return_val_if_fail (BOLT_IS_PROXY (proxy), FALSE); ++ ++ name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (proxy)); ++ ++ return name_owner != NULL; ++} ++ ++static GParamSpec * ++find_property (BoltProxy *proxy, ++ const char *name, ++ GError **error) ++{ ++ GParamSpec *res = NULL; ++ GParamSpec **pp; ++ guint n; ++ ++ pp = g_object_class_list_properties (G_OBJECT_GET_CLASS (proxy), &n); ++ ++ for (guint i = 0; i < n; i++) ++ { ++ GParamSpec *pspec = pp[i]; ++ ++ if (bolt_streq (pspec->name, name)) ++ { ++ res = pspec; ++ break; ++ } ++ } ++ ++ if (pp == NULL) ++ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_PROPERTY, ++ "could not find property '%s'", name); ++ ++ g_free (pp); ++ return res; ++} ++ ++static GVariant * ++bolt_proxy_get_cached_property (BoltProxy *proxy, ++ const char *name) ++{ ++ const char *bus_name = NULL; ++ GParamSpec *pspec; ++ GVariant *var; ++ ++ g_return_val_if_fail (BOLT_IS_PROXY (proxy), NULL); ++ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (proxy), name); ++ ++ if (pspec == NULL) ++ return NULL; ++ ++ bus_name = g_param_spec_get_nick (pspec); ++ var = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), bus_name); ++ ++ return var; ++} ++ ++gboolean ++bolt_proxy_get_property_bool (BoltProxy *proxy, ++ const char *name, ++ gboolean *value) ++{ ++ g_autoptr(GVariant) var = NULL; ++ ++ var = bolt_proxy_get_cached_property (proxy, name); ++ ++ if (var == NULL) ++ return FALSE; ++ else if (value) ++ *value = g_variant_get_boolean (var); ++ ++ return TRUE; ++} ++ ++gboolean ++bolt_proxy_get_property_enum (BoltProxy *proxy, ++ const char *name, ++ gint *value) ++{ ++ g_autoptr(GVariant) var = NULL; ++ const char *str = NULL; ++ const char *bus_name = NULL; ++ GParamSpec *pspec; ++ GEnumValue *ev; ++ ++ g_return_val_if_fail (BOLT_IS_PROXY (proxy), FALSE); ++ ++ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (proxy), name); ++ ++ if (pspec == NULL) ++ return FALSE; ++ ++ bus_name = g_param_spec_get_nick (pspec); ++ var = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), bus_name); ++ if (var == NULL) ++ return FALSE; ++ ++ str = g_variant_get_string (var, NULL); ++ ++ if (str == NULL) ++ return FALSE; ++ ++ ev = g_enum_get_value_by_nick (G_PARAM_SPEC_ENUM (pspec)->enum_class, str); ++ ++ if (ev == NULL) ++ return FALSE; ++ ++ if (value) ++ *value = ev->value; ++ ++ return TRUE; ++} ++ ++gboolean ++bolt_proxy_get_property_flags (BoltProxy *proxy, ++ const char *name, ++ guint *value) ++{ ++ g_autoptr(GVariant) var = NULL; ++ const char *str = NULL; ++ const char *bus_name = NULL; ++ GFlagsClass *flags_class; ++ GParamSpec *pspec; ++ guint v; ++ gboolean ok; ++ ++ g_return_val_if_fail (BOLT_IS_PROXY (proxy), FALSE); ++ ++ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (proxy), name); ++ ++ if (pspec == NULL || !G_IS_PARAM_SPEC_FLAGS (pspec)) ++ return FALSE; ++ ++ bus_name = g_param_spec_get_nick (pspec); ++ var = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), bus_name); ++ if (var == NULL) ++ return FALSE; ++ ++ str = g_variant_get_string (var, NULL); ++ ++ if (str == NULL) ++ return FALSE; ++ ++ flags_class = G_PARAM_SPEC_FLAGS (pspec)->flags_class; ++ ok = bolt_flags_class_from_string (flags_class, str, &v, NULL); ++ ++ if (ok && value) ++ *value = v; ++ ++ return ok; ++} ++ ++gboolean ++bolt_proxy_get_property_uint32 (BoltProxy *proxy, ++ const char *name, ++ guint *value) ++{ ++ g_autoptr(GVariant) var = NULL; ++ ++ var = bolt_proxy_get_cached_property (proxy, name); ++ ++ if (var == NULL) ++ return FALSE; ++ else if (value) ++ *value = g_variant_get_uint32 (var); ++ ++ return TRUE; ++} ++ ++gboolean ++bolt_proxy_get_property_int64 (BoltProxy *proxy, ++ const char *name, ++ gint64 *value) ++{ ++ g_autoptr(GVariant) var = NULL; ++ ++ var = bolt_proxy_get_cached_property (proxy, name); ++ ++ if (var == NULL) ++ return FALSE; ++ else if (value) ++ *value = g_variant_get_int64 (var); ++ ++ return TRUE; ++} ++ ++gboolean ++bolt_proxy_get_property_uint64 (BoltProxy *proxy, ++ const char *name, ++ guint64 *value) ++{ ++ g_autoptr(GVariant) var = NULL; ++ ++ var = bolt_proxy_get_cached_property (proxy, name); ++ ++ if (var == NULL) ++ return FALSE; ++ else if (value) ++ *value = g_variant_get_uint64 (var); ++ ++ return TRUE; ++} ++ ++const char * ++bolt_proxy_get_property_string (BoltProxy *proxy, ++ const char *name) ++{ ++ g_autoptr(GVariant) var = NULL; ++ const char *val = NULL; ++ ++ var = bolt_proxy_get_cached_property (proxy, name); ++ ++ if (var != NULL) ++ val = g_variant_get_string (var, NULL); ++ ++ if (val && *val == '\0') ++ val = NULL; ++ ++ return val; ++} ++ ++gboolean ++bolt_proxy_set_property (BoltProxy *proxy, ++ const char *name, ++ GVariant *value, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GParamSpec *pp; ++ const char *iface; ++ gboolean ok = FALSE; ++ GVariant *res; ++ ++ pp = find_property (proxy, name, NULL); ++ if (pp != NULL) ++ name = g_param_spec_get_nick (pp); ++ ++ iface = g_dbus_proxy_get_interface_name (G_DBUS_PROXY (proxy)); ++ ++ res = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), ++ "org.freedesktop.DBus.Properties.Set", ++ g_variant_new ("(ssv)", ++ iface, ++ name, ++ value), ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, ++ cancellable, ++ error); ++ ++ if (res) ++ { ++ g_variant_unref (res); ++ ok = TRUE; ++ } ++ ++ return ok; ++} ++ ++void ++bolt_proxy_set_property_async (BoltProxy *proxy, ++ const char *name, ++ GVariant *value, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GParamSpec *pp; ++ const char *iface; ++ ++ pp = find_property (proxy, name, NULL); ++ ++ if (pp != NULL) ++ name = g_param_spec_get_nick (pp); ++ ++ iface = g_dbus_proxy_get_interface_name (G_DBUS_PROXY (proxy)); ++ ++ g_dbus_proxy_call (G_DBUS_PROXY (proxy), ++ "org.freedesktop.DBus.Properties.Set", ++ g_variant_new ("(ssv)", ++ iface, ++ name, ++ value), ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, ++ cancellable, ++ callback, ++ user_data); ++} ++ ++gboolean ++bolt_proxy_set_property_finish (GAsyncResult *res, ++ GError **error) ++{ ++ BoltProxy *proxy; ++ GVariant *val = NULL; ++ ++ proxy = (BoltProxy *) g_async_result_get_source_object (res); ++ val = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error); ++ ++ if (val == NULL) ++ return FALSE; ++ ++ g_variant_unref (val); ++ return TRUE; ++} +diff --git a/panels/thunderbolt/bolt-proxy.h b/panels/thunderbolt/bolt-proxy.h +new file mode 100644 +index 000000000000..c05eb8c8850f +--- /dev/null ++++ b/panels/thunderbolt/bolt-proxy.h +@@ -0,0 +1,97 @@ ++/* ++ * Copyright © 2017 Red Hat, Inc ++ * ++ * This program 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, see . ++ * ++ * Authors: ++ * Christian J. Kellner ++ */ ++ ++#pragma once ++ ++#include ++ ++G_BEGIN_DECLS ++ ++typedef struct BoltProxySignal ++{ ++ ++ const char *theirs; ++ void (*handle)(GObject *self, ++ GDBusProxy *bus_proxy, ++ GVariant *params); ++ ++} BoltProxySignal; ++ ++#define BOLT_TYPE_PROXY (bolt_proxy_get_type ()) ++G_DECLARE_DERIVABLE_TYPE (BoltProxy, bolt_proxy, BOLT, PROXY, GDBusProxy) ++ ++struct _BoltProxyClass ++{ ++ GDBusProxyClass parent; ++ ++ /* virtuals */ ++ const BoltProxySignal * (*get_dbus_signals) (guint *n); ++}; ++ ++gboolean bolt_proxy_get_dbus_property (GObject *proxy, ++ GParamSpec *spec, ++ GValue *value); ++ ++gboolean bolt_proxy_has_name_owner (BoltProxy *proxy); ++ ++gboolean bolt_proxy_get_property_bool (BoltProxy *proxy, ++ const char *name, ++ gboolean *value); ++ ++gboolean bolt_proxy_get_property_enum (BoltProxy *proxy, ++ const char *name, ++ gint *value); ++ ++gboolean bolt_proxy_get_property_flags (BoltProxy *proxy, ++ const char *name, ++ guint *value); ++ ++gboolean bolt_proxy_get_property_uint32 (BoltProxy *proxy, ++ const char *name, ++ guint *value); ++ ++gboolean bolt_proxy_get_property_int64 (BoltProxy *proxy, ++ const char *name, ++ gint64 *value); ++ ++gboolean bolt_proxy_get_property_uint64 (BoltProxy *proxy, ++ const char *name, ++ guint64 *value); ++ ++const char * bolt_proxy_get_property_string (BoltProxy *proxy, ++ const char *name); ++ ++gboolean bolt_proxy_set_property (BoltProxy *proxy, ++ const char *name, ++ GVariant *value, ++ GCancellable *cancellable, ++ GError **error); ++ ++void bolt_proxy_set_property_async (BoltProxy *proxy, ++ const char *name, ++ GVariant *value, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data); ++ ++gboolean bolt_proxy_set_property_finish (GAsyncResult *res, ++ GError **error); ++ ++G_END_DECLS +diff --git a/panels/thunderbolt/bolt-str.c b/panels/thunderbolt/bolt-str.c +new file mode 100644 +index 000000000000..fe0580d4863a +--- /dev/null ++++ b/panels/thunderbolt/bolt-str.c +@@ -0,0 +1,117 @@ ++/* ++ * Copyright © 2017 Red Hat, Inc ++ * ++ * This program 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, see . ++ * ++ * Authors: ++ * Christian J. Kellner ++ */ ++ ++#include "config.h" ++ ++#include "bolt-str.h" ++ ++#include ++ ++typedef void (* zero_fn_t) (void *s, ++ size_t n); ++void ++bolt_erase_n (void *data, gsize n) ++{ ++#if !HAVE_FN_EXPLICIT_BZERO ++ #warning no explicit bzero, using fallback ++ static volatile zero_fn_t explicit_bzero = bzero; ++#endif ++ ++ explicit_bzero (data, n); ++} ++ ++void ++bolt_str_erase (char *str) ++{ ++ if (str == NULL) ++ return; ++ ++ bolt_erase_n (str, strlen (str)); ++} ++ ++void ++bolt_str_erase_clear (char **str) ++{ ++ g_return_if_fail (str != NULL); ++ if (*str == NULL) ++ return; ++ ++ bolt_str_erase (*str); ++ g_free (*str); ++ *str = NULL; ++} ++ ++GStrv ++bolt_strv_from_ptr_array (GPtrArray **array) ++{ ++ GPtrArray *a; ++ ++ if (array == NULL || *array == NULL) ++ return NULL; ++ ++ a = *array; ++ ++ if (a->len == 0 || a->pdata[a->len - 1] != NULL) ++ g_ptr_array_add (a, NULL); ++ ++ *array = NULL; ++ return (GStrv) g_ptr_array_free (a, FALSE); ++} ++ ++char * ++bolt_strdup_validate (const char *string) ++{ ++ g_autofree char *str = NULL; ++ gboolean ok; ++ gsize l; ++ ++ if (string == NULL) ++ return NULL; ++ ++ str = g_strdup (string); ++ str = g_strstrip (str); ++ ++ l = strlen (str); ++ if (l == 0) ++ return NULL; ++ ++ ok = g_utf8_validate (str, l, NULL); ++ ++ if (!ok) ++ return NULL; ++ ++ return g_steal_pointer (&str); ++} ++ ++char * ++bolt_strstrip (char *string) ++{ ++ char *str; ++ ++ if (string == NULL) ++ return NULL; ++ ++ str = g_strstrip (string); ++ ++ if (strlen (str) == 0) ++ g_clear_pointer (&str, g_free); ++ ++ return str; ++} +diff --git a/panels/thunderbolt/bolt-str.h b/panels/thunderbolt/bolt-str.h +new file mode 100644 +index 000000000000..ecf95a7ed885 +--- /dev/null ++++ b/panels/thunderbolt/bolt-str.h +@@ -0,0 +1,43 @@ ++/* ++ * Copyright © 2017 Red Hat, Inc ++ * ++ * This program 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, see . ++ * ++ * Authors: ++ * Christian J. Kellner ++ */ ++ ++#pragma once ++ ++#include ++#include ++ ++G_BEGIN_DECLS ++ ++void bolt_erase_n (void *data, ++ gsize n); ++void bolt_str_erase (char *str); ++void bolt_str_erase_clear (char **str); ++ ++#define bolt_streq(s1, s2) (g_strcmp0 (s1, s2) == 0) ++ ++GStrv bolt_strv_from_ptr_array (GPtrArray **array); ++ ++#define bolt_yesno(val) val ? "yes" : "no" ++ ++char *bolt_strdup_validate (const char *string); ++ ++char *bolt_strstrip (char *string); ++ ++G_END_DECLS +diff --git a/panels/thunderbolt/bolt-time.c b/panels/thunderbolt/bolt-time.c +new file mode 100644 +index 000000000000..606aed69a444 +--- /dev/null ++++ b/panels/thunderbolt/bolt-time.c +@@ -0,0 +1,44 @@ ++/* ++ * Copyright © 2018 Red Hat, Inc ++ * ++ * This program 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, see . ++ * ++ * Authors: ++ * Christian J. Kellner ++ */ ++ ++#include "config.h" ++ ++#include "bolt-time.h" ++ ++char * ++bolt_epoch_format (guint64 seconds, const char *format) ++{ ++ g_autoptr(GDateTime) dt = NULL; ++ ++ dt = g_date_time_new_from_unix_utc ((gint64) seconds); ++ ++ if (dt == NULL) ++ return NULL; ++ ++ return g_date_time_format (dt, format); ++} ++ ++guint64 ++bolt_now_in_seconds (void) ++{ ++ gint64 now = g_get_real_time (); ++ ++ return (guint64) now / G_USEC_PER_SEC; ++} +diff --git a/panels/thunderbolt/bolt-time.h b/panels/thunderbolt/bolt-time.h +new file mode 100644 +index 000000000000..fc3ed9741940 +--- /dev/null ++++ b/panels/thunderbolt/bolt-time.h +@@ -0,0 +1,32 @@ ++/* ++ * Copyright © 2018 Red Hat, Inc ++ * ++ * This program 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, see . ++ * ++ * Authors: ++ * Christian J. Kellner ++ */ ++ ++#pragma once ++ ++#include ++ ++G_BEGIN_DECLS ++ ++char * bolt_epoch_format (guint64 seconds, ++ const char *format); ++ ++guint64 bolt_now_in_seconds (void); ++ ++G_END_DECLS +diff --git a/panels/thunderbolt/cc-bolt-device-dialog.c b/panels/thunderbolt/cc-bolt-device-dialog.c +new file mode 100644 +index 000000000000..11469d46cb0b +--- /dev/null ++++ b/panels/thunderbolt/cc-bolt-device-dialog.c +@@ -0,0 +1,476 @@ ++/* 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 . ++ * ++ * Authors: Christian J. Kellner ++ * ++ */ ++ ++#include ++ ++#include ++ ++#include "bolt-device.h" ++#include "bolt-error.h" ++#include "bolt-time.h" ++ ++#include "cc-thunderbolt-resources.h" ++ ++#include "cc-bolt-device-dialog.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; ++ ++ /* 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 ++dialog_authorize_done (CcBoltDeviceDialog *dialog, ++ gboolean ok, ++ GError *error) ++{ ++ if (!ok) ++ g_prefix_error (&error, _("Failed to authorize device: ")); ++ ++ dialog_operation_done (dialog, GTK_WIDGET (dialog->connect_button), error); ++} ++ ++static void ++on_device_authorized (GObject *source, ++ GAsyncResult *res, ++ gpointer user_data) ++{ ++ g_autoptr(GError) err = NULL; ++ CcBoltDeviceDialog *dialog = CC_BOLT_DEVICE_DIALOG (user_data); ++ gboolean ok; ++ ++ ok = bolt_device_authorize_finish (BOLT_DEVICE (source), res, &err); ++ dialog_authorize_done (dialog, ok, err); ++} ++ ++static void ++on_device_enrolled (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_enroll_device_finish (dialog->client, res, NULL, &err); ++ dialog_authorize_done (dialog, ok, err); ++} ++ ++static void ++on_connect_button_clicked_cb (CcBoltDeviceDialog *dialog) ++{ ++ BoltDevice *device = dialog->device; ++ gboolean stored; ++ ++ g_return_if_fail (device != NULL); ++ ++ dialog_operation_start (dialog); ++ ++ stored = bolt_device_is_stored (device); ++ if (stored) ++ { ++ bolt_device_authorize_async (device, ++ BOLT_AUTHCTRL_NONE, ++ dialog->cancel, ++ on_device_authorized, ++ dialog); ++ } ++ else ++ { ++ const char *uid = bolt_device_get_uid (device); ++ ++ bolt_client_enroll_device_async (dialog->client, ++ uid, ++ BOLT_POLICY_DEFAULT, ++ BOLT_AUTHCTRL_NONE, ++ dialog->cancel, ++ on_device_enrolled, ++ 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, 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)); ++} ++ ++/* 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) ++{ ++ 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); ++ } ++ ++ 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); ++} ++ ++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; ++} +diff --git a/panels/thunderbolt/cc-bolt-device-dialog.h b/panels/thunderbolt/cc-bolt-device-dialog.h +new file mode 100644 +index 000000000000..d2c44c8589a8 +--- /dev/null ++++ b/panels/thunderbolt/cc-bolt-device-dialog.h +@@ -0,0 +1,45 @@ ++/* 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 . ++ * ++ * Authors: Christian J. Kellner ++ * ++ */ ++ ++#pragma once ++ ++#include ++ ++#include "bolt-client.h" ++#include "bolt-device.h" ++ ++G_BEGIN_DECLS ++ ++#define CC_TYPE_BOLT_DEVICE_DIALOG cc_bolt_device_dialog_get_type () ++G_DECLARE_FINAL_TYPE (CcBoltDeviceDialog, cc_bolt_device_dialog, CC, BOLT_DEVICE_DIALOG, GtkDialog); ++ ++ ++CcBoltDeviceDialog * cc_bolt_device_dialog_new (void); ++ ++void cc_bolt_device_dialog_set_client (CcBoltDeviceDialog *dialog, ++ BoltClient *client); ++ ++void cc_bolt_device_dialog_set_device (CcBoltDeviceDialog *dialog, ++ BoltDevice *device); ++BoltDevice * cc_bolt_device_dialog_peek_device (CcBoltDeviceDialog *dialog); ++ ++gboolean cc_bolt_device_dialog_device_equal (CcBoltDeviceDialog *dialog, ++ BoltDevice *device); ++ ++G_END_DECLS +diff --git a/panels/thunderbolt/cc-bolt-device-dialog.ui b/panels/thunderbolt/cc-bolt-device-dialog.ui +new file mode 100644 +index 000000000000..cd19796db20d +--- /dev/null ++++ b/panels/thunderbolt/cc-bolt-device-dialog.ui +@@ -0,0 +1,359 @@ ++ ++ ++ ++ ++ ++ +diff --git a/panels/thunderbolt/cc-bolt-device-entry.c b/panels/thunderbolt/cc-bolt-device-entry.c +new file mode 100644 +index 000000000000..1e6d6e75a228 +--- /dev/null ++++ b/panels/thunderbolt/cc-bolt-device-entry.c +@@ -0,0 +1,218 @@ ++/* 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 . ++ * ++ * Authors: Christian J. Kellner ++ * ++ */ ++ ++#include ++ ++#include "bolt-str.h" ++ ++#include "cc-bolt-device-entry.h" ++ ++#include "cc-thunderbolt-resources.h" ++ ++#include ++ ++struct _CcBoltDeviceEntry ++{ ++ GtkListBoxRow parent; ++ ++ BoltDevice *device; ++ ++ /* main ui */ ++ GtkLabel *name_label; ++ GtkLabel *status_label; ++}; ++ ++static const char * device_status_to_brief_for_ui (BoltDevice *dev); ++ ++enum ++{ ++ SIGNAL_STATUS_CHANGED, ++ SIGNAL_LAST ++}; ++ ++static guint signals[SIGNAL_LAST] = {0}; ++ ++G_DEFINE_TYPE (CcBoltDeviceEntry, cc_bolt_device_entry, GTK_TYPE_LIST_BOX_ROW); ++ ++#define RESOURCE_UI "/org/gnome/control-center/thunderbolt/cc-bolt-device-entry.ui" ++ ++static void ++entry_set_name (CcBoltDeviceEntry *entry) ++{ ++ g_autofree char *name = NULL; ++ BoltDevice *dev = entry->device; ++ ++ g_return_if_fail (dev != NULL); ++ ++ name = bolt_device_get_display_name (dev); ++ ++ gtk_label_set_label (entry->name_label, name); ++} ++ ++static void ++entry_update_status (CcBoltDeviceEntry *entry) ++{ ++ const char *brief; ++ BoltStatus status; ++ ++ status = bolt_device_get_status (entry->device); ++ brief = device_status_to_brief_for_ui (entry->device); ++ ++ gtk_label_set_label (entry->status_label, brief); ++ ++ g_signal_emit (entry, ++ signals[SIGNAL_STATUS_CHANGED], ++ 0, ++ status); ++} ++ ++static void ++on_device_notify_cb (GObject *gobject, ++ GParamSpec *pspec, ++ gpointer user_data) ++{ ++ CcBoltDeviceEntry *entry = CC_BOLT_DEVICE_ENTRY (user_data); ++ const char *what; ++ ++ what = g_param_spec_get_name (pspec); ++ ++ if (bolt_streq (what, "status")) ++ entry_update_status (entry); ++ else if (bolt_streq (what, "label") || ++ bolt_streq (what, "name") || ++ bolt_streq (what, "vendor")) ++ entry_set_name (entry); ++} ++ ++/* device helpers */ ++ ++static const char * ++device_status_to_brief_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: ++ case BOLT_STATUS_AUTHORIZED_DPONLY: ++ return C_("Thunderbolt Device Status", "Connected"); ++ ++ case BOLT_STATUS_AUTH_ERROR: ++ return C_("Thunderbolt Device Status", "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: ++ if (nopcie) ++ return C_("Thunderbolt Device Status", "Connected"); ++ else ++ return C_("Thunderbolt Device Status", "Authorized"); ++ ++ case BOLT_STATUS_UNKNOWN: ++ break; /* use function default */ ++ } ++ ++ return C_("Thunderbolt Device Status", "Unknown"); ++} ++ ++static void ++cc_bolt_device_entry_finalize (GObject *object) ++{ ++ CcBoltDeviceEntry *entry = CC_BOLT_DEVICE_ENTRY (object); ++ ++ g_clear_object (&entry->device); ++ ++ G_OBJECT_CLASS (cc_bolt_device_entry_parent_class)->finalize (object); ++} ++ ++static void ++cc_bolt_device_entry_class_init (CcBoltDeviceEntryClass *klass) ++{ ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); ++ ++ object_class->finalize = cc_bolt_device_entry_finalize; ++ ++ gtk_widget_class_set_template_from_resource (widget_class, RESOURCE_UI); ++ gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceEntry, name_label); ++ gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceEntry, status_label); ++ ++ signals[SIGNAL_STATUS_CHANGED] = ++ g_signal_new ("status-changed", ++ G_TYPE_FROM_CLASS (object_class), ++ G_SIGNAL_RUN_LAST, ++ 0, ++ NULL, NULL, ++ NULL, ++ G_TYPE_NONE, ++ 1, BOLT_TYPE_STATUS); ++} ++ ++static void ++cc_bolt_device_entry_init (CcBoltDeviceEntry *entry) ++{ ++ g_resources_register (cc_thunderbolt_get_resource ()); ++ gtk_widget_init_template (GTK_WIDGET (entry)); ++} ++ ++/* public function */ ++ ++CcBoltDeviceEntry * ++cc_bolt_device_entry_new (BoltDevice *device) ++{ ++ CcBoltDeviceEntry *entry; ++ ++ entry = g_object_new (CC_TYPE_BOLT_DEVICE_ENTRY, NULL); ++ entry->device = g_object_ref (device); ++ ++ entry_set_name (entry); ++ entry_update_status (entry); ++ ++ g_signal_connect_object (entry->device, ++ "notify", ++ G_CALLBACK (on_device_notify_cb), ++ entry, ++ 0); ++ ++ return entry; ++} ++ ++BoltDevice * ++cc_bolt_device_entry_get_device (CcBoltDeviceEntry *entry) ++{ ++ g_return_val_if_fail (entry != NULL, NULL); ++ g_return_val_if_fail (CC_IS_BOLT_DEVICE_ENTRY (entry), NULL); ++ ++ return entry->device; ++} +diff --git a/panels/thunderbolt/cc-bolt-device-entry.h b/panels/thunderbolt/cc-bolt-device-entry.h +new file mode 100644 +index 000000000000..f93cee5f825b +--- /dev/null ++++ b/panels/thunderbolt/cc-bolt-device-entry.h +@@ -0,0 +1,34 @@ ++/* 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 . ++ * ++ * Authors: Christian J. Kellner ++ * ++ */ ++ ++#pragma once ++ ++#include ++#include "bolt-device.h" ++ ++G_BEGIN_DECLS ++ ++#define CC_TYPE_BOLT_DEVICE_ENTRY cc_bolt_device_entry_get_type () ++G_DECLARE_FINAL_TYPE (CcBoltDeviceEntry, cc_bolt_device_entry, CC, BOLT_DEVICE_ENTRY, GtkListBoxRow); ++ ++ ++CcBoltDeviceEntry * cc_bolt_device_entry_new (BoltDevice *device); ++BoltDevice * cc_bolt_device_entry_get_device (CcBoltDeviceEntry *entry); ++ ++G_END_DECLS +diff --git a/panels/thunderbolt/cc-bolt-device-entry.ui b/panels/thunderbolt/cc-bolt-device-entry.ui +new file mode 100644 +index 000000000000..37f865333d71 +--- /dev/null ++++ b/panels/thunderbolt/cc-bolt-device-entry.ui +@@ -0,0 +1,49 @@ ++ ++ ++ ++ ++ ++ +diff --git a/panels/thunderbolt/cc-bolt-panel.c b/panels/thunderbolt/cc-bolt-panel.c +new file mode 100644 +index 000000000000..e67e3625eb2c +--- /dev/null ++++ b/panels/thunderbolt/cc-bolt-panel.c +@@ -0,0 +1,958 @@ ++/* 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 . ++ * ++ * Authors: Christian J. Kellner ++ * ++ */ ++ ++#include ++ ++#include ++#include ++ ++#include ++#include ++ ++#include "cc-bolt-device-dialog.h" ++#include "cc-bolt-device-entry.h" ++ ++#include "bolt-client.h" ++#include "bolt-str.h" ++ ++#include "cc-thunderbolt-resources.h" ++ ++#define CC_TYPE_BOLT_PANEL cc_bolt_panel_get_type () ++G_DECLARE_FINAL_TYPE (CcBoltPanel, cc_bolt_panel, CC, BOLT_PANEL, CcPanel); ++ ++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); ++ ++/* device related helpers helpers */ ++static gint device_entries_sort_by_recency (GtkListBoxRow *a_row, ++ GtkListBoxRow *b_row, ++ gpointer user_data); ++ ++static gint device_entries_sort_by_syspath (GtkListBoxRow *a_row, ++ GtkListBoxRow *b_row, ++ gpointer user_data); ++ ++#define RESOURCE_PANEL_UI "/org/gnome/control-center/thunderbolt/cc-bolt-panel.ui" ++ ++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 setup 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); ++ ++ 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_SYNC_CREATE); ++ ++ g_object_bind_property (panel->authmode_switch, "active", ++ panel->pending_box, "sensitive", ++ 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(GError) err = NULL; ++ g_autoptr(GPtrArray) devices = NULL; ++ g_autoptr(GHashTable) old = NULL; ++ ++ devices = bolt_client_list_devices (panel->client, panel->cancel, &err); ++ ++ if (devices == NULL) ++ { ++ 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 (guint 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 == NULL) ++ 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) ++{ ++ BoltClient *client = panel->client; ++ BoltSecurity sl; ++ gboolean notb = TRUE; ++ const char *text = NULL; ++ const char *name_owner; ++ ++ 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 = NULL; ++ 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) ++{ ++ CcBoltPanel *panel = CC_BOLT_PANEL (user_data); ++ ++ cc_bolt_panel_name_owner_changed (panel); ++} ++ ++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 == NULL) ++ { ++ 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 == NULL) ++ 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) ++{ ++ CcBoltPanel *panel = CC_BOLT_PANEL (user_data); ++ ++ cc_bolt_panel_authmode_sync (panel); ++} ++ ++/* 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(GError) err = NULL; ++ g_autoptr(CcBoltPanel) panel = user_data; ++ GPermission *permission; ++ gboolean is_allowed; ++ const char *name; ++ ++ permission = polkit_permission_new_finish (res, &err); ++ panel->permission = permission; ++ ++ if (panel->permission == NULL) ++ { ++ 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 (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 (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); ++} ++ ++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 (&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, RESOURCE_PANEL_UI); ++ 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, container); ++ ++ 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, authmode_mode); ++ gtk_widget_class_bind_template_child (widget_class, CcBoltPanel, authmode_switch); ++ gtk_widget_class_bind_template_child (widget_class, CcBoltPanel, authmode_spinner); ++ ++ gtk_widget_class_bind_template_child (widget_class, CcBoltPanel, devices_stack); ++ gtk_widget_class_bind_template_child (widget_class, CcBoltPanel, devices_box); ++ gtk_widget_class_bind_template_child (widget_class, CcBoltPanel, pending_box); ++ gtk_widget_class_bind_template_child (widget_class, CcBoltPanel, devices_list); ++ 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, ++ panel, ++ NULL); ++ ++ gtk_list_box_set_sort_func (panel->pending_list, ++ device_entries_sort_by_syspath, ++ 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)); ++ ++} +diff --git a/panels/thunderbolt/cc-bolt-panel.ui b/panels/thunderbolt/cc-bolt-panel.ui +new file mode 100644 +index 000000000000..5ec6748600b9 +--- /dev/null ++++ b/panels/thunderbolt/cc-bolt-panel.ui +@@ -0,0 +1,594 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ False ++ False ++ 6 ++ end ++ ++ ++ True ++ ++ ++ ++ ++ +diff --git a/panels/thunderbolt/gnome-thunderbolt-panel.desktop.in.in b/panels/thunderbolt/gnome-thunderbolt-panel.desktop.in.in +new file mode 100644 +index 000000000000..db2477e45a74 +--- /dev/null ++++ b/panels/thunderbolt/gnome-thunderbolt-panel.desktop.in.in +@@ -0,0 +1,17 @@ ++[Desktop Entry] ++Name=Thunderbolt ++Comment=Manage Thunderbolt devices ++Exec=gnome-control-center thunderbolt ++Icon=thunderbolt ++Terminal=false ++Type=Application ++NoDisplay=true ++StartupNotify=true ++Categories=GNOME;GTK;Settings;X-GNOME-Settings-Panel;HardwareSettings;X-GNOME-DevicesSettings;X-GNOME-ConnectivitySettings; ++OnlyShowIn=GNOME;Unity; ++X-GNOME-Bugzilla-Bugzilla=GNOME ++X-GNOME-Bugzilla-Product=gnome-control-center ++X-GNOME-Bugzilla-Component=thunderbolt ++X-GNOME-Bugzilla-Version=@VERSION@ ++# Translators: those are keywords for the thunderbolt control-center panel. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! ++Keywords=Thunderbolt; +diff --git a/panels/thunderbolt/meson.build b/panels/thunderbolt/meson.build +new file mode 100644 +index 000000000000..e855661574fc +--- /dev/null ++++ b/panels/thunderbolt/meson.build +@@ -0,0 +1,74 @@ ++panels_list += cappletname ++ ++desktop = 'gnome-@0@-panel.desktop'.format(cappletname) ++desktop_in = configure_file( ++ input: desktop + '.in.in', ++ output: desktop + '.in', ++ configuration: desktop_conf ++) ++ ++i18n.merge_file( ++ desktop, ++ type: 'desktop', ++ input: desktop_in, ++ output: desktop, ++ po_dir: po_dir, ++ install: true, ++ install_dir: control_center_desktopdir ++) ++ ++sources = files( ++ 'bolt-client.c', ++ 'bolt-device.c', ++ 'bolt-enums.c', ++ 'bolt-error.c', ++ 'bolt-proxy.c', ++ 'bolt-str.c', ++ 'bolt-time.c', ++ 'cc-bolt-panel.c', ++ 'cc-bolt-device-dialog.c', ++ 'cc-bolt-device-entry.c', ++) ++ ++enum_headers = [ ++ 'bolt-enums.h', ++ 'bolt-error.h' ++] ++ ++sources += gnome.mkenums_simple( ++ 'bolt-enum-types', ++ sources: enum_headers) ++ ++resource_data = files( ++ 'cc-bolt-device-dialog.ui', ++ 'cc-bolt-device-entry.ui', ++ 'cc-bolt-panel.ui' ++) ++ ++sources += gnome.compile_resources( ++ 'cc-' + cappletname + '-resources', ++ cappletname + '.gresource.xml', ++ source_dir: '.', ++ c_name: 'cc_' + cappletname, ++ dependencies: resource_data, ++ export: true ++) ++ ++deps = common_deps + [ ++ gnome_desktop_dep, ++ polkit_gobject_dep, ++ m_dep, ++] ++ ++cflags += [ ++ '-DGNOMELOCALEDIR="@0@"'.format(control_center_localedir), ++ '-DBINDIR="@0@"'.format(control_center_bindir) ++] ++ ++panels_libs += static_library( ++ cappletname, ++ sources: sources, ++ include_directories: top_inc, ++ dependencies: deps, ++ c_args: cflags ++) +diff --git a/panels/thunderbolt/thunderbolt.gresource.xml b/panels/thunderbolt/thunderbolt.gresource.xml +new file mode 100644 +index 000000000000..8953d6243275 +--- /dev/null ++++ b/panels/thunderbolt/thunderbolt.gresource.xml +@@ -0,0 +1,9 @@ ++ ++ ++ ++ cc-bolt-device-dialog.ui ++ cc-bolt-device-entry.ui ++ cc-bolt-panel.ui ++ ++ ++ +diff --git a/panels/thunderbolt/update-from-bolt.sh b/panels/thunderbolt/update-from-bolt.sh +new file mode 100755 +index 000000000000..8b22f0831781 +--- /dev/null ++++ b/panels/thunderbolt/update-from-bolt.sh +@@ -0,0 +1,50 @@ ++#!/bin/bash ++ ++if [ $# -ne 1 ]; then ++ echo "$0: usage: " ++ exit 1 ++fi ++ ++boltsrc="$1" ++ ++function die() { ++ echo $* ++ exit 1 ++} ++ ++function copyone() { ++ dst=$1 ++ src="$boltsrc/$dst" ++ ++ search=(common cli) ++ for base in ${search[*]} ++ do ++ path="$boltsrc/$base/$dst" ++ if [ -f $path ]; then ++ src=$path ++ break; ++ fi ++ done ++ ++ if [ ! -f $src ]; then ++ echo -e "$dst \t[ skipped ] $src (ENOENT)" ++ elif cmp -s $src $dst; then ++ echo -e "$dst \t[ unchanged ]" ++ else ++ cp $src $dst || die "$dst [failed] source: $src" ++ echo -e "$dst \t[ updated ] $src" ++ git add $dst ++ fi ++} ++ ++names=(client device enums error names proxy str time) ++ ++for fn in ${names[*]} ++do ++ header="bolt-$fn.h" ++ source="bolt-$fn.c" ++ ++ copyone $header ++ copyone $source ++done ++ +diff --git a/shell/cc-panel-list.c b/shell/cc-panel-list.c +index 0fd093cf9758..99d8a91144ad 100644 +--- a/shell/cc-panel-list.c ++++ b/shell/cc-panel-list.c +@@ -276,6 +276,7 @@ static const gchar * const panel_order[] = { + "wifi", + "mobile-broadband", + "bluetooth", ++ "thunderbolt", + "background", + "notifications", + "search", +diff --git a/shell/cc-panel-loader.c b/shell/cc-panel-loader.c +index 675833c129d7..9b8aca5c6f9b 100644 +--- a/shell/cc-panel-loader.c ++++ b/shell/cc-panel-loader.c +@@ -54,6 +54,9 @@ extern GType cc_region_panel_get_type (void); + extern GType cc_search_panel_get_type (void); + extern GType cc_sharing_panel_get_type (void); + extern GType cc_sound_panel_get_type (void); ++#ifdef BUILD_THUNDERBOLT ++extern GType cc_bolt_panel_get_type (void); ++#endif /* BUILD_THUNDERBOLT */ + extern GType cc_ua_panel_get_type (void); + extern GType cc_user_panel_get_type (void); + #ifdef BUILD_WACOM +@@ -99,6 +102,9 @@ static struct { + PANEL_TYPE("search", cc_search_panel_get_type ), + PANEL_TYPE("sharing", cc_sharing_panel_get_type ), + PANEL_TYPE("sound", cc_sound_panel_get_type ), ++#ifdef BUILD_THUNDERBOLT ++ PANEL_TYPE("thunderbolt", cc_bolt_panel_get_type ), ++#endif + PANEL_TYPE("universal-access", cc_ua_panel_get_type ), + PANEL_TYPE("user-accounts", cc_user_panel_get_type ), + #ifdef BUILD_WACOM +-- +2.17.0 + diff --git a/SOURCES/0004-thunderbolt-move-to-the-Devices-page.patch b/SOURCES/0004-thunderbolt-move-to-the-Devices-page.patch new file mode 100644 index 0000000..23659fa --- /dev/null +++ b/SOURCES/0004-thunderbolt-move-to-the-Devices-page.patch @@ -0,0 +1,51 @@ +From 2d1da22e17f703e27ff1b3177e35a54aa0c3aecc Mon Sep 17 00:00:00 2001 +From: Christian Kellner +Date: Fri, 13 Apr 2018 16:03:21 +0200 +Subject: [PATCH 4/4] thunderbolt: move to the 'Devices' page + +The 'Devices' page is a fitting place for the thunderbolt, being +an IO technology. It is expected that people that need to go to +that page will be sent there via a gnome-shell notification, so +there is no need for it to be on the main page. +Ok'ed by the design team (jimmac). +--- + panels/thunderbolt/gnome-thunderbolt-panel.desktop.in.in | 2 +- + shell/cc-panel-list.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/panels/thunderbolt/gnome-thunderbolt-panel.desktop.in.in b/panels/thunderbolt/gnome-thunderbolt-panel.desktop.in.in +index db2477e45a74..abd317341bfd 100644 +--- a/panels/thunderbolt/gnome-thunderbolt-panel.desktop.in.in ++++ b/panels/thunderbolt/gnome-thunderbolt-panel.desktop.in.in +@@ -7,7 +7,7 @@ Terminal=false + Type=Application + NoDisplay=true + StartupNotify=true +-Categories=GNOME;GTK;Settings;X-GNOME-Settings-Panel;HardwareSettings;X-GNOME-DevicesSettings;X-GNOME-ConnectivitySettings; ++Categories=GNOME;GTK;Settings;X-GNOME-Settings-Panel;HardwareSettings;X-GNOME-DevicesSettings; + OnlyShowIn=GNOME;Unity; + X-GNOME-Bugzilla-Bugzilla=GNOME + X-GNOME-Bugzilla-Product=gnome-control-center +diff --git a/shell/cc-panel-list.c b/shell/cc-panel-list.c +index 99d8a91144ad..f5b83509d646 100644 +--- a/shell/cc-panel-list.c ++++ b/shell/cc-panel-list.c +@@ -276,7 +276,6 @@ static const gchar * const panel_order[] = { + "wifi", + "mobile-broadband", + "bluetooth", +- "thunderbolt", + "background", + "notifications", + "search", +@@ -295,6 +294,7 @@ static const gchar * const panel_order[] = { + "mouse", + "printers", + "removable-media", ++ "thunderbolt", + "wacom", + "color", + +-- +2.17.0 + diff --git a/SOURCES/distro-logo.patch b/SOURCES/distro-logo.patch new file mode 100644 index 0000000..57e9ad3 --- /dev/null +++ b/SOURCES/distro-logo.patch @@ -0,0 +1,99 @@ +From 73be5fcb0764cb8e7bdcbcf3ee06b833078d576a Mon Sep 17 00:00:00 2001 +From: Matthias Clasen +Date: Sun, 31 Mar 2013 20:28:19 -0400 +Subject: [PATCH] info: Switch around GNOME and distro information + +This makes the distribution logo prominent, and puts GNOME version +information in the small print. + +https://bugzilla.gnome.org/show_bug.cgi?id=695691 +--- + panels/info/cc-info-overview-panel.c | 7 ++----- + panels/info/info-overview.ui | 14 ++++++++------ + 2 files changed, 10 insertions(+), 11 deletions(-) + +diff --git a/panels/info/cc-info-overview-panel.c b/panels/info/cc-info-overview-panel.c +index 7a5879c6b..ce15e92d0 100644 +--- a/panels/info/cc-info-overview-panel.c ++++ b/panels/info/cc-info-overview-panel.c +@@ -446,7 +446,7 @@ static char * + get_os_name (void) + { + GHashTable *os_info; +- gchar *name, *version_id, *pretty_name, *build_id; ++ gchar *name, *version_id, *build_id; + gchar *result = NULL; + g_autofree gchar *name_version = NULL; + +@@ -457,12 +457,9 @@ get_os_name (void) + + name = g_hash_table_lookup (os_info, "NAME"); + version_id = g_hash_table_lookup (os_info, "VERSION_ID"); +- pretty_name = g_hash_table_lookup (os_info, "PRETTY_NAME"); + build_id = g_hash_table_lookup (os_info, "BUILD_ID"); + +- if (pretty_name) +- name_version = g_strdup (pretty_name); +- else if (name && version_id) ++ if (name && version_id) + name_version = g_strdup_printf ("%s %s", name, version_id); + else + name_version = g_strdup (_("Unknown")); +diff --git a/panels/info/info-overview.ui b/panels/info/info-overview.ui +index 219a83c4c..aa87fbec2 100644 +--- a/panels/info/info-overview.ui ++++ b/panels/info/info-overview.ui +@@ -12,13 +12,14 @@ + + True + False +- 18 ++ 6 + vertical + + + True + False +- /org/gnome/control-center/info/GnomeLogoVerticalMedium.svg ++ 128 ++ fedora-logo-icon + + + False +@@ -27,11 +28,12 @@ + + + +- ++ + True + False + Version 3.0 + True ++ 24 + + + +@@ -118,8 +120,8 @@ + True + False + 1 +- OS name +- os_name_label ++ GNOME ++ version_label + +@@ -228,7 +230,7 @@ + + + +- ++ + True + False + 0 +-- +2.13.0 + diff --git a/SPECS/gnome-control-center.spec b/SPECS/gnome-control-center.spec new file mode 100644 index 0000000..6f51383 --- /dev/null +++ b/SPECS/gnome-control-center.spec @@ -0,0 +1,228 @@ +%define gnome_online_accounts_version 3.25.3 +%define glib2_version 2.53.0 +%define gnome_desktop_version 3.27.90 +%define gsd_version 3.25.90 +%define gsettings_desktop_schemas_version 3.27.2 +%define gtk3_version 3.22.20 +%define upower_version 0.99.6 +%define cheese_version 3.28.0 +%define gnome_bluetooth_version 3.18.2 + +Name: gnome-control-center +Version: 3.28.2 +Release: 4%{?dist} +Summary: Utilities to configure the GNOME desktop + +License: GPLv2+ and CC-BY-SA +URL: http://www.gnome.org +Source0: https://download.gnome.org/sources/gnome-control-center/3.28/gnome-control-center-%{version}.tar.xz + +# https://bugzilla.gnome.org/show_bug.cgi?id=695691 +Patch0: distro-logo.patch +# thunderbolt panel backported to 3.28.x +# https://gitlab.gnome.org/gicmo/gnome-control-center/commits/thunderbolt_3_28_1 +Patch1: 0001-shell-Don-t-set-per-panel-icon.patch +Patch2: 0002-shell-Icon-name-helper-returns-symbolic-name.patch +Patch3: 0003-thunderbolt-new-panel-for-device-management.patch +Patch4: 0004-thunderbolt-move-to-the-Devices-page.patch + +# Backport of F29 screen sharing UI +Patch5: 0001-sharing-Enable-settings-widget-for-gnome-remote-desk.patch + +Patch6: 0001-wacom-Update-Test-your-settings-button-sensitivity-o.patch + +BuildRequires: chrpath +BuildRequires: cups-devel +BuildRequires: desktop-file-utils +BuildRequires: docbook-style-xsl libxslt +BuildRequires: gettext +BuildRequires: libXxf86misc-devel +BuildRequires: meson +BuildRequires: pkgconfig(accountsservice) +BuildRequires: pkgconfig(cheese) >= %{cheese_version} +BuildRequires: pkgconfig(cheese-gtk) +BuildRequires: pkgconfig(clutter-gtk-1.0) +BuildRequires: pkgconfig(colord) +BuildRequires: pkgconfig(colord-gtk) +BuildRequires: pkgconfig(gdk-pixbuf-2.0) +BuildRequires: pkgconfig(gdk-wayland-3.0) +BuildRequires: pkgconfig(gio-2.0) >= %{glib2_version} +BuildRequires: pkgconfig(gnome-desktop-3.0) >= %{gnome_desktop_version} +BuildRequires: pkgconfig(gnome-settings-daemon) >= %{gsd_version} +BuildRequires: pkgconfig(goa-1.0) >= %{gnome_online_accounts_version} +BuildRequires: pkgconfig(goa-backend-1.0) +BuildRequires: pkgconfig(grilo-0.3) +BuildRequires: pkgconfig(gsettings-desktop-schemas) >= %{gsettings_desktop_schemas_version} +BuildRequires: pkgconfig(gtk+-3.0) >= %{gtk3_version} +BuildRequires: pkgconfig(gudev-1.0) +BuildRequires: pkgconfig(ibus-1.0) +BuildRequires: pkgconfig(libcanberra-gtk3) +BuildRequires: pkgconfig(libgtop-2.0) +BuildRequires: pkgconfig(libnm) +BuildRequires: pkgconfig(libnma) +BuildRequires: pkgconfig(libpulse) +BuildRequires: pkgconfig(libpulse-mainloop-glib) +BuildRequires: pkgconfig(libsecret-1) +BuildRequires: pkgconfig(libsoup-2.4) +BuildRequires: pkgconfig(libxml-2.0) +BuildRequires: pkgconfig(mm-glib) +BuildRequires: pkgconfig(polkit-gobject-1) +BuildRequires: pkgconfig(pwquality) +BuildRequires: pkgconfig(smbclient) +BuildRequires: pkgconfig(upower-glib) >= %{upower_version} +BuildRequires: pkgconfig(x11) +BuildRequires: pkgconfig(xi) +%ifnarch s390 s390x +BuildRequires: pkgconfig(gnome-bluetooth-1.0) >= %{gnome_bluetooth_version} +BuildRequires: pkgconfig(libwacom) +%endif + +# Versioned library deps +Requires: cheese-libs%{?_isa} >= %{cheese_version} +Requires: glib2%{?_isa} >= %{glib2_version} +Requires: gnome-desktop3%{?_isa} >= %{gnome_desktop_version} +Requires: gnome-online-accounts%{?_isa} >= %{gnome_online_accounts_version} +Requires: gnome-settings-daemon%{?_isa} >= %{gsd_version} +Requires: gsettings-desktop-schemas%{?_isa} >= %{gsettings_desktop_schemas_version} +Requires: gtk3%{?_isa} >= %{gtk3_version} +Requires: upower%{?_isa} >= %{upower_version} +%ifnarch s390 s390x +Requires: gnome-bluetooth%{?_isa} >= 1:%{gnome_bluetooth_version} +%endif + +Requires: %{name}-filesystem = %{version}-%{release} +# For user accounts +Requires: accountsservice +Requires: alsa-lib +# For the thunderbolt panel +Requires: bolt +# For the color panel +Requires: colord +# For the printers panel +Requires: cups-pk-helper +Requires: dbus-x11 +# For the info/details panel +Requires: glx-utils +# For the user languages +Requires: iso-codes +# For the network panel +Requires: nm-connection-editor +Recommends: NetworkManager-wifi +%if 0%{?fedora} +# For the sharing panel +Requires: rygel +%endif +# For the info/details panel +Requires: switcheroo-control +# For the keyboard panel +Requires: /usr/bin/gkbd-keyboard-display + +Recommends: vino +Recommends: system-config-printer-libs + +# Renamed in F28 +Provides: control-center = 1:%{version}-%{release} +Provides: control-center%{?_isa} = 1:%{version}-%{release} +Obsoletes: control-center < 1:%{version}-%{release} + +%description +This package contains configuration utilities for the GNOME desktop, which +allow to configure accessibility options, desktop fonts, keyboard and mouse +properties, sound setup, desktop theme and background, user interface +properties, screen resolution, and other settings. + +%package filesystem +Summary: GNOME Control Center directories +# NOTE: this is an "inverse dep" subpackage. It gets pulled in +# NOTE: by the main package and MUST not depend on the main package +BuildArch: noarch +# Renamed in F28 +Provides: control-center-filesystem = 1:%{version}-%{release} +Obsoletes: control-center-filesystem < 1:%{version}-%{release} + +%description filesystem +The GNOME control-center provides a number of extension points +for applications. This package contains directories where applications +can install configuration files that are picked up by the control-center +utilities. + +%prep +%autosetup -p1 + +%build +%meson -Ddocumentation=true +%meson_build + +%install +%meson_install + +# We do want this +mkdir -p $RPM_BUILD_ROOT%{_datadir}/gnome/wm-properties + +# We don't want these +rm -rf $RPM_BUILD_ROOT%{_datadir}/gnome/autostart +rm -rf $RPM_BUILD_ROOT%{_datadir}/gnome/cursor-fonts + +# Remove rpath +chrpath --delete $RPM_BUILD_ROOT%{_bindir}/gnome-control-center + +%find_lang %{name} --all-name --with-gnome + +%files -f %{name}.lang +%license COPYING +%doc AUTHORS NEWS README +%{_bindir}/gnome-control-center +%{_datadir}/applications/*.desktop +%{_datadir}/bash-completion/completions/gnome-control-center +%{_datadir}/dbus-1/services/org.gnome.ControlCenter.SearchProvider.service +%{_datadir}/dbus-1/services/org.gnome.ControlCenter.service +%{_datadir}/gettext/ +%{_datadir}/glib-2.0/schemas/org.gnome.ControlCenter.gschema.xml +%{_datadir}/gnome-control-center/icons/ +%{_datadir}/gnome-control-center/keybindings/*.xml +%{_datadir}/gnome-control-center/pixmaps +%{_datadir}/gnome-control-center/sounds/gnome-sounds-default.xml +%{_datadir}/gnome-shell/search-providers/gnome-control-center-search-provider.ini +%{_datadir}/icons/hicolor/*/*/* +%{_datadir}/man/man1/gnome-control-center.1* +%{_datadir}/metainfo/gnome-control-center.appdata.xml +%{_datadir}/pixmaps/faces +%{_datadir}/pkgconfig/gnome-keybindings.pc +%{_datadir}/polkit-1/actions/org.gnome.controlcenter.*.policy +%{_datadir}/polkit-1/rules.d/gnome-control-center.rules +%{_datadir}/sounds/gnome/default/*/*.ogg +%{_libexecdir}/cc-remote-login-helper +%{_libexecdir}/gnome-control-center-search-provider + +%files filesystem +%dir %{_datadir}/gnome-control-center +%dir %{_datadir}/gnome-control-center/keybindings +%dir %{_datadir}/gnome-control-center/sounds +%dir %{_datadir}/gnome/wm-properties + +%changelog +* Mon Feb 11 2019 Carlos Garnacho - 3.28.2-4 +- Update "Test your settings" wacom button sensitivity on device availability +- Resolves: #1656995 + +* Thu Dec 13 2018 Marek Kasik - 3.28.2-3 +- Recommend system-config-printer-libs as a dependency +- Resolves: #1637370 + +* Tue Aug 14 2018 Jonas Ådahl - 3.28.2-1 +- Backport screen sharing UI (rhbz#1615810) + +* Tue May 29 2018 Kalev Lember - 3.28.2-1 +- Update to 3.28.2 + +* Wed May 23 2018 Pete Walter - 3.28.1-4 +- Change NetworkManager-wifi requires to recommends (#1478661) + +* Tue May 22 2018 Ray Strode - 3.28.1-3 +- Change vino requires to a vino recommends + +* Fri Apr 13 2018 Kalev Lember - 3.28.1-2 +- Backport new thunderbolt panel + +* Tue Apr 10 2018 Pete Walter - 3.28.1-1 +- Rename control-center to gnome-control-center