Skip to content

Commit

Permalink
power: System76 Charge Threshold profiles
Browse files Browse the repository at this point in the history
  • Loading branch information
ids1024 committed Jan 19, 2021
1 parent 30e1be8 commit 6e86c1a
Show file tree
Hide file tree
Showing 10 changed files with 677 additions and 4 deletions.
194 changes: 194 additions & 0 deletions panels/power/cc-charge-threshold-dialog.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
#include "cc-charge-threshold-dialog.h"
#include "cc-charge-threshold-row.h"
#include "list-box-helper.h"

ChargeProfile charge_profile_from_thresholds (guchar start, guchar end) {
if (start == 48 && end == 60)
return CHARGE_PROFILE_MAX_LIFESPAN;
else if (start == 68 && end == 80)
return CHARGE_PROFILE_BALANCED;
else if (start == 88 && end == 100)
return CHARGE_PROFILE_FULL_CHARGE;
else
return CHARGE_PROFILE_UNDEFINED;
}

void charge_profile_get_thresholds (ChargeProfile profile, guchar *start, guchar *end) {
switch (profile) {
case CHARGE_PROFILE_MAX_LIFESPAN:
*start = 48;
*end = 60;
break;
case CHARGE_PROFILE_BALANCED:
*start = 68;
*end = 80;
break;
case CHARGE_PROFILE_FULL_CHARGE:
default:
*start = 88;
*end = 100;
break;
}
}

const char *charge_profile_get_title (ChargeProfile profile) {
switch (profile) {
case CHARGE_PROFILE_MAX_LIFESPAN:
return "Max lifespan";
case CHARGE_PROFILE_BALANCED:
return "Balanced";
case CHARGE_PROFILE_FULL_CHARGE:
return "Full Charge";
default:
return "Undefined";
}
}

struct _CcChargeThresholdDialog {
GtkDialog parent_instance;

S76PowerDaemon *power_proxy;
GtkScale *scale;
GtkListBox *listbox;
GtkListBoxRow *max_lifespan_row;
GtkListBoxRow *balanced_row;
GtkListBoxRow *full_charge_row;
GtkRadioButton *max_lifespan_radio;
GtkRadioButton *balanced_radio;
GtkRadioButton *full_charge_radio;
GtkRadioButton *previous_radio;
GtkRadioButton *hidden_radio;
};

G_DEFINE_TYPE (CcChargeThresholdDialog, cc_charge_threshold_dialog, GTK_TYPE_DIALOG)

static void radio_toggled_cb (CcChargeThresholdDialog *self, GtkRadioButton *radio);

static void
set_charge_thresholds_ready(GObject *source_object,
GAsyncResult *res,
gpointer user_data) {
g_autoptr(GError) error = NULL;
CcChargeThresholdDialog *self = CC_CHARGE_THRESHOLD_DIALOG (user_data);
GtkRadioButton *previous_radio = self->previous_radio;

s76_power_daemon_call_set_charge_thresholds_finish (self->power_proxy, res, &error);
if (error) {
g_signal_handlers_block_by_func (previous_radio, radio_toggled_cb, self);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (previous_radio), TRUE);
g_signal_handlers_unblock_by_func (previous_radio, radio_toggled_cb, self);

if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
g_warning ("Failed to call set-charge-thresholds: %s", error->message);
return;
}
}

static void
radio_toggled_cb (CcChargeThresholdDialog *self, GtkRadioButton *radio)
{
ChargeProfile profile;
guchar start, end;
GVariant *thresholds = NULL;
gboolean state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radio));

if (!state)
{
self->previous_radio = radio;
return;
}

if (radio == self->max_lifespan_radio)
profile = CHARGE_PROFILE_MAX_LIFESPAN;
else if (radio == self->balanced_radio)
profile = CHARGE_PROFILE_BALANCED;
else if (radio == self->full_charge_radio)
profile = CHARGE_PROFILE_FULL_CHARGE;
else
return;

