summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBastien Nocera <hadess@hadess.net>2020-08-06 23:33:10 +0200
committerBastien Nocera <hadess@hadess.net>2020-10-29 16:58:24 +0100
commitf1bcaf1fbc1a103460fa807e07f01f961091ea63 (patch)
tree1f2c38dcb69787e6c0c9fdc489445ee81e2ac5a0
parent2def2718248cdc9d49f3d1eed289b898d6964944 (diff)
downloadgnome-control-center-wip/hadess/power-profiles.tar.gz
power: Add "Power Mode" sectionwip/hadess/power-profiles
Use power-profiles-daemon[1] to implement switchable power profiles. The performance profile will only be available on systems which provide this functionality. [1]: https://gitlab.freedesktop.org/hadess/power-profiles-daemon
-rw-r--r--panels/power/cc-power-panel.c316
-rw-r--r--panels/power/cc-power-profile-row.c403
-rw-r--r--panels/power/cc-power-profile-row.h54
-rw-r--r--panels/power/meson.build3
-rw-r--r--panels/power/power-profiles.css7
-rw-r--r--panels/power/power.gresource.xml1
-rw-r--r--po/POTFILES.in1
7 files changed, 784 insertions, 1 deletions
diff --git a/panels/power/cc-power-panel.c b/panels/power/cc-power-panel.c
index 92844e495..ab7695256 100644
--- a/panels/power/cc-power-panel.c
+++ b/panels/power/cc-power-panel.c
@@ -33,6 +33,7 @@
#include "list-box-helper.h"
#include "cc-battery-row.h"
#include "cc-brightness-scale.h"
+#include "cc-power-profile-row.h"
#include "cc-power-panel.h"
#include "cc-power-resources.h"
#include "cc-util.h"
@@ -116,6 +117,11 @@ struct _CcPowerPanel
GtkWidget *als_switch;
GtkWidget *als_row;
+ GDBusProxy *power_profiles_proxy;
+ guint power_profiles_prop_id;
+ GtkWidget *power_profiles_row[NUM_CC_POWER_PROFILES];
+ gboolean power_profiles_in_update;
+
GtkWidget *power_button_combo;
GtkWidget *idle_delay_combo;
@@ -320,6 +326,7 @@ load_custom_css (CcPowerPanel *self)
/* use custom CSS */
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_resource (provider, "/org/gnome/control-center/power/battery-levels.css");
+ gtk_css_provider_load_from_resource (provider, "/org/gnome/control-center/power/power-profiles.css");
gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
@@ -1656,6 +1663,314 @@ add_power_saving_section (CcPowerPanel *self)
}
static void
+performance_profile_set_active (CcPowerPanel *self,
+ const char *profile_str)
+{
+ CcPowerProfile profile = cc_power_profile_from_str (profile_str);
+ GtkRadioButton *button;
+
+ button = cc_power_profile_row_get_radio_button (CC_POWER_PROFILE_ROW (self->power_profiles_row[profile]));
+ g_assert (button);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+}
+
+static void
+performance_profile_set_inhibited (CcPowerPanel *self,
+ const char *performance_inhibited)
+{
+ GtkWidget *row;
+
+ row = self->power_profiles_row[CC_POWER_PROFILE_PERFORMANCE];
+ g_assert (row != NULL);
+ cc_power_profile_row_set_performance_inhibited (CC_POWER_PROFILE_ROW (row),
+ performance_inhibited);
+}
+
+static void
+power_profiles_row_activated_cb (GtkListBox *box,
+ GtkListBoxRow *box_row,
+ gpointer user_data)
+{
+ if (!gtk_widget_is_sensitive (GTK_WIDGET (box_row)))
+ return;
+
+ cc_power_profile_row_set_active (CC_POWER_PROFILE_ROW(box_row), TRUE);
+}
+
+static gint
+perf_profile_list_box_sort (GtkListBoxRow *row1,
+ GtkListBoxRow *row2,
+ gpointer user_data)
+{
+ CcPowerProfile row1_profile, row2_profile;
+
+ row1_profile = cc_power_profile_row_get_profile (CC_POWER_PROFILE_ROW (row1));
+ row2_profile = cc_power_profile_row_get_profile (CC_POWER_PROFILE_ROW (row2));
+
+ if (row1_profile < row2_profile)
+ return -1;
+ if (row1_profile > row2_profile)
+ return 1;
+ return 0;
+}
+
+static const char *
+variant_lookup_string (GVariant *dict,
+ const char *key)
+{
+ GVariant *variant;
+
+ variant = g_variant_lookup_value (dict, key, G_VARIANT_TYPE_STRING);
+ if (!variant)
+ return NULL;
+ return g_variant_get_string (variant, NULL);
+}
+
+static void
+power_profiles_properties_changed_cb (CcPowerPanel *self,
+ GVariant *changed_properties,
+ GStrv invalidated_properties,
+ GDBusProxy *proxy)
+{
+ g_autoptr(GVariantIter) iter = NULL;
+ const char *key;
+ g_autoptr(GVariant) value = NULL;
+
+ g_variant_get (changed_properties, "a{sv}", &iter);
+ while (g_variant_iter_next (iter, "{&sv}", &key, &value))
+ {
+ if (g_strcmp0 (key, "PerformanceInhibited") == 0)
+ {
+ performance_profile_set_inhibited (self,
+ g_variant_get_string (value, NULL));
+ }
+ else if (g_strcmp0 (key, "ActiveProfile") == 0)
+ {
+ self->power_profiles_in_update = TRUE;
+ performance_profile_set_active (self, g_variant_get_string (value, NULL));
+ self->power_profiles_in_update = FALSE;
+ }
+ else
+ {
+ g_debug ("Unhandled change on '%s' property", key);
+ }
+ }
+}
+
+static void
+set_active_profile_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ g_autoptr(GVariant) variant = NULL;
+ g_autoptr(GError) error = NULL;
+
+ variant = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+ res, &error);
+ if (!variant)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Could not set active profile: %s", error->message);
+ }
+}
+
+static void
+power_profile_button_toggled_cb (CcPowerProfileRow *row,
+ gpointer user_data)
+{
+ CcPowerPanel *self = user_data;
+ CcPowerProfile profile;
+ g_autoptr(GDBusConnection) connection = NULL;
+ g_autoptr(GError) error = NULL;
+
+ if (!cc_power_profile_row_get_active (row))
+ return;
+ if (self->power_profiles_in_update)
+ return;
+
+ profile = cc_power_profile_row_get_profile (row);
+
+ connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM,
+ cc_panel_get_cancellable (CC_PANEL (self)),
+ &error);
+ if (!connection)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("system bus not available: %s", error->message);
+ return;
+ }
+
+ g_dbus_connection_call (connection,
+ "net.hadess.PowerProfiles",
+ "/net/hadess/PowerProfiles",
+ "org.freedesktop.DBus.Properties",
+ "Set",
+ g_variant_new ("(ssv)",
+ "net.hadess.PowerProfiles",
+ "ActiveProfile",
+ g_variant_new_string (cc_power_profile_to_str (profile))),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ cc_panel_get_cancellable (CC_PANEL (self)),
+ set_active_profile_cb,
+ NULL);
+}
+
+static void
+add_power_profiles_section (CcPowerPanel *self)
+{
+ GtkWidget *widget, *box, *label, *row;
+ g_autofree gchar *s = NULL;
+ g_autoptr(GDBusConnection) connection = NULL;
+ g_autoptr(GVariant) variant = NULL;
+ g_autoptr(GVariant) props = NULL;
+ guint i, num_children;
+ g_autoptr(GError) error = NULL;
+ const char *performance_inhibited;
+ const char *active_profile;
+ g_autoptr(GVariant) profiles = NULL;
+ GtkRadioButton *last_button;
+
+ self->power_profiles_proxy = cc_object_storage_create_dbus_proxy_sync (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ "net.hadess.PowerProfiles",
+ "/net/hadess/PowerProfiles",
+ "net.hadess.PowerProfiles",
+ NULL,
+ &error);
+
+ if (!self->power_profiles_proxy)
+ {
+ g_debug ("Could not create Power Profiles proxy: %s", error->message);
+ return;
+ }
+
+ connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM,
+ cc_panel_get_cancellable (CC_PANEL (self)),
+ &error);
+ if (!connection)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("system bus not available: %s", error->message);
+ return;
+ }
+
+ variant = g_dbus_connection_call_sync (connection,
+ "net.hadess.PowerProfiles",
+ "/net/hadess/PowerProfiles",
+ "org.freedesktop.DBus.Properties",
+ "GetAll",
+ g_variant_new ("(s)",
+ "net.hadess.PowerProfiles"),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+
+ if (!variant)
+ {
+ g_debug ("Failed to get properties for Power Profiles: %s",
+ error->message);
+ g_clear_object (&self->power_profiles_proxy);
+ return;
+ }
+
+ s = g_strdup_printf ("<b>%s</b>", _("Power Mode"));
+ label = gtk_label_new (s);
+ gtk_widget_show (label);
+ gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+ gtk_widget_set_halign (label, GTK_ALIGN_START);
+ gtk_box_pack_start (GTK_BOX (self->vbox_power), label, FALSE, TRUE, 0);
+
+ label = gtk_label_new (_("Affects system performance and power usage."));
+ gtk_widget_show (label);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_widget_set_margin_bottom (label, 6);
+ gtk_box_pack_start (GTK_BOX (self->vbox_power), label, FALSE, TRUE, 0);
+
+ widget = gtk_list_box_new ();
+ gtk_widget_show (widget);
+ self->boxes_reverse = g_list_prepend (self->boxes_reverse, widget);
+ g_signal_connect_object (widget, "keynav-failed", G_CALLBACK (keynav_failed), self, G_CONNECT_SWAPPED);
+ gtk_list_box_set_selection_mode (GTK_LIST_BOX (widget), GTK_SELECTION_NONE);
+ gtk_list_box_set_sort_func (GTK_LIST_BOX (widget),
+ perf_profile_list_box_sort,
+ NULL, NULL);
+ g_signal_connect_object (G_OBJECT (widget), "row-activated",
+ G_CALLBACK (power_profiles_row_activated_cb), NULL, 0);
+ gtk_list_box_set_header_func (GTK_LIST_BOX (widget),
+ cc_list_box_update_header_func,
+ NULL, NULL);
+
+ atk_object_add_relationship (ATK_OBJECT (gtk_widget_get_accessible (label)),
+ ATK_RELATION_LABEL_FOR,
+ ATK_OBJECT (gtk_widget_get_accessible (widget)));
+ atk_object_add_relationship (ATK_OBJECT (gtk_widget_get_accessible (widget)),
+ ATK_RELATION_LABELLED_BY,
+ ATK_OBJECT (gtk_widget_get_accessible (label)));
+
+ box = gtk_frame_new (NULL);
+ gtk_widget_show (box);
+ gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_IN);
+ gtk_widget_set_margin_bottom (box, 32);
+ gtk_container_add (GTK_CONTAINER (box), widget);
+ gtk_box_pack_start (GTK_BOX (self->vbox_power), box, FALSE, TRUE, 0);
+
+ props = g_variant_get_child_value (variant, 0);
+ performance_inhibited = variant_lookup_string (props, "PerformanceInhibited");
+ active_profile = variant_lookup_string (props, "ActiveProfile");
+
+ last_button = NULL;
+ profiles = g_variant_lookup_value (props, "Profiles", NULL);
+ num_children = g_variant_n_children (profiles);
+ for (i = 0; i < num_children; i++)
+ {
+ g_autoptr(GVariant) profile_variant;
+ const char *name;
+ GtkRadioButton *button;
+ CcPowerProfile profile;
+
+ profile_variant = g_variant_get_child_value (profiles, i);
+ if (!profile_variant ||
+ !g_variant_is_of_type (profile_variant, G_VARIANT_TYPE ("a{sv}")))
+ continue;
+
+ name = variant_lookup_string (profile_variant, "Profile");
+ if (!name)
+ continue;
+ g_debug ("Adding row for profile '%s' (driver: %s)",
+ name, variant_lookup_string (profile_variant, "Driver"));
+
+ profile = cc_power_profile_from_str (name);
+ row = cc_power_profile_row_new (cc_power_profile_from_str (name),
+ performance_inhibited);
+ g_signal_connect_object (G_OBJECT (row), "button-toggled",
+ G_CALLBACK (power_profile_button_toggled_cb), self,
+ 0);
+ self->power_profiles_row[profile] = row;
+ gtk_widget_show (row);
+ gtk_container_add (GTK_CONTAINER (widget), row);
+ gtk_size_group_add_widget (self->row_sizegroup, row);
+
+ /* Connect radio button to group */
+ button = cc_power_profile_row_get_radio_button (CC_POWER_PROFILE_ROW (row));
+ gtk_radio_button_join_group (button, last_button);
+ last_button = button;
+ }
+
+ self->power_profiles_in_update = TRUE;
+ performance_profile_set_active (self, active_profile);
+ self->power_profiles_in_update = FALSE;
+
+ self->power_profiles_prop_id = g_signal_connect_object (G_OBJECT (self->power_profiles_proxy), "g-properties-changed",
+ G_CALLBACK (power_profiles_properties_changed_cb), self, G_CONNECT_SWAPPED);
+}
+
+static void
add_battery_percentage (CcPowerPanel *self,
GtkListBox *listbox)
{
@@ -1913,6 +2228,7 @@ cc_power_panel_init (CcPowerPanel *self)
add_battery_section (self);
add_device_section (self);
+ add_power_profiles_section (self);
add_power_saving_section (self);
add_general_section (self);
diff --git a/panels/power/cc-power-profile-row.c b/panels/power/cc-power-profile-row.c
new file mode 100644
index 000000000..c6934f27a
--- /dev/null
+++ b/panels/power/cc-power-profile-row.c
@@ -0,0 +1,403 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* cc-list-row.c
+ *
+ * Copyright 2020 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author(s):
+ * Bastien Nocera <hadess@hadess.net>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "cc-power-profile-row"
+
+#include <config.h>
+
+#include <glib/gi18n.h>
+#include "cc-power-profile-row.h"
+
+struct _CcPowerProfileRow
+{
+ GtkListBoxRow parent_instance;
+
+ CcPowerProfile power_profile;
+ char *performance_inhibited;
+ GtkRadioButton *button;
+ GtkWidget *subtext;
+};
+
+G_DEFINE_TYPE (CcPowerProfileRow, cc_power_profile_row, GTK_TYPE_LIST_BOX_ROW)
+
+enum {
+ PROP_0,
+ PROP_POWER_PROFILE,
+ PROP_PERFORMANCE_INHIBITED,
+ N_PROPS
+};
+
+enum {
+ BUTTON_TOGGLED,
+ N_SIGNALS
+};
+
+static GParamSpec *properties[N_PROPS];
+static guint signals[N_SIGNALS];
+
+static const char *
+get_performance_inhibited_text (const char *inhibited)
+{
+ if (!inhibited || *inhibited == '\0')
+ return NULL;
+
+ if (g_str_equal (inhibited, "lap-detected"))
+ return _("Lap detected: performance mode unavailable");
+ if (g_str_equal (inhibited, "high-operating-temperature"))
+ return _("High hardware temperature: performance mode unavailable");
+ return _("Performance mode unavailable");
+}
+
+static void
+performance_profile_set_inhibited (CcPowerProfileRow *row,
+ const char *performance_inhibited)
+{
+ const char *text;
+ gboolean inhibited = FALSE;
+
+ if (row->power_profile != CC_POWER_PROFILE_PERFORMANCE)
+ return;
+
+ gtk_style_context_remove_class (gtk_widget_get_style_context (row->subtext),
+ GTK_STYLE_CLASS_DIM_LABEL);
+ gtk_style_context_remove_class (gtk_widget_get_style_context (row->subtext),
+ GTK_STYLE_CLASS_ERROR);
+
+ text = get_performance_inhibited_text (performance_inhibited);
+ if (text)
+ inhibited = TRUE;
+ else
+ text = _("High performance and power usage.");
+ gtk_label_set_text (GTK_LABEL (row->subtext), text);
+
+ gtk_style_context_add_class (gtk_widget_get_style_context (row->subtext),
+ inhibited ? GTK_STYLE_CLASS_ERROR : GTK_STYLE_CLASS_DIM_LABEL);
+ gtk_widget_set_sensitive (GTK_WIDGET (row), !inhibited);
+}
+
+static void
+cc_power_profile_row_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ CcPowerProfileRow *self = (CcPowerProfileRow *)object;
+
+ switch (prop_id)
+ {
+ case PROP_POWER_PROFILE:
+ g_value_set_int (value, self->power_profile);
+ break;
+
+ case PROP_PERFORMANCE_INHIBITED:
+ g_value_set_string (value, self->performance_inhibited);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+cc_power_profile_row_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CcPowerProfileRow *row = (CcPowerProfileRow *)object;
+
+ switch (prop_id)
+ {
+ case PROP_POWER_PROFILE:
+ g_assert (row->power_profile == -1);
+ row->power_profile = g_value_get_int (value);
+ g_assert (row->power_profile != -1);
+ break;
+
+ case PROP_PERFORMANCE_INHIBITED:
+ cc_power_profile_row_set_performance_inhibited (row, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static GtkWidget *
+performance_row_new (const gchar *title,
+ const gchar *icon_name,
+ const gchar *class_name,
+ const gchar *subtitle)
+{
+ PangoAttrList *attributes;
+ GtkWidget *grid, *button, *label, *image;
+ GtkStyleContext *context;
+
+ grid = gtk_grid_new ();
+ g_object_set (G_OBJECT (grid),
+ "margin-top", 6,
+ "margin-bottom", 6,
+ NULL);
+ gtk_widget_show (grid);
+
+ button = gtk_radio_button_new (NULL);
+ g_object_set (G_OBJECT (button),
+ "margin-end", 18,
+ "margin-start", 6,
+ NULL);
+ gtk_widget_show (button);
+ g_object_set_data (G_OBJECT (grid), "button", button);
+ gtk_grid_attach (GTK_GRID (grid), button, 0, 0, 1, 2);
+
+ image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
+ gtk_widget_set_margin_end (image, 6);
+ gtk_widget_show (image);
+ gtk_grid_attach (GTK_GRID (grid), image, 1, 0, 1, 1);
+
+ context = gtk_widget_get_style_context (image);
+ gtk_style_context_add_class (context, "power-profile");
+ if (class_name != NULL)
+ gtk_style_context_add_class (context, class_name);
+
+ label = gtk_label_new (title);
+ g_object_set (G_OBJECT (label),
+ "ellipsize", PANGO_ELLIPSIZE_END,
+ "halign", GTK_ALIGN_START,
+ "expand", TRUE,
+ "use-markup", TRUE,
+ "use-underline", TRUE,
+ "visible", TRUE,
+ "xalign", 0.0,
+ NULL);
+ gtk_widget_show (label);
+ gtk_grid_attach (GTK_GRID (grid), label, 2, 0, 1, 1);
+
+ attributes = pango_attr_list_new ();
+ pango_attr_list_insert (attributes, pango_attr_scale_new (0.9));
+
+ label = gtk_label_new (subtitle);
+ g_object_set (G_OBJECT (label),
+ "ellipsize", PANGO_ELLIPSIZE_END,
+ "halign", GTK_ALIGN_START,
+ "expand", TRUE,
+ "use-markup", TRUE,
+ "use-underline", TRUE,
+ "visible", TRUE,
+ "xalign", 0.0,
+ "attributes", attributes,
+ NULL);
+ gtk_style_context_add_class (gtk_widget_get_style_context (label),
+ GTK_STYLE_CLASS_DIM_LABEL);
+ g_object_set_data (G_OBJECT (grid), "subtext", label);
+ gtk_grid_attach (GTK_GRID (grid), label, 1, 1, 2, 1);
+
+ pango_attr_list_unref (attributes);
+
+ return grid;
+}
+
+static void
+cc_power_profile_row_button_toggled_cb (GObject *row)
+{
+ g_signal_emit (row, signals[BUTTON_TOGGLED], 0);
+}
+
+static void
+cc_power_profile_row_constructed (GObject *object)
+{
+ CcPowerProfileRow *row;
+ GtkWidget *box, *title;
+ const char *text, *subtext, *icon_name, *class_name;
+
+ row = CC_POWER_PROFILE_ROW (object);
+
+ switch (row->power_profile)
+ {
+ case CC_POWER_PROFILE_PERFORMANCE:
+ text = _("Performance");
+ subtext = _("High performance and power usage.");
+ icon_name = "power-profile-performance-symbolic";
+ class_name = "performance";
+ break;
+ case CC_POWER_PROFILE_BALANCED:
+ text = _("Balanced Power");
+ subtext = _("Standard performance and power usage.");
+ icon_name = "power-profile-balanced-symbolic";
+ class_name = NULL;
+ break;
+ case CC_POWER_PROFILE_POWER_SAVER:
+ text = _("Power Saver");
+ subtext = _("Reduced performance and power usage.");
+ icon_name = "power-profile-power-saver-symbolic";
+ class_name = "low-power";
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ gtk_list_box_row_set_selectable (GTK_LIST_BOX_ROW (row), FALSE);
+ gtk_widget_show (GTK_WIDGET (row));
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ g_object_set (G_OBJECT (box),
+ "margin-end", 12,
+ "margin-start", 12,
+ "visible", TRUE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (row), box);
+
+ title = performance_row_new (text, icon_name, class_name, subtext);
+ row->subtext = g_object_get_data (G_OBJECT (title), "subtext");
+ row->button = g_object_get_data (G_OBJECT (title), "button");
+ g_signal_connect_object (G_OBJECT (row->button), "toggled",
+ G_CALLBACK (cc_power_profile_row_button_toggled_cb),
+ row, G_CONNECT_SWAPPED);
+ if (row->power_profile == CC_POWER_PROFILE_PERFORMANCE)
+ performance_profile_set_inhibited (row, row->performance_inhibited);
+ gtk_box_pack_start (GTK_BOX (box), title, TRUE, TRUE, 0);
+}
+
+static void
+cc_power_profile_row_class_init (CcPowerProfileRowClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = cc_power_profile_row_get_property;
+ object_class->set_property = cc_power_profile_row_set_property;
+ object_class->constructed = cc_power_profile_row_constructed;
+
+ properties[PROP_POWER_PROFILE] =
+ g_param_spec_int ("power-profile",
+ "Power Profile",
+ "Power profile for the row",
+ -1, CC_POWER_PROFILE_POWER_SAVER,
+ -1,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
+
+ properties[PROP_PERFORMANCE_INHIBITED] =
+ g_param_spec_string ("performance-inhibited",
+ "Performance Inhibited",
+ "Performance inhibition reason",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ signals[BUTTON_TOGGLED] =
+ g_signal_new ("button-toggled",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 0);
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+cc_power_profile_row_init (CcPowerProfileRow *row)
+{
+ row->power_profile = -1;
+}
+
+CcPowerProfile
+cc_power_profile_row_get_profile (CcPowerProfileRow *row)
+{
+ g_return_val_if_fail (CC_IS_POWER_PROFILE_ROW (row), -1);
+
+ return row->power_profile;
+}
+
+GtkRadioButton *
+cc_power_profile_row_get_radio_button (CcPowerProfileRow *row)
+{
+ g_return_val_if_fail (CC_IS_POWER_PROFILE_ROW (row), NULL);
+
+ return row->button;
+}
+
+void
+cc_power_profile_row_set_active (CcPowerProfileRow *row,
+ gboolean active)
+{
+ g_return_if_fail (CC_IS_POWER_PROFILE_ROW (row));
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (row->button), active);
+}
+
+void
+cc_power_profile_row_set_performance_inhibited (CcPowerProfileRow *row,
+ const char *performance_inhibited)
+{
+ g_return_if_fail (CC_IS_POWER_PROFILE_ROW (row));
+
+ g_clear_pointer (&row->performance_inhibited, g_free);
+ row->performance_inhibited = g_strdup (performance_inhibited);
+ performance_profile_set_inhibited (row, row->performance_inhibited);
+}
+
+gboolean
+cc_power_profile_row_get_active (CcPowerProfileRow *self)
+{
+ g_return_val_if_fail (CC_IS_POWER_PROFILE_ROW (self), FALSE);
+
+ return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->button));
+}
+
+GtkWidget *
+cc_power_profile_row_new (CcPowerProfile power_profile,
+ const char *performance_inhibited)
+{
+ return g_object_new (CC_TYPE_POWER_PROFILE_ROW,
+ "power-profile", power_profile,
+ "performance-inhibited", performance_inhibited,
+ NULL);
+}
+
+CcPowerProfile
+cc_power_profile_from_str (const char *profile)
+{
+ if (g_strcmp0 (profile, "power-saver") == 0)
+ return CC_POWER_PROFILE_POWER_SAVER;
+ if (g_strcmp0 (profile, "balanced") == 0)
+ return CC_POWER_PROFILE_BALANCED;
+ if (g_strcmp0 (profile, "performance") == 0)
+ return CC_POWER_PROFILE_PERFORMANCE;
+
+ g_assert_not_reached ();
+}
+
+const char *
+cc_power_profile_to_str (CcPowerProfile profile)
+{
+ switch (profile)
+ {
+ case CC_POWER_PROFILE_POWER_SAVER:
+ return "power-saver";
+ case CC_POWER_PROFILE_BALANCED:
+ return "balanced";
+ case CC_POWER_PROFILE_PERFORMANCE:
+ return "performance";
+ default:
+ g_assert_not_reached ();
+ }
+}
diff --git a/panels/power/cc-power-profile-row.h b/panels/power/cc-power-profile-row.h
new file mode 100644
index 000000000..72b3df93f
--- /dev/null
+++ b/panels/power/cc-power-profile-row.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* cc-list-row.h
+ *
+ * Copyright 2020 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author(s):
+ * Bastien Nocera <hadess@hadess.net>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+ CC_POWER_PROFILE_PERFORMANCE = 0,
+ CC_POWER_PROFILE_BALANCED = 1,
+ CC_POWER_PROFILE_POWER_SAVER = 2,
+ NUM_CC_POWER_PROFILES
+} CcPowerProfile;
+
+#define CC_TYPE_POWER_PROFILE_ROW (cc_power_profile_row_get_type())
+G_DECLARE_FINAL_TYPE (CcPowerProfileRow, cc_power_profile_row, CC, POWER_PROFILE_ROW, GtkListBoxRow)
+
+GtkWidget *cc_power_profile_row_new (CcPowerProfile power_profile,
+ const char *performance_inhibited);
+CcPowerProfile cc_power_profile_row_get_profile (CcPowerProfileRow *row);
+GtkRadioButton *cc_power_profile_row_get_radio_button (CcPowerProfileRow *row);
+void cc_power_profile_row_set_active (CcPowerProfileRow *row, gboolean active);
+gboolean cc_power_profile_row_get_active (CcPowerProfileRow *self);
+void cc_power_profile_row_set_performance_inhibited (CcPowerProfileRow *row,
+ const char *performance_inhibited);
+
+CcPowerProfile cc_power_profile_from_str (const char *profile);
+const char *cc_power_profile_to_str (CcPowerProfile profile);
+
+G_END_DECLS
diff --git a/panels/power/meson.build b/panels/power/meson.build
index 45ff95d3d..625059dd2 100644
--- a/panels/power/meson.build
+++ b/panels/power/meson.build
@@ -20,7 +20,8 @@ i18n.merge_file(
sources = files(
'cc-battery-row.c',
'cc-brightness-scale.c',
- 'cc-power-panel.c'
+ 'cc-power-panel.c',
+ 'cc-power-profile-row.c',
)
sources += gnome.mkenums_simple(
diff --git a/panels/power/power-profiles.css b/panels/power/power-profiles.css
new file mode 100644
index 000000000..1c3149320
--- /dev/null
+++ b/panels/power/power-profiles.css
@@ -0,0 +1,7 @@
+.power-profile.low-power {
+ color: @success_color;
+}
+
+.power-profile.performance {
+ color: @error_color;
+}
diff --git a/panels/power/power.gresource.xml b/panels/power/power.gresource.xml
index 3b0fed18c..23cb3f8d7 100644
--- a/panels/power/power.gresource.xml
+++ b/panels/power/power.gresource.xml
@@ -4,5 +4,6 @@
<file preprocess="xml-stripblanks">cc-battery-row.ui</file>
<file preprocess="xml-stripblanks">cc-power-panel.ui</file>
<file>battery-levels.css</file>
+ <file>power-profiles.css</file>
</gresource>
</gresources>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index ad2892009..b5ab2696f 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -152,6 +152,7 @@ panels/online-accounts/online-accounts.ui
panels/power/cc-battery-row.c
panels/power/cc-power-panel.c
panels/power/cc-power-panel.ui
+panels/power/cc-power-profile-row.c
panels/power/gnome-power-panel.desktop.in.in
panels/printers/authentication-dialog.ui
panels/printers/cc-printers-panel.c