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