charge_profile_get_thresholds (profile, &start, &end);
thresholds = g_variant_new ("(yy)", start, end);

s76_power_daemon_call_set_charge_thresholds (self->power_proxy, thresholds, NULL, set_charge_thresholds_ready, self);
}

static void
row_activated_cb (CcChargeThresholdDialog *self, GtkListBoxRow *row)
{
if (row == self->max_lifespan_row)
gtk_button_clicked (GTK_BUTTON (self->max_lifespan_radio));
else if (row == self->balanced_row)
gtk_button_clicked (GTK_BUTTON (self->balanced_radio));
else if (row == self->full_charge_row)
gtk_button_clicked (GTK_BUTTON (self->full_charge_radio));
}

static void
cc_charge_threshold_dialog_class_init (CcChargeThresholdDialogClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);

g_type_ensure (CC_TYPE_CHARGE_THRESHOLD_ROW);

gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/power/cc-charge-threshold-dialog.ui");

gtk_widget_class_bind_template_child (widget_class, CcChargeThresholdDialog, listbox);
gtk_widget_class_bind_template_child (widget_class, CcChargeThresholdDialog, max_lifespan_row);
gtk_widget_class_bind_template_child (widget_class, CcChargeThresholdDialog, balanced_row);
gtk_widget_class_bind_template_child (widget_class, CcChargeThresholdDialog, full_charge_row);
gtk_widget_class_bind_template_child (widget_class, CcChargeThresholdDialog, max_lifespan_radio);
gtk_widget_class_bind_template_child (widget_class, CcChargeThresholdDialog, balanced_radio);
gtk_widget_class_bind_template_child (widget_class, CcChargeThresholdDialog, full_charge_radio);
gtk_widget_class_bind_template_child (widget_class, CcChargeThresholdDialog, hidden_radio);

gtk_widget_class_bind_template_callback (widget_class, radio_toggled_cb);
gtk_widget_class_bind_template_callback (widget_class, row_activated_cb);
}

static void
cc_charge_threshold_dialog_init (CcChargeThresholdDialog *self)
{
gtk_widget_init_template (GTK_WIDGET (self));

self->power_proxy = NULL;

gtk_list_box_set_header_func (self->listbox, cc_list_box_update_header_func, NULL, NULL);
}

CcChargeThresholdDialog*
cc_charge_threshold_dialog_new (S76PowerDaemon *power_proxy, ChargeProfile profile)
{
GtkRadioButton *radio = NULL;
CcChargeThresholdDialog *dialog = g_object_new (CC_TYPE_CHARGE_THRESHOLD_DIALOG,
"use-header-bar", 1,
NULL);
dialog->power_proxy = power_proxy;

switch (profile) {
case CHARGE_PROFILE_MAX_LIFESPAN:
radio = dialog->max_lifespan_radio;
break;
case CHARGE_PROFILE_BALANCED:
radio = dialog->balanced_radio;
break;
case CHARGE_PROFILE_FULL_CHARGE:
radio = dialog->full_charge_radio;
break;
default:
break;
}

if (radio)
{
g_signal_handlers_block_by_func (radio, radio_toggled_cb, dialog);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), TRUE);
g_signal_handlers_unblock_by_func (radio, radio_toggled_cb, dialog);
}
else
{
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->hidden_radio), TRUE);
}

