GCC Code Coverage Report


Directory: ./
File: panels/power/cc-power-panel.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 654 0.0%
Functions: 0 45 0.0%
Branches: 0 384 0.0%

Line Branch Exec Source
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 *
3 * Copyright (C) 2010 Red Hat, Inc
4 * Copyright (C) 2008 William Jon McCann <jmccann@redhat.com>
5 * Copyright (C) 2010,2015 Richard Hughes <richard@hughsie.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include <config.h>
23
24 #include <libupower-glib/upower.h>
25 #include <glib/gi18n.h>
26 #include <gnome-settings-daemon/gsd-enums.h>
27 #include <gio/gdesktopappinfo.h>
28
29 #include "shell/cc-object-storage.h"
30 #include "cc-list-row.h"
31 #include "cc-battery-row.h"
32 #include "cc-hostname.h"
33 #include "cc-power-profile-row.h"
34 #include "cc-power-profile-info-row.h"
35 #include "cc-power-panel.h"
36 #include "cc-power-resources.h"
37 #include "cc-util.h"
38
39 struct _CcPowerPanel
40 {
41 CcPanel parent_instance;
42
43 AdwSwitchRow *als_row;
44 AdwDialog *automatic_suspend_dialog;
45 CcListRow *automatic_suspend_row;
46 GtkListBox *battery_listbox;
47 AdwSwitchRow *battery_percentage_row;
48 AdwPreferencesGroup *battery_section;
49 AdwComboRow *blank_screen_row;
50 GtkListBox *device_listbox;
51 AdwPreferencesGroup *device_section;
52 AdwSwitchRow *dim_screen_row;
53 AdwPreferencesGroup *general_section;
54 GtkListStore *mobile_time_liststore;
55 AdwComboRow *power_button_row;
56 GtkListBox *power_profile_listbox;
57 GtkListBox *power_profile_info_listbox;
58 AdwPreferencesGroup *power_profile_section;
59 AdwSwitchRow *power_saver_low_battery_row;
60 GtkComboBox *suspend_on_battery_delay_combo;
61 AdwSwitchRow *suspend_on_battery_switch_row;
62 GtkWidget *suspend_on_battery_group;
63 GtkComboBox *suspend_on_ac_delay_combo;
64 AdwSwitchRow *suspend_on_ac_switch_row;
65
66 GSettings *gsd_settings;
67 GSettings *session_settings;
68 GSettings *interface_settings;
69 UpClient *up_client;
70 GPtrArray *devices;
71 gboolean has_batteries;
72 char *chassis_type;
73
74 GDBusProxy *iio_proxy;
75 guint iio_proxy_watch_id;
76 gboolean has_brightness;
77
78 GDBusProxy *power_profiles_proxy;
79 guint power_profiles_prop_id;
80 CcPowerProfileRow *power_profiles_row[NUM_CC_POWER_PROFILES];
81 gboolean power_profiles_in_update;
82 gboolean has_performance_degraded;
83 };
84
85 CC_PANEL_REGISTER (CcPowerPanel, cc_power_panel)
86
87 enum
88 {
89 ACTION_MODEL_TEXT,
90 ACTION_MODEL_VALUE
91 };
92
93 static const char *
94 cc_power_panel_get_help_uri (CcPanel *panel)
95 {
96 return "help:gnome-help/power";
97 }
98
99 static void
100 add_battery (CcPowerPanel *self, UpDevice *device, gboolean primary)
101 {
102 CcBatteryRow *row = cc_battery_row_new (device, primary);
103
104 gtk_list_box_append (self->battery_listbox, GTK_WIDGET (row));
105 gtk_widget_set_visible (GTK_WIDGET (self->battery_section), TRUE);
106 }
107
108 static void
109 add_device (CcPowerPanel *self, UpDevice *device)
110 {
111 CcBatteryRow *row = cc_battery_row_new (device, FALSE);
112
113 gtk_list_box_append (self->device_listbox, GTK_WIDGET (row));
114 gtk_widget_set_visible (GTK_WIDGET (self->device_section), TRUE);
115 }
116
117 static void
118 empty_listbox (GtkListBox *listbox)
119 {
120 GtkWidget *child;
121
122 while ((child = gtk_widget_get_first_child (GTK_WIDGET (listbox))) != NULL)
123 gtk_list_box_remove (listbox, child);
124 }
125
126 static void
127 update_power_saver_low_battery_row_visibility (CcPowerPanel *self)
128 {
129 g_autoptr(UpDevice) composite = NULL;
130 UpDeviceKind kind;
131
132 composite = up_client_get_display_device (self->up_client);
133 g_object_get (composite, "kind", &kind, NULL);
134 gtk_widget_set_visible (GTK_WIDGET (self->power_saver_low_battery_row),
135 self->power_profiles_proxy && kind == UP_DEVICE_KIND_BATTERY);
136 }
137
138 static void
139 up_client_changed (CcPowerPanel *self)
140 {
141 gint i;
142 UpDeviceKind kind;
143 guint n_batteries;
144 gboolean on_ups;
145 g_autoptr(UpDevice) composite = NULL;
146
147 empty_listbox (self->battery_listbox);
148 gtk_widget_set_visible (GTK_WIDGET (self->battery_section), FALSE);
149
150 empty_listbox (self->device_listbox);
151 gtk_widget_set_visible (GTK_WIDGET (self->device_section), FALSE);
152
153 on_ups = FALSE;
154 n_batteries = 0;
155 composite = up_client_get_display_device (self->up_client);
156 g_object_get (composite, "kind", &kind, NULL);
157 if (kind == UP_DEVICE_KIND_UPS)
158 {
159 on_ups = TRUE;
160 }
161 else
162 {
163 gboolean is_extra_battery = FALSE;
164
165 /* Count the batteries */
166 for (i = 0; self->devices != NULL && i < self->devices->len; i++)
167 {
168 UpDevice *device = (UpDevice*) g_ptr_array_index (self->devices, i);
169 gboolean is_power_supply = FALSE;
170 g_object_get (device,
171 "kind", &kind,
172 "power-supply", &is_power_supply,
173 NULL);
174 if (kind == UP_DEVICE_KIND_BATTERY &&
175 is_power_supply)
176 {
177 n_batteries++;
178 if (is_extra_battery == FALSE)
179 {
180 is_extra_battery = TRUE;
181 g_object_set_data (G_OBJECT (device), "is-main-battery", GINT_TO_POINTER(TRUE));
182 }
183 }
184 }
185 }
186
187 if (n_batteries > 1)
188 adw_preferences_group_set_title (self->battery_section, _("Battery Levels"));
189 else if (on_ups)
190 {
191 /* Translators: UPS is an Uninterruptible Power Supply:
192 * https://en.wikipedia.org/wiki/Uninterruptible_power_supply */
193 adw_preferences_group_set_title (self->battery_section, _("UPS"));
194 }
195 else
196 adw_preferences_group_set_title (self->battery_section, _("Battery Level"));
197
198 if (!on_ups && n_batteries > 1)
199 add_battery (self, composite, TRUE);
200
201 for (i = 0; self->devices != NULL && i < self->devices->len; i++)
202 {
203 UpDevice *device = (UpDevice*) g_ptr_array_index (self->devices, i);
204 gboolean is_power_supply = FALSE;
205 g_object_get (device,
206 "kind", &kind,
207 "power-supply", &is_power_supply,
208 NULL);
209 if (kind == UP_DEVICE_KIND_LINE_POWER)
210 {
211 /* do nothing */
212 }
213 else if (kind == UP_DEVICE_KIND_UPS && on_ups)
214 {
215 add_battery (self, device, TRUE);
216 }
217 else if (kind == UP_DEVICE_KIND_BATTERY && is_power_supply && !on_ups && n_batteries == 1)
218 {
219 add_battery (self, device, TRUE);
220 }
221 else if (kind == UP_DEVICE_KIND_BATTERY && is_power_supply)
222 {
223 add_battery (self, device, FALSE);
224 }
225 else
226 {
227 add_device (self, device);
228 }
229 }
230
231 update_power_saver_low_battery_row_visibility (self);
232 }
233
234 static void
235 up_client_device_removed (CcPowerPanel *self,
236 const char *object_path)
237 {
238 guint i;
239
240 if (self->devices == NULL)
241 return;
242
243 for (i = 0; i < self->devices->len; i++)
244 {
245 UpDevice *device = g_ptr_array_index (self->devices, i);
246
247 if (g_strcmp0 (object_path, up_device_get_object_path (device)) == 0)
248 {
249 g_ptr_array_remove_index (self->devices, i);
250 break;
251 }
252 }
253
254 up_client_changed (self);
255 }
256
257 static void
258 up_client_device_added (CcPowerPanel *self,
259 UpDevice *device)
260 {
261 g_ptr_array_add (self->devices, g_object_ref (device));
262 g_signal_connect_object (G_OBJECT (device), "notify",
263 G_CALLBACK (up_client_changed), self, G_CONNECT_SWAPPED);
264 up_client_changed (self);
265 }
266
267 static void
268 als_row_changed_cb (CcPowerPanel *self)
269 {
270 gboolean enabled;
271 enabled = adw_switch_row_get_active (self->als_row);
272 g_debug ("Setting ALS enabled %s", enabled ? "on" : "off");
273 g_settings_set_boolean (self->gsd_settings, "ambient-enabled", enabled);
274 }
275
276 static void
277 als_enabled_state_changed (CcPowerPanel *self)
278 {
279 gboolean enabled;
280 gboolean visible = FALSE;
281
282 if (self->iio_proxy != NULL)
283 {
284 g_autoptr(GVariant) v = g_dbus_proxy_get_cached_property (self->iio_proxy, "HasAmbientLight");
285 if (v != NULL)
286 visible = g_variant_get_boolean (v);
287 }
288
289 if (gtk_widget_get_visible (GTK_WIDGET (self->als_row)) == visible)
290 return;
291
292 enabled = g_settings_get_boolean (self->gsd_settings, "ambient-enabled");
293 g_debug ("ALS enabled: %s", enabled ? "on" : "off");
294 g_signal_handlers_block_by_func (self->als_row, als_row_changed_cb, self);
295 adw_switch_row_set_active (self->als_row, enabled);
296 gtk_widget_set_visible (GTK_WIDGET (self->als_row), visible && self->has_brightness);
297 g_signal_handlers_unblock_by_func (self->als_row, als_row_changed_cb, self);
298 }
299
300 static void
301 combo_time_changed_cb (CcPowerPanel *self, GtkWidget *widget)
302 {
303 GtkTreeIter iter;
304 GtkTreeModel *model;
305 gint value;
306 gboolean ret;
307 const gchar *key = (const gchar *)g_object_get_data (G_OBJECT(widget), "_gsettings_key");
308
309 /* no selection */
310 ret = gtk_combo_box_get_active_iter (GTK_COMBO_BOX(widget), &iter);
311 if (!ret)
312 return;
313
314 /* get entry */
315 model = gtk_combo_box_get_model (GTK_COMBO_BOX(widget));
316 gtk_tree_model_get (model, &iter,
317 1, &value,
318 -1);
319
320 /* set both keys */
321 g_settings_set_int (self->gsd_settings, key, value);
322 }
323
324 static void
325 set_value_for_combo (GtkComboBox *combo_box, gint value)
326 {
327 GtkTreeIter iter;
328 g_autoptr(GtkTreeIter) insert = NULL;
329 GtkTreeIter new;
330 GtkTreeModel *model;
331 gint value_tmp;
332 gint value_last = 0;
333 g_autofree gchar *text = NULL;
334 gboolean ret;
335
336 /* get entry */
337 model = gtk_combo_box_get_model (combo_box);
338 ret = gtk_tree_model_get_iter_first (model, &iter);
339 if (!ret)
340 return;
341
342 /* try to make the UI match the setting */
343 do
344 {
345 gtk_tree_model_get (model, &iter,
346 ACTION_MODEL_VALUE, &value_tmp,
347 -1);
348 if (value_tmp == value)
349 {
350 gtk_combo_box_set_active_iter (combo_box, &iter);
351 return;
352 }
353
354 /* Insert before if the next value is larger or the value is lower
355 * again (i.e. "Never" is zero and last). */
356 if (!insert && (value_tmp > value || value_last > value_tmp))
357 insert = gtk_tree_iter_copy (&iter);
358
359 value_last = value_tmp;
360 } while (gtk_tree_model_iter_next (model, &iter));
361
362 /* The value is not listed, so add it at the best point (or the end). */
363 gtk_list_store_insert_before (GTK_LIST_STORE (model), &new, insert);
364
365 text = cc_util_time_to_string_text (value * 1000);
366 gtk_list_store_set (GTK_LIST_STORE (model), &new,
367 ACTION_MODEL_TEXT, text,
368 ACTION_MODEL_VALUE, value,
369 -1);
370 gtk_combo_box_set_active_iter (combo_box, &new);
371 }
372
373 static void
374 set_value_for_combo_row (AdwComboRow *combo_row, gint value)
375 {
376 g_autoptr (GObject) new_item = NULL;
377 gboolean insert = FALSE;
378 guint insert_before = 0;
379 guint i;
380 GListModel *model;
381 gint value_last = 0;
382 g_autofree gchar *text = NULL;
383
384 /* try to make the UI match the setting */
385 model = adw_combo_row_get_model (combo_row);
386 for (i = 0; i < g_list_model_get_n_items (model); i++)
387 {
388 g_autoptr (GObject) item = g_list_model_get_item (model, i);
389 gint value_tmp = GPOINTER_TO_UINT (g_object_get_data (item, "value"));
390 if (value_tmp == value)
391 {
392 adw_combo_row_set_selected (combo_row, i);
393 return;
394 }
395
396 /* Insert before if the next value is larger or the value is lower
397 * again (i.e. "Never" is zero and last). */
398 if (!insert && (value_tmp > value || value_last > value_tmp))
399 {
400 insert = TRUE;
401 insert_before = i;
402 }
403
404 value_last = value_tmp;
405 }
406
407 /* The value is not listed, so add it at the best point (or the end). */
408 text = cc_util_time_to_string_text (value * 1000);
409 gtk_string_list_append (GTK_STRING_LIST (model), text);
410
411 new_item = g_list_model_get_item (model, i);
412 g_object_set_data (G_OBJECT (new_item), "value", GUINT_TO_POINTER (value));
413
414 adw_combo_row_set_selected (combo_row, insert_before);
415 }
416
417 static void
418 set_ac_battery_ui_mode (CcPowerPanel *self)
419 {
420 GPtrArray *devices;
421 guint i;
422
423 self->has_batteries = FALSE;
424 devices = up_client_get_devices2 (self->up_client);
425 g_debug ("got %d devices from upower\n", devices ? devices->len : 0);
426
427 for (i = 0; devices != NULL && i < devices->len; i++)
428 {
429 UpDevice *device;
430 gboolean is_power_supply;
431 UpDeviceKind kind;
432
433 device = g_ptr_array_index (devices, i);
434 g_object_get (device,
435 "kind", &kind,
436 "power-supply", &is_power_supply,
437 NULL);
438 if (kind == UP_DEVICE_KIND_UPS ||
439 (kind == UP_DEVICE_KIND_BATTERY && is_power_supply))
440 {
441 self->has_batteries = TRUE;
442 break;
443 }
444 }
445 g_clear_pointer (&devices, g_ptr_array_unref);
446
447 if (!self->has_batteries)
448 {
449 gtk_widget_set_visible (GTK_WIDGET (self->suspend_on_battery_group), FALSE);
450 adw_preferences_row_set_title (ADW_PREFERENCES_ROW (self->suspend_on_ac_switch_row), _("When _Idle"));
451 }
452 }
453
454 static gboolean
455 keynav_failed_cb (CcPowerPanel *self, GtkDirectionType direction, GtkWidget *list)
456 {
457 if (direction != GTK_DIR_UP && direction != GTK_DIR_DOWN)
458 return FALSE;
459
460 direction = GTK_DIR_UP ? GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD;
461
462 return gtk_widget_child_focus (GTK_WIDGET (self), direction);
463 }
464
465 static void
466 blank_screen_row_changed_cb (CcPowerPanel *self)
467 {
468 g_autoptr (GObject) item = NULL;
469 GListModel *model;
470 gint selected_index;
471 gint value;
472
473 model = adw_combo_row_get_model (self->blank_screen_row);
474 selected_index = adw_combo_row_get_selected (self->blank_screen_row);
475 if (selected_index == -1)
476 return;
477
478 item = g_list_model_get_item (model, selected_index);
479 value = GPOINTER_TO_UINT (g_object_get_data (item, "value"));
480
481 g_settings_set_uint (self->session_settings, "idle-delay", value);
482 }
483
484 static void
485 power_button_row_changed_cb (CcPowerPanel *self)
486 {
487 g_autoptr (GObject) item = NULL;
488 GListModel *model;
489 gint selected_index;
490 gint value;
491
492 model = adw_combo_row_get_model (self->power_button_row);
493 selected_index = adw_combo_row_get_selected (self->power_button_row);
494 if (selected_index == -1)
495 return;
496
497 item = g_list_model_get_item (model, selected_index);
498 value = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (item), "value"));
499
500 g_settings_set_enum (self->gsd_settings, "power-button-action", value);
501 }
502
503 static void
504 als_enabled_setting_changed (CcPowerPanel *self)
505 {
506 als_enabled_state_changed (self);
507 }
508
509 static void
510 iio_proxy_appeared_cb (GDBusConnection *connection,
511 const gchar *name,
512 const gchar *name_owner,
513 gpointer user_data)
514 {
515 CcPowerPanel *self = CC_POWER_PANEL (user_data);
516 g_autoptr(GError) error = NULL;
517
518 self->iio_proxy =
519 cc_object_storage_create_dbus_proxy_sync (G_BUS_TYPE_SYSTEM,
520 G_DBUS_PROXY_FLAGS_NONE,
521 "net.hadess.SensorProxy",
522 "/net/hadess/SensorProxy",
523 "net.hadess.SensorProxy",
524 NULL, &error);
525 if (error != NULL)
526 {
527 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
528 g_warning ("Could not create IIO sensor proxy: %s", error->message);
529 return;
530 }
531
532 g_signal_connect_object (G_OBJECT (self->iio_proxy), "g-properties-changed",
533 G_CALLBACK (als_enabled_state_changed), self,
534 G_CONNECT_SWAPPED);
535 als_enabled_state_changed (self);
536 }
537
538 static void
539 iio_proxy_vanished_cb (GDBusConnection *connection,
540 const gchar *name,
541 gpointer user_data)
542 {
543 CcPowerPanel *self = CC_POWER_PANEL (user_data);
544 g_clear_object (&self->iio_proxy);
545 als_enabled_state_changed (self);
546 }
547
548 static gboolean
549 get_sleep_type (GValue *value,
550 GVariant *variant,
551 gpointer data)
552 {
553 gboolean enabled;
554
555 if (g_strcmp0 (g_variant_get_string (variant, NULL), "nothing") == 0)
556 enabled = FALSE;
557 else
558 enabled = TRUE;
559
560 g_value_set_boolean (value, enabled);
561
562 return TRUE;
563 }
564
565 static GVariant *
566 set_sleep_type (const GValue *value,
567 const GVariantType *expected_type,
568 gpointer data)
569 {
570 GVariant *res;
571
572 if (g_value_get_boolean (value))
573 res = g_variant_new_string ("suspend");
574 else
575 res = g_variant_new_string ("nothing");
576
577 return res;
578 }
579
580 static void
581 populate_power_button_row (AdwComboRow *combo_row,
582 gboolean can_suspend,
583 gboolean can_hibernate)
584 {
585 g_autoptr (GtkStringList) string_list = NULL;
586 struct {
587 char *name;
588 GsdPowerButtonActionType value;
589 } actions[] = {
590 { N_("Suspend"), GSD_POWER_BUTTON_ACTION_SUSPEND },
591 { N_("Power Off"), GSD_POWER_BUTTON_ACTION_INTERACTIVE },
592 { N_("Hibernate"), GSD_POWER_BUTTON_ACTION_HIBERNATE },
593 { N_("Nothing"), GSD_POWER_BUTTON_ACTION_NOTHING }
594 };
595 guint item_index = 0;
596 guint i;
597
598 string_list = gtk_string_list_new (NULL);
599 for (i = 0; i < G_N_ELEMENTS (actions); i++)
600 {
601 g_autoptr (GObject) item = NULL;
602
603 if (!can_suspend && actions[i].value == GSD_POWER_BUTTON_ACTION_SUSPEND)
604 continue;
605
606 if (!can_hibernate && actions[i].value == GSD_POWER_BUTTON_ACTION_HIBERNATE)
607 continue;
608
609 gtk_string_list_append (string_list, _(actions[i].name));
610
611 item = g_list_model_get_item (G_LIST_MODEL (string_list), item_index++);
612 g_object_set_data (item, "value", GUINT_TO_POINTER (actions[i].value));
613 }
614
615 adw_combo_row_set_model (combo_row, G_LIST_MODEL (string_list));
616 }
617
618 #define NEVER 0
619
620 static void
621 update_automatic_suspend_label (CcPowerPanel *self)
622 {
623 GsdPowerActionType ac_action;
624 GsdPowerActionType battery_action;
625 gint ac_timeout;
626 gint battery_timeout;
627 const gchar *s;
628
629 ac_action = g_settings_get_enum (self->gsd_settings, "sleep-inactive-ac-type");
630 battery_action = g_settings_get_enum (self->gsd_settings, "sleep-inactive-battery-type");
631 ac_timeout = g_settings_get_int (self->gsd_settings, "sleep-inactive-ac-timeout");
632 battery_timeout = g_settings_get_int (self->gsd_settings, "sleep-inactive-battery-timeout");
633
634 if (ac_timeout < 0)
635 g_warning ("Invalid negative timeout for 'sleep-inactive-ac-timeout': %d", ac_timeout);
636 if (battery_timeout < 0)
637 g_warning ("Invalid negative timeout for 'sleep-inactive-battery-timeout': %d", battery_timeout);
638
639 if (ac_action == GSD_POWER_ACTION_NOTHING || ac_timeout < 0)
640 ac_timeout = NEVER;
641 if (battery_action == GSD_POWER_ACTION_NOTHING || battery_timeout < 0)
642 battery_timeout = NEVER;
643
644 if (self->has_batteries)
645 {
646 if (ac_timeout == NEVER && battery_timeout == NEVER)
647 s = _("Off");
648 else if (ac_timeout == NEVER && battery_timeout > 0)
649 s = _("On Battery Power");
650 else if (ac_timeout > 0 && battery_timeout == NEVER)
651 s = _("When Plugged In");
652 else
653 s = _("On");
654 }
655 else
656 {
657 if (ac_timeout == NEVER)
658 s = _("Off");
659 else
660 s = _("On");
661 }
662
663 cc_list_row_set_secondary_label (self->automatic_suspend_row, s);
664 }
665
666 static void
667 on_suspend_settings_changed (CcPowerPanel *self,
668 const char *key)
669 {
670 if (g_str_has_prefix (key, "sleep-inactive-"))
671 {
672 update_automatic_suspend_label (self);
673 }
674 }
675
676 static gboolean
677 can_suspend_or_hibernate (CcPowerPanel *self,
678 const char *method_name)
679 {
680 g_autoptr(GDBusConnection) connection = NULL;
681 g_autoptr(GVariant) variant = NULL;
682 g_autoptr(GError) error = NULL;
683 const char *s;
684
685 connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM,
686 cc_panel_get_cancellable (CC_PANEL (self)),
687 &error);
688 if (!connection)
689 {
690 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
691 g_warning ("system bus not available: %s", error->message);
692 return FALSE;
693 }
694
695 variant = g_dbus_connection_call_sync (connection,
696 "org.freedesktop.login1",
697 "/org/freedesktop/login1",
698 "org.freedesktop.login1.Manager",
699 method_name,
700 NULL,
701 NULL,
702 G_DBUS_CALL_FLAGS_NONE,
703 -1,
704 cc_panel_get_cancellable (CC_PANEL (self)),
705 &error);
706
707 if (!variant)
708 {
709 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
710 g_debug ("Failed to call %s(): %s", method_name, error->message);
711 return FALSE;
712 }
713
714 g_variant_get (variant, "(&s)", &s);
715 return g_strcmp0 (s, "yes") == 0;
716 }
717
718 static void
719 got_brightness_cb (GObject *source_object,
720 GAsyncResult *res,
721 gpointer user_data)
722 {
723 g_autoptr(GVariant) result = NULL;
724 g_autoptr(GError) error = NULL;
725 gint32 brightness = -1.0;
726 CcPowerPanel *self;
727
728 result = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, &error);
729 if (!result)
730 {
731 g_debug ("Failed to get Brightness property: %s", error->message);
732 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
733 return;
734 }
735 else
736 {
737 g_autoptr(GVariant) v = NULL;
738 g_variant_get (result, "(v)", &v);
739 brightness = v ? g_variant_get_int32 (v) : -1.0;
740 }
741
742 self = user_data;
743 self->has_brightness = brightness >= 0.0;
744
745 gtk_widget_set_visible (GTK_WIDGET (self->dim_screen_row), self->has_brightness);
746 als_enabled_state_changed (self);
747 }
748
749 static void
750 populate_blank_screen_row (AdwComboRow *combo_row)
751 {
752 g_autoptr (GtkStringList) string_list = NULL;
753 g_autoptr (GObject) never_object = NULL;
754 gint minutes[] = { 1, 2, 3, 4, 5, 8, 10, 12, 15 };
755 guint i;
756
757 string_list = gtk_string_list_new (NULL);
758 for (i = 0; i < G_N_ELEMENTS (minutes); i++)
759 {
760 g_autoptr (GObject) item = NULL;
761 g_autofree gchar *text = NULL;
762
763 /* Translators: Option for "Blank Screen" in "Power" panel */
764 text = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "%d minute", "%d minutes", minutes[i]), minutes[i]);
765 gtk_string_list_append (string_list, text);
766
767 item = g_list_model_get_item (G_LIST_MODEL (string_list), i);
768 g_object_set_data (item, "value", GUINT_TO_POINTER (minutes[i] * 60));
769 }
770
771 gtk_string_list_append (string_list, C_("Idle time", "Never"));
772 never_object = g_list_model_get_item (G_LIST_MODEL (string_list), i);
773 g_object_set_data (never_object, "value", GUINT_TO_POINTER (0));
774
775 adw_combo_row_set_model (combo_row, G_LIST_MODEL (string_list));
776 }
777
778 static void
779 setup_power_saving (CcPowerPanel *self)
780 {
781 g_autoptr(GDBusConnection) connection = NULL;
782 g_autoptr(GError) error = NULL;
783 int value;
784
785 /* ambient light sensor */
786 self->iio_proxy_watch_id =
787 g_bus_watch_name (G_BUS_TYPE_SYSTEM,
788 "net.hadess.SensorProxy",
789 G_BUS_NAME_WATCHER_FLAGS_NONE,
790 iio_proxy_appeared_cb,
791 iio_proxy_vanished_cb,
792 self, NULL);
793 g_signal_connect_object (self->gsd_settings, "changed",
794 G_CALLBACK (als_enabled_setting_changed), self, G_CONNECT_SWAPPED);
795
796 connection = g_bus_get_sync (G_BUS_TYPE_SESSION,
797 cc_panel_get_cancellable (CC_PANEL (self)),
798 &error);
799 if (connection)
800 {
801 g_dbus_connection_call (connection,
802 "org.gnome.SettingsDaemon.Power",
803 "/org/gnome/SettingsDaemon/Power",
804 "org.freedesktop.DBus.Properties",
805 "Get",
806 g_variant_new ("(ss)",
807 "org.gnome.SettingsDaemon.Power.Screen",
808 "Brightness"),
809 NULL,
810 G_DBUS_CALL_FLAGS_NONE,
811 -1,
812 cc_panel_get_cancellable (CC_PANEL (self)),
813 got_brightness_cb,
814 self);
815 }
816 else
817 {
818 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
819 g_warning ("session bus not available: %s", error->message);
820 }
821
822
823 g_settings_bind (self->gsd_settings, "idle-dim",
824 self->dim_screen_row, "active",
825 G_SETTINGS_BIND_DEFAULT);
826
827 g_signal_handlers_block_by_func (self->blank_screen_row, blank_screen_row_changed_cb, self);
828 populate_blank_screen_row (self->blank_screen_row);
829 value = g_settings_get_uint (self->session_settings, "idle-delay");
830 set_value_for_combo_row (self->blank_screen_row, value);
831 g_signal_handlers_unblock_by_func (self->blank_screen_row, blank_screen_row_changed_cb, self);
832
833 /* The default values for these settings are unfortunate for us;
834 * timeout == 0, action == suspend means 'do nothing' - just
835 * as timout === anything, action == nothing.
836 * For our switch/combobox combination, the second choice works
837 * much better, so translate the first to the second here.
838 */
839 if (g_settings_get_int (self->gsd_settings, "sleep-inactive-ac-timeout") == 0)
840 {
841 g_settings_set_enum (self->gsd_settings, "sleep-inactive-ac-type", GSD_POWER_ACTION_NOTHING);
842 g_settings_set_int (self->gsd_settings, "sleep-inactive-ac-timeout", 3600);
843 }
844 if (g_settings_get_int (self->gsd_settings, "sleep-inactive-battery-timeout") == 0)
845 {
846 g_settings_set_enum (self->gsd_settings, "sleep-inactive-battery-type", GSD_POWER_ACTION_NOTHING);
847 g_settings_set_int (self->gsd_settings, "sleep-inactive-battery-timeout", 1800);
848 }
849
850 /* Automatic suspend row */
851 if (can_suspend_or_hibernate (self, "CanSuspend") &&
852 g_strcmp0 (self->chassis_type, "vm") != 0)
853 {
854 gtk_widget_set_visible (GTK_WIDGET (self->automatic_suspend_row), TRUE);
855
856 g_signal_connect_object (self->gsd_settings, "changed", G_CALLBACK (on_suspend_settings_changed), self, G_CONNECT_SWAPPED);
857
858 g_settings_bind_with_mapping (self->gsd_settings, "sleep-inactive-battery-type",
859 self->suspend_on_battery_switch_row, "active",
860 G_SETTINGS_BIND_DEFAULT,
861 get_sleep_type, set_sleep_type, NULL, NULL);
862
863 g_object_set_data (G_OBJECT (self->suspend_on_battery_delay_combo), "_gsettings_key", "sleep-inactive-battery-timeout");
864 value = g_settings_get_int (self->gsd_settings, "sleep-inactive-battery-timeout");
865 set_value_for_combo (self->suspend_on_battery_delay_combo, value);
866 g_signal_connect_object (self->suspend_on_battery_delay_combo, "changed",
867 G_CALLBACK (combo_time_changed_cb), self, G_CONNECT_SWAPPED);
868 g_object_bind_property (self->suspend_on_battery_switch_row, "active", self->suspend_on_battery_delay_combo, "sensitive",
869 G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
870
871 g_settings_bind_with_mapping (self->gsd_settings, "sleep-inactive-ac-type",
872 self->suspend_on_ac_switch_row, "active",
873 G_SETTINGS_BIND_DEFAULT,
874 get_sleep_type, set_sleep_type, NULL, NULL);
875
876 g_object_set_data (G_OBJECT (self->suspend_on_ac_delay_combo), "_gsettings_key", "sleep-inactive-ac-timeout");
877 value = g_settings_get_int (self->gsd_settings, "sleep-inactive-ac-timeout");
878 set_value_for_combo (self->suspend_on_ac_delay_combo, value);
879 g_signal_connect_object (self->suspend_on_ac_delay_combo, "changed",
880 G_CALLBACK (combo_time_changed_cb), self, G_CONNECT_SWAPPED);
881 g_object_bind_property (self->suspend_on_ac_switch_row, "active", self->suspend_on_ac_delay_combo, "sensitive",
882 G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
883
884 set_ac_battery_ui_mode (self);
885 update_automatic_suspend_label (self);
886 }
887 }
888
889 static const char *
890 variant_lookup_string (GVariant *dict,
891 const char *key)
892 {
893 GVariant *variant;
894
895 variant = g_variant_lookup_value (dict, key, G_VARIANT_TYPE_STRING);
896 if (!variant)
897 return NULL;
898 return g_variant_get_string (variant, NULL);
899 }
900
901 static void
902 performance_profile_set_active (CcPowerPanel *self,
903 const char *profile_str)
904 {
905 CcPowerProfile profile = cc_power_profile_from_str (profile_str);
906 GtkCheckButton *button;
907
908 button = cc_power_profile_row_get_radio_button (CC_POWER_PROFILE_ROW (self->power_profiles_row[profile]));
909 if (!button) {
910 g_warning ("Not setting profile '%s' as it doesn't have a widget", profile_str);
911 return;
912 }
913 gtk_check_button_set_active (GTK_CHECK_BUTTON (button), TRUE);
914 }
915
916 static void
917 power_profile_update_info_boxes (CcPowerPanel *self)
918 {
919 g_autoptr(GVariant) degraded_variant = NULL;
920 g_autoptr(GVariant) holds_variant = NULL;
921 g_autoptr(GVariant) profile_variant = NULL;
922 guint i, num_children;
923 const char *degraded = NULL;
924 const char *profile;
925 CcPowerProfileInfoRow *row;
926 int next_insert = 0;
927
928 empty_listbox (self->power_profile_info_listbox);
929 gtk_widget_set_visible (GTK_WIDGET (self->power_profile_info_listbox), FALSE);
930
931 profile_variant = g_dbus_proxy_get_cached_property (self->power_profiles_proxy, "ActiveProfile");
932 if (!profile_variant)
933 {
934 g_warning ("No 'ActiveProfile' property on power-profiles-daemon service");
935 return;
936 }
937 profile = g_variant_get_string (profile_variant, NULL);
938
939 degraded_variant = g_dbus_proxy_get_cached_property (self->power_profiles_proxy, "PerformanceDegraded");
940 if (degraded_variant)
941 degraded = g_variant_get_string (degraded_variant, NULL);
942 if (degraded && *degraded != '\0')
943 {
944 const char *text;
945
946 gtk_widget_set_visible (GTK_WIDGET (self->power_profile_info_listbox), TRUE);
947
948 if (g_str_equal (degraded, "high-operating-temperature"))
949 text = _("Performance mode temporarily disabled due to high operating temperature");
950 else if (g_str_equal (degraded, "lap-detected"))
951 text = _("Lap detected: performance mode temporarily unavailable. Move the device to a stable surface to restore.");
952 else
953 text = _("Performance mode temporarily disabled");
954
955 row = cc_power_profile_info_row_new (text);
956 gtk_list_box_append (self->power_profile_info_listbox, GTK_WIDGET (row));
957 if (g_str_equal (profile, "performance"))
958 next_insert = 1;
959 }
960
961 holds_variant = g_dbus_proxy_get_cached_property (self->power_profiles_proxy, "ActiveProfileHolds");
962 if (!holds_variant)
963 {
964 g_warning ("No 'ActiveProfileHolds' property on power-profiles-daemon service");
965 return;
966 }
967
968 num_children = g_variant_n_children (holds_variant);
969 for (i = 0; i < num_children; i++)
970 {
971 g_autoptr(GDesktopAppInfo) app_info = NULL;
972 g_autoptr(GVariant) hold_variant = NULL;
973 g_autofree char *text = NULL;
974 const char *app_id, *held_profile, *reason, *name;
975
976 hold_variant = g_variant_get_child_value (holds_variant, i);
977 if (!hold_variant || !g_variant_is_of_type (hold_variant, G_VARIANT_TYPE ("a{sv}")))
978 continue;
979
980 app_id = variant_lookup_string (hold_variant, "ApplicationId");
981 if (!app_id)
982 continue;
983
984 gtk_widget_set_visible (GTK_WIDGET (self->power_profile_info_listbox), TRUE);
985
986 app_info = g_desktop_app_info_new (app_id);
987 name = app_info ? g_app_info_get_name (G_APP_INFO (app_info)) : app_id;
988 held_profile = variant_lookup_string (hold_variant, "Profile");
989 reason = variant_lookup_string (hold_variant, "Reason");
990 g_debug ("Adding info row for %s hold by %s: %s", held_profile, app_id, reason);
991
992 if (g_strcmp0 (held_profile, "power-saver") == 0 &&
993 g_strcmp0 (app_id, "org.gnome.SettingsDaemon.Power") == 0)
994 {
995 text = g_strdup (_("Power saver enabled due to low battery. Previous mode will be restored when battery is charged."));
996 }
997 else
998 {
999 switch (cc_power_profile_from_str (held_profile))
1000 {
1001 case CC_POWER_PROFILE_POWER_SAVER:
1002 /* translators: "%s" is an application name */
1003 text = g_strdup_printf (_("Power Saver mode activated by “%s”"), name);
1004 break;
1005 case CC_POWER_PROFILE_PERFORMANCE:
1006 /* translators: "%s" is an application name */
1007 text = g_strdup_printf (_("Performance mode activated by “%s”"), name);
1008 break;
1009 default:
1010 g_assert_not_reached ();
1011 }
1012 }
1013
1014 row = cc_power_profile_info_row_new (text);
1015 if (g_strcmp0 (held_profile, profile) != 0)
1016 gtk_list_box_insert (GTK_LIST_BOX (self->power_profile_info_listbox), GTK_WIDGET (row), -1);
1017 else
1018 gtk_list_box_insert (GTK_LIST_BOX (self->power_profile_info_listbox), GTK_WIDGET (row), next_insert);
1019 }
1020 }
1021
1022 static gint
1023 perf_profile_list_box_sort (GtkListBoxRow *row1,
1024 GtkListBoxRow *row2,
1025 gpointer user_data)
1026 {
1027 CcPowerProfile row1_profile, row2_profile;
1028
1029 row1_profile = cc_power_profile_row_get_profile (CC_POWER_PROFILE_ROW (row1));
1030 row2_profile = cc_power_profile_row_get_profile (CC_POWER_PROFILE_ROW (row2));
1031
1032 if (row1_profile < row2_profile)
1033 return -1;
1034 if (row1_profile > row2_profile)
1035 return 1;
1036 return 0;
1037 }
1038
1039 static void
1040 power_profiles_properties_changed_cb (CcPowerPanel *self,
1041 GVariant *changed_properties,
1042 GStrv invalidated_properties,
1043 GDBusProxy *proxy)
1044 {
1045 g_autoptr(GVariantIter) iter = NULL;
1046 const char *key;
1047 g_autoptr(GVariant) value = NULL;
1048
1049 g_variant_get (changed_properties, "a{sv}", &iter);
1050 while (g_variant_iter_next (iter, "{&sv}", &key, &value))
1051 {
1052 if (g_strcmp0 (key, "PerformanceDegraded") == 0 ||
1053 g_strcmp0 (key, "ActiveProfileHolds") == 0)
1054 {
1055 power_profile_update_info_boxes (self);
1056 }
1057 else if (g_strcmp0 (key, "ActiveProfile") == 0)
1058 {
1059 self->power_profiles_in_update = TRUE;
1060 performance_profile_set_active (self, g_variant_get_string (value, NULL));
1061 self->power_profiles_in_update = FALSE;
1062 }
1063 else
1064 {
1065 g_debug ("Unhandled change on '%s' property", key);
1066 }
1067 }
1068 }
1069
1070 static void
1071 set_active_profile_cb (GObject *source_object,
1072 GAsyncResult *res,
1073 gpointer user_data)
1074 {
1075 g_autoptr(GVariant) variant = NULL;
1076 g_autoptr(GError) error = NULL;
1077
1078 variant = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
1079 res, &error);
1080 if (!variant)
1081 {
1082 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1083 g_warning ("Could not set active profile: %s", error->message);
1084 }
1085 }
1086
1087 static void
1088 power_profile_button_toggled_cb (CcPowerProfileRow *row,
1089 gpointer user_data)
1090 {
1091 CcPowerPanel *self = user_data;
1092 CcPowerProfile profile;
1093 g_autoptr(GDBusConnection) connection = NULL;
1094 g_autoptr(GError) error = NULL;
1095
1096 if (!cc_power_profile_row_get_active (row))
1097 return;
1098 if (self->power_profiles_in_update)
1099 return;
1100
1101 profile = cc_power_profile_row_get_profile (row);
1102
1103 connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM,
1104 cc_panel_get_cancellable (CC_PANEL (self)),
1105 &error);
1106 if (!connection)
1107 {
1108 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1109 g_warning ("system bus not available: %s", error->message);
1110 return;
1111 }
1112
1113 g_dbus_connection_call (connection,
1114 "net.hadess.PowerProfiles",
1115 "/net/hadess/PowerProfiles",
1116 "org.freedesktop.DBus.Properties",
1117 "Set",
1118 g_variant_new ("(ssv)",
1119 "net.hadess.PowerProfiles",
1120 "ActiveProfile",
1121 g_variant_new_string (cc_power_profile_to_str (profile))),
1122 NULL,
1123 G_DBUS_CALL_FLAGS_NONE,
1124 -1,
1125 cc_panel_get_cancellable (CC_PANEL (self)),
1126 set_active_profile_cb,
1127 NULL);
1128 }
1129
1130 static void
1131 setup_power_profiles (CcPowerPanel *self)
1132 {
1133 g_autoptr(GDBusConnection) connection = NULL;
1134 g_autoptr(GVariant) variant = NULL;
1135 g_autoptr(GVariant) props = NULL;
1136 guint i, num_children;
1137 g_autoptr(GError) error = NULL;
1138 const char *performance_degraded;
1139 const char *active_profile;
1140 g_autoptr(GVariant) profiles = NULL;
1141 GtkCheckButton *last_button;
1142
1143 self->power_profiles_proxy = cc_object_storage_create_dbus_proxy_sync (G_BUS_TYPE_SYSTEM,
1144 G_DBUS_PROXY_FLAGS_NONE,
1145 "net.hadess.PowerProfiles",
1146 "/net/hadess/PowerProfiles",
1147 "net.hadess.PowerProfiles",
1148 NULL,
1149 &error);
1150
1151 if (!self->power_profiles_proxy)
1152 {
1153 g_debug ("Could not create Power Profiles proxy: %s", error->message);
1154 return;
1155 }
1156
1157 connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM,
1158 cc_panel_get_cancellable (CC_PANEL (self)),
1159 &error);
1160 if (!connection)
1161 {
1162 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1163 g_warning ("system bus not available: %s", error->message);
1164 return;
1165 }
1166
1167 variant = g_dbus_connection_call_sync (connection,
1168 "net.hadess.PowerProfiles",
1169 "/net/hadess/PowerProfiles",
1170 "org.freedesktop.DBus.Properties",
1171 "GetAll",
1172 g_variant_new ("(s)",
1173 "net.hadess.PowerProfiles"),
1174 NULL,
1175 G_DBUS_CALL_FLAGS_NONE,
1176 -1,
1177 NULL,
1178 &error);
1179
1180 if (!variant)
1181 {
1182 g_debug ("Failed to get properties for Power Profiles: %s",
1183 error->message);
1184 g_clear_object (&self->power_profiles_proxy);
1185 return;
1186 }
1187
1188 gtk_widget_set_visible (GTK_WIDGET (self->power_profile_section), TRUE);
1189
1190 props = g_variant_get_child_value (variant, 0);
1191 performance_degraded = variant_lookup_string (props, "PerformanceDegraded");
1192 self->has_performance_degraded = performance_degraded != NULL;
1193 active_profile = variant_lookup_string (props, "ActiveProfile");
1194
1195 last_button = NULL;
1196 profiles = g_variant_lookup_value (props, "Profiles", NULL);
1197 num_children = g_variant_n_children (profiles);
1198 for (i = 0; i < num_children; i++)
1199 {
1200 g_autoptr(GVariant) profile_variant;
1201 const char *name;
1202 GtkCheckButton *button;
1203 CcPowerProfile profile;
1204 CcPowerProfileRow *row;
1205
1206 profile_variant = g_variant_get_child_value (profiles, i);
1207 if (!profile_variant ||
1208 !g_variant_is_of_type (profile_variant, G_VARIANT_TYPE ("a{sv}")))
1209 continue;
1210
1211 name = variant_lookup_string (profile_variant, "Profile");
1212 if (!name)
1213 continue;
1214 g_debug ("Adding row for profile '%s' (driver: %s)",
1215 name, variant_lookup_string (profile_variant, "Driver"));
1216
1217 profile = cc_power_profile_from_str (name);
1218 row = cc_power_profile_row_new (cc_power_profile_from_str (name));
1219 g_signal_connect_object (G_OBJECT (row), "button-toggled",
1220 G_CALLBACK (power_profile_button_toggled_cb), self,
1221 0);
1222 self->power_profiles_row[profile] = row;
1223 gtk_list_box_append (self->power_profile_listbox, GTK_WIDGET (row));
1224
1225 /* Connect radio button to group */
1226 button = cc_power_profile_row_get_radio_button (row);
1227 gtk_check_button_set_group (button, last_button);
1228 last_button = button;
1229 }
1230
1231 self->power_profiles_in_update = TRUE;
1232 performance_profile_set_active (self, active_profile);
1233 self->power_profiles_in_update = FALSE;
1234
1235 self->power_profiles_prop_id = g_signal_connect_object (G_OBJECT (self->power_profiles_proxy), "g-properties-changed",
1236 G_CALLBACK (power_profiles_properties_changed_cb), self, G_CONNECT_SWAPPED);
1237
1238 if (self->has_performance_degraded)
1239 power_profile_update_info_boxes (self);
1240
1241 update_power_saver_low_battery_row_visibility (self);
1242 }
1243
1244 static void
1245 setup_general_section (CcPowerPanel *self)
1246 {
1247 gboolean can_suspend, can_hibernate, show_section = FALSE;
1248
1249 can_suspend = can_suspend_or_hibernate (self, "CanSuspend");
1250 can_hibernate = can_suspend_or_hibernate (self, "CanHibernate");
1251
1252 if ((can_hibernate || can_suspend) &&
1253 g_strcmp0 (self->chassis_type, "vm") != 0 &&
1254 g_strcmp0 (self->chassis_type, "tablet") != 0 &&
1255 g_strcmp0 (self->chassis_type, "handset") != 0)
1256 {
1257 gtk_widget_set_visible (GTK_WIDGET (self->power_button_row), TRUE);
1258
1259 g_signal_handlers_block_by_func (self->power_button_row,
1260 power_button_row_changed_cb,
1261 self);
1262 populate_power_button_row (self->power_button_row,
1263 can_suspend,
1264 can_hibernate);
1265 set_value_for_combo_row (self->power_button_row,
1266 g_settings_get_enum (self->gsd_settings, "power-button-action"));
1267 g_signal_handlers_unblock_by_func (self->power_button_row,
1268 power_button_row_changed_cb,
1269 self);
1270
1271 show_section = TRUE;
1272 }
1273
1274 if (self->has_batteries)
1275 {
1276 gtk_widget_set_visible (GTK_WIDGET (self->battery_percentage_row), TRUE);
1277
1278 g_settings_bind (self->interface_settings, "show-battery-percentage",
1279 self->battery_percentage_row, "active",
1280 G_SETTINGS_BIND_DEFAULT);
1281
1282 show_section = TRUE;
1283 }
1284
1285 gtk_widget_set_visible (GTK_WIDGET (self->general_section), show_section);
1286 }
1287
1288 static gint
1289 battery_sort_func (GtkListBoxRow *a, GtkListBoxRow *b, gpointer data)
1290 {
1291 CcBatteryRow *row_a = CC_BATTERY_ROW (a);
1292 CcBatteryRow *row_b = CC_BATTERY_ROW (b);
1293 gboolean a_primary;
1294 gboolean b_primary;
1295 UpDeviceKind a_kind;
1296 UpDeviceKind b_kind;
1297
1298 a_primary = cc_battery_row_get_primary(row_a);
1299 b_primary = cc_battery_row_get_primary(row_b);
1300
1301 if (a_primary)
1302 return -1;
1303 else if (b_primary)
1304 return 1;
1305
1306 a_kind = cc_battery_row_get_kind(row_a);
1307 b_kind = cc_battery_row_get_kind(row_b);
1308
1309 return a_kind - b_kind;
1310 }
1311
1312 static void
1313 cc_power_panel_dispose (GObject *object)
1314 {
1315 CcPowerPanel *self = CC_POWER_PANEL (object);
1316
1317 g_signal_handlers_disconnect_by_func (self->blank_screen_row, blank_screen_row_changed_cb, self);
1318 g_signal_handlers_disconnect_by_func (self->power_button_row, power_button_row_changed_cb, self);
1319
1320 g_clear_pointer (&self->chassis_type, g_free);
1321 g_clear_object (&self->gsd_settings);
1322 g_clear_object (&self->session_settings);
1323 g_clear_object (&self->interface_settings);
1324 g_clear_pointer (&self->devices, g_ptr_array_unref);
1325 g_clear_object (&self->up_client);
1326 g_clear_object (&self->iio_proxy);
1327 g_clear_object (&self->power_profiles_proxy);
1328 if (self->iio_proxy_watch_id != 0)
1329 g_bus_unwatch_name (self->iio_proxy_watch_id);
1330 self->iio_proxy_watch_id = 0;
1331
1332 G_OBJECT_CLASS (cc_power_panel_parent_class)->dispose (object);
1333 }
1334
1335 static void
1336 cc_power_panel_class_init (CcPowerPanelClass *klass)
1337 {
1338 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1339 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1340 CcPanelClass *panel_class = CC_PANEL_CLASS (klass);
1341
1342 object_class->dispose = cc_power_panel_dispose;
1343
1344 panel_class->get_help_uri = cc_power_panel_get_help_uri;
1345
1346 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/power/cc-power-panel.ui");
1347
1348 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, als_row);
1349 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, automatic_suspend_dialog);
1350 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, automatic_suspend_row);
1351 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, battery_listbox);
1352 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, battery_percentage_row);
1353 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, battery_section);
1354 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, blank_screen_row);
1355 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, device_listbox);
1356 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, device_section);
1357 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, dim_screen_row);
1358 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, general_section);
1359 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, mobile_time_liststore);
1360 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, power_button_row);
1361 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, power_profile_listbox);
1362 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, power_profile_info_listbox);
1363 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, power_profile_section);
1364 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, power_saver_low_battery_row);
1365 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, suspend_on_battery_delay_combo);
1366 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, suspend_on_battery_switch_row);
1367 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, suspend_on_battery_group);
1368 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, suspend_on_ac_delay_combo);
1369 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, suspend_on_ac_switch_row);
1370
1371 gtk_widget_class_bind_template_callback (widget_class, als_row_changed_cb);
1372 gtk_widget_class_bind_template_callback (widget_class, blank_screen_row_changed_cb);
1373 gtk_widget_class_bind_template_callback (widget_class, keynav_failed_cb);
1374 gtk_widget_class_bind_template_callback (widget_class, power_button_row_changed_cb);
1375 }
1376
1377 static void
1378 cc_power_panel_init (CcPowerPanel *self)
1379 {
1380 guint i;
1381 g_autoptr(GtkCssProvider) provider = NULL;
1382
1383 g_resources_register (cc_power_get_resource ());
1384
1385 gtk_widget_init_template (GTK_WIDGET (self));
1386
1387 provider = gtk_css_provider_new ();
1388 gtk_css_provider_load_from_resource (provider, "/org/gnome/control-center/power/power.css");
1389 gtk_style_context_add_provider_for_display (gdk_display_get_default (),
1390 GTK_STYLE_PROVIDER (provider),
1391 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
1392
1393 self->chassis_type = cc_hostname_get_chassis_type (cc_hostname_get_default ());
1394
1395 /* Use a different list of suspend times based on the chassis type */
1396 if (g_strcmp0 (self->chassis_type, "tablet") == 0 ||
1397 g_strcmp0 (self->chassis_type, "watch") == 0 ||
1398 g_strcmp0 (self->chassis_type, "handset") == 0)
1399 {
1400 gtk_combo_box_set_model (GTK_COMBO_BOX (self->suspend_on_battery_delay_combo),
1401 GTK_TREE_MODEL (self->mobile_time_liststore));
1402 gtk_combo_box_set_model (GTK_COMBO_BOX (self->suspend_on_ac_delay_combo),
1403 GTK_TREE_MODEL (self->mobile_time_liststore));
1404 }
1405
1406 self->up_client = up_client_new ();
1407
1408 self->gsd_settings = g_settings_new ("org.gnome.settings-daemon.plugins.power");
1409 self->session_settings = g_settings_new ("org.gnome.desktop.session");
1410 self->interface_settings = g_settings_new ("org.gnome.desktop.interface");
1411
1412 gtk_list_box_set_sort_func (self->battery_listbox,
1413 (GtkListBoxSortFunc)battery_sort_func, NULL, NULL);
1414
1415 gtk_list_box_set_sort_func (self->device_listbox,
1416 (GtkListBoxSortFunc)battery_sort_func, NULL, NULL);
1417
1418 gtk_list_box_set_sort_func (self->power_profile_listbox,
1419 perf_profile_list_box_sort,
1420 NULL, NULL);
1421 setup_power_profiles (self);
1422
1423 setup_power_saving (self);
1424 g_settings_bind (self->gsd_settings, "power-saver-profile-on-low-battery",
1425 self->power_saver_low_battery_row, "active",
1426 G_SETTINGS_BIND_DEFAULT);
1427
1428 setup_general_section (self);
1429
1430 /* populate batteries */
1431 g_signal_connect_object (self->up_client, "device-added", G_CALLBACK (up_client_device_added), self, G_CONNECT_SWAPPED);
1432 g_signal_connect_object (self->up_client, "device-removed", G_CALLBACK (up_client_device_removed), self, G_CONNECT_SWAPPED);
1433
1434 self->devices = up_client_get_devices2 (self->up_client);
1435 for (i = 0; self->devices != NULL && i < self->devices->len; i++) {
1436 UpDevice *device = g_ptr_array_index (self->devices, i);
1437 g_signal_connect_object (G_OBJECT (device), "notify",
1438 G_CALLBACK (up_client_changed), self, G_CONNECT_SWAPPED);
1439 }
1440 up_client_changed (self);
1441 }
1442