From 92fd2634c3de68fd55bdba469a9f0cca205467b2 Mon Sep 17 00:00:00 2001 From: guibuilder_pel7x64builder0 Date: Mon, 26 Nov 2018 17:14:16 +0100 Subject: [PATCH] control-center package update Signed-off-by: guibuilder_pel7x64builder0 --- ...ft-hyphens-from-japanese-translation.patch | 144 + ...x-compilation-with-RHEL-cups-version.patch | 36 + ...mouse-Handle-Synaptics-devices-again.patch | 153 + SOURCES/control-center-python3.patch | 8 + SOURCES/distro-logo.patch | 99 + SOURCES/gnuc99-standard.patch | 26 + SOURCES/thunderbolt-panel.patch | 6564 +++++++++++++++++ SPECS/control-center.spec | 978 +++ 8 files changed, 8008 insertions(+) create mode 100644 SOURCES/0001-po-Remove-soft-hyphens-from-japanese-translation.patch create mode 100644 SOURCES/0001-printers-Fix-compilation-with-RHEL-cups-version.patch create mode 100644 SOURCES/0002-mouse-Handle-Synaptics-devices-again.patch create mode 100644 SOURCES/control-center-python3.patch create mode 100644 SOURCES/distro-logo.patch create mode 100644 SOURCES/gnuc99-standard.patch create mode 100644 SOURCES/thunderbolt-panel.patch create mode 100644 SPECS/control-center.spec diff --git a/SOURCES/0001-po-Remove-soft-hyphens-from-japanese-translation.patch b/SOURCES/0001-po-Remove-soft-hyphens-from-japanese-translation.patch new file mode 100644 index 0000000..116e0e4 --- /dev/null +++ b/SOURCES/0001-po-Remove-soft-hyphens-from-japanese-translation.patch @@ -0,0 +1,144 @@ +From 370f8df6d298353e2b2c02756255be79e811cffd Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Fri, 15 Jun 2018 20:19:03 +0200 +Subject: [PATCH] po: Remove soft hyphens from japanese translation + +--- + po/ja.po | 30 ++++++++++++------------------ + 1 file changed, 12 insertions(+), 18 deletions(-) + +diff --git a/po/ja.po b/po/ja.po +index 5f4767f39..65322c7f9 100644 +--- a/po/ja.po ++++ b/po/ja.po +@@ -166,7 +166,7 @@ msgstr "現在の背景" + + #. Translators: Add soft hyphens to your translations so that the icon view won't clip your translations. See https://bugzilla.gnome.org/show_bug.cgi?id=647087#c13 for details + #: ../panels/background/gnome-background-panel.desktop.in.in.h:2 +-msgid "Back­ground" ++msgid "Background" + msgstr "背景" + + #: ../panels/background/gnome-background-panel.desktop.in.in.h:3 +@@ -861,7 +861,7 @@ msgstr "D75" + + #. Translators: Add soft hyphens to your translations so that the icon view won't clip your translations. See https://bugzilla.gnome.org/show_bug.cgi?id=647087#c13 for details + #: ../panels/color/gnome-color-panel.desktop.in.in.h:2 +-msgid "Co­lor" ++msgid "Color" + msgstr "色管理" + + #: ../panels/color/gnome-color-panel.desktop.in.in.h:3 +@@ -1312,7 +1312,7 @@ msgstr "終了" + + #. Translators: Add soft hyphens to your translations so that the icon view won't clip your translations. See https://bugzilla.gnome.org/show_bug.cgi?id=647087#c13 for details + #: ../panels/display/gnome-display-panel.desktop.in.in.h:2 +-msgid "Dis­plays" ++msgid "Displays" + msgstr "ディスプレイ" + + #: ../panels/display/gnome-display-panel.desktop.in.in.h:3 +@@ -1486,7 +1486,7 @@ msgstr "" + + #. Translators: Add soft hyphens to your translations so that the icon view won't clip your translations. See https://bugzilla.gnome.org/show_bug.cgi?id=647087#c13 for details + #: ../panels/info/gnome-info-overview-panel.desktop.in.in.h:2 +-msgid "Ab­out" ++msgid "About" + msgstr "情報" + + #: ../panels/info/gnome-info-overview-panel.desktop.in.in.h:3 +@@ -1913,7 +1913,7 @@ msgstr "ショートカットの追加" + + #. Translators: Add soft hyphens to your translations so that the icon view won't clip your translations. See https://bugzilla.gnome.org/show_bug.cgi?id=647087#c13 for details + #: ../panels/keyboard/gnome-keyboard-panel.desktop.in.in.h:2 +-msgid "Key­board" ++msgid "Keyboard" + msgstr "キーボード" + + #: ../panels/keyboard/gnome-keyboard-panel.desktop.in.in.h:3 +@@ -2006,7 +2006,7 @@ msgstr "設定を確認する(_S)" + + #. Translators: Add soft hyphens to your translations so that the icon view won't clip your translations. See https://bugzilla.gnome.org/show_bug.cgi?id=647087#c13 for details + #: ../panels/mouse/gnome-mouse-panel.desktop.in.in.h:2 +-msgid "Mouse & Touch­pad" ++msgid "Mouse & Touchpad" + msgstr "マウスとタッチパッド" + + #: ../panels/mouse/gnome-mouse-panel.desktop.in.in.h:3 +@@ -3751,7 +3751,7 @@ msgstr "ロック画面にメッセージの内容を表示する(_O)" + + #. Translators: Add soft hyphens to your translations so that the icon view won't clip your translations. See https://bugzilla.gnome.org/show_bug.cgi?id=647087#c13 for details + #: ../panels/notifications/gnome-notifications-panel.desktop.in.in.h:2 +-msgid "No­ti­fi­ca­tions" ++msgid "Notifications" + msgstr "通知" + + #: ../panels/notifications/gnome-notifications-panel.desktop.in.in.h:3 +@@ -3805,7 +3805,7 @@ msgstr "%s 削除しました" + + #. Translators: Add soft hyphens to your translations so that the icon view won't clip your translations. See https://bugzilla.gnome.org/show_bug.cgi?id=647087#c13 for details + #: ../panels/online-accounts/gnome-online-accounts-panel.desktop.in.in.h:2 +-msgid "On­line Accounts" ++msgid "Online Accounts" + msgstr "オンラインアカウント" + + #: ../panels/online-accounts/gnome-online-accounts-panel.desktop.in.in.h:3 +@@ -4110,7 +4110,7 @@ msgstr "デバイス" + + #. Translators: Add soft hyphens to your translations so that the icon view won't clip your translations. See https://bugzilla.gnome.org/show_bug.cgi?id=647087#c13 for details + #: ../panels/power/gnome-power-panel.desktop.in.in.h:2 +-msgid "Po­wer" ++msgid "Power" + msgstr "電源管理" + + #: ../panels/power/gnome-power-panel.desktop.in.in.h:3 +@@ -4262,7 +4262,7 @@ msgstr "PPDファイルのインストール…" + + #. Translators: Add soft hyphens to your translations so that the icon view won't clip your translations. See https://bugzilla.gnome.org/show_bug.cgi?id=647087#c13 for details + #: ../panels/printers/gnome-printers-panel.desktop.in.in.h:2 +-msgid "Prin­ters" ++msgid "Printers" + msgstr "プリンター" + + #: ../panels/printers/gnome-printers-panel.desktop.in.in.h:3 +@@ -4894,7 +4894,7 @@ msgstr "プライバシーポリシー" + + #. Translators: Add soft hyphens to your translations so that the icon view won't clip your translations. See https://bugzilla.gnome.org/show_bug.cgi?id=647087#c13 for details + #: ../panels/privacy/gnome-privacy-panel.desktop.in.in.h:2 +-msgid "Pri­va­cy" ++msgid "Privacy" + msgstr "プライバシー" + + #: ../panels/privacy/gnome-privacy-panel.desktop.in.in.h:3 +@@ -5124,7 +5124,7 @@ msgstr "用紙サイズ" + + #. Translators: Add soft hyphens to your translations so that the icon view won't clip your translations. See https://bugzilla.gnome.org/show_bug.cgi?id=647087#c13 for details + #: ../panels/region/gnome-region-panel.desktop.in.in.h:2 +-msgid "Re­gion & Lan­guage" ++msgid "Region & Language" + msgstr "地域と言語" + + #: ../panels/region/gnome-region-panel.desktop.in.in.h:3 +@@ -7621,9 +7621,6 @@ msgstr "結果がありません" + #~ msgid "Your account" + #~ msgstr "あなたのアカウント" + +-#~ msgid "Color" +-#~ msgstr "色" +- + #~ msgid "Wayland" + #~ msgstr "Wayland" + +@@ -7843,9 +7840,6 @@ msgstr "結果がありません" + #~ msgid "Print _Test Page" + #~ msgstr "テストページを印刷(_T)" + +-#~ msgid "Privacy" +-#~ msgstr "プライバシー" +- + #~ msgctxt "Search Location" + #~ msgid "Other" + #~ msgstr "その他" +-- +2.14.3 + diff --git a/SOURCES/0001-printers-Fix-compilation-with-RHEL-cups-version.patch b/SOURCES/0001-printers-Fix-compilation-with-RHEL-cups-version.patch new file mode 100644 index 0000000..e35d1ee --- /dev/null +++ b/SOURCES/0001-printers-Fix-compilation-with-RHEL-cups-version.patch @@ -0,0 +1,36 @@ +From 5c7cc82171f1312c9af2df6b5d5cc07c5f17be50 Mon Sep 17 00:00:00 2001 +From: Rui Matos +Date: Mon, 23 Oct 2017 16:06:14 +0200 +Subject: [PATCH] printers: Fix compilation with RHEL cups version + +--- + panels/printers/pp-printer.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/panels/printers/pp-printer.c b/panels/printers/pp-printer.c +index 79a6301a0..bec3b962c 100644 +--- a/panels/printers/pp-printer.c ++++ b/panels/printers/pp-printer.c +@@ -551,7 +551,7 @@ print_file_thread (GTask *task, + { + if (ippGetState (response) == IPP_ERROR) + g_warning ("An error has occured during printing of test page."); +- if (ippGetState (response) == IPP_STATE_IDLE) ++ if (ippGetState (response) == IPP_IDLE) + ret = TRUE; + + ippDelete (response); +-- +2.13.6 + +--- gnome-control-center-3.28.1/panels/printers/pp-job.c.orig 2018-04-10 15:18:04.000000000 +0200 ++++ gnome-control-center-3.28.1/panels/printers/pp-job.c 2018-05-17 16:52:50.139979170 +0200 +@@ -449,7 +449,7 @@ + + length = g_strv_length (auth_info); + +- request = ippNewRequest (IPP_OP_CUPS_AUTHENTICATE_JOB); ++ request = ippNewRequest (CUPS_AUTHENTICATE_JOB); + ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, + "job-uri", NULL, job_uri); + ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME, diff --git a/SOURCES/0002-mouse-Handle-Synaptics-devices-again.patch b/SOURCES/0002-mouse-Handle-Synaptics-devices-again.patch new file mode 100644 index 0000000..fb33a5e --- /dev/null +++ b/SOURCES/0002-mouse-Handle-Synaptics-devices-again.patch @@ -0,0 +1,153 @@ +From 6881c089a00b184358de3e6c80f2711f59ec5e04 Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Mon, 13 Mar 2017 19:58:42 +0100 +Subject: [PATCH 2/3] mouse: Handle Synaptics devices again + +If mutter can handle those, so can g-c-c +--- + panels/mouse/cc-mouse-caps-helper.c | 85 ++++++++++++++++++++++++++++++++--- + panels/mouse/gnome-mouse-properties.c | 10 +---- + 2 files changed, 79 insertions(+), 16 deletions(-) + +diff --git a/panels/mouse/cc-mouse-caps-helper.c b/panels/mouse/cc-mouse-caps-helper.c +index 65e11df67..521f318d4 100644 +--- a/panels/mouse/cc-mouse-caps-helper.c ++++ b/panels/mouse/cc-mouse-caps-helper.c +@@ -25,9 +25,72 @@ + #include "cc-mouse-caps-helper.h" + + static gboolean +-touchpad_check_capabilities_x11 (gboolean *have_two_finger_scrolling, +- gboolean *have_edge_scrolling, +- gboolean *have_tap_to_click) ++touchpad_check_capabilities_x11_synaptics (gboolean *have_two_finger_scrolling, ++ gboolean *have_edge_scrolling, ++ gboolean *have_tap_to_click) ++{ ++ Display *display; ++ GList *devicelist, *l; ++ Atom realtype, prop_two_finger_scroll, prop_edge_scroll, prop_tap_action; ++ int realformat; ++ unsigned long nitems, bytes_after; ++ unsigned char *data; ++ ++ display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); ++ prop_two_finger_scroll = XInternAtom (display, "Synaptics Two-Finger Scrolling", False); ++ prop_edge_scroll = XInternAtom (display, "Synaptics Edge Scrolling", False); ++ prop_tap_action = XInternAtom (display, "Synaptics Tap Action", False); ++ if (!prop_two_finger_scroll || !prop_edge_scroll || !prop_tap_action) ++ return FALSE; ++ ++ *have_two_finger_scrolling = FALSE; ++ *have_edge_scrolling = FALSE; ++ *have_tap_to_click = FALSE; ++ ++ gdk_error_trap_push (); ++ ++ devicelist = gdk_seat_get_slaves (gdk_display_get_default_seat (gdk_display_get_default ()), ++ GDK_SEAT_CAPABILITY_ALL_POINTING); ++ for (l = devicelist; l != NULL; l = l->next) { ++ GdkDevice *device = l->data; ++ ++ if (gdk_device_get_source (device) != GDK_SOURCE_TOUCHPAD && ++ gdk_device_get_source (device) != GDK_SOURCE_MOUSE) ++ continue; ++ ++ /* xorg-x11-drv-synaptics */ ++ if ((XIGetProperty (display, gdk_x11_device_get_id (device), prop_two_finger_scroll, ++ 0, 2, False, XA_INTEGER, &realtype, &realformat, &nitems, ++ &bytes_after, &data) == Success) && (realtype != None)) { ++ *have_two_finger_scrolling = TRUE; ++ XFree (data); ++ } ++ ++ if ((XIGetProperty (display, gdk_x11_device_get_id (device), prop_edge_scroll, ++ 0, 3, False, XA_INTEGER, &realtype, &realformat, &nitems, ++ &bytes_after, &data) == Success) && (realtype != None)) { ++ *have_edge_scrolling = TRUE; ++ XFree (data); ++ } ++ ++ if ((XIGetProperty (display, gdk_x11_device_get_id (device), prop_tap_action, ++ 0, 8, False, XA_INTEGER, &realtype, &realformat, &nitems, ++ &bytes_after, &data) == Success) && (realtype != None)) { ++ *have_tap_to_click = TRUE; ++ XFree (data); ++ } ++ } ++ g_list_free (devicelist); ++ ++ gdk_error_trap_pop_ignored (); ++ ++ return TRUE; ++} ++ ++static gboolean ++touchpad_check_capabilities_x11_libinput (gboolean *have_two_finger_scrolling, ++ gboolean *have_edge_scrolling, ++ gboolean *have_tap_to_click) + { + Display *display; + GList *devicelist, *l; +@@ -91,10 +154,18 @@ cc_touchpad_check_capabilities (gboolean *have_two_finger_scrolling, + gboolean *have_edge_scrolling, + gboolean *have_tap_to_click) + { +- if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) +- return touchpad_check_capabilities_x11 (have_two_finger_scrolling, +- have_edge_scrolling, +- have_tap_to_click); ++ if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) { ++ if (cc_synaptics_check ()) { ++ return touchpad_check_capabilities_x11_synaptics (have_two_finger_scrolling, ++ have_edge_scrolling, ++ have_tap_to_click); ++ } ++ ++ return touchpad_check_capabilities_x11_libinput (have_two_finger_scrolling, ++ have_edge_scrolling, ++ have_tap_to_click); ++ } ++ + /* else we unconditionally show all touchpad knobs */ + *have_two_finger_scrolling = TRUE; + *have_edge_scrolling = TRUE; +diff --git a/panels/mouse/gnome-mouse-properties.c b/panels/mouse/gnome-mouse-properties.c +index 95a74e418..e2053a5cd 100644 +--- a/panels/mouse/gnome-mouse-properties.c ++++ b/panels/mouse/gnome-mouse-properties.c +@@ -61,7 +61,6 @@ struct _CcMousePropertiesPrivate + gboolean have_mouse; + gboolean have_touchpad; + gboolean have_touchscreen; +- gboolean have_synaptics; + + gboolean left_handed; + GtkGesture *left_gesture; +@@ -81,10 +80,6 @@ setup_touchpad_options (CcMousePropertiesPrivate *d) + gboolean have_edge_scrolling; + gboolean have_tap_to_click; + +- gtk_widget_set_visible (WID ("touchpad-frame"), !d->have_synaptics); +- if (d->have_synaptics) +- return; +- + gtk_widget_set_visible (WID ("touchpad-frame"), d->have_touchpad); + if (!d->have_touchpad) + return; +@@ -387,11 +382,8 @@ cc_mouse_properties_init (CcMouseProperties *object) + G_CALLBACK (device_changed), d); + + d->have_mouse = mouse_is_present (); +- d->have_touchpad = touchpad_is_present (); ++ d->have_touchpad = touchpad_is_present () || cc_synaptics_check (); + d->have_touchscreen = touchscreen_is_present (); +- d->have_synaptics = cc_synaptics_check (); +- if (d->have_synaptics) +- g_warning ("Detected synaptics X driver, please migrate to libinput"); + + d->changing_scroll = FALSE; + +-- +2.13.6 + diff --git a/SOURCES/control-center-python3.patch b/SOURCES/control-center-python3.patch new file mode 100644 index 0000000..f540a9b --- /dev/null +++ b/SOURCES/control-center-python3.patch @@ -0,0 +1,8 @@ +--- gnome-control-center-3.28.1/meson_post_install.py.python3 2018-04-10 15:18:04.000000000 +0200 ++++ gnome-control-center-3.28.1/meson_post_install.py 2018-05-17 16:59:53.834542265 +0200 +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python3 ++#!/usr/bin/env python + + import os + import subprocess diff --git a/SOURCES/distro-logo.patch b/SOURCES/distro-logo.patch new file mode 100644 index 0000000..57e9ad3 --- /dev/null +++ b/SOURCES/distro-logo.patch @@ -0,0 +1,99 @@ +From 73be5fcb0764cb8e7bdcbcf3ee06b833078d576a Mon Sep 17 00:00:00 2001 +From: Matthias Clasen +Date: Sun, 31 Mar 2013 20:28:19 -0400 +Subject: [PATCH] info: Switch around GNOME and distro information + +This makes the distribution logo prominent, and puts GNOME version +information in the small print. + +https://bugzilla.gnome.org/show_bug.cgi?id=695691 +--- + panels/info/cc-info-overview-panel.c | 7 ++----- + panels/info/info-overview.ui | 14 ++++++++------ + 2 files changed, 10 insertions(+), 11 deletions(-) + +diff --git a/panels/info/cc-info-overview-panel.c b/panels/info/cc-info-overview-panel.c +index 7a5879c6b..ce15e92d0 100644 +--- a/panels/info/cc-info-overview-panel.c ++++ b/panels/info/cc-info-overview-panel.c +@@ -446,7 +446,7 @@ static char * + get_os_name (void) + { + GHashTable *os_info; +- gchar *name, *version_id, *pretty_name, *build_id; ++ gchar *name, *version_id, *build_id; + gchar *result = NULL; + g_autofree gchar *name_version = NULL; + +@@ -457,12 +457,9 @@ get_os_name (void) + + name = g_hash_table_lookup (os_info, "NAME"); + version_id = g_hash_table_lookup (os_info, "VERSION_ID"); +- pretty_name = g_hash_table_lookup (os_info, "PRETTY_NAME"); + build_id = g_hash_table_lookup (os_info, "BUILD_ID"); + +- if (pretty_name) +- name_version = g_strdup (pretty_name); +- else if (name && version_id) ++ if (name && version_id) + name_version = g_strdup_printf ("%s %s", name, version_id); + else + name_version = g_strdup (_("Unknown")); +diff --git a/panels/info/info-overview.ui b/panels/info/info-overview.ui +index 219a83c4c..aa87fbec2 100644 +--- a/panels/info/info-overview.ui ++++ b/panels/info/info-overview.ui +@@ -12,13 +12,14 @@ + + True + False +- 18 ++ 6 + vertical + + + True + False +- /org/gnome/control-center/info/GnomeLogoVerticalMedium.svg ++ 128 ++ fedora-logo-icon + + + False +@@ -27,11 +28,12 @@ + + + +- ++ + True + False + Version 3.0 + True ++ 24 + + + +@@ -118,8 +120,8 @@ + True + False + 1 +- OS name +- os_name_label ++ GNOME ++ version_label + +@@ -228,7 +230,7 @@ + + + +- ++ + True + False + 0 +-- +2.13.0 + diff --git a/SOURCES/gnuc99-standard.patch b/SOURCES/gnuc99-standard.patch new file mode 100644 index 0000000..9611f96 --- /dev/null +++ b/SOURCES/gnuc99-standard.patch @@ -0,0 +1,26 @@ +From 0511d5d926e3cf505d1e8318918c217f8cc1416d Mon Sep 17 00:00:00 2001 +From: Christian Kellner +Date: Mon, 11 Jun 2018 17:56:27 +0200 +Subject: [PATCH] meson: default to the gnuc99 c standard + +--- + meson.build | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/meson.build b/meson.build +index 84e04334c..0636d41b6 100644 +--- a/meson.build ++++ b/meson.build +@@ -2,7 +2,8 @@ project( + 'gnome-control-center', 'c', + version: '3.28.2', + license: 'GPL2+', +- meson_version: '>= 0.43.0' ++ meson_version: '>= 0.43.0', ++ default_options: ['c_std=gnu99'] + ) + + control_center_prefix = get_option('prefix') +-- +2.17.1 + diff --git a/SOURCES/thunderbolt-panel.patch b/SOURCES/thunderbolt-panel.patch new file mode 100644 index 0000000..774fa7b --- /dev/null +++ b/SOURCES/thunderbolt-panel.patch @@ -0,0 +1,6564 @@ +From ec695fae92ef7470ef05211160e431f5c3486299 Mon Sep 17 00:00:00 2001 +From: Christian Kellner +Date: Tue, 10 Apr 2018 09:43:22 +0200 +Subject: [PATCH 1/4] shell: Don't set per-panel icon + +The control center app is considered one single application with +a single icon to represent it. Therefore get rid of per-panel +icons. +--- + shell/cc-window.c | 7 +------ + 1 file changed, 1 insertion(+), 6 deletions(-) + +diff --git a/shell/cc-window.c b/shell/cc-window.c +index 557819e0c76c..33f1ddcad511 100644 +--- a/shell/cc-window.c ++++ b/shell/cc-window.c +@@ -118,7 +118,6 @@ activate_panel (CcWindow *self, + GIcon *gicon) + { + GtkWidget *box, *title_widget; +- const gchar *icon_name; + + if (!id) + return FALSE; +@@ -144,12 +143,8 @@ activate_panel (CcWindow *self, + gtk_stack_set_visible_child_name (GTK_STACK (self->stack), id); + + /* set the title of the window */ +- icon_name = get_icon_name_from_g_icon (gicon); +- + gtk_window_set_role (GTK_WINDOW (self), id); + gtk_header_bar_set_title (GTK_HEADER_BAR (self->panel_headerbar), name); +- gtk_window_set_default_icon_name (icon_name); +- gtk_window_set_icon_name (GTK_WINDOW (self), icon_name); + + title_widget = cc_panel_get_title_widget (CC_PANEL (self->current_panel)); + gtk_header_bar_set_custom_title (GTK_HEADER_BAR (self->panel_headerbar), title_widget); +@@ -778,4 +773,4 @@ cc_window_set_search_item (CcWindow *center, + gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (center->search_bar), TRUE); + gtk_entry_set_text (GTK_ENTRY (center->search_entry), search); + gtk_editable_set_position (GTK_EDITABLE (center->search_entry), -1); +-} +\ No newline at end of file ++} +-- +2.17.0 + +From b24a8e9aa82b64de970d8137181bf8a03b6f724a Mon Sep 17 00:00:00 2001 +From: Christian Kellner +Date: Tue, 10 Apr 2018 09:47:48 +0200 +Subject: [PATCH 2/4] shell: Icon name helper returns symbolic name + +The helper function to get the icon name from a GIcon directly +returns the symbolic icon now. This makes it in turn possible +to also directly check if the theme has the icon with the symbolic +name instead of checking of for the full colored one and then +deriving the symbolic name from that. The latter (old) practice +will fail if there is a symbolic icon in the theme that has no +full color icon (like e.g. thunderbolt). +--- + shell/cc-window.c | 19 ++++++++++--------- + 1 file changed, 10 insertions(+), 9 deletions(-) + +diff --git a/shell/cc-window.c b/shell/cc-window.c +index 33f1ddcad511..3af9cf0bd9fc 100644 +--- a/shell/cc-window.c ++++ b/shell/cc-window.c +@@ -88,8 +88,8 @@ enum + }; + + /* Auxiliary methods */ +-static const gchar * +-get_icon_name_from_g_icon (GIcon *gicon) ++static gchar * ++get_symbolic_icon_name_from_g_icon (GIcon *gicon) + { + const gchar * const *names; + GtkIconTheme *icon_theme; +@@ -103,8 +103,11 @@ get_icon_name_from_g_icon (GIcon *gicon) + + for (i = 0; names[i] != NULL; i++) + { +- if (gtk_icon_theme_has_icon (icon_theme, names[i])) +- return names[i]; ++ g_autofree gchar *name = NULL; ++ name = g_strdup_printf ("%s-symbolic", names[i]); ++ ++ if (gtk_icon_theme_has_icon (icon_theme, name)) ++ return g_steal_pointer (&name); + } + + return NULL; +@@ -248,9 +251,8 @@ setup_model (CcWindow *shell) + g_autofree gchar *name = NULL; + g_autofree gchar *description = NULL; + g_autofree gchar *id = NULL; +- g_autofree gchar *symbolic_icon = NULL; ++ g_autofree gchar *icon_name = NULL; + g_autofree GStrv keywords = NULL; +- const gchar *icon_name; + + gtk_tree_model_get (model, &iter, + COL_CATEGORY, &category, +@@ -261,8 +263,7 @@ setup_model (CcWindow *shell) + COL_KEYWORDS, &keywords, + -1); + +- icon_name = get_icon_name_from_g_icon (icon); +- symbolic_icon = g_strdup_printf ("%s-symbolic", icon_name); ++ icon_name = get_symbolic_icon_name_from_g_icon (icon); + + cc_panel_list_add_panel (CC_PANEL_LIST (shell->panel_list), + category, +@@ -270,7 +271,7 @@ setup_model (CcWindow *shell) + name, + description, + keywords, +- symbolic_icon); ++ icon_name); + + valid = gtk_tree_model_iter_next (model, &iter); + } +-- +2.17.0 + +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 + +From 2d1da22e17f703e27ff1b3177e35a54aa0c3aecc Mon Sep 17 00:00:00 2001 +From: Christian Kellner +Date: Fri, 13 Apr 2018 16:03:21 +0200 +Subject: [PATCH 4/4] thunderbolt: move to the 'Devices' page + +The 'Devices' page is a fitting place for the thunderbolt, being +an IO technology. It is expected that people that need to go to +that page will be sent there via a gnome-shell notification, so +there is no need for it to be on the main page. +Ok'ed by the design team (jimmac). +--- + panels/thunderbolt/gnome-thunderbolt-panel.desktop.in.in | 2 +- + shell/cc-panel-list.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/panels/thunderbolt/gnome-thunderbolt-panel.desktop.in.in b/panels/thunderbolt/gnome-thunderbolt-panel.desktop.in.in +index db2477e45a74..abd317341bfd 100644 +--- a/panels/thunderbolt/gnome-thunderbolt-panel.desktop.in.in ++++ b/panels/thunderbolt/gnome-thunderbolt-panel.desktop.in.in +@@ -7,7 +7,7 @@ Terminal=false + Type=Application + NoDisplay=true + StartupNotify=true +-Categories=GNOME;GTK;Settings;X-GNOME-Settings-Panel;HardwareSettings;X-GNOME-DevicesSettings;X-GNOME-ConnectivitySettings; ++Categories=GNOME;GTK;Settings;X-GNOME-Settings-Panel;HardwareSettings;X-GNOME-DevicesSettings; + OnlyShowIn=GNOME;Unity; + X-GNOME-Bugzilla-Bugzilla=GNOME + X-GNOME-Bugzilla-Product=gnome-control-center +diff --git a/shell/cc-panel-list.c b/shell/cc-panel-list.c +index 99d8a91144ad..f5b83509d646 100644 +--- a/shell/cc-panel-list.c ++++ b/shell/cc-panel-list.c +@@ -276,7 +276,6 @@ static const gchar * const panel_order[] = { + "wifi", + "mobile-broadband", + "bluetooth", +- "thunderbolt", + "background", + "notifications", + "search", +@@ -295,6 +294,7 @@ static const gchar * const panel_order[] = { + "mouse", + "printers", + "removable-media", ++ "thunderbolt", + "wacom", + "color", + +-- +2.17.0 + diff --git a/SPECS/control-center.spec b/SPECS/control-center.spec new file mode 100644 index 0000000..541d7cd --- /dev/null +++ b/SPECS/control-center.spec @@ -0,0 +1,978 @@ +%define gnome_online_accounts_version 3.25.3 +%define glib2_version 2.53.0 +%define gnome_desktop_version 3.27.90 +%define gsd_version 3.25.90 +%define gsettings_desktop_schemas_version 3.27.2 +%define gtk3_version 3.22.20 +%define upower_version 0.99.6 +%define cheese_version 3.28.0 +%define gnome_bluetooth_version 3.18.2 + +Name: control-center +Epoch: 1 +Version: 3.28.1 +Release: 4%{?dist} +Summary: Utilities to configure the GNOME desktop + +License: GPLv2+ and CC-BY-SA +URL: http://www.gnome.org +Source0: https://download.gnome.org/sources/gnome-control-center/3.28/gnome-control-center-%{version}.tar.xz + +# https://bugzilla.gnome.org/show_bug.cgi?id=695691 +Patch0: distro-logo.patch +Patch1: 0002-mouse-Handle-Synaptics-devices-again.patch +Patch4: 0001-printers-Fix-compilation-with-RHEL-cups-version.patch +# Fix the build with Python 2 +Patch5: control-center-python3.patch +Patch6: thunderbolt-panel.patch +Patch7: gnuc99-standard.patch +Patch8: 0001-po-Remove-soft-hyphens-from-japanese-translation.patch + +BuildRequires: chrpath +BuildRequires: cups-devel +BuildRequires: desktop-file-utils +BuildRequires: docbook-style-xsl libxslt +BuildRequires: gettext +BuildRequires: libXxf86misc-devel +BuildRequires: meson +BuildRequires: pkgconfig(accountsservice) +BuildRequires: pkgconfig(cheese) >= %{cheese_version} +BuildRequires: pkgconfig(cheese-gtk) +BuildRequires: pkgconfig(clutter-gtk-1.0) +BuildRequires: pkgconfig(colord) +BuildRequires: pkgconfig(colord-gtk) +BuildRequires: pkgconfig(gdk-pixbuf-2.0) +BuildRequires: pkgconfig(gdk-wayland-3.0) +BuildRequires: pkgconfig(gio-2.0) >= %{glib2_version} +BuildRequires: pkgconfig(gnome-desktop-3.0) >= %{gnome_desktop_version} +BuildRequires: pkgconfig(gnome-settings-daemon) >= %{gsd_version} +BuildRequires: pkgconfig(goa-1.0) >= %{gnome_online_accounts_version} +BuildRequires: pkgconfig(goa-backend-1.0) +BuildRequires: pkgconfig(grilo-0.3) +BuildRequires: pkgconfig(gsettings-desktop-schemas) >= %{gsettings_desktop_schemas_version} +BuildRequires: pkgconfig(gtk+-3.0) >= %{gtk3_version} +BuildRequires: pkgconfig(gudev-1.0) +BuildRequires: pkgconfig(ibus-1.0) +BuildRequires: pkgconfig(libcanberra-gtk3) +BuildRequires: pkgconfig(libgtop-2.0) +BuildRequires: pkgconfig(libnm) +BuildRequires: pkgconfig(libnma) +BuildRequires: pkgconfig(libpulse) +BuildRequires: pkgconfig(libpulse-mainloop-glib) +BuildRequires: pkgconfig(libsoup-2.4) +BuildRequires: pkgconfig(libxml-2.0) +BuildRequires: pkgconfig(mm-glib) +BuildRequires: pkgconfig(polkit-gobject-1) +BuildRequires: pkgconfig(pwquality) +BuildRequires: pkgconfig(smbclient) +BuildRequires: pkgconfig(upower-glib) >= %{upower_version} +BuildRequires: pkgconfig(x11) +BuildRequires: pkgconfig(xi) +%ifnarch s390 s390x +BuildRequires: pkgconfig(gnome-bluetooth-1.0) >= %{gnome_bluetooth_version} +BuildRequires: pkgconfig(libwacom) +%endif + +# Versioned library deps +Requires: cheese-libs%{?_isa} >= %{cheese_version} +Requires: glib2%{?_isa} >= %{glib2_version} +Requires: gnome-desktop3%{?_isa} >= %{gnome_desktop_version} +Requires: gnome-online-accounts%{?_isa} >= %{gnome_online_accounts_version} +Requires: gnome-settings-daemon%{?_isa} >= %{gsd_version} +Requires: gsettings-desktop-schemas%{?_isa} >= %{gsettings_desktop_schemas_version} +Requires: gtk3%{?_isa} >= %{gtk3_version} +Requires: upower%{?_isa} >= %{upower_version} +%ifnarch s390 s390x +Requires: gnome-bluetooth%{?_isa} >= 1:%{gnome_bluetooth_version} +%endif + +Requires: %{name}-filesystem = %{epoch}:%{version}-%{release} +# For user accounts +Requires: accountsservice +Requires: alsa-lib +# For the thunderbolt panel +Requires: bolt +# For the color panel +Requires: colord +# For the printers panel +Requires: cups-pk-helper +Requires: dbus-x11 +# For the info/details panel +Requires: glx-utils +# For the user languages +Requires: iso-codes +# For the network panel +Requires: nm-connection-editor +Requires: NetworkManager-wifi +%if 0%{?fedora} +# For the sharing panel +Requires: rygel +%endif +# For the info/details panel +Requires: vino +# For the keyboard panel +Requires: /usr/bin/gkbd-keyboard-display + +Provides: control-center-extra = %{epoch}:%{version}-%{release} +Obsoletes: control-center-extra < 1:2.30.3-3 +Obsoletes: accountsdialog <= 0.6 +Provides: accountsdialog = %{epoch}:%{version}-%{release} +Obsoletes: desktop-effects <= 0.8.7-3 +Provides: desktop-effects = %{epoch}:%{version}-%{release} +Provides: control-center-devel = %{epoch}:%{version}-%{release} +Obsoletes: control-center-devel < 1:3.1.4-2 + +%description +This package contains configuration utilities for the GNOME desktop, which +allow to configure accessibility options, desktop fonts, keyboard and mouse +properties, sound setup, desktop theme and background, user interface +properties, screen resolution, and other settings. + +%package filesystem +Summary: GNOME Control Center directories +# NOTE: this is an "inverse dep" subpackage. It gets pulled in +# NOTE: by the main package and MUST not depend on the main package + +%description filesystem +The GNOME control-center provides a number of extension points +for applications. This package contains directories where applications +can install configuration files that are picked up by the control-center +utilities. + +%prep +%autosetup -n gnome-control-center-%{version} -p1 + +%build +%meson -Ddocumentation=true +%meson_build + +%install +%meson_install + +# We do want this +mkdir -p $RPM_BUILD_ROOT%{_datadir}/gnome/wm-properties + +# We don't want these +rm -rf $RPM_BUILD_ROOT%{_datadir}/gnome/autostart +rm -rf $RPM_BUILD_ROOT%{_datadir}/gnome/cursor-fonts + +# Remove rpath +chrpath --delete $RPM_BUILD_ROOT%{_bindir}/gnome-control-center + +%find_lang %{name} --all-name --with-gnome + +%post +/sbin/ldconfig +update-desktop-database &> /dev/null || : +touch --no-create %{_datadir}/icons/hicolor &>/dev/null || : + +%postun +/sbin/ldconfig +update-desktop-database &> /dev/null || : +if [ $1 -eq 0 ]; then + touch --no-create %{_datadir}/icons/hicolor &>/dev/null + gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : +fi + +%posttrans +gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : + +%files -f %{name}.lang +%license COPYING +%doc AUTHORS NEWS README +%{_bindir}/gnome-control-center +%{_datadir}/applications/*.desktop +%{_datadir}/bash-completion/completions/gnome-control-center +%{_datadir}/dbus-1/services/org.gnome.ControlCenter.SearchProvider.service +%{_datadir}/dbus-1/services/org.gnome.ControlCenter.service +%{_datadir}/gettext/ +%{_datadir}/glib-2.0/schemas/org.gnome.ControlCenter.gschema.xml +%{_datadir}/gnome-control-center/icons/ +%{_datadir}/gnome-control-center/keybindings/*.xml +%{_datadir}/gnome-control-center/pixmaps +%{_datadir}/gnome-control-center/sounds/gnome-sounds-default.xml +%{_datadir}/gnome-shell/search-providers/gnome-control-center-search-provider.ini +%{_datadir}/icons/hicolor/*/*/* +%{_datadir}/man/man1/gnome-control-center.1* +%{_datadir}/metainfo/gnome-control-center.appdata.xml +%{_datadir}/pixmaps/faces +%{_datadir}/pkgconfig/gnome-keybindings.pc +%{_datadir}/polkit-1/actions/org.gnome.controlcenter.*.policy +%{_datadir}/polkit-1/rules.d/gnome-control-center.rules +%{_datadir}/sounds/gnome/default/*/*.ogg +%{_libexecdir}/cc-remote-login-helper +%{_libexecdir}/gnome-control-center-search-provider + +%files filesystem +%dir %{_datadir}/gnome-control-center +%dir %{_datadir}/gnome-control-center/keybindings +%dir %{_datadir}/gnome-control-center/sounds +%dir %{_datadir}/gnome/wm-properties + +%changelog +* Tue Sep 04 2018 Kalev Lember - 3.28.1-4 +- Backport two additional upstream patches for thunderbolt panel +- Resolves: #1594880 + +* Tue Jun 19 2018 Carlos Garnacho - 3.28.1-3 +- Remove outdated soft hyphens from Japanese translation +- Resolves: #1519109 + +* Mon Jun 11 2018 Christian Kellner - 3.28.1-2 +- Include thunderbolt panel +- Resolves: #1567179 + +* Thu May 17 2018 Kalev Lember - 3.28.1-1 +- Update to 3.28.1 +- Resolves: #1567179 + +* Tue Apr 17 2018 Carlos Garnacho - 3.26.2-9 +- Add support for Wacom Pro Pen 3D styli + Resolves: #1557256 + +* Tue Feb 20 2018 Bastien Nocera - 3.26.2-8 ++ control-center-3.26.2-8 +- Fix Wi-Fi networks not getting updated +- Show "Wi-Fi disabled" page when Wi-Fi is disabled +- Resolves: #1545713 + +* Tue Feb 20 2018 Bastien Nocera - 3.26.2-7 ++ control-center-3.26.2-7 +- Allow empty IPv6 gateway +- Resolves: #1467308 + +* Fri Feb 16 2018 Bastien Nocera - 3.26.2-6 ++ control-center-3.26.2-6 +- Fix Clone MAC not being saved in Ethernet device properties +- Resolves: #1467295 + +* Tue Feb 13 2018 Ray Strode - 3.26.2-5 +- Fix crash with root user + Resolves: #1544369 + +* Wed Feb 07 2018 Carlos Garnacho - 1:3.26.2-4 +- Handle multiple tablet devices sharing the same event node +- Resolves: #1528356 + +* Tue Dec 19 2017 Marek Kasik - 1:3.26.2-3 +- Don't show the supply level bar by default +- Resolves: #1525062 + +* Wed Nov 15 2017 Rui Matos - 1:3.26.2-2 +- Fix display arrangement widget +- Resolves: #1512448 + +* Wed Nov 01 2017 Kalev Lember - 1:3.26.2-1 +- Update to 3.26.2 +- Related: #1481407 + +* Mon Oct 30 2017 Rui Matos - 1:3.26.1-3 +- network/eap-method-tls: Don't require a private key password +- Resolves: #1415760 + +* Fri Oct 20 2017 Rui Matos - 1:3.26.1-2 +- Rebase to 3.26.1 +- Resolves: #1481407 + +* Sun Oct 08 2017 Kalev Lember - 1:3.26.1-1 +- Update to 3.26.1 + +* Wed Sep 13 2017 Kalev Lember - 1:3.26.0-1 +- Update to 3.26.0 + +* Tue Sep 05 2017 Kalev Lember - 1:3.25.92.1-1 +- Update to 3.25.92.1 + +* Thu Aug 24 2017 Kalev Lember - 1:3.25.91-1 +- Update to 3.25.91 + +* Wed Aug 02 2017 Fedora Release Engineering - 1:3.25.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Wed Jul 26 2017 Kalev Lember - 1:3.25.4-1 +- Update to 3.25.4 +- Rebase distro-logo.patch + +* Wed Jul 26 2017 Fedora Release Engineering - 1:3.24.2-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Mon Jun 12 2017 Kevin Fenzi - 1:3.24.2-2 +- Rebuild for new libgtop2 + +* Wed May 10 2017 Kalev Lember - 1:3.24.2-1 +- Update to 3.24.2 + +* Wed Apr 12 2017 Kalev Lember - 1:3.24.1-1 +- Update to 3.24.1 + +* Tue Mar 28 2017 Bastien Nocera - 3.24.0-2 ++ control-center-3.24.0-2 +- Require NetworkManager-wifi so it doesn't get auto-removed + +* Tue Mar 21 2017 Kalev Lember - 1:3.24.0-1 +- Update to 3.24.0 + +* Thu Mar 16 2017 Kalev Lember - 1:3.23.92-1 +- Update to 3.23.92 + +* Mon Mar 06 2017 Kalev Lember - 1:3.23.91-1 +- Update to 3.23.91 +- Restore distro-logo.patch + +* Wed Feb 15 2017 Richard Hughes - 1:3.23.90-1 +- Update to 3.23.90 + +* Fri Feb 10 2017 Fedora Release Engineering - 1:3.22.1-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Fri Oct 21 2016 Bastien Nocera - 3.22.1-3 ++ control-center-3.22.1-3 +- Remove trailing spaces from renderer strings + +* Fri Oct 21 2016 Bastien Nocera - 1:3.22.1-2 ++ control-center-3.22.1-2 +- Show the correct GPUs available when under Wayland or with dual GPUs + +* Wed Oct 12 2016 Kalev Lember - 1:3.22.1-1 +- Update to 3.22.1 + +* Thu Sep 22 2016 Kalev Lember - 1:3.22.0-1 +- Update to 3.22.0 +- Rebase distro-logo.patch + +* Wed Apr 13 2016 Kalev Lember - 1:3.20.1-1 +- Update to 3.20.1 + +* Tue Mar 22 2016 Kalev Lember - 1:3.20.0-1 +- Update to 3.20.0 + +* Thu Mar 17 2016 Kalev Lember - 1:3.19.92-1 +- Update to 3.19.92 + +* Fri Mar 04 2016 Kalev Lember - 1:3.19.91-1 +- Update to 3.19.91 + +* Wed Feb 17 2016 Richard Hughes - 1:3.19.90-1 +- Update to 3.19.90 + +* Wed Feb 03 2016 Fedora Release Engineering - 1:3.19.5-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Wed Jan 20 2016 Kalev Lember - 1:3.19.5-1 +- Update to 3.19.5 + +* Mon Jan 18 2016 Michael Catanzaro - 1:3.19.4-2 +- Add Recommends: gnome-color-manager for the Color panel + +* Fri Dec 18 2015 Kalev Lember - 1:3.19.4-1 +- Update to 3.19.4 +- Build with grilo 0.3 + +* Tue Dec 15 2015 Kalev Lember - 1:3.19.3-1 +- Update to 3.19.3 + +* Tue Nov 10 2015 Kalev Lember - 1:3.18.2-1 +- Update to 3.18.2 + +* Mon Oct 12 2015 Kalev Lember - 1:3.18.1-1 +- Update to 3.18.1 + +* Mon Sep 21 2015 Kalev Lember - 1:3.18.0-1 +- Update to 3.18.0 + +* Tue Sep 15 2015 Kalev Lember - 1:3.17.92-2 +- Set minimum gnome-bluetooth version + +* Tue Sep 15 2015 Kalev Lember - 1:3.17.92-1 +- Update to 3.17.92 + +* Tue Aug 18 2015 Kalev Lember - 1:3.17.90-1 +- Update to 3.17.90 +- Use make_install macro + +* Mon Aug 17 2015 Kalev Lember - 1:3.17.3-2 +- Rebuilt for libcheese soname bump + +* Wed Jul 22 2015 David King - 1:3.17.3-1 +- Update to 3.17.3 +- Use pkgconfig for BuildRequires + +* Wed Jun 17 2015 Fedora Release Engineering - 1:3.17.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Fri Jun 05 2015 Kalev Lember - 1:3.17.2-1 +- Update to 3.17.2 + +* Tue May 12 2015 Kalev Lember - 1:3.16.2-1 +- Update to 3.16.2 + +* Thu Apr 16 2015 Kalev Lember - 1:3.16.1-1 +- Update to 3.16.1 + +* Mon Mar 23 2015 Kalev Lember - 1:3.16.0-1 +- Update to 3.16.0 + +* Tue Mar 17 2015 Kalev Lember - 1:3.15.92-1 +- Update to 3.15.92 + +* Tue Mar 03 2015 Kalev Lember - 1:3.15.91-1 +- Update to 3.15.91 +- Use the %%license macro for the COPYING file + +* Tue Feb 17 2015 Richard Hughes - 1:3.15.90-1 +- Update to 3.15.90 + +* Sun Jan 25 2015 David King - 1:3.15.4-1 +- Update to 3.15.4 +- Depend on gudev in order to build udev device manager + +* Tue Nov 11 2014 Kalev Lember - 1:3.14.2-1 +- Update to 3.14.2 + +* Fri Oct 31 2014 Richard Hughes - 1:3.14.1-3 +- Do not depend on rygel on non-Fedora; the UI should do the right thing. + +* Wed Oct 15 2014 Kalev Lember - 1:3.14.1-2 +- Fix a symbol collision with cheese + +* Tue Oct 14 2014 Kalev Lember - 1:3.14.1-1 +- Update to 3.14.1 + +* Mon Sep 22 2014 Kalev Lember - 1:3.14.0-1 +- Update to 3.14.0 + +* Wed Sep 17 2014 Kalev Lember - 1:3.13.92-1 +- Update to 3.13.92 + +* Wed Sep 03 2014 Kalev Lember - 1:3.13.91-1 +- Update to 3.13.91 + +* Tue Aug 19 2014 Kalev Lember - 1:3.13.90-1 +- Update to 3.13.90 + +* Mon Aug 18 2014 Kalev Lember - 1:3.13.4-3 +- Rebuilt for upower 0.99.1 soname bump + +* Sat Aug 16 2014 Fedora Release Engineering - 1:3.13.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Wed Jul 23 2014 Kalev Lember - 1:3.13.4-1 +- Update to 3.13.4 +- Drop obsolete gnome-menus and redhat-menus deps + +* Fri Jun 27 2014 Rex Dieter 3.13.3-3 +- drop needless scriptlet deps too + +* Fri Jun 27 2014 Bastien Nocera 3.13.3-2 +- Don't run update-mime-database in post, we don't ship mime XML + files anymore + +* Thu Jun 26 2014 Richard Hughes - 1:3.13.3-1 +- Update to 3.13.3 + +* Tue Jun 24 2014 Richard Hughes - 1:3.13.2-1 +- Update to 3.13.2 + +* Sat Jun 07 2014 Fedora Release Engineering - 1:3.13.1-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Thu May 22 2014 Adam Williamson - 1:3.13.1-5 +- backport upstream fix for BGO #730080 (i need it, so everyone gets it) + +* Wed May 07 2014 Kalev Lember - 1:3.13.1-4 +- Drop gnome-icon-theme and gnome-icon-theme-symbolic dependencies + +* Mon May 05 2014 Kalev Lember - 1:3.13.1-3 +- Drop unused libXrandr dep + +* Wed Apr 30 2014 Richard Hughes - 1:3.13.1-2 +- Rebuild for libgtop + +* Mon Apr 28 2014 Richard Hughes - 1:3.13.1-1 +- Update to 3.13.1 + +* Wed Apr 16 2014 Kalev Lember - 1:3.12.1-1 +- Update to 3.12.1 + +* Sat Apr 05 2014 Kalev Lember - 1:3.12.0-2 +- Update dep versions + +* Mon Mar 24 2014 Richard Hughes - 1:3.12.0-1 +- Update to 3.12.0 + +* Tue Mar 18 2014 Richard Hughes - 1:3.11.92-1 +- Update to 3.11.92 + +* Wed Mar 05 2014 Richard Hughes - 1:3.11.91-1 +- Update to 3.11.91 + +* Wed Feb 19 2014 Richard Hughes - 1:3.11.90-2 +- Rebuilt for gnome-desktop soname bump + +* Tue Feb 18 2014 Richard Hughes - 1:3.11.90-1 +- Update to 3.11.90 + +* Tue Feb 04 2014 Richard Hughes - 1:3.11.5-1 +- Update to 3.11.5 + +* Tue Dec 17 2013 Richard Hughes - 1:3.11.3-1 +- Update to 3.11.3 + +* Mon Nov 25 2013 Richard Hughes - 1:3.11.2-1 +- Update to 3.11.2 + +* Wed Nov 13 2013 Bastien Nocera - 1:3.11.1-2 +- Add vino dependency + +* Thu Oct 31 2013 Florian Müllner - 1:3.11.1-1 +- Update to 3.11.1 + +* Mon Oct 28 2013 Richard Hughes - 1:3.10.1-1 +- Update to 3.10.1 + +* Wed Sep 25 2013 Richard Hughes - 1:3.10.0-1 +- Update to 3.10.0 + +* Wed Sep 18 2013 Kalev Lember - 1:3.9.92-1 +- Update to 3.9.92 + +* Wed Sep 04 2013 Kalev Lember - 1:3.9.91-1 +- Update to 3.9.91 + +* Tue Sep 03 2013 Kalev Lember - 1:3.9.90.1-2 +- Rebuilt for libgnome-desktop soname bump + +* Thu Aug 22 2013 Kalev Lember - 1:3.9.90.1-1 +- Update to 3.9.90.1 + +* Thu Aug 22 2013 Kalev Lember - 1:3.9.90-1 +- Update to 3.9.90 +- Drop obsolete build deps + +* Thu Aug 15 2013 Kalev Lember - 1:3.9.5-2 +- Rebuilt with bluetooth support + +* Wed Jul 31 2013 Adam Williamson - 1:3.9.5-1 +- Update to 3.9.5 +- buildrequires libsoup-devel + +* Tue Jul 30 2013 Richard Hughes - 1:3.9.3-2 +- Rebuild for colord soname bump + +* Tue Jul 16 2013 Richard Hughes - 1:3.9.3-1 +- Update to 3.9.3 + +* Wed Jun 26 2013 Debarshi Ray - 1:3.9.2.1-2 +- Add 'Requires: rygel' for the sharing panel + +* Mon Jun 03 2013 Kalev Lember - 1:3.9.2.1-1 +- Update to 3.9.2.1 + +* Tue May 14 2013 Richard Hughes - 1:3.8.2-1 +- Update to 3.8.2 + +* Mon May 06 2013 Kalev Lember - 1:3.8.1.5-1 +- Update to 3.8.1.5 + +* Fri May 3 2013 Matthias Clasen - 1:3.8.1-3 +- Improve the distro logo patch + +* Tue Apr 16 2013 Ray Strode - 1:3.8.1-2 +- Add a requires for the keyboard viewer + +* Tue Apr 16 2013 Richard Hughes - 1:3.8.1-1 +- Update to 3.8.1 + +* Mon Apr 1 2013 Matthias Clasen - 1:3.8.0-3 +- Apply the patch, too + +* Sun Mar 31 2013 Matthias Clasen - 1:3.8.0-2 +- Show the Fedora logo in the details panel + +* Tue Mar 26 2013 Richard Hughes - 1:3.8.0-1 +- Update to 3.8.0 + +* Wed Mar 20 2013 Richard Hughes - 1:3.7.92-1 +- Update to 3.7.92 + +* Tue Mar 05 2013 Debarshi Ray - 1:3.7.91-1 +- Update to 3.7.91 + +* Sat Feb 23 2013 Kalev Lember - 1:3.7.90-2 +- Buildrequire libsmbclient-devel for the printer panel + +* Fri Feb 22 2013 Kalev Lember - 1:3.7.90-1 +- Update to 3.7.90 + +* Thu Feb 07 2013 Richard Hughes - 1:3.7.5.1-1 +- Update to 3.7.5.1 + +* Tue Feb 05 2013 Richard Hughes - 1:3.7.5-1 +- Update to 3.7.5 + +* Fri Jan 25 2013 Peter Robinson 1:3.7.4-3 +- Rebuild for new cogl + +* Wed Jan 16 2013 Matthias Clasen - 1:3.7.4-2 +- Fix linking against libgd + +* Wed Jan 16 2013 Richard Hughes - 1:3.7.4-1 +- Update to 3.7.4 + +* Fri Dec 21 2012 Kalev Lember - 1:3.7.3-1 +- Update to 3.7.3 +- Drop upstreamed wacom-osd-window patch +- Adjust for the statically linked plugins and panel applet removal + +* Tue Nov 20 2012 Richard Hughes - 1:3.7.1-1 +- Update to 3.7.1 + +* Wed Nov 14 2012 Kalev Lember - 1:3.6.3-1 +- Update to 3.6.3 + +* Wed Nov 07 2012 Bastien Nocera 3.6.2-2 +- Require glx-utils for the info panel + +* Tue Oct 23 2012 Kalev Lember - 1:3.6.2-1 +- Update to 3.6.2 + +* Mon Oct 08 2012 Bastien Nocera 3.6.1-1 +- Update to 3.6.1 + +* Fri Oct 5 2012 Olivier Fourdan - 1:3.6.0-2 +- Add Wacom OSD window from upstream bug #683567 + +* Tue Sep 25 2012 Richard Hughes - 1:3.6.0-1 +- Update to 3.6.0 + +* Wed Sep 19 2012 Richard Hughes - 1:3.5.92-1 +- Update to 3.5.92 + +* Thu Sep 06 2012 Richard Hughes - 1:3.5.91-1 +- Update to 3.5.91 + +* Sun Aug 26 2012 Matthias Clasen - 1:3.5.90-2 +- Drop apg dependency, it is no longer used + +* Wed Aug 22 2012 Richard Hughes - 1:3.5.90-1 +- Update to 3.5.90 + +* Sat Aug 18 2012 Debarshi Ray - 1:3.5.6-2 +- Add Requires: nm-connection-editor (RH #849268) + +* Wed Aug 15 2012 Debarshi Ray - 1:3.5.6-1 +- Update to 3.5.6 + +* Wed Aug 15 2012 Dan Horák - 1:3.5.5-4 +- no wacom support on s390(x) + +* Wed Aug 15 2012 Debarshi Ray - 1:3.5.5-3 +- Rebuild against newer gnome-bluetooth + +* Fri Jul 27 2012 Fedora Release Engineering - 1:3.5.5-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Thu Jul 19 2012 Matthias Clasen - 1:3.5.5-1 +- Update to 3.5.5 + +* Mon Jul 02 2012 Dan Horák - 1:3.5.4-2 +- fix build on s390(x) without Bluetooth + +* Wed Jun 27 2012 Richard Hughes - 1:3.5.4-1 +- Update to 3.5.4 + +* Tue Jun 26 2012 Richard Hughes - 1:3.5.3-1 +- Update to 3.5.3 + +* Wed Jun 06 2012 Richard Hughes - 1:3.5.2-1 +- Update to 3.5.2 + +* Fri May 18 2012 Richard Hughes - 1:3.4.2-1 +- Update to 3.4.2 + +* Tue May 08 2012 Bastien Nocera 3.4.1-2 +- Disable Bluetooth panel on s390 + +* Mon Apr 16 2012 Richard Hughes - 1:3.4.1-1 +- Update to 3.4.1 + +* Thu Apr 12 2012 Marek Kasik - 3.4.0-2 +- Add support for FirewallD1 API +- Resolves: #802381 + +* Mon Mar 26 2012 Richard Hughes - 3.4.0-1 +- New upstream version. + +* Tue Mar 20 2012 Richard Hughes 3.3.92-1 +- Update to 3.3.92 + +* Mon Mar 05 2012 Bastien Nocera 3.3.91-1 +- Update to 3.3.91 + +* Wed Feb 22 2012 Bastien Nocera 3.3.90-1 +- Update to 3.3.90 + +* Tue Feb 7 2012 Matthias Clasen 3.3.5-1 +- Update to 3.3.5 + +* Wed Jan 18 2012 Bastien Nocera 3.3.4.1-1 +- Update to 3.3.4.1 + +* Tue Jan 17 2012 Matthias Clasen 3.3.4-2 +- Use systemd for session tracking + +* Tue Jan 17 2012 Bastien Nocera 3.3.4-1 +- Update to 3.3.4 + +* Thu Jan 12 2012 Fedora Release Engineering - 1:3.3.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Wed Dec 21 2011 Matthias Clasen 3.3.3-1 +- Update to 3.3.3 + +* Wed Nov 23 2011 Matthias Clasen 3.3.2-1 +- Update to 3.3.2 + +* Fri Nov 11 2011 Bastien Nocera 3.2.2-1 +- Update to 3.2.2 + +* Wed Oct 26 2011 Fedora Release Engineering - 1:3.2.1-2 +- Rebuilt for glibc bug#747377 + +* Mon Oct 17 2011 Bastien Nocera 3.2.1-1 +- Update to 3.2.1 + +* Wed Sep 28 2011 Ray - 1:3.2.0-1 +- Update to 3.2.0 + +* Tue Sep 20 2011 Matthias Clasen 3.1.92-1 +- Update to 3.1.92 + +* Tue Sep 6 2011 Matthias Clasen 3.1.91-1 +- Update to 3.1.91 + +* Wed Aug 31 2011 Matthias Clasen 3.1.90-1 +- Update to 3.1.90 + +* Mon Aug 22 2011 Matthias Clasen 3.1.5-3 +- Fix a crash without configured layouts + +* Fri Aug 19 2011 Matthias Clasen 3.1.5-2 +- Obsolete control-center-devel + +* Thu Aug 18 2011 Matthias Clasen 3.1.5-1 +- Update to 3.1.5 + +* Wed Aug 17 2011 Christoph Wickert - 3.1.4-2 +- Fix autostart behavior (#729271) + +* Mon Jul 25 2011 Matthias Clasen 3.1.4-1 +- Update to 3.1.4 + +* Mon Jul 04 2011 Bastien Nocera 3.1.3-1 +- Update to 3.1.3 + +* Fri Jun 17 2011 Tomas Bzatek - 3.0.2-1 +- Update to 3.0.2 + +* Wed Jun 15 2011 Bastien Nocera 3.0.1.1-4 +- Rebuild against new gnome-desktop3 libs + +* Wed Apr 27 2011 Matthias Clasen - 3.0.1.1-3 +- Rebuild against newer cheese-libs + +* Tue Apr 26 2011 Matthias Clasen - 3.0.1.1-1 +- Update to 3.0.1.1 + +* Tue Apr 26 2011 Bastien Nocera 3.0.1-1 +- Update to 3.0.1 + +* Thu Apr 7 2011 Matthias Clasen 3.0.0.1-3 +- Only autostart the sound applet in GNOME 3 (#693548) + +* Wed Apr 6 2011 Matthias Clasen 3.0.0.1-2 +- Add a way to connect to hidden access points + +* Wed Apr 6 2011 Matthias Clasen 3.0.0.1-1 +- Update to 3.0.0.1 + +* Mon Apr 04 2011 Bastien Nocera 3.0.0-1 +- Update to 3.0.0 + +* Mon Mar 28 2011 Matthias Clasen 2.91.93-1 +- 2.91.93 + +* Fri Mar 25 2011 Matthias Clasen 2.91.92-4 +- Rebuild against newer cheese + +* Thu Mar 24 2011 Matthias Clasen 2.91.92-3 +- Rebuild against NetworkManager 0.9 + +* Mon Mar 21 2011 Matthias Clasen 2.91.92-1 +- Update to 2.91.92 + +* Thu Mar 17 2011 Ray Strode 2.91.91-6 +- Drop incomplete "Supervised" account type + Resolves: #688363 + +* Tue Mar 15 2011 Bastien Nocera 2.91.91-5 +- We now replace desktop-effects, with the info panel (#684565) + +* Mon Mar 14 2011 Bastien Nocera 2.91.91-4 +- Add gnome-icon-theme-symbolic dependency (#678696) + +* Wed Mar 09 2011 Richard Hughes 2.91.91-3 +- Ensure we have NetworkManager-glib-devel to get the network panel +- Explicitly list all the panels so we know if one goes missing + +* Tue Mar 8 2011 Matthias Clasen 2.91.91-2 +- Rebuild against NetworkManager 0.9, to get the network panel + +* Tue Mar 08 2011 Bastien Nocera 2.91.91-1 +- Update to 2.91.91 +- Disable libsocialweb support until Flickr integration is fixed upstream + +* Mon Feb 28 2011 Matthias Clasen - 1:2.91.90-2 +- Fix a typo in the autostart condition for the sound applet + +* Tue Feb 22 2011 Matthias Clasen - 1:2.91.90-1 +- Update to 2.91.90 + +* Sun Feb 13 2011 Christopher Aillon - 1:2.91.6-9 +- Rebuild against new libxklavier + +* Thu Feb 10 2011 Matthias Clasen 2.91.6-8 +- Rebuild against newer gtk + +* Tue Feb 08 2011 Fedora Release Engineering - 1:2.91.6-7 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Mon Feb 07 2011 Bastien Nocera 2.91.6-6 +- Add missing apg Requires (#675227) + +* Sat Feb 05 2011 Bastien Nocera 2.91.6-5 +- Fix crasher running region and language with KDE apps installed + +* Fri Feb 04 2011 Bastien Nocera 2.91.6-4 +- Fix crasher running date and time on the live CD + +* Thu Feb 03 2011 Bastien Nocera 2.91.6-3 +- Add missing iso-codes dependencies + +* Thu Feb 03 2011 Bastien Nocera 2.91.6-2 +- Rebuild against newer GTK+ 3.x + +* Wed Feb 2 2011 Matthias Clasen 2.91.6-1 +- Update to 2.91.6 + +* Mon Jan 10 2011 Matthias Clasen 2.91.5-1 +- Update to 2.91.5 + +* Sat Jan 8 2011 Matthias Clasen 2.91.4-1 +- Update to 2.91.4 + +* Fri Dec 10 2010 Bill Nottingham 2.91.3-4 +- user-accounts: require accountsserivce, obsolete accountsdialog + +* Fri Dec 3 2010 Matthias Clasen 2.91.3-3 +- Fix initial window size + +* Fri Dec 3 2010 Matthias Clasen 2.91.3-2 +- Rebuild against new gtk + +* Wed Dec 01 2010 Bastien Nocera 2.91.3-1 +- Update to 2.91.3 + +* Fri Nov 12 2010 Adam Williamson 2.91.2-2 +- add upstream patch to fix sound module to link against libxml + https://bugzilla.gnome.org/show_bug.cgi?id=634467 + +* Wed Nov 10 2010 Bastien Nocera 2.91.2-1 +- Update to 2.91.2 + +* Wed Oct 06 2010 Richard Hughes 2.91.0-2 +- Rebuild with a new gnome-settings-daemon + +* Wed Oct 06 2010 Richard Hughes 2.91.0-1 +- Update to 2.91.0 + +* Wed Sep 29 2010 jkeating - 1:2.90.1-4 +- Rebuilt for gcc bug 634757 + +* Fri Sep 24 2010 Bastien Nocera 2.90.1-3 +- Force enable libsocialweb support, it's disabled by default + +* Fri Sep 24 2010 Bastien Nocera 2.90.1-2 +- Add libsocialweb BR for the flickr support in background + +* Wed Sep 22 2010 Bastien Nocera 2.90.1-1 +- Update to 2.90.1 + +* Thu Aug 12 2010 Colin Walters - 1:2.31.6-1 +- New upstream + +* Wed Jul 21 2010 Bastien Nocera 2.31.5-2 +- Trim BuildRequires +- Remove libgail-gnome dependency (#616632) + +* Tue Jul 13 2010 Matthias Clasen 2.31.5-1 +- Update to 2.31.5 + +* Wed Jun 30 2010 Matthias Clasen 2.31.4.2-1 +- Update to 2.31.4.2 + +* Wed Jun 30 2010 Matthias Clasen 2.31.4.1-1 +- Update to 2.31.4.1 + +* Wed Jun 23 2010 Bastien Nocera 2.31.3-2 +- Add patches to compile against GTK+ 3.x + +* Tue Jun 8 2010 Matthias Clasen 2.31.3-1 +- Update to 2.31.3 + +* Wed Jun 2 2010 Matthias Clasen 2.31.2-3 +- Add Provides/Obsoletes for the no-longer-existing -extra package + +* Fri May 28 2010 Matthias Clasen 2.31.2-2 +- Update to 2.31.2 +- Remove vendor prefixes from desktop files, since that breaks + the new shell + +* Tue May 11 2010 Matthias Clasen 2.30.1-2 +- Install PolicyKit policy for setting the default background + in the right location + +* Tue Apr 27 2010 Matthias Clasen 2.30.1-1 +- Update to 2.30.1 +- Spec file cleanups + +* Mon Mar 29 2010 Matthias Clasen 2.30.0-1 +- Update to 2.30.0 + +* Mon Mar 22 2010 Bastien Nocera 2.29.92-3 +- Fix crash on exit in gnome-about-me (#574256) + +* Wed Mar 10 2010 Bastien Nocera 2.29.92-2 +- Remove obsoleted patches + +* Tue Mar 09 2010 Bastien Nocera 2.29.92-1 +- Update to 2.29.92 + +* Wed Feb 24 2010 Matthias Clasen - 2.29.91-1 +- Update to 2.29.91 + +* Mon Feb 15 2010 Matthias Clasen - 2.29.90-2 +- Properly initialize threads in the appearance capplet + +* Wed Feb 10 2010 Bastien Nocera 2.29.90-1 +- Update to 2.29.90 + +* Tue Jan 26 2010 Matthias Clasen - 2.29.6-1 +- Update to 2.29.6 + +* Sun Jan 17 2010 Matthias Clasen - 2.29.4-2 +- Rebuild + +* Mon Jan 4 2010 Matthias Clasen - 2.29.4-1 +- Update to 2.29.4 +- Drop many upstreamed patches