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 #include <handy.h>
29
30 #include "shell/cc-object-storage.h"
31 #include "list-box-helper.h"
32 #include "cc-battery-row.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 /* Uncomment this to test the behaviour of the panel in
40 * battery-less situations:
41 *
42 * #define TEST_NO_BATTERIES
43 */
44
45 /* Uncomment this to test the behaviour of the panel with
46 * multiple appearing devices
47 *
48 * #define TEST_FAKE_DEVICES
49 */
50
51 /* Uncomment this to test the behaviour of a desktop machine
52 * with a UPS
53 *
54 * #define TEST_UPS
55 */
56
57 struct _CcPowerPanel
58 {
59 CcPanel parent_instance;
60
61 GtkListBoxRow *als_row;
62 GtkSwitch *als_switch;
63 GtkDialog *automatic_suspend_dialog;
64 GtkLabel *automatic_suspend_label;
65 GtkListBoxRow *automatic_suspend_row;
66 GtkListBox *battery_listbox;
67 HdyActionRow *battery_percentage_row;
68 GtkSwitch *battery_percentage_switch;
69 GtkSizeGroup *battery_row_sizegroup;
70 HdyPreferencesGroup *battery_section;
71 HdyComboRow *blank_screen_row;
72 GtkListBox *device_listbox;
73 HdyPreferencesGroup *device_section;
74 GtkListBoxRow *dim_screen_row;
75 GtkSwitch *dim_screen_switch;
76 HdyPreferencesGroup *general_section;
77 GtkSizeGroup *level_sizegroup;
78 HdyComboRow *power_button_row;
79 GtkListBox *power_profile_listbox;
80 GtkListBox *power_profile_info_listbox;
81 HdyPreferencesGroup *power_profile_section;
82 HdyActionRow *power_saver_low_battery_row;
83 GtkSwitch *power_saver_low_battery_switch;
84 GtkSizeGroup *row_sizegroup;
85 GtkComboBox *suspend_on_battery_delay_combo;
86 GtkLabel *suspend_on_battery_delay_label;
87 GtkLabel *suspend_on_battery_label;
88 GtkSwitch *suspend_on_battery_switch;
89 GtkComboBox *suspend_on_ac_delay_combo;
90 GtkLabel *suspend_on_ac_label;
91 GtkSwitch *suspend_on_ac_switch;
92
93 GSettings *gsd_settings;
94 GSettings *session_settings;
95 GSettings *interface_settings;
96 UpClient *up_client;
97 GPtrArray *devices;
98 gboolean has_batteries;
99 char *chassis_type;
100
101 GDBusProxy *iio_proxy;
102 guint iio_proxy_watch_id;
103 gboolean has_brightness;
104
105 GDBusProxy *power_profiles_proxy;
106 guint power_profiles_prop_id;
107 CcPowerProfileRow *power_profiles_row[NUM_CC_POWER_PROFILES];
108 gboolean power_profiles_in_update;
109 gboolean has_performance_degraded;
110 };
111
112 CC_PANEL_REGISTER (CcPowerPanel, cc_power_panel)
113
114 enum
115 {
116 ACTION_MODEL_TEXT,
117 ACTION_MODEL_VALUE
118 };
119
120 static void
cc_power_panel_dispose(GObject * object)121 cc_power_panel_dispose (GObject *object)
122 {
123 CcPowerPanel *self = CC_POWER_PANEL (object);
124
125 g_clear_pointer (&self->chassis_type, g_free);
126 g_clear_object (&self->gsd_settings);
127 g_clear_object (&self->session_settings);
128 g_clear_object (&self->interface_settings);
129 g_clear_pointer ((GtkWidget **) &self->automatic_suspend_dialog, gtk_widget_destroy);
130 g_clear_pointer (&self->devices, g_ptr_array_unref);
131 g_clear_object (&self->up_client);
132 g_clear_object (&self->iio_proxy);
133 g_clear_object (&self->power_profiles_proxy);
134 if (self->iio_proxy_watch_id != 0)
135 g_bus_unwatch_name (self->iio_proxy_watch_id);
136 self->iio_proxy_watch_id = 0;
137
138 G_OBJECT_CLASS (cc_power_panel_parent_class)->dispose (object);
139 }
140
141 static const char *
cc_power_panel_get_help_uri(CcPanel * panel)142 cc_power_panel_get_help_uri (CcPanel *panel)
143 {
144 return "help:gnome-help/power";
145 }
146
147 static char *
get_chassis_type(GCancellable * cancellable)148 get_chassis_type (GCancellable *cancellable)
149 {
150 g_autoptr(GError) error = NULL;
151 g_autoptr(GVariant) inner = NULL;
152 g_autoptr(GVariant) variant = NULL;
153 g_autoptr(GDBusConnection) connection = NULL;
154
155 connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM,
156 cancellable,
157 &error);
158 if (!connection)
159 {
160 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
161 g_warning ("system bus not available: %s", error->message);
162 return NULL;
163 }
164
165 variant = g_dbus_connection_call_sync (connection,
166 "org.freedesktop.hostname1",
167 "/org/freedesktop/hostname1",
168 "org.freedesktop.DBus.Properties",
169 "Get",
170 g_variant_new ("(ss)",
171 "org.freedesktop.hostname1",
172 "Chassis"),
173 NULL,
174 G_DBUS_CALL_FLAGS_NONE,
175 -1,
176 cancellable,
177 &error);
178 if (!variant)
179 {
180 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
181 g_debug ("Failed to get property '%s': %s", "Chassis", error->message);
182 return NULL;
183 }
184
185 g_variant_get (variant, "(v)", &inner);
186 return g_variant_dup_string (inner, NULL);
187 }
188
189 static void
load_custom_css(CcPowerPanel * self,const char * path)190 load_custom_css (CcPowerPanel *self,
191 const char *path)
192 {
193 g_autoptr(GtkCssProvider) provider = NULL;
194
195 /* use custom CSS */
196 provider = gtk_css_provider_new ();
197 gtk_css_provider_load_from_resource (provider, path);
198 gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
199 GTK_STYLE_PROVIDER (provider),
200 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
201 }
202
203 static void
add_battery(CcPowerPanel * panel,UpDevice * device,gboolean primary)204 add_battery (CcPowerPanel *panel, UpDevice *device, gboolean primary)
205 {
206 CcBatteryRow *row = cc_battery_row_new (device, primary);
207 cc_battery_row_set_level_sizegroup (row, panel->level_sizegroup);
208 cc_battery_row_set_row_sizegroup (row, panel->battery_row_sizegroup);
209
210 gtk_container_add (GTK_CONTAINER (panel->battery_listbox), GTK_WIDGET (row));
211 gtk_widget_set_visible (GTK_WIDGET (panel->battery_section), TRUE);
212 }
213
214 static void
add_device(CcPowerPanel * self,UpDevice * device)215 add_device (CcPowerPanel *self, UpDevice *device)
216 {
217 CcBatteryRow *row = cc_battery_row_new (device, FALSE);
218 cc_battery_row_set_level_sizegroup (row, self->level_sizegroup);
219 cc_battery_row_set_row_sizegroup (row, self->row_sizegroup);
220
221 gtk_container_add (GTK_CONTAINER (self->device_listbox), GTK_WIDGET (row));
222 gtk_widget_set_visible (GTK_WIDGET (self->device_section), TRUE);
223 }
224
225 static void
empty_listbox(GtkListBox * listbox)226 empty_listbox (GtkListBox *listbox)
227 {
228 g_autoptr(GList) children = NULL;
229 GList *l;
230
231 children = gtk_container_get_children (GTK_CONTAINER (listbox));
232 for (l = children; l != NULL; l = l->next)
233 gtk_container_remove (GTK_CONTAINER (listbox), l->data);
234 }
235
236 static void
update_power_saver_low_battery_row_visibility(CcPowerPanel * self)237 update_power_saver_low_battery_row_visibility (CcPowerPanel *self)
238 {
239 g_autoptr(UpDevice) composite = NULL;
240 UpDeviceKind kind;
241
242 composite = up_client_get_display_device (self->up_client);
243 g_object_get (composite, "kind", &kind, NULL);
244 gtk_widget_set_visible (GTK_WIDGET (self->power_saver_low_battery_row),
245 self->power_profiles_proxy && kind == UP_DEVICE_KIND_BATTERY);
246 }
247
248 static void
up_client_changed(CcPowerPanel * self)249 up_client_changed (CcPowerPanel *self)
250 {
251 gint i;
252 UpDeviceKind kind;
253 guint n_batteries;
254 gboolean on_ups;
255 g_autoptr(UpDevice) composite = NULL;
256
257 empty_listbox (self->battery_listbox);
258 gtk_widget_hide (GTK_WIDGET (self->battery_section));
259
260 empty_listbox (self->device_listbox);
261 gtk_widget_hide (GTK_WIDGET (self->device_section));
262
263 #ifdef TEST_FAKE_DEVICES
264 {
265 static gboolean fake_devices_added = FALSE;
266 UpDevice *device;
267
268 if (!fake_devices_added)
269 {
270 fake_devices_added = TRUE;
271 g_print ("adding fake devices\n");
272 device = up_device_new ();
273 g_object_set (device,
274 "kind", UP_DEVICE_KIND_MOUSE,
275 "native-path", "dummy:native-path1",
276 "model", "My mouse",
277 "percentage", 71.0,
278 "state", UP_DEVICE_STATE_DISCHARGING,
279 "time-to-empty", 287,
280 "icon-name", "battery-full-symbolic",
281 "power-supply", FALSE,
282 "is-present", TRUE,
283 "battery-level", UP_DEVICE_LEVEL_NORMAL,
284 NULL);
285 g_ptr_array_add (self->devices, device);
286 device = up_device_new ();
287 g_object_set (device,
288 "kind", UP_DEVICE_KIND_KEYBOARD,
289 "native-path", "dummy:native-path2",
290 "model", "My keyboard",
291 "percentage", 59.0,
292 "state", UP_DEVICE_STATE_DISCHARGING,
293 "time-to-empty", 250,
294 "icon-name", "battery-good-symbolic",
295 "power-supply", FALSE,
296 "is-present", TRUE,
297 "battery-level", UP_DEVICE_LEVEL_NONE,
298 NULL);
299 g_ptr_array_add (self->devices, device);
300 device = up_device_new ();
301 g_object_set (device,
302 "kind", UP_DEVICE_KIND_BATTERY,
303 "native-path", "dummy:native-path3",
304 "model", "Battery from some factory",
305 "percentage", 100.0,
306 "state", UP_DEVICE_STATE_FULLY_CHARGED,
307 "energy", 55.0,
308 "energy-full", 55.0,
309 "energy-rate", 15.0,
310 "time-to-empty", 400,
311 "time-to-full", 0,
312 "icon-name", "battery-full-charged-symbolic",
313 "power-supply", TRUE,
314 "is-present", TRUE,
315 "battery-level", UP_DEVICE_LEVEL_NONE,
316 NULL);
317 g_ptr_array_add (self->devices, device);
318 }
319 }
320 #endif
321
322 #ifdef TEST_UPS
323 {
324 static gboolean fake_devices_added = FALSE;
325 UpDevice *device;
326
327 if (!fake_devices_added)
328 {
329 fake_devices_added = TRUE;
330 g_print ("adding fake UPS\n");
331 device = up_device_new ();
332 g_object_set (device,
333 "kind", UP_DEVICE_KIND_UPS,
334 "native-path", "dummy:usb-hiddev0",
335 "model", "APC UPS",
336 "percentage", 70.0,
337 "state", UP_DEVICE_STATE_DISCHARGING,
338 "is-present", TRUE,
339 "power-supply", TRUE,
340 "battery-level", UP_DEVICE_LEVEL_NONE,
341 NULL);
342 g_ptr_array_add (self->devices, device);
343 }
344 }
345 #endif
346
347 on_ups = FALSE;
348 n_batteries = 0;
349 composite = up_client_get_display_device (self->up_client);
350 g_object_get (composite, "kind", &kind, NULL);
351 if (kind == UP_DEVICE_KIND_UPS)
352 {
353 on_ups = TRUE;
354 }
355 else
356 {
357 gboolean is_extra_battery = FALSE;
358
359 /* Count the batteries */
360 for (i = 0; self->devices != NULL && i < self->devices->len; i++)
361 {
362 UpDevice *device = (UpDevice*) g_ptr_array_index (self->devices, i);
363 gboolean is_power_supply = FALSE;
364 g_object_get (device,
365 "kind", &kind,
366 "power-supply", &is_power_supply,
367 NULL);
368 if (kind == UP_DEVICE_KIND_BATTERY &&
369 is_power_supply)
370 {
371 n_batteries++;
372 if (is_extra_battery == FALSE)
373 {
374 is_extra_battery = TRUE;
375 g_object_set_data (G_OBJECT (device), "is-main-battery", GINT_TO_POINTER(TRUE));
376 }
377 }
378 }
379 }
380
381 if (n_batteries > 1)
382 hdy_preferences_group_set_title (self->battery_section, _("Batteries"));
383 else
384 hdy_preferences_group_set_title (self->battery_section, _("Battery"));
385
386 if (!on_ups && n_batteries > 1)
387 add_battery (self, composite, TRUE);
388
389 for (i = 0; self->devices != NULL && i < self->devices->len; i++)
390 {
391 UpDevice *device = (UpDevice*) g_ptr_array_index (self->devices, i);
392 gboolean is_power_supply = FALSE;
393 g_object_get (device,
394 "kind", &kind,
395 "power-supply", &is_power_supply,
396 NULL);
397 if (kind == UP_DEVICE_KIND_LINE_POWER)
398 {
399 /* do nothing */
400 }
401 else if (kind == UP_DEVICE_KIND_UPS && on_ups)
402 {
403 add_battery (self, device, TRUE);
404 }
405 else if (kind == UP_DEVICE_KIND_BATTERY && is_power_supply && !on_ups && n_batteries == 1)
406 {
407 add_battery (self, device, TRUE);
408 }
409 else if (kind == UP_DEVICE_KIND_BATTERY && is_power_supply)
410 {
411 add_battery (self, device, FALSE);
412 }
413 else
414 {
415 add_device (self, device);
416 }
417 }
418
419 update_power_saver_low_battery_row_visibility (self);
420 }
421
422 static void
up_client_device_removed(CcPowerPanel * self,const char * object_path)423 up_client_device_removed (CcPowerPanel *self,
424 const char *object_path)
425 {
426 guint i;
427
428 if (self->devices == NULL)
429 return;
430
431 for (i = 0; i < self->devices->len; i++)
432 {
433 UpDevice *device = g_ptr_array_index (self->devices, i);
434
435 if (g_strcmp0 (object_path, up_device_get_object_path (device)) == 0)
436 {
437 g_ptr_array_remove_index (self->devices, i);
438 break;
439 }
440 }
441
442 up_client_changed (self);
443 }
444
445 static void
up_client_device_added(CcPowerPanel * self,UpDevice * device)446 up_client_device_added (CcPowerPanel *self,
447 UpDevice *device)
448 {
449 g_ptr_array_add (self->devices, g_object_ref (device));
450 g_signal_connect_object (G_OBJECT (device), "notify",
451 G_CALLBACK (up_client_changed), self, G_CONNECT_SWAPPED);
452 up_client_changed (self);
453 }
454
455 static void
als_switch_changed_cb(CcPowerPanel * self)456 als_switch_changed_cb (CcPowerPanel *self)
457 {
458 gboolean enabled;
459 enabled = gtk_switch_get_active (self->als_switch);
460 g_debug ("Setting ALS enabled %s", enabled ? "on" : "off");
461 g_settings_set_boolean (self->gsd_settings, "ambient-enabled", enabled);
462 }
463
464 static void
als_enabled_state_changed(CcPowerPanel * self)465 als_enabled_state_changed (CcPowerPanel *self)
466 {
467 gboolean enabled;
468 gboolean visible = FALSE;
469
470 if (self->iio_proxy != NULL)
471 {
472 g_autoptr(GVariant) v = g_dbus_proxy_get_cached_property (self->iio_proxy, "HasAmbientLight");
473 if (v != NULL)
474 visible = g_variant_get_boolean (v);
475 }
476
477 enabled = g_settings_get_boolean (self->gsd_settings, "ambient-enabled");
478 g_debug ("ALS enabled: %s", enabled ? "on" : "off");
479 g_signal_handlers_block_by_func (self->als_switch, als_switch_changed_cb, self);
480 gtk_switch_set_active (self->als_switch, enabled);
481 gtk_widget_set_visible (GTK_WIDGET (self->als_row), visible && self->has_brightness);
482 g_signal_handlers_unblock_by_func (self->als_switch, als_switch_changed_cb, self);
483 }
484
485 static void
combo_time_changed_cb(CcPowerPanel * self,GtkWidget * widget)486 combo_time_changed_cb (CcPowerPanel *self, GtkWidget *widget)
487 {
488 GtkTreeIter iter;
489 GtkTreeModel *model;
490 gint value;
491 gboolean ret;
492 const gchar *key = (const gchar *)g_object_get_data (G_OBJECT(widget), "_gsettings_key");
493
494 /* no selection */
495 ret = gtk_combo_box_get_active_iter (GTK_COMBO_BOX(widget), &iter);
496 if (!ret)
497 return;
498
499 /* get entry */
500 model = gtk_combo_box_get_model (GTK_COMBO_BOX(widget));
501 gtk_tree_model_get (model, &iter,
502 1, &value,
503 -1);
504
505 /* set both keys */
506 g_settings_set_int (self->gsd_settings, key, value);
507 }
508
509 static void
set_value_for_combo(GtkComboBox * combo_box,gint value)510 set_value_for_combo (GtkComboBox *combo_box, gint value)
511 {
512 GtkTreeIter iter;
513 g_autoptr(GtkTreeIter) insert = NULL;
514 GtkTreeIter new;
515 GtkTreeModel *model;
516 gint value_tmp;
517 gint value_last = 0;
518 g_autofree gchar *text = NULL;
519 gboolean ret;
520
521 /* get entry */
522 model = gtk_combo_box_get_model (combo_box);
523 ret = gtk_tree_model_get_iter_first (model, &iter);
524 if (!ret)
525 return;
526
527 /* try to make the UI match the setting */
528 do
529 {
530 gtk_tree_model_get (model, &iter,
531 ACTION_MODEL_VALUE, &value_tmp,
532 -1);
533 if (value_tmp == value)
534 {
535 gtk_combo_box_set_active_iter (combo_box, &iter);
536 return;
537 }
538
539 /* Insert before if the next value is larger or the value is lower
540 * again (i.e. "Never" is zero and last). */
541 if (!insert && (value_tmp > value || value_last > value_tmp))
542 insert = gtk_tree_iter_copy (&iter);
543
544 value_last = value_tmp;
545 } while (gtk_tree_model_iter_next (model, &iter));
546
547 /* The value is not listed, so add it at the best point (or the end). */
548 gtk_list_store_insert_before (GTK_LIST_STORE (model), &new, insert);
549
550 text = cc_util_time_to_string_text (value * 1000);
551 gtk_list_store_set (GTK_LIST_STORE (model), &new,
552 ACTION_MODEL_TEXT, text,
553 ACTION_MODEL_VALUE, value,
554 -1);
555 gtk_combo_box_set_active_iter (combo_box, &new);
556 }
557
558 static void
set_value_for_combo_row(HdyComboRow * combo_row,gint value)559 set_value_for_combo_row (HdyComboRow *combo_row, gint value)
560 {
561 gboolean insert = FALSE;
562 guint insert_before = 0;
563 guint i;
564 HdyValueObject *new;
565 GListModel *model;
566 gint value_last = 0;
567 g_autofree gchar *text = NULL;
568
569 /* try to make the UI match the setting */
570 model = hdy_combo_row_get_model (combo_row);
571 for (i = 0; i < g_list_model_get_n_items (model); i++)
572 {
573 HdyValueObject *value_object = g_list_model_get_item (model, i);
574 gint value_tmp = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (value_object), "value"));
575 if (value_tmp == value)
576 {
577 hdy_combo_row_set_selected_index (combo_row, i);
578 return;
579 }
580
581 /* Insert before if the next value is larger or the value is lower
582 * again (i.e. "Never" is zero and last). */
583 if (!insert && (value_tmp > value || value_last > value_tmp))
584 {
585 insert = TRUE;
586 insert_before = i;
587 }
588
589 value_last = value_tmp;
590 }
591
592 /* The value is not listed, so add it at the best point (or the end). */
593 text = cc_util_time_to_string_text (value * 1000);
594 new = hdy_value_object_new_string (text);
595 g_object_set_data (G_OBJECT (new), "value",
596 GUINT_TO_POINTER (value));
597 g_list_store_insert (G_LIST_STORE (model), insert_before, new);
598 hdy_combo_row_set_selected_index (combo_row, insert_before);
599 }
600
601 static void
set_ac_battery_ui_mode(CcPowerPanel * self)602 set_ac_battery_ui_mode (CcPowerPanel *self)
603 {
604 gboolean has_batteries = FALSE;
605 GPtrArray *devices;
606 guint i;
607
608 devices = up_client_get_devices2 (self->up_client);
609 g_debug ("got %d devices from upower\n", devices ? devices->len : 0);
610
611 for (i = 0; devices != NULL && i < devices->len; i++)
612 {
613 UpDevice *device;
614 gboolean is_power_supply;
615 UpDeviceKind kind;
616
617 device = g_ptr_array_index (devices, i);
618 g_object_get (device,
619 "kind", &kind,
620 "power-supply", &is_power_supply,
621 NULL);
622 if (kind == UP_DEVICE_KIND_UPS ||
623 (kind == UP_DEVICE_KIND_BATTERY && is_power_supply))
624 {
625 has_batteries = TRUE;
626 break;
627 }
628 }
629 g_clear_pointer (&devices, g_ptr_array_unref);
630
631 #ifdef TEST_NO_BATTERIES
632 g_print ("forcing no batteries\n");
633 has_batteries = FALSE;
634 #endif
635
636 self->has_batteries = has_batteries;
637
638 if (!has_batteries)
639 {
640 gtk_widget_hide (GTK_WIDGET (self->suspend_on_battery_switch));
641 gtk_widget_hide (GTK_WIDGET (self->suspend_on_battery_label));
642 gtk_widget_hide (GTK_WIDGET (self->suspend_on_battery_delay_label));
643 gtk_widget_hide (GTK_WIDGET (self->suspend_on_battery_delay_combo));
644 gtk_label_set_label (self->suspend_on_ac_label, _("When _idle"));
645 }
646 }
647
648 static gboolean
keynav_failed_cb(CcPowerPanel * self,GtkDirectionType direction,GtkWidget * list)649 keynav_failed_cb (CcPowerPanel *self, GtkDirectionType direction, GtkWidget *list)
650 {
651 if (direction != GTK_DIR_UP && direction != GTK_DIR_DOWN)
652 return FALSE;
653
654 direction = GTK_DIR_UP ? GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD;
655
656 return gtk_widget_child_focus (GTK_WIDGET (self), direction);
657 }
658
659 static void
blank_screen_row_changed_cb(CcPowerPanel * self)660 blank_screen_row_changed_cb (CcPowerPanel *self)
661 {
662 GListModel *model;
663 gint selected_index;
664 HdyValueObject *value_object;
665 gint value;
666
667 model = hdy_combo_row_get_model (self->blank_screen_row);
668 selected_index = hdy_combo_row_get_selected_index (self->blank_screen_row);
669 value_object = g_list_model_get_item (model, selected_index);
670 value = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (value_object), "value"));
671
672 g_settings_set_uint (self->session_settings, "idle-delay", value);
673 }
674
675 static void
power_button_row_changed_cb(CcPowerPanel * self)676 power_button_row_changed_cb (CcPowerPanel *self)
677 {
678 GListModel *model;
679 gint selected_index;
680 HdyValueObject *value_object;
681 gint value;
682
683 model = hdy_combo_row_get_model (self->power_button_row);
684 selected_index = hdy_combo_row_get_selected_index (self->power_button_row);
685 value_object = g_list_model_get_item (model, selected_index);
686 value = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (value_object), "value"));
687
688 g_settings_set_enum (self->gsd_settings, "power-button-action", value);
689 }
690
691 static void
als_enabled_setting_changed(CcPowerPanel * self)692 als_enabled_setting_changed (CcPowerPanel *self)
693 {
694 als_enabled_state_changed (self);
695 }
696
697 static void
iio_proxy_appeared_cb(GDBusConnection * connection,const gchar * name,const gchar * name_owner,gpointer user_data)698 iio_proxy_appeared_cb (GDBusConnection *connection,
699 const gchar *name,
700 const gchar *name_owner,
701 gpointer user_data)
702 {
703 CcPowerPanel *self = CC_POWER_PANEL (user_data);
704 g_autoptr(GError) error = NULL;
705
706 self->iio_proxy =
707 cc_object_storage_create_dbus_proxy_sync (G_BUS_TYPE_SYSTEM,
708 G_DBUS_PROXY_FLAGS_NONE,
709 "net.hadess.SensorProxy",
710 "/net/hadess/SensorProxy",
711 "net.hadess.SensorProxy",
712 NULL, &error);
713 if (error != NULL)
714 {
715 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
716 g_warning ("Could not create IIO sensor proxy: %s", error->message);
717 return;
718 }
719
720 g_signal_connect_object (G_OBJECT (self->iio_proxy), "g-properties-changed",
721 G_CALLBACK (als_enabled_state_changed), self,
722 G_CONNECT_SWAPPED);
723 als_enabled_state_changed (self);
724 }
725
726 static void
iio_proxy_vanished_cb(GDBusConnection * connection,const gchar * name,gpointer user_data)727 iio_proxy_vanished_cb (GDBusConnection *connection,
728 const gchar *name,
729 gpointer user_data)
730 {
731 CcPowerPanel *self = CC_POWER_PANEL (user_data);
732 g_clear_object (&self->iio_proxy);
733 als_enabled_state_changed (self);
734 }
735
736 static void
automatic_suspend_row_activated_cb(CcPowerPanel * self)737 automatic_suspend_row_activated_cb (CcPowerPanel *self)
738 {
739 GtkWidget *toplevel;
740
741 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
742 gtk_window_set_transient_for (GTK_WINDOW (self->automatic_suspend_dialog), GTK_WINDOW (toplevel));
743 gtk_window_set_modal (GTK_WINDOW (self->automatic_suspend_dialog), TRUE);
744 gtk_window_present (GTK_WINDOW (self->automatic_suspend_dialog));
745 }
746
747 static gboolean
automatic_suspend_label_mnemonic_activate_cb(CcPowerPanel * self)748 automatic_suspend_label_mnemonic_activate_cb (CcPowerPanel *self)
749 {
750 automatic_suspend_row_activated_cb (self);
751 return TRUE;
752 }
753
754 static gboolean
get_sleep_type(GValue * value,GVariant * variant,gpointer data)755 get_sleep_type (GValue *value,
756 GVariant *variant,
757 gpointer data)
758 {
759 gboolean enabled;
760
761 if (g_strcmp0 (g_variant_get_string (variant, NULL), "nothing") == 0)
762 enabled = FALSE;
763 else
764 enabled = TRUE;
765
766 g_value_set_boolean (value, enabled);
767
768 return TRUE;
769 }
770
771 static GVariant *
set_sleep_type(const GValue * value,const GVariantType * expected_type,gpointer data)772 set_sleep_type (const GValue *value,
773 const GVariantType *expected_type,
774 gpointer data)
775 {
776 GVariant *res;
777
778 if (g_value_get_boolean (value))
779 res = g_variant_new_string ("suspend");
780 else
781 res = g_variant_new_string ("nothing");
782
783 return res;
784 }
785
786 static void
populate_power_button_row(HdyComboRow * combo_row,gboolean can_suspend,gboolean can_hibernate)787 populate_power_button_row (HdyComboRow *combo_row,
788 gboolean can_suspend,
789 gboolean can_hibernate)
790 {
791 g_autoptr (GListStore) list_store = NULL;
792 struct {
793 char *name;
794 GsdPowerButtonActionType value;
795 } actions[] = {
796 { N_("Suspend"), GSD_POWER_BUTTON_ACTION_SUSPEND },
797 { N_("Power Off"), GSD_POWER_BUTTON_ACTION_INTERACTIVE },
798 { N_("Hibernate"), GSD_POWER_BUTTON_ACTION_HIBERNATE },
799 { N_("Nothing"), GSD_POWER_BUTTON_ACTION_NOTHING }
800 };
801 guint i;
802
803 list_store = g_list_store_new (HDY_TYPE_VALUE_OBJECT);
804 for (i = 0; i < G_N_ELEMENTS (actions); i++)
805 {
806 g_autoptr (HdyValueObject) value_object = NULL;
807
808 if (!can_suspend && actions[i].value == GSD_POWER_BUTTON_ACTION_SUSPEND)
809 continue;
810
811 if (!can_hibernate && actions[i].value == GSD_POWER_BUTTON_ACTION_HIBERNATE)
812 continue;
813
814 value_object = hdy_value_object_new_string (_(actions[i].name));
815 g_object_set_data (G_OBJECT (value_object),
816 "value",
817 GUINT_TO_POINTER (actions[i].value));
818 g_list_store_append (list_store, value_object);
819 }
820
821 hdy_combo_row_bind_name_model (combo_row,
822 G_LIST_MODEL (list_store),
823 (HdyComboRowGetNameFunc) hdy_value_object_dup_string,
824 NULL, NULL);
825 }
826
827 #define NEVER 0
828
829 static void
update_automatic_suspend_label(CcPowerPanel * self)830 update_automatic_suspend_label (CcPowerPanel *self)
831 {
832 GsdPowerActionType ac_action;
833 GsdPowerActionType battery_action;
834 gint ac_timeout;
835 gint battery_timeout;
836 const gchar *s;
837
838 ac_action = g_settings_get_enum (self->gsd_settings, "sleep-inactive-ac-type");
839 battery_action = g_settings_get_enum (self->gsd_settings, "sleep-inactive-battery-type");
840 ac_timeout = g_settings_get_int (self->gsd_settings, "sleep-inactive-ac-timeout");
841 battery_timeout = g_settings_get_int (self->gsd_settings, "sleep-inactive-battery-timeout");
842
843 if (ac_timeout < 0)
844 g_warning ("Invalid negative timeout for 'sleep-inactive-ac-timeout': %d", ac_timeout);
845 if (battery_timeout < 0)
846 g_warning ("Invalid negative timeout for 'sleep-inactive-battery-timeout': %d", battery_timeout);
847
848 if (ac_action == GSD_POWER_ACTION_NOTHING || ac_timeout < 0)
849 ac_timeout = NEVER;
850 if (battery_action == GSD_POWER_ACTION_NOTHING || battery_timeout < 0)
851 battery_timeout = NEVER;
852
853 if (self->has_batteries)
854 {
855 if (ac_timeout == NEVER && battery_timeout == NEVER)
856 s = _("Off");
857 else if (ac_timeout == NEVER && battery_timeout > 0)
858 s = _("When on battery power");
859 else if (ac_timeout > 0 && battery_timeout == NEVER)
860 s = _("When plugged in");
861 else
862 s = _("On");
863 }
864 else
865 {
866 if (ac_timeout == NEVER)
867 s = _("Off");
868 else
869 s = _("On");
870 }
871
872 if (self->automatic_suspend_label)
873 gtk_label_set_label (self->automatic_suspend_label, s);
874 }
875
876 static void
on_suspend_settings_changed(CcPowerPanel * self,const char * key)877 on_suspend_settings_changed (CcPowerPanel *self,
878 const char *key)
879 {
880 if (g_str_has_prefix (key, "sleep-inactive-"))
881 {
882 update_automatic_suspend_label (self);
883 }
884 }
885
886 static gboolean
can_suspend_or_hibernate(CcPowerPanel * self,const char * method_name)887 can_suspend_or_hibernate (CcPowerPanel *self,
888 const char *method_name)
889 {
890 g_autoptr(GDBusConnection) connection = NULL;
891 g_autoptr(GVariant) variant = NULL;
892 g_autoptr(GError) error = NULL;
893 const char *s;
894
895 connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM,
896 cc_panel_get_cancellable (CC_PANEL (self)),
897 &error);
898 if (!connection)
899 {
900 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
901 g_warning ("system bus not available: %s", error->message);
902 return FALSE;
903 }
904
905 variant = g_dbus_connection_call_sync (connection,
906 "org.freedesktop.login1",
907 "/org/freedesktop/login1",
908 "org.freedesktop.login1.Manager",
909 method_name,
910 NULL,
911 NULL,
912 G_DBUS_CALL_FLAGS_NONE,
913 -1,
914 cc_panel_get_cancellable (CC_PANEL (self)),
915 &error);
916
917 if (!variant)
918 {
919 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
920 g_debug ("Failed to call %s(): %s", method_name, error->message);
921 return FALSE;
922 }
923
924 g_variant_get (variant, "(&s)", &s);
925 return g_strcmp0 (s, "yes") == 0;
926 }
927
928 static void
got_brightness_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)929 got_brightness_cb (GObject *source_object,
930 GAsyncResult *res,
931 gpointer user_data)
932 {
933 g_autoptr(GVariant) result = NULL;
934 g_autoptr(GError) error = NULL;
935 gint32 brightness = -1.0;
936 CcPowerPanel *self;
937
938 result = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, &error);
939 if (!result)
940 {
941 g_debug ("Failed to get Brightness property: %s", error->message);
942 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
943 return;
944 }
945 else
946 {
947 g_autoptr(GVariant) v = NULL;
948 g_variant_get (result, "(v)", &v);
949 brightness = v ? g_variant_get_int32 (v) : -1.0;
950 }
951
952 self = user_data;
953 self->has_brightness = brightness >= 0.0;
954
955 gtk_widget_set_visible (GTK_WIDGET (self->dim_screen_row), self->has_brightness);
956 als_enabled_state_changed (self);
957 }
958
959 static void
populate_blank_screen_row(HdyComboRow * combo_row)960 populate_blank_screen_row (HdyComboRow *combo_row)
961 {
962 g_autoptr (GListStore) list_store = g_list_store_new (HDY_TYPE_VALUE_OBJECT);
963 gint minutes[] = { 1, 2, 3, 4, 5, 8, 10, 12, 15 };
964 guint i;
965 g_autoptr (HdyValueObject) never_value_object = NULL;
966
967 for (i = 0; i < G_N_ELEMENTS (minutes); i++)
968 {
969 gchar *text = NULL;
970 g_autoptr (HdyValueObject) value_object = NULL;
971
972 /* Translators: Option for "Blank Screen" in "Power" panel */
973 text = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "%d minute", "%d minutes", minutes[i]), minutes[i]);
974 value_object = hdy_value_object_new_take_string (text);
975
976 g_object_set_data (G_OBJECT (value_object), "value", GUINT_TO_POINTER (minutes[i] * 60));
977 g_list_store_append (list_store, value_object);
978 }
979
980 never_value_object = hdy_value_object_new_string (C_("Idle time", "Never"));
981 g_object_set_data (G_OBJECT (never_value_object), "value", GUINT_TO_POINTER (0));
982 g_list_store_append (list_store, never_value_object);
983
984 hdy_combo_row_bind_name_model (combo_row,
985 G_LIST_MODEL (list_store),
986 (HdyComboRowGetNameFunc) hdy_value_object_dup_string,
987 NULL, NULL);
988 }
989
990 static void
setup_power_saving(CcPowerPanel * self)991 setup_power_saving (CcPowerPanel *self)
992 {
993 g_autoptr(GDBusConnection) connection = NULL;
994 g_autoptr(GError) error = NULL;
995 int value;
996
997 /* ambient light sensor */
998 self->iio_proxy_watch_id =
999 g_bus_watch_name (G_BUS_TYPE_SYSTEM,
1000 "net.hadess.SensorProxy",
1001 G_BUS_NAME_WATCHER_FLAGS_NONE,
1002 iio_proxy_appeared_cb,
1003 iio_proxy_vanished_cb,
1004 self, NULL);
1005 g_signal_connect_object (self->gsd_settings, "changed",
1006 G_CALLBACK (als_enabled_setting_changed), self, G_CONNECT_SWAPPED);
1007
1008 connection = g_bus_get_sync (G_BUS_TYPE_SESSION,
1009 cc_panel_get_cancellable (CC_PANEL (self)),
1010 &error);
1011 if (connection)
1012 {
1013 g_dbus_connection_call (connection,
1014 "org.gnome.SettingsDaemon.Power",
1015 "/org/gnome/SettingsDaemon/Power",
1016 "org.freedesktop.DBus.Properties",
1017 "Get",
1018 g_variant_new ("(ss)",
1019 "org.gnome.SettingsDaemon.Power.Screen",
1020 "Brightness"),
1021 NULL,
1022 G_DBUS_CALL_FLAGS_NONE,
1023 -1,
1024 cc_panel_get_cancellable (CC_PANEL (self)),
1025 got_brightness_cb,
1026 self);
1027 }
1028 else
1029 {
1030 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1031 g_warning ("session bus not available: %s", error->message);
1032 }
1033
1034
1035 g_settings_bind (self->gsd_settings, "idle-dim",
1036 self->dim_screen_switch, "active",
1037 G_SETTINGS_BIND_DEFAULT);
1038
1039 g_signal_handlers_block_by_func (self->blank_screen_row, blank_screen_row_changed_cb, self);
1040 populate_blank_screen_row (self->blank_screen_row);
1041 value = g_settings_get_uint (self->session_settings, "idle-delay");
1042 set_value_for_combo_row (self->blank_screen_row, value);
1043 g_signal_handlers_unblock_by_func (self->blank_screen_row, blank_screen_row_changed_cb, self);
1044
1045 /* The default values for these settings are unfortunate for us;
1046 * timeout == 0, action == suspend means 'do nothing' - just
1047 * as timout === anything, action == nothing.
1048 * For our switch/combobox combination, the second choice works
1049 * much better, so translate the first to the second here.
1050 */
1051 if (g_settings_get_int (self->gsd_settings, "sleep-inactive-ac-timeout") == 0)
1052 {
1053 g_settings_set_enum (self->gsd_settings, "sleep-inactive-ac-type", GSD_POWER_ACTION_NOTHING);
1054 g_settings_set_int (self->gsd_settings, "sleep-inactive-ac-timeout", 3600);
1055 }
1056 if (g_settings_get_int (self->gsd_settings, "sleep-inactive-battery-timeout") == 0)
1057 {
1058 g_settings_set_enum (self->gsd_settings, "sleep-inactive-battery-type", GSD_POWER_ACTION_NOTHING);
1059 g_settings_set_int (self->gsd_settings, "sleep-inactive-battery-timeout", 1800);
1060 }
1061
1062 /* Automatic suspend row */
1063 if (can_suspend_or_hibernate (self, "CanSuspend"))
1064 {
1065 gtk_widget_show (GTK_WIDGET (self->automatic_suspend_row));
1066 atk_object_set_name (ATK_OBJECT (gtk_widget_get_accessible (GTK_WIDGET (self->automatic_suspend_row))), _("Automatic suspend"));
1067
1068 g_signal_connect (self->automatic_suspend_dialog, "delete-event", G_CALLBACK (gtk_widget_hide_on_delete), NULL);
1069 g_signal_connect_object (self->gsd_settings, "changed", G_CALLBACK (on_suspend_settings_changed), self, G_CONNECT_SWAPPED);
1070
1071 g_settings_bind_with_mapping (self->gsd_settings, "sleep-inactive-battery-type",
1072 self->suspend_on_battery_switch, "active",
1073 G_SETTINGS_BIND_DEFAULT,
1074 get_sleep_type, set_sleep_type, NULL, NULL);
1075
1076 g_object_set_data (G_OBJECT (self->suspend_on_battery_delay_combo), "_gsettings_key", "sleep-inactive-battery-timeout");
1077 value = g_settings_get_int (self->gsd_settings, "sleep-inactive-battery-timeout");
1078 set_value_for_combo (self->suspend_on_battery_delay_combo, value);
1079 g_signal_connect_object (self->suspend_on_battery_delay_combo, "changed",
1080 G_CALLBACK (combo_time_changed_cb), self, G_CONNECT_SWAPPED);
1081 g_object_bind_property (self->suspend_on_battery_switch, "active", self->suspend_on_battery_delay_combo, "sensitive",
1082 G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
1083
1084 g_settings_bind_with_mapping (self->gsd_settings, "sleep-inactive-ac-type",
1085 self->suspend_on_ac_switch, "active",
1086 G_SETTINGS_BIND_DEFAULT,
1087 get_sleep_type, set_sleep_type, NULL, NULL);
1088
1089 g_object_set_data (G_OBJECT (self->suspend_on_ac_delay_combo), "_gsettings_key", "sleep-inactive-ac-timeout");
1090 value = g_settings_get_int (self->gsd_settings, "sleep-inactive-ac-timeout");
1091 set_value_for_combo (self->suspend_on_ac_delay_combo, value);
1092 g_signal_connect_object (self->suspend_on_ac_delay_combo, "changed",
1093 G_CALLBACK (combo_time_changed_cb), self, G_CONNECT_SWAPPED);
1094 g_object_bind_property (self->suspend_on_ac_switch, "active", self->suspend_on_ac_delay_combo, "sensitive",
1095 G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
1096
1097 set_ac_battery_ui_mode (self);
1098 update_automatic_suspend_label (self);
1099 }
1100 }
1101
1102 static const char *
variant_lookup_string(GVariant * dict,const char * key)1103 variant_lookup_string (GVariant *dict,
1104 const char *key)
1105 {
1106 GVariant *variant;
1107
1108 variant = g_variant_lookup_value (dict, key, G_VARIANT_TYPE_STRING);
1109 if (!variant)
1110 return NULL;
1111 return g_variant_get_string (variant, NULL);
1112 }
1113
1114 static void
performance_profile_set_active(CcPowerPanel * self,const char * profile_str)1115 performance_profile_set_active (CcPowerPanel *self,
1116 const char *profile_str)
1117 {
1118 CcPowerProfile profile = cc_power_profile_from_str (profile_str);
1119 GtkRadioButton *button;
1120
1121 button = cc_power_profile_row_get_radio_button (CC_POWER_PROFILE_ROW (self->power_profiles_row[profile]));
1122 g_assert (button);
1123 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
1124 }
1125
1126 static void
performance_profile_set_inhibited(CcPowerPanel * self,const char * performance_inhibited)1127 performance_profile_set_inhibited (CcPowerPanel *self,
1128 const char *performance_inhibited)
1129 {
1130 CcPowerProfileRow *row;
1131
1132 row = self->power_profiles_row[CC_POWER_PROFILE_PERFORMANCE];
1133 if (!row)
1134 return;
1135 cc_power_profile_row_set_performance_inhibited (row, performance_inhibited);
1136 }
1137
1138 static void
power_profile_update_info_boxes(CcPowerPanel * self)1139 power_profile_update_info_boxes (CcPowerPanel *self)
1140 {
1141 g_autoptr(GVariant) degraded_variant = NULL;
1142 g_autoptr(GVariant) holds_variant = NULL;
1143 g_autoptr(GVariant) profile_variant = NULL;
1144 guint i, num_children;
1145 const char *degraded = NULL;
1146 const char *profile;
1147 CcPowerProfileInfoRow *row;
1148 int next_insert = 0;
1149
1150 empty_listbox (self->power_profile_info_listbox);
1151 gtk_widget_hide (GTK_WIDGET (self->power_profile_info_listbox));
1152
1153 profile_variant = g_dbus_proxy_get_cached_property (self->power_profiles_proxy, "ActiveProfile");
1154 if (!profile_variant)
1155 {
1156 g_warning ("No 'ActiveProfile' property on power-profiles-daemon service");
1157 return;
1158 }
1159 profile = g_variant_get_string (profile_variant, NULL);
1160
1161 degraded_variant = g_dbus_proxy_get_cached_property (self->power_profiles_proxy, "PerformanceDegraded");
1162 if (degraded_variant)
1163 degraded = g_variant_get_string (degraded_variant, NULL);
1164 if (degraded && *degraded != '\0')
1165 {
1166 const char *text;
1167
1168 gtk_widget_show (GTK_WIDGET (self->power_profile_info_listbox));
1169
1170 if (g_str_equal (degraded, "high-operating-temperature"))
1171 text = _("Performance mode temporarily disabled due to high operating temperature.");
1172 else if (g_str_equal (degraded, "lap-detected"))
1173 text = _("Lap detected: performance mode temporarily unavailable. Move the device to a stable surface to restore.");
1174 else
1175 text = _("Performance mode temporarily disabled.");
1176
1177 row = cc_power_profile_info_row_new (text);
1178 gtk_widget_show (GTK_WIDGET (row));
1179 gtk_container_add (GTK_CONTAINER (self->power_profile_info_listbox), GTK_WIDGET (row));
1180 if (g_str_equal (profile, "performance"))
1181 next_insert = 1;
1182 }
1183
1184 holds_variant = g_dbus_proxy_get_cached_property (self->power_profiles_proxy, "ActiveProfileHolds");
1185 if (!holds_variant)
1186 {
1187 g_warning ("No 'ActiveProfileHolds' property on power-profiles-daemon service");
1188 return;
1189 }
1190
1191 num_children = g_variant_n_children (holds_variant);
1192 for (i = 0; i < num_children; i++)
1193 {
1194 g_autoptr(GDesktopAppInfo) app_info = NULL;
1195 g_autoptr(GVariant) hold_variant = NULL;
1196 g_autofree char *text = NULL;
1197 const char *app_id, *held_profile, *reason, *name;
1198
1199 hold_variant = g_variant_get_child_value (holds_variant, i);
1200 if (!hold_variant || !g_variant_is_of_type (hold_variant, G_VARIANT_TYPE ("a{sv}")))
1201 continue;
1202
1203 app_id = variant_lookup_string (hold_variant, "ApplicationId");
1204 if (!app_id)
1205 continue;
1206
1207 gtk_widget_show (GTK_WIDGET (self->power_profile_info_listbox));
1208
1209 app_info = g_desktop_app_info_new (app_id);
1210 name = app_info ? g_app_info_get_name (G_APP_INFO (app_info)) : app_id;
1211 held_profile = variant_lookup_string (hold_variant, "Profile");
1212 reason = variant_lookup_string (hold_variant, "Reason");
1213 g_debug ("Adding info row for %s hold by %s: %s", held_profile, app_id, reason);
1214
1215 if (g_strcmp0 (held_profile, "power-saver") == 0 &&
1216 g_strcmp0 (app_id, "org.gnome.SettingsDaemon.Power") == 0)
1217 {
1218 text = g_strdup (_("Low battery: power saver enabled. Previous mode will be restored when battery is sufficiently charged."));
1219 }
1220 else
1221 {
1222 switch (cc_power_profile_from_str (held_profile))
1223 {
1224 case CC_POWER_PROFILE_POWER_SAVER:
1225 /* translators: "%s" is an application name */
1226 text = g_strdup_printf (_("Power Saver mode activated by “%s”."), name);
1227 break;
1228 case CC_POWER_PROFILE_PERFORMANCE:
1229 /* translators: "%s" is an application name */
1230 text = g_strdup_printf (_("Performance mode activated by “%s”."), name);
1231 break;
1232 default:
1233 g_assert_not_reached ();
1234 }
1235 }
1236
1237 row = cc_power_profile_info_row_new (text);
1238 gtk_widget_show (GTK_WIDGET (row));
1239 if (g_strcmp0 (held_profile, profile) != 0)
1240 gtk_list_box_insert (GTK_LIST_BOX (self->power_profile_info_listbox), GTK_WIDGET (row), -1);
1241 else
1242 gtk_list_box_insert (GTK_LIST_BOX (self->power_profile_info_listbox), GTK_WIDGET (row), next_insert);
1243 }
1244 }
1245
1246 static void
power_profiles_row_activated_cb(GtkListBox * box,GtkListBoxRow * box_row,gpointer user_data)1247 power_profiles_row_activated_cb (GtkListBox *box,
1248 GtkListBoxRow *box_row,
1249 gpointer user_data)
1250 {
1251 if (!gtk_widget_is_sensitive (GTK_WIDGET (box_row)))
1252 return;
1253
1254 cc_power_profile_row_set_active (CC_POWER_PROFILE_ROW(box_row), TRUE);
1255 }
1256
1257 static gint
perf_profile_list_box_sort(GtkListBoxRow * row1,GtkListBoxRow * row2,gpointer user_data)1258 perf_profile_list_box_sort (GtkListBoxRow *row1,
1259 GtkListBoxRow *row2,
1260 gpointer user_data)
1261 {
1262 CcPowerProfile row1_profile, row2_profile;
1263
1264 row1_profile = cc_power_profile_row_get_profile (CC_POWER_PROFILE_ROW (row1));
1265 row2_profile = cc_power_profile_row_get_profile (CC_POWER_PROFILE_ROW (row2));
1266
1267 if (row1_profile < row2_profile)
1268 return -1;
1269 if (row1_profile > row2_profile)
1270 return 1;
1271 return 0;
1272 }
1273
1274 static void
power_profiles_properties_changed_cb(CcPowerPanel * self,GVariant * changed_properties,GStrv invalidated_properties,GDBusProxy * proxy)1275 power_profiles_properties_changed_cb (CcPowerPanel *self,
1276 GVariant *changed_properties,
1277 GStrv invalidated_properties,
1278 GDBusProxy *proxy)
1279 {
1280 g_autoptr(GVariantIter) iter = NULL;
1281 const char *key;
1282 g_autoptr(GVariant) value = NULL;
1283
1284 g_variant_get (changed_properties, "a{sv}", &iter);
1285 while (g_variant_iter_next (iter, "{&sv}", &key, &value))
1286 {
1287 if (g_strcmp0 (key, "PerformanceInhibited") == 0)
1288 {
1289 if (!self->has_performance_degraded)
1290 performance_profile_set_inhibited (self,
1291 g_variant_get_string (value, NULL));
1292 }
1293 else if (g_strcmp0 (key, "PerformanceDegraded") == 0 ||
1294 g_strcmp0 (key, "ActiveProfileHolds") == 0)
1295 {
1296 power_profile_update_info_boxes (self);
1297 }
1298 else if (g_strcmp0 (key, "ActiveProfile") == 0)
1299 {
1300 self->power_profiles_in_update = TRUE;
1301 performance_profile_set_active (self, g_variant_get_string (value, NULL));
1302 self->power_profiles_in_update = FALSE;
1303 }
1304 else
1305 {
1306 g_debug ("Unhandled change on '%s' property", key);
1307 }
1308 }
1309 }
1310
1311 static void
set_active_profile_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)1312 set_active_profile_cb (GObject *source_object,
1313 GAsyncResult *res,
1314 gpointer user_data)
1315 {
1316 g_autoptr(GVariant) variant = NULL;
1317 g_autoptr(GError) error = NULL;
1318
1319 variant = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
1320 res, &error);
1321 if (!variant)
1322 {
1323 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1324 g_warning ("Could not set active profile: %s", error->message);
1325 }
1326 }
1327
1328 static void
power_profile_button_toggled_cb(CcPowerProfileRow * row,gpointer user_data)1329 power_profile_button_toggled_cb (CcPowerProfileRow *row,
1330 gpointer user_data)
1331 {
1332 CcPowerPanel *self = user_data;
1333 CcPowerProfile profile;
1334 g_autoptr(GDBusConnection) connection = NULL;
1335 g_autoptr(GError) error = NULL;
1336
1337 if (!cc_power_profile_row_get_active (row))
1338 return;
1339 if (self->power_profiles_in_update)
1340 return;
1341
1342 profile = cc_power_profile_row_get_profile (row);
1343
1344 connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM,
1345 cc_panel_get_cancellable (CC_PANEL (self)),
1346 &error);
1347 if (!connection)
1348 {
1349 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1350 g_warning ("system bus not available: %s", error->message);
1351 return;
1352 }
1353
1354 g_dbus_connection_call (connection,
1355 "net.hadess.PowerProfiles",
1356 "/net/hadess/PowerProfiles",
1357 "org.freedesktop.DBus.Properties",
1358 "Set",
1359 g_variant_new ("(ssv)",
1360 "net.hadess.PowerProfiles",
1361 "ActiveProfile",
1362 g_variant_new_string (cc_power_profile_to_str (profile))),
1363 NULL,
1364 G_DBUS_CALL_FLAGS_NONE,
1365 -1,
1366 cc_panel_get_cancellable (CC_PANEL (self)),
1367 set_active_profile_cb,
1368 NULL);
1369 }
1370
1371 static void
setup_power_profiles(CcPowerPanel * self)1372 setup_power_profiles (CcPowerPanel *self)
1373 {
1374 g_autoptr(GDBusConnection) connection = NULL;
1375 g_autoptr(GVariant) variant = NULL;
1376 g_autoptr(GVariant) props = NULL;
1377 guint i, num_children;
1378 g_autoptr(GError) error = NULL;
1379 const char *performance_inhibited = NULL;
1380 const char *performance_degraded;
1381 const char *active_profile;
1382 g_autoptr(GVariant) profiles = NULL;
1383 GtkRadioButton *last_button;
1384
1385 self->power_profiles_proxy = cc_object_storage_create_dbus_proxy_sync (G_BUS_TYPE_SYSTEM,
1386 G_DBUS_PROXY_FLAGS_NONE,
1387 "net.hadess.PowerProfiles",
1388 "/net/hadess/PowerProfiles",
1389 "net.hadess.PowerProfiles",
1390 NULL,
1391 &error);
1392
1393 if (!self->power_profiles_proxy)
1394 {
1395 g_debug ("Could not create Power Profiles proxy: %s", error->message);
1396 return;
1397 }
1398
1399 connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM,
1400 cc_panel_get_cancellable (CC_PANEL (self)),
1401 &error);
1402 if (!connection)
1403 {
1404 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1405 g_warning ("system bus not available: %s", error->message);
1406 return;
1407 }
1408
1409 variant = g_dbus_connection_call_sync (connection,
1410 "net.hadess.PowerProfiles",
1411 "/net/hadess/PowerProfiles",
1412 "org.freedesktop.DBus.Properties",
1413 "GetAll",
1414 g_variant_new ("(s)",
1415 "net.hadess.PowerProfiles"),
1416 NULL,
1417 G_DBUS_CALL_FLAGS_NONE,
1418 -1,
1419 NULL,
1420 &error);
1421
1422 if (!variant)
1423 {
1424 g_debug ("Failed to get properties for Power Profiles: %s",
1425 error->message);
1426 g_clear_object (&self->power_profiles_proxy);
1427 return;
1428 }
1429
1430 gtk_widget_show (GTK_WIDGET (self->power_profile_section));
1431
1432 props = g_variant_get_child_value (variant, 0);
1433 performance_degraded = variant_lookup_string (props, "PerformanceDegraded");
1434 self->has_performance_degraded = performance_degraded != NULL;
1435 if (performance_degraded == NULL)
1436 performance_inhibited = variant_lookup_string (props, "PerformanceInhibited");
1437 active_profile = variant_lookup_string (props, "ActiveProfile");
1438
1439 last_button = NULL;
1440 profiles = g_variant_lookup_value (props, "Profiles", NULL);
1441 num_children = g_variant_n_children (profiles);
1442 for (i = 0; i < num_children; i++)
1443 {
1444 g_autoptr(GVariant) profile_variant;
1445 const char *name;
1446 GtkRadioButton *button;
1447 CcPowerProfile profile;
1448 CcPowerProfileRow *row;
1449
1450 profile_variant = g_variant_get_child_value (profiles, i);
1451 if (!profile_variant ||
1452 !g_variant_is_of_type (profile_variant, G_VARIANT_TYPE ("a{sv}")))
1453 continue;
1454
1455 name = variant_lookup_string (profile_variant, "Profile");
1456 if (!name)
1457 continue;
1458 g_debug ("Adding row for profile '%s' (driver: %s)",
1459 name, variant_lookup_string (profile_variant, "Driver"));
1460
1461 profile = cc_power_profile_from_str (name);
1462 row = cc_power_profile_row_new (cc_power_profile_from_str (name));
1463 cc_power_profile_row_set_performance_inhibited (row, performance_inhibited);
1464 g_signal_connect_object (G_OBJECT (row), "button-toggled",
1465 G_CALLBACK (power_profile_button_toggled_cb), self,
1466 0);
1467 self->power_profiles_row[profile] = row;
1468 gtk_widget_show (GTK_WIDGET (row));
1469 gtk_container_add (GTK_CONTAINER (self->power_profile_listbox), GTK_WIDGET (row));
1470 gtk_size_group_add_widget (self->row_sizegroup, GTK_WIDGET (row));
1471
1472 /* Connect radio button to group */
1473 button = cc_power_profile_row_get_radio_button (row);
1474 gtk_radio_button_join_group (button, last_button);
1475 last_button = button;
1476 }
1477
1478 self->power_profiles_in_update = TRUE;
1479 performance_profile_set_active (self, active_profile);
1480 self->power_profiles_in_update = FALSE;
1481
1482 self->power_profiles_prop_id = g_signal_connect_object (G_OBJECT (self->power_profiles_proxy), "g-properties-changed",
1483 G_CALLBACK (power_profiles_properties_changed_cb), self, G_CONNECT_SWAPPED);
1484
1485 if (self->has_performance_degraded)
1486 power_profile_update_info_boxes (self);
1487
1488 update_power_saver_low_battery_row_visibility (self);
1489 }
1490
1491 static void
setup_general_section(CcPowerPanel * self)1492 setup_general_section (CcPowerPanel *self)
1493 {
1494 gboolean can_suspend, can_hibernate, show_section = FALSE;
1495
1496 can_suspend = can_suspend_or_hibernate (self, "CanSuspend");
1497 can_hibernate = can_suspend_or_hibernate (self, "CanHibernate");
1498
1499 if ((can_hibernate || can_suspend) &&
1500 g_strcmp0 (self->chassis_type, "vm") != 0 &&
1501 g_strcmp0 (self->chassis_type, "tablet") != 0 &&
1502 g_strcmp0 (self->chassis_type, "handset") != 0)
1503 {
1504 gtk_widget_show (GTK_WIDGET (self->power_button_row));
1505
1506 g_signal_handlers_block_by_func (self->power_button_row,
1507 power_button_row_changed_cb,
1508 self);
1509 populate_power_button_row (self->power_button_row,
1510 can_suspend,
1511 can_hibernate);
1512 set_value_for_combo_row (self->power_button_row,
1513 g_settings_get_enum (self->gsd_settings, "power-button-action"));
1514 g_signal_handlers_unblock_by_func (self->power_button_row,
1515 power_button_row_changed_cb,
1516 self);
1517
1518 show_section = TRUE;
1519 }
1520
1521 if (self->has_batteries)
1522 {
1523 gtk_widget_show (GTK_WIDGET (self->battery_percentage_row));
1524
1525 g_settings_bind (self->interface_settings, "show-battery-percentage",
1526 self->battery_percentage_switch, "active",
1527 G_SETTINGS_BIND_DEFAULT);
1528
1529 show_section = TRUE;
1530 }
1531
1532 gtk_widget_set_visible (GTK_WIDGET (self->general_section), show_section);
1533 }
1534
1535 static gint
battery_sort_func(GtkListBoxRow * a,GtkListBoxRow * b,gpointer data)1536 battery_sort_func (GtkListBoxRow *a, GtkListBoxRow *b, gpointer data)
1537 {
1538 CcBatteryRow *row_a = CC_BATTERY_ROW (a);
1539 CcBatteryRow *row_b = CC_BATTERY_ROW (b);
1540 gboolean a_primary;
1541 gboolean b_primary;
1542 UpDeviceKind a_kind;
1543 UpDeviceKind b_kind;
1544
1545 a_primary = cc_battery_row_get_primary(row_a);
1546 b_primary = cc_battery_row_get_primary(row_b);
1547
1548 if (a_primary)
1549 return -1;
1550 else if (b_primary)
1551 return 1;
1552
1553 a_kind = cc_battery_row_get_kind(row_a);
1554 b_kind = cc_battery_row_get_kind(row_b);
1555
1556 return a_kind - b_kind;
1557 }
1558
1559 static void
cc_power_panel_class_init(CcPowerPanelClass * klass)1560 cc_power_panel_class_init (CcPowerPanelClass *klass)
1561 {
1562 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1563 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1564 CcPanelClass *panel_class = CC_PANEL_CLASS (klass);
1565
1566 object_class->dispose = cc_power_panel_dispose;
1567
1568 panel_class->get_help_uri = cc_power_panel_get_help_uri;
1569
1570 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/power/cc-power-panel.ui");
1571
1572 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, als_row);
1573 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, als_switch);
1574 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, automatic_suspend_dialog);
1575 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, automatic_suspend_label);
1576 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, automatic_suspend_row);
1577 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, battery_listbox);
1578 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, battery_percentage_row);
1579 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, battery_percentage_switch);
1580 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, battery_row_sizegroup);
1581 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, battery_section);
1582 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, blank_screen_row);
1583 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, device_listbox);
1584 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, device_section);
1585 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, dim_screen_row);
1586 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, dim_screen_switch);
1587 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, general_section);
1588 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, level_sizegroup);
1589 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, power_button_row);
1590 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, power_profile_listbox);
1591 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, power_profile_info_listbox);
1592 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, power_profile_section);
1593 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, power_saver_low_battery_row);
1594 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, power_saver_low_battery_switch);
1595 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, row_sizegroup);
1596 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, suspend_on_battery_delay_combo);
1597 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, suspend_on_battery_delay_label);
1598 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, suspend_on_battery_label);
1599 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, suspend_on_battery_switch);
1600 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, suspend_on_ac_delay_combo);
1601 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, suspend_on_ac_label);
1602 gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, suspend_on_ac_switch);
1603
1604 gtk_widget_class_bind_template_callback (widget_class, als_switch_changed_cb);
1605 gtk_widget_class_bind_template_callback (widget_class, automatic_suspend_label_mnemonic_activate_cb);
1606 gtk_widget_class_bind_template_callback (widget_class, blank_screen_row_changed_cb);
1607 gtk_widget_class_bind_template_callback (widget_class, keynav_failed_cb);
1608 gtk_widget_class_bind_template_callback (widget_class, power_button_row_changed_cb);
1609 gtk_widget_class_bind_template_callback (widget_class, power_profiles_row_activated_cb);
1610 gtk_widget_class_bind_template_callback (widget_class, automatic_suspend_row_activated_cb);
1611 }
1612
1613 static void
cc_power_panel_init(CcPowerPanel * self)1614 cc_power_panel_init (CcPowerPanel *self)
1615 {
1616 guint i;
1617
1618 g_resources_register (cc_power_get_resource ());
1619
1620 gtk_widget_init_template (GTK_WIDGET (self));
1621 load_custom_css (self, "/org/gnome/control-center/power/battery-levels.css");
1622 load_custom_css (self, "/org/gnome/control-center/power/power-profiles.css");
1623
1624 self->chassis_type = get_chassis_type (cc_panel_get_cancellable (CC_PANEL (self)));
1625
1626 self->up_client = up_client_new ();
1627
1628 self->gsd_settings = g_settings_new ("org.gnome.settings-daemon.plugins.power");
1629 self->session_settings = g_settings_new ("org.gnome.desktop.session");
1630 self->interface_settings = g_settings_new ("org.gnome.desktop.interface");
1631
1632 gtk_list_box_set_sort_func (self->battery_listbox,
1633 (GtkListBoxSortFunc)battery_sort_func, NULL, NULL);
1634
1635 gtk_list_box_set_sort_func (self->device_listbox,
1636 (GtkListBoxSortFunc)battery_sort_func, NULL, NULL);
1637
1638 gtk_list_box_set_sort_func (self->power_profile_listbox,
1639 perf_profile_list_box_sort,
1640 NULL, NULL);
1641 setup_power_profiles (self);
1642
1643 setup_power_saving (self);
1644
1645 setup_general_section (self);
1646
1647 /* populate batteries */
1648 g_signal_connect_object (self->up_client, "device-added", G_CALLBACK (up_client_device_added), self, G_CONNECT_SWAPPED);
1649 g_signal_connect_object (self->up_client, "device-removed", G_CALLBACK (up_client_device_removed), self, G_CONNECT_SWAPPED);
1650
1651 self->devices = up_client_get_devices2 (self->up_client);
1652 for (i = 0; self->devices != NULL && i < self->devices->len; i++) {
1653 UpDevice *device = g_ptr_array_index (self->devices, i);
1654 g_signal_connect_object (G_OBJECT (device), "notify",
1655 G_CALLBACK (up_client_changed), self, G_CONNECT_SWAPPED);
1656 }
1657 up_client_changed (self);
1658 }
1659