return dialog;
}
42 changes: 42 additions & 0 deletions panels/power/cc-charge-threshold-dialog.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/* cc-brightness-scale.h
*
* Copyright (C) 2020 System76, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/

#pragma once

#include <gtk/gtk.h>
#include "cc-system76-power-generated.h"

G_BEGIN_DECLS

typedef enum {
CHARGE_PROFILE_MAX_LIFESPAN,
CHARGE_PROFILE_BALANCED,
CHARGE_PROFILE_FULL_CHARGE,
CHARGE_PROFILE_UNDEFINED,
} ChargeProfile;

#define CC_TYPE_CHARGE_THRESHOLD_DIALOG (cc_charge_threshold_dialog_get_type())
G_DECLARE_FINAL_TYPE (CcChargeThresholdDialog, cc_charge_threshold_dialog, CC, CHARGE_THRESHOLD_DIALOG, GtkDialog)
CcChargeThresholdDialog* cc_charge_threshold_dialog_new (S76PowerDaemon *power_proxy, ChargeProfile profile);
ChargeProfile charge_profile_from_thresholds (guchar start, guchar end);
void charge_profile_get_thresholds (ChargeProfile profile, guchar *start, guchar *end);
const char *charge_profile_get_title (ChargeProfile profile);

G_END_DECLS
90 changes: 90 additions & 0 deletions panels/power/cc-charge-threshold-dialog.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-requires gtk+ 3.0 -->
<template class="CcChargeThresholdDialog" parent="GtkDialog">
<property name="modal">True</property>
<child internal-child="vbox">
<object class="GtkBox">
<property name="visible">True</property>
<property name="margin">15</property>
<property name="spacing">15</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="halign">start</property>
<property name="wrap">True</property>
<property name="width-chars">70</property>
<property name="max-width-chars">70</property>
<property name="label">Increase the lifespan of your battery by setting a maximum charge value below 100%. Recommended for users who prefer to keep their chargers plugged into their system.</property>
</object>
</child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<child>
<object class="GtkListBox" id="listbox">
<property name="visible">True</property>
<property name="selection-mode">GTK_SELECTION_BROWSE</property>
<signal name="row-activated" handler="row_activated_cb" object="CcChargeThresholdDialog" swapped="yes" />
<child>
<object class="CcChargeThresholdRow" id="full_charge_row">
<property name="visible">True</property>
<property name="title">Full Charge</property>
<property name="description">Battery is charged to its full capacity for longer use on battery power. Charging resumes when the battery falls below 88% charge.</property>
<child internal-child="radio">
<object class="GtkRadioButton" id="full_charge_radio">
<property name="group">max_lifespan_radio</property>
<signal name="toggled" handler="radio_toggled_cb" object="CcChargeThresholdDialog" swapped="yes" />
</object>
</child>
</object>
</child>
<child>
<object class="CcChargeThresholdRow" id="balanced_row">
<property name="visible">True</property>
<property name="title">Balanced</property>
<property name="description">Use this threshold when charger can be unplugged at any time. Charging stops when battery reaches 80% charge, and resumes when battery falls below 68%.</property>
<child internal-child="radio">
<object class="GtkRadioButton" id="balanced_radio">
<property name="group">max_lifespan_radio</property>
<signal name="toggled" handler="radio_toggled_cb" object="CcChargeThresholdDialog" swapped="yes" />
</object>
</child>
</object>
</child>
<child>
<object class="CcChargeThresholdRow" id="max_lifespan_row">
<property name="visible">True</property>
<property name="title">Maximum Lifespan</property>
<property name="description">Use this threshold if you keep your charger plugged in at all times. Charging stops when battery reaches 60% charge, and resumes when battery falls below 48%.</property>
<child internal-child="radio">
<object class="GtkRadioButton" id="max_lifespan_radio">
<property name="group">max_lifespan_radio</property>
<signal name="toggled" handler="radio_toggled_cb" object="CcChargeThresholdDialog" swapped="yes" />
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<!-- Hack to show no radio button selected when profile is undefined -->
<object class="GtkRadioButton" id="hidden_radio">
<property name="visible">False</property>
<property name="group">max_lifespan_radio</property>
<signal name="toggled" handler="radio_toggled_cb" object="CcChargeThresholdDialog" swapped="yes" />
</object>
</child>
</object>
</child>
<child internal-child="headerbar">
<object class="GtkHeaderBar">
<property name="title">Battery Charge Thresholds</property>
<property name="can_focus">False</property>
<property name="show_close_button">True</property>
</object>
</child>
</template>
</interface>
Loading

0 comments on commit 6e86c1a

Please sign in to comment.