1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2  *
3  * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
4  * Copyright (C) 2011 Richard Hughes <richard@hughsie.com>
5  * Copyright (C) 2011 Ritesh Khadgaray <khadgaray@gmail.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, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA.
20  *
21  */
22 
23 #include "config.h"
24 
25 #include <fcntl.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <sys/wait.h>
31 #include <glib/gi18n.h>
32 #include <gdk/gdkx.h>
33 #include <gtk/gtk.h>
34 #include <libupower-glib/upower.h>
35 #include <libnotify/notify.h>
36 #include <canberra-gtk.h>
37 #include <gio/gunixfdlist.h>
38 
39 #include <X11/extensions/dpms.h>
40 
41 #define GNOME_DESKTOP_USE_UNSTABLE_API
42 #include <libcinnamon-desktop/gnome-rr.h>
43 
44 #include "gpm-common.h"
45 #include "gpm-phone.h"
46 #include "gpm-idletime.h"
47 #include "cinnamon-settings-profile.h"
48 #include "cinnamon-settings-session.h"
49 #include "csd-enums.h"
50 #include "csd-power-manager.h"
51 #include "csd-power-helper.h"
52 #include "csd-power-proxy.h"
53 #include "csd-power-screen-proxy.h"
54 #include "csd-power-keyboard-proxy.h"
55 
56 #define GNOME_SESSION_DBUS_NAME                 "org.gnome.SessionManager"
57 #define GNOME_SESSION_DBUS_PATH                 "/org/gnome/SessionManager"
58 #define GNOME_SESSION_DBUS_PATH_PRESENCE        "/org/gnome/SessionManager/Presence"
59 #define GNOME_SESSION_DBUS_INTERFACE            "org.gnome.SessionManager"
60 #define GNOME_SESSION_DBUS_INTERFACE_PRESENCE   "org.gnome.SessionManager.Presence"
61 
62 #define UPOWER_DBUS_NAME                        "org.freedesktop.UPower"
63 #define UPOWER_DBUS_PATH_KBDBACKLIGHT           "/org/freedesktop/UPower/KbdBacklight"
64 #define UPOWER_DBUS_INTERFACE_KBDBACKLIGHT      "org.freedesktop.UPower.KbdBacklight"
65 
66 #define CSD_POWER_SETTINGS_SCHEMA               "org.cinnamon.settings-daemon.plugins.power"
67 #define CSD_XRANDR_SETTINGS_SCHEMA              "org.cinnamon.settings-daemon.plugins.xrandr"
68 #define CSD_SAVER_SETTINGS_SCHEMA               "org.cinnamon.desktop.screensaver"
69 #define CSD_SESSION_SETTINGS_SCHEMA             "org.cinnamon.desktop.session"
70 #define CSD_CINNAMON_SESSION_SCHEMA             "org.cinnamon.SessionManager"
71 
72 #define CSD_POWER_DBUS_PATH                     "/org/cinnamon/SettingsDaemon/Power"
73 #define CSD_POWER_DBUS_INTERFACE                "org.cinnamon.SettingsDaemon.Power"
74 #define CSD_POWER_DBUS_INTERFACE_SCREEN         "org.cinnamon.SettingsDaemon.Power.Screen"
75 #define CSD_POWER_DBUS_INTERFACE_KEYBOARD       "org.cinnamon.SettingsDaemon.Power.Keyboard"
76 
77 #define GS_DBUS_NAME                            "org.cinnamon.ScreenSaver"
78 #define GS_DBUS_PATH                            "/org/cinnamon/ScreenSaver"
79 #define GS_DBUS_INTERFACE                       "org.cinnamon.ScreenSaver"
80 
81 #define CSD_POWER_MANAGER_NOTIFY_TIMEOUT_NEVER          0 /* ms */
82 #define CSD_POWER_MANAGER_NOTIFY_TIMEOUT_SHORT          10 * 1000 /* ms */
83 #define CSD_POWER_MANAGER_NOTIFY_TIMEOUT_LONG           30 * 1000 /* ms */
84 
85 #define CSD_POWER_MANAGER_CRITICAL_ALERT_TIMEOUT        5 /* seconds */
86 #define CSD_POWER_MANAGER_LID_CLOSE_SAFETY_TIMEOUT      30 /* seconds */
87 
88 #define LOGIND_DBUS_NAME                       "org.freedesktop.login1"
89 #define LOGIND_DBUS_PATH                       "/org/freedesktop/login1"
90 #define LOGIND_DBUS_INTERFACE                  "org.freedesktop.login1.Manager"
91 
92 /* Keep this in sync with gnome-shell */
93 #define SCREENSAVER_FADE_TIME                           10 /* seconds */
94 
95 #define XSCREENSAVER_WATCHDOG_TIMEOUT                   120 /* seconds */
96 
97 enum {
98         CSD_POWER_IDLETIME_NULL_ID,
99         CSD_POWER_IDLETIME_DIM_ID,
100         CSD_POWER_IDLETIME_LOCK_ID,
101         CSD_POWER_IDLETIME_BLANK_ID,
102         CSD_POWER_IDLETIME_SLEEP_ID
103 };
104 
105 /* on ACPI machines we have 4-16 levels, on others it's ~150 */
106 #define BRIGHTNESS_STEP_AMOUNT(max) ((max) < 20 ? 1 : (max) / 20)
107 
108 /* take a discrete value with offset and convert to percentage */
109 static int
abs_to_percentage(int min,int max,int value)110 abs_to_percentage (int min, int max, int value)
111 {
112         g_return_val_if_fail (max > min, -1);
113         g_return_val_if_fail (value >= min, -1);
114         g_return_val_if_fail (value <= max, -1);
115         return (((value - min) * 100) / (max - min));
116 }
117 #define ABS_TO_PERCENTAGE(min, max, value) abs_to_percentage(min, max, value)
118 #define PERCENTAGE_TO_ABS(min, max, value) (min + (((max - min) * value) / 100))
119 
120 #define CSD_POWER_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CSD_TYPE_POWER_MANAGER, CsdPowerManagerPrivate))
121 
122 typedef enum {
123         CSD_POWER_IDLE_MODE_NORMAL,
124         CSD_POWER_IDLE_MODE_DIM,
125         CSD_POWER_IDLE_MODE_BLANK,
126         CSD_POWER_IDLE_MODE_SLEEP
127 } CsdPowerIdleMode;
128 
129 struct CsdPowerManagerPrivate
130 {
131         CinnamonSettingsSession    *session;
132         guint                    p_name_id;
133         guint                    s_name_id;
134         guint                    k_name_id;
135         CsdPower                 *power_iface;
136         CsdScreen                *screen_iface;
137         CsdKeyboard              *keyboard_iface;
138         gboolean                 lid_is_closed;
139         gboolean                 on_battery;
140         GSettings               *settings;
141         GSettings               *settings_screensaver;
142         GSettings               *settings_xrandr;
143         GSettings               *settings_desktop_session;
144         GSettings               *settings_cinnamon_session;
145         UpClient                *up_client;
146         GDBusConnection         *connection;
147         GCancellable            *bus_cancellable;
148         GDBusProxy              *upower_kbd_proxy;
149         gboolean				backlight_helper_force;
150         gchar*                  backlight_helper_preference_args;
151         gint                     kbd_brightness_now;
152         gint                     kbd_brightness_max;
153         gint                     kbd_brightness_old;
154         gint                     kbd_brightness_pre_dim;
155         GnomeRRScreen           *x11_screen;
156         gboolean                 use_time_primary;
157         gchar                   *previous_summary;
158         GIcon                   *previous_icon;
159         GpmPhone                *phone;
160         GPtrArray               *devices_array;
161         guint                    action_percentage;
162         guint                    action_time;
163         guint                    critical_percentage;
164         guint                    critical_time;
165         guint                    low_percentage;
166         guint                    low_time;
167         gint                     pre_dim_brightness; /* level, not percentage */
168         UpDevice                *device_composite;
169         NotifyNotification      *notification_discharging;
170         NotifyNotification      *notification_low;
171         ca_context              *canberra_context;
172         ca_proplist             *critical_alert_loop_props;
173         guint32                  critical_alert_timeout_id;
174         GDBusProxy              *screensaver_proxy;
175         GDBusProxy              *session_proxy;
176         GDBusProxy              *session_presence_proxy;
177         GpmIdletime             *idletime;
178         CsdPowerIdleMode         current_idle_mode;
179         guint                    lid_close_safety_timer_id;
180         GtkStatusIcon           *status_icon;
181         guint                    xscreensaver_watchdog_timer_id;
182         gboolean                 is_virtual_machine;
183         gint                     fd_close_loop_end;
184 
185         /* logind stuff */
186         GDBusProxy              *logind_proxy;
187         gboolean                 inhibit_lid_switch_enabled;
188         gint                     inhibit_lid_switch_fd;
189         gboolean                 inhibit_lid_switch_taken;
190         gint                     inhibit_suspend_fd;
191         gboolean                 inhibit_suspend_taken;
192         guint                    inhibit_lid_switch_timer_id;
193 };
194 
195 enum {
196         PROP_0,
197 };
198 
199 static void     csd_power_manager_finalize    (GObject              *object);
200 
201 static UpDevice *engine_get_composite_device (CsdPowerManager *manager, UpDevice *original_device);
202 static UpDevice *engine_update_composite_device (CsdPowerManager *manager, UpDevice *original_device);
203 static GIcon    *engine_get_icon (CsdPowerManager *manager);
204 static gchar    *engine_get_summary (CsdPowerManager *manager);
205 
206 static gboolean  external_monitor_is_connected (GnomeRRScreen *screen);
207 static void      do_power_action_type (CsdPowerManager *manager, CsdPowerActionType action_type);
208 static void      do_lid_closed_action (CsdPowerManager *manager);
209 static void      inhibit_lid_switch (CsdPowerManager *manager);
210 static void      uninhibit_lid_switch (CsdPowerManager *manager);
211 static void      setup_locker_process (gpointer user_data);
212 static void      lock_screen_with_custom_saver (CsdPowerManager *manager, gchar *custom_saver, gboolean idle_lock);
213 static void      lock_screensaver (CsdPowerManager *manager);
214 static void      kill_lid_close_safety_timer (CsdPowerManager *manager);
215 
216 int             backlight_get_output_id (CsdPowerManager *manager);
217 
218 #if UP_CHECK_VERSION(0,99,0)
219 static void device_properties_changed_cb (UpDevice *device, GParamSpec *pspec, CsdPowerManager *manager);
220 #endif
221 
222 G_DEFINE_TYPE (CsdPowerManager, csd_power_manager, G_TYPE_OBJECT)
223 
224 static gpointer manager_object = NULL;
225 
226 GQuark
csd_power_manager_error_quark(void)227 csd_power_manager_error_quark (void)
228 {
229         static GQuark quark = 0;
230         if (!quark)
231                 quark = g_quark_from_static_string ("csd_power_manager_error");
232         return quark;
233 }
234 
235 static gboolean
play_loop_timeout_cb(CsdPowerManager * manager)236 play_loop_timeout_cb (CsdPowerManager *manager)
237 {
238         ca_context *context;
239         context = ca_gtk_context_get_for_screen (gdk_screen_get_default ());
240         ca_context_play_full (context, 0,
241                               manager->priv->critical_alert_loop_props,
242                               NULL,
243                               NULL);
244         return TRUE;
245 }
246 
247 static gboolean
play_loop_stop(CsdPowerManager * manager)248 play_loop_stop (CsdPowerManager *manager)
249 {
250         if (manager->priv->critical_alert_timeout_id == 0) {
251                 g_warning ("no sound loop present to stop");
252                 return FALSE;
253         }
254 
255         if (manager->priv->critical_alert_timeout_id) {
256             g_source_remove (manager->priv->critical_alert_timeout_id);
257             manager->priv->critical_alert_timeout_id = 0;
258         }
259 
260         ca_proplist_destroy (manager->priv->critical_alert_loop_props);
261 
262         manager->priv->critical_alert_loop_props = NULL;
263         manager->priv->critical_alert_timeout_id = 0;
264 
265         return TRUE;
266 }
267 
268 static gboolean
play_loop_start(CsdPowerManager * manager,const gchar * id,const gchar * desc,gboolean force,guint timeout)269 play_loop_start (CsdPowerManager *manager,
270                  const gchar *id,
271                  const gchar *desc,
272                  gboolean force,
273                  guint timeout)
274 {
275         ca_context *context;
276 
277         if (timeout == 0) {
278                 g_warning ("received invalid timeout");
279                 return FALSE;
280         }
281 
282         /* if a sound loop is already running, stop the existing loop */
283         if (manager->priv->critical_alert_timeout_id != 0) {
284                 g_warning ("was instructed to play a sound loop with one already playing");
285                 play_loop_stop (manager);
286         }
287 
288         ca_proplist_create (&(manager->priv->critical_alert_loop_props));
289         ca_proplist_sets (manager->priv->critical_alert_loop_props,
290                           CA_PROP_EVENT_ID, id);
291         ca_proplist_sets (manager->priv->critical_alert_loop_props,
292                           CA_PROP_EVENT_DESCRIPTION, desc);
293 
294         manager->priv->critical_alert_timeout_id = g_timeout_add_seconds (timeout,
295                                                                           (GSourceFunc) play_loop_timeout_cb,
296                                                                           manager);
297         g_source_set_name_by_id (manager->priv->critical_alert_timeout_id,
298                                  "[CsdPowerManager] play-loop");
299 
300         /* play the sound, using sounds from the naming spec */
301         context = ca_gtk_context_get_for_screen (gdk_screen_get_default ());
302         ca_context_play (context, 0,
303                          CA_PROP_EVENT_ID, id,
304                          CA_PROP_EVENT_DESCRIPTION, desc, NULL);
305         return TRUE;
306 }
307 
308 static gboolean
should_lock_on_suspend(CsdPowerManager * manager)309 should_lock_on_suspend (CsdPowerManager *manager)
310 {
311     gboolean lock;
312 
313     lock = g_settings_get_boolean (manager->priv->settings,
314                                    "lock-on-suspend");
315 
316     return lock;
317 }
318 
319 static void
notify_close_if_showing(NotifyNotification * notification)320 notify_close_if_showing (NotifyNotification *notification)
321 {
322         gboolean ret;
323         GError *error = NULL;
324 
325         if (notification == NULL)
326                 return;
327         ret = notify_notification_close (notification, &error);
328         if (!ret) {
329                 g_warning ("failed to close notification: %s",
330                            error->message);
331                 g_error_free (error);
332         }
333 }
334 
335 static const gchar *
get_first_themed_icon_name(GIcon * icon)336 get_first_themed_icon_name (GIcon *icon)
337 {
338         const gchar* const *icon_names;
339         const gchar *icon_name = NULL;
340 
341         /* no icon */
342         if (icon == NULL)
343                 goto out;
344 
345         /* just use the first icon */
346         icon_names = g_themed_icon_get_names (G_THEMED_ICON (icon));
347         if (icon_names != NULL)
348                 icon_name = icon_names[0];
349 out:
350         return icon_name;
351 }
352 
353 typedef enum {
354         WARNING_NONE            = 0,
355         WARNING_DISCHARGING     = 1,
356         WARNING_LOW             = 2,
357         WARNING_CRITICAL        = 3,
358         WARNING_ACTION          = 4
359 } CsdPowerManagerWarning;
360 
361 static void
engine_emit_changed(CsdPowerManager * manager,gboolean icon_changed,gboolean state_changed)362 engine_emit_changed (CsdPowerManager *manager,
363                      gboolean         icon_changed,
364                      gboolean         state_changed)
365 {
366         /* not yet connected to the bus */
367         if (manager->priv->power_iface == NULL)
368                 return;
369 
370         if (icon_changed) {
371                 GIcon *gicon;
372                 gchar *gicon_str;
373 
374                 gicon = engine_get_icon (manager);
375                 gicon_str = g_icon_to_string (gicon);
376 
377                 csd_power_set_icon (manager->priv->power_iface, gicon_str);
378 
379                 g_free (gicon_str);
380                 g_object_unref (gicon);
381         }
382 
383         if (state_changed) {
384                 gchar *tooltip;
385 
386                 tooltip = engine_get_summary (manager);
387 
388                 csd_power_set_tooltip (manager->priv->power_iface, tooltip);
389 
390                 g_free (tooltip);
391         }
392 }
393 
394 static CsdPowerManagerWarning
engine_get_warning_csr(CsdPowerManager * manager,UpDevice * device)395 engine_get_warning_csr (CsdPowerManager *manager, UpDevice *device)
396 {
397         gdouble percentage;
398 
399         /* get device properties */
400         g_object_get (device, "percentage", &percentage, NULL);
401 
402         if (percentage < 26.0f)
403                 return WARNING_LOW;
404         else if (percentage < 13.0f)
405                 return WARNING_CRITICAL;
406         return WARNING_NONE;
407 }
408 
409 static CsdPowerManagerWarning
engine_get_warning_percentage(CsdPowerManager * manager,UpDevice * device)410 engine_get_warning_percentage (CsdPowerManager *manager, UpDevice *device)
411 {
412         gdouble percentage;
413 
414         /* get device properties */
415         g_object_get (device, "percentage", &percentage, NULL);
416 
417         if (percentage <= manager->priv->action_percentage)
418                 return WARNING_ACTION;
419         if (percentage <= manager->priv->critical_percentage)
420                 return WARNING_CRITICAL;
421         if (percentage <= manager->priv->low_percentage)
422                 return WARNING_LOW;
423         return WARNING_NONE;
424 }
425 
426 static CsdPowerManagerWarning
engine_get_warning_time(CsdPowerManager * manager,UpDevice * device)427 engine_get_warning_time (CsdPowerManager *manager, UpDevice *device)
428 {
429         UpDeviceKind kind;
430         gint64 time_to_empty;
431 
432         /* get device properties */
433         g_object_get (device,
434                       "kind", &kind,
435                       "time-to-empty", &time_to_empty,
436                       NULL);
437 
438         /* this is probably an error condition */
439         if (time_to_empty == 0) {
440                 g_debug ("time zero, falling back to percentage for %s",
441                          up_device_kind_to_string (kind));
442                 return engine_get_warning_percentage (manager, device);
443         }
444 
445         if (time_to_empty <= manager->priv->action_time)
446                 return WARNING_ACTION;
447         if (time_to_empty <= manager->priv->critical_time)
448                 return WARNING_CRITICAL;
449         if (time_to_empty <= manager->priv->low_time)
450                 return WARNING_LOW;
451         return WARNING_NONE;
452 }
453 
454 /**
455  * This gets the possible engine state for the device according to the
456  * policy, which could be per-percent, or per-time.
457  **/
458 static CsdPowerManagerWarning
engine_get_warning(CsdPowerManager * manager,UpDevice * device)459 engine_get_warning (CsdPowerManager *manager, UpDevice *device)
460 {
461         UpDeviceKind kind;
462         UpDeviceState state;
463         CsdPowerManagerWarning warning_type;
464 
465         /* get device properties */
466         g_object_get (device,
467                       "kind", &kind,
468                       "state", &state,
469                       NULL);
470 
471         /* default to no engine */
472         warning_type = WARNING_NONE;
473 
474         /* if the device in question is on ac, don't give a warning */
475         if (state == UP_DEVICE_STATE_CHARGING)
476                 goto out;
477 
478         if (kind == UP_DEVICE_KIND_MOUSE ||
479             kind == UP_DEVICE_KIND_KEYBOARD) {
480 
481                 warning_type = engine_get_warning_csr (manager, device);
482 
483         } else if (kind == UP_DEVICE_KIND_UPS ||
484                    kind == UP_DEVICE_KIND_MEDIA_PLAYER ||
485                    kind == UP_DEVICE_KIND_TABLET ||
486                    kind == UP_DEVICE_KIND_COMPUTER ||
487                    kind == UP_DEVICE_KIND_PDA) {
488 
489                 warning_type = engine_get_warning_percentage (manager, device);
490 
491         } else if (kind == UP_DEVICE_KIND_PHONE) {
492 
493                 warning_type = engine_get_warning_percentage (manager, device);
494 
495         } else if (kind == UP_DEVICE_KIND_BATTERY) {
496                 /* only use the time when it is accurate, and settings is not disabled */
497                 if (manager->priv->use_time_primary)
498                         warning_type = engine_get_warning_time (manager, device);
499                 else
500                         warning_type = engine_get_warning_percentage (manager, device);
501         }
502 
503         /* If we have no important engines, we should test for discharging */
504         if (warning_type == WARNING_NONE) {
505                 if (state == UP_DEVICE_STATE_DISCHARGING)
506                         warning_type = WARNING_DISCHARGING;
507         }
508 
509  out:
510         return warning_type;
511 }
512 
513 static gchar *
engine_get_summary(CsdPowerManager * manager)514 engine_get_summary (CsdPowerManager *manager)
515 {
516         guint i;
517         GPtrArray *array;
518         UpDevice *device;
519         UpDeviceState state;
520         GString *tooltip = NULL;
521         gchar *part;
522         gboolean is_present;
523 
524 
525         /* need to get AC state */
526         tooltip = g_string_new ("");
527 
528         /* do we have specific device types? */
529         array = manager->priv->devices_array;
530         for (i=0;i<array->len;i++) {
531                 device = g_ptr_array_index (array, i);
532                 g_object_get (device,
533                               "is-present", &is_present,
534                               "state", &state,
535                               NULL);
536                 if (!is_present)
537                         continue;
538                 if (state == UP_DEVICE_STATE_EMPTY)
539                         continue;
540                 part = gpm_upower_get_device_summary (device);
541                 if (part != NULL)
542                         g_string_append_printf (tooltip, "%s\n", part);
543                 g_free (part);
544         }
545 
546         /* remove the last \n */
547         g_string_truncate (tooltip, tooltip->len-1);
548 
549         g_debug ("tooltip: %s", tooltip->str);
550 
551         return g_string_free (tooltip, FALSE);
552 }
553 
554 static GIcon *
engine_get_icon_priv(CsdPowerManager * manager,UpDeviceKind device_kind,CsdPowerManagerWarning warning,gboolean use_state)555 engine_get_icon_priv (CsdPowerManager *manager,
556                       UpDeviceKind device_kind,
557                       CsdPowerManagerWarning warning,
558                       gboolean use_state)
559 {
560         guint i;
561         GPtrArray *array;
562         UpDevice *device;
563         CsdPowerManagerWarning warning_temp;
564         UpDeviceKind kind;
565         UpDeviceState state;
566         gboolean is_present;
567 
568         /* do we have specific device types? */
569         array = manager->priv->devices_array;
570         for (i=0;i<array->len;i++) {
571                 device = g_ptr_array_index (array, i);
572 
573                 /* get device properties */
574                 g_object_get (device,
575                               "kind", &kind,
576                               "state", &state,
577                               "is-present", &is_present,
578                               NULL);
579 
580                 /* if battery then use composite device to cope with multiple batteries */
581                 if (kind == UP_DEVICE_KIND_BATTERY)
582                         device = engine_get_composite_device (manager, device);
583 
584                 warning_temp = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(device),
585                                                                   "engine-warning-old"));
586                 if (kind == device_kind && is_present) {
587                         if (warning != WARNING_NONE) {
588                                 if (warning_temp == warning)
589                                         return gpm_upower_get_device_icon (device, TRUE);
590                                 continue;
591                         }
592                         if (use_state) {
593                                 if (state == UP_DEVICE_STATE_CHARGING ||
594                                     state == UP_DEVICE_STATE_DISCHARGING)
595                                         return gpm_upower_get_device_icon (device, TRUE);
596                                 continue;
597                         }
598                         return gpm_upower_get_device_icon (device, TRUE);
599                 }
600         }
601         return NULL;
602 }
603 
604 static GIcon *
engine_get_icon(CsdPowerManager * manager)605 engine_get_icon (CsdPowerManager *manager)
606 {
607         GIcon *icon = NULL;
608 
609 
610         /* we try CRITICAL: BATTERY, UPS, MOUSE, KEYBOARD */
611         icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_BATTERY, WARNING_CRITICAL, FALSE);
612         if (icon != NULL)
613                 return icon;
614         icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_UPS, WARNING_CRITICAL, FALSE);
615         if (icon != NULL)
616                 return icon;
617         icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_MOUSE, WARNING_CRITICAL, FALSE);
618         if (icon != NULL)
619                 return icon;
620         icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_KEYBOARD, WARNING_CRITICAL, FALSE);
621         if (icon != NULL)
622                 return icon;
623 
624         /* we try CRITICAL: BATTERY, UPS, MOUSE, KEYBOARD */
625         icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_BATTERY, WARNING_LOW, FALSE);
626         if (icon != NULL)
627                 return icon;
628         icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_UPS, WARNING_LOW, FALSE);
629         if (icon != NULL)
630                 return icon;
631         icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_MOUSE, WARNING_LOW, FALSE);
632         if (icon != NULL)
633                 return icon;
634         icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_KEYBOARD, WARNING_LOW, FALSE);
635         if (icon != NULL)
636                 return icon;
637 
638         /* we try (DIS)CHARGING: BATTERY, UPS */
639         icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_BATTERY, WARNING_NONE, TRUE);
640         if (icon != NULL)
641                 return icon;
642         icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_UPS, WARNING_NONE, TRUE);
643         if (icon != NULL)
644                 return icon;
645 
646         /* we try PRESENT: BATTERY, UPS */
647         icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_BATTERY, WARNING_NONE, FALSE);
648         if (icon != NULL)
649                 return icon;
650         icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_UPS, WARNING_NONE, FALSE);
651         if (icon != NULL)
652                 return icon;
653 
654         /* do not show an icon */
655         return NULL;
656 }
657 
658 static gboolean
engine_recalculate_state_icon(CsdPowerManager * manager)659 engine_recalculate_state_icon (CsdPowerManager *manager)
660 {
661         GIcon *icon;
662 
663         /* show a different icon if we are disconnected */
664         icon = engine_get_icon (manager);
665         gtk_status_icon_set_visible (manager->priv->status_icon, FALSE);
666 
667         if (icon == NULL) {
668                 /* none before, now none */
669                 if (manager->priv->previous_icon == NULL)
670                         return FALSE;
671 
672                 g_object_unref (manager->priv->previous_icon);
673                 manager->priv->previous_icon = NULL;
674                 return TRUE;
675         }
676 
677         /* no icon before, now icon */
678         if (manager->priv->previous_icon == NULL) {
679 
680                 /* set fallback icon */
681                 gtk_status_icon_set_visible (manager->priv->status_icon, FALSE);
682                 gtk_status_icon_set_from_gicon (manager->priv->status_icon, icon);
683                 manager->priv->previous_icon = icon;
684                 return TRUE;
685         }
686 
687         /* icon before, now different */
688         if (!g_icon_equal (manager->priv->previous_icon, icon)) {
689 
690                 /* set fallback icon */
691                 gtk_status_icon_set_from_gicon (manager->priv->status_icon, icon);
692 
693                 g_object_unref (manager->priv->previous_icon);
694                 manager->priv->previous_icon = icon;
695                 return TRUE;
696         }
697 
698         g_debug ("no change");
699         /* nothing to do */
700         g_object_unref (icon);
701         return FALSE;
702 }
703 
704 static gboolean
engine_recalculate_state_summary(CsdPowerManager * manager)705 engine_recalculate_state_summary (CsdPowerManager *manager)
706 {
707         gchar *summary;
708 
709         summary = engine_get_summary (manager);
710         if (manager->priv->previous_summary == NULL) {
711                 manager->priv->previous_summary = summary;
712 
713                 /* set fallback tooltip */
714                 gtk_status_icon_set_tooltip_text (manager->priv->status_icon,
715                                                   summary);
716 
717                 return TRUE;
718         }
719 
720         if (strcmp (manager->priv->previous_summary, summary) != 0) {
721                 g_free (manager->priv->previous_summary);
722                 manager->priv->previous_summary = summary;
723 
724                 /* set fallback tooltip */
725                 gtk_status_icon_set_tooltip_text (manager->priv->status_icon,
726                                                   summary);
727 
728                 return TRUE;
729         }
730         g_debug ("no change");
731         /* nothing to do */
732         g_free (summary);
733         return FALSE;
734 }
735 
736 static void
engine_recalculate_state(CsdPowerManager * manager)737 engine_recalculate_state (CsdPowerManager *manager)
738 {
739         gboolean icon_changed = FALSE;
740         gboolean state_changed = FALSE;
741 
742         icon_changed = engine_recalculate_state_icon (manager);
743         state_changed = engine_recalculate_state_summary (manager);
744 
745         /* only emit if the icon or summary has changed */
746         if (icon_changed || state_changed)
747                 engine_emit_changed (manager, icon_changed, state_changed);
748 }
749 
750 static UpDevice *
engine_get_composite_device(CsdPowerManager * manager,UpDevice * original_device)751 engine_get_composite_device (CsdPowerManager *manager,
752                              UpDevice *original_device)
753 {
754         guint battery_devices = 0;
755         GPtrArray *array;
756         UpDevice *device;
757         UpDeviceKind kind;
758         UpDeviceKind original_kind;
759         guint i;
760 
761         /* get the type of the original device */
762         g_object_get (original_device,
763                       "kind", &original_kind,
764                       NULL);
765 
766         /* find out how many batteries in the system */
767         array = manager->priv->devices_array;
768         for (i=0;i<array->len;i++) {
769                 device = g_ptr_array_index (array, i);
770                 g_object_get (device,
771                               "kind", &kind,
772                               NULL);
773                 if (kind == original_kind)
774                         battery_devices++;
775         }
776 
777         /* just use the original device if only one primary battery */
778         if (battery_devices <= 1) {
779                 g_debug ("using original device as only one primary battery");
780                 device = original_device;
781                 goto out;
782         }
783 
784         /* use the composite device */
785         device = manager->priv->device_composite;
786 out:
787         /* return composite device or original device */
788         return device;
789 }
790 
791 static UpDevice *
engine_update_composite_device(CsdPowerManager * manager,UpDevice * original_device)792 engine_update_composite_device (CsdPowerManager *manager,
793                                 UpDevice *original_device)
794 {
795         guint i;
796         gdouble percentage = 0.0;
797         gdouble energy = 0.0;
798         gdouble energy_full = 0.0;
799         gdouble energy_rate = 0.0;
800         gdouble energy_total = 0.0;
801         gdouble energy_full_total = 0.0;
802         gdouble energy_rate_total = 0.0;
803         gint64 time_to_empty = 0;
804         gint64 time_to_full = 0;
805         guint battery_devices = 0;
806         gboolean is_charging = FALSE;
807         gboolean is_discharging = FALSE;
808         gboolean is_fully_charged = TRUE;
809         GPtrArray *array;
810         UpDevice *device;
811         UpDeviceState state;
812         UpDeviceKind kind;
813         UpDeviceKind original_kind;
814 
815         /* get the type of the original device */
816         g_object_get (original_device,
817                       "kind", &original_kind,
818                       NULL);
819 
820         /* update the composite device */
821         array = manager->priv->devices_array;
822         for (i=0;i<array->len;i++) {
823                 device = g_ptr_array_index (array, i);
824                 g_object_get (device,
825                               "kind", &kind,
826                               "state", &state,
827                               "energy", &energy,
828                               "energy-full", &energy_full,
829                               "energy-rate", &energy_rate,
830                               NULL);
831                 if (kind != original_kind)
832                         continue;
833 
834                 /* one of these will be charging or discharging */
835                 if (state == UP_DEVICE_STATE_CHARGING)
836                         is_charging = TRUE;
837                 if (state == UP_DEVICE_STATE_DISCHARGING)
838                         is_discharging = TRUE;
839                 if (state != UP_DEVICE_STATE_FULLY_CHARGED)
840                         is_fully_charged = FALSE;
841 
842                 /* sum up composite */
843                 energy_total += energy;
844                 energy_full_total += energy_full;
845                 energy_rate_total += energy_rate;
846                 battery_devices++;
847         }
848 
849         /* just use the original device if only one primary battery */
850         if (battery_devices == 1) {
851                 g_debug ("using original device as only one primary battery");
852                 device = original_device;
853                 goto out;
854         }
855 
856         /* use percentage weighted for each battery capacity */
857         if (energy_full_total > 0.0)
858                 percentage = 100.0 * energy_total / energy_full_total;
859 
860         /* set composite state */
861         if (is_charging)
862                 state = UP_DEVICE_STATE_CHARGING;
863         else if (is_discharging)
864                 state = UP_DEVICE_STATE_DISCHARGING;
865         else if (is_fully_charged)
866                 state = UP_DEVICE_STATE_FULLY_CHARGED;
867         else
868                 state = UP_DEVICE_STATE_UNKNOWN;
869 
870         /* calculate a quick and dirty time remaining value */
871         if (energy_rate_total > 0) {
872                 if (state == UP_DEVICE_STATE_DISCHARGING)
873                         time_to_empty = 3600 * (energy_total / energy_rate_total);
874                 else if (state == UP_DEVICE_STATE_CHARGING)
875                         time_to_full = 3600 * ((energy_full_total - energy_total) / energy_rate_total);
876         }
877 
878         /* okay, we can use the composite device */
879         device = manager->priv->device_composite;
880 
881         g_debug ("printing composite device");
882         g_object_set (device,
883                       "energy", energy,
884                       "energy-full", energy_full,
885                       "energy-rate", energy_rate,
886                       "time-to-empty", time_to_empty,
887                       "time-to-full", time_to_full,
888                       "percentage", percentage,
889                       "state", state,
890                       NULL);
891 
892         /* force update of icon */
893         if (engine_recalculate_state_icon (manager))
894                 engine_emit_changed (manager, TRUE, FALSE);
895 out:
896         /* return composite device or original device */
897         return device;
898 }
899 
900 static void
engine_device_add(CsdPowerManager * manager,UpDevice * device)901 engine_device_add (CsdPowerManager *manager, UpDevice *device)
902 {
903         CsdPowerManagerWarning warning;
904         UpDeviceState state;
905         UpDeviceKind kind;
906         UpDevice *composite;
907 
908         /* assign warning */
909         warning = engine_get_warning (manager, device);
910         g_object_set_data (G_OBJECT(device),
911                            "engine-warning-old",
912                            GUINT_TO_POINTER(warning));
913 
914         /* get device properties */
915         g_object_get (device,
916                       "kind", &kind,
917                       "state", &state,
918                       NULL);
919 
920         /* add old state for transitions */
921         g_debug ("adding %s with state %s",
922                  up_device_get_object_path (device), up_device_state_to_string (state));
923         g_object_set_data (G_OBJECT(device),
924                            "engine-state-old",
925                            GUINT_TO_POINTER(state));
926 
927         if (kind == UP_DEVICE_KIND_BATTERY) {
928                 g_debug ("updating because we added a device");
929                 composite = engine_update_composite_device (manager, device);
930 
931                 /* get the same values for the composite device */
932                 warning = engine_get_warning (manager, composite);
933                 g_object_set_data (G_OBJECT(composite),
934                                    "engine-warning-old",
935                                    GUINT_TO_POINTER(warning));
936                 g_object_get (composite, "state", &state, NULL);
937                 g_object_set_data (G_OBJECT(composite),
938                                    "engine-state-old",
939                                    GUINT_TO_POINTER(state));
940         }
941 
942 #if UP_CHECK_VERSION(0,99,0)
943         g_ptr_array_add (manager->priv->devices_array, g_object_ref(device));
944 
945         g_signal_connect (device, "notify",
946                           G_CALLBACK (device_properties_changed_cb), manager);
947 #endif
948 
949 }
950 
951 static gboolean
engine_coldplug(CsdPowerManager * manager)952 engine_coldplug (CsdPowerManager *manager)
953 {
954         guint i;
955         GPtrArray *array = NULL;
956         UpDevice *device;
957 #if ! UP_CHECK_VERSION(0,99,0)
958         gboolean ret;
959         GError *error = NULL;
960 
961         /* get devices from UPower */
962         ret = up_client_enumerate_devices_sync (manager->priv->up_client, NULL, &error);
963         if (!ret) {
964                 g_warning ("failed to get device list: %s", error->message);
965                 g_error_free (error);
966                 goto out;
967         }
968 #endif
969 
970         /* connected mobile phones */
971         gpm_phone_coldplug (manager->priv->phone);
972 
973         engine_recalculate_state (manager);
974 
975         /* add to database */
976         array = up_client_get_devices (manager->priv->up_client);
977         for (i = 0; array != NULL && i < array->len; i++) {
978                 device = g_ptr_array_index (array, i);
979                 engine_device_add (manager, device);
980         }
981 #if ! UP_CHECK_VERSION(0,99,0)
982 out:
983 #endif
984         if (array != NULL)
985                 g_ptr_array_unref (array);
986         /* never repeat */
987         return FALSE;
988 }
989 
990 static void
engine_device_added_cb(UpClient * client,UpDevice * device,CsdPowerManager * manager)991 engine_device_added_cb (UpClient *client, UpDevice *device, CsdPowerManager *manager)
992 {
993         /* add to list */
994         g_ptr_array_add (manager->priv->devices_array, g_object_ref (device));
995         engine_recalculate_state (manager);
996 }
997 
998 static void
999 #if UP_CHECK_VERSION(0,99,0)
engine_device_removed_cb(UpClient * client,const char * object_path,CsdPowerManager * manager)1000 engine_device_removed_cb (UpClient *client, const char *object_path, CsdPowerManager *manager)
1001 {
1002         guint i;
1003 
1004         for (i = 0; i < manager->priv->devices_array->len; i++) {
1005                 UpDevice *device = g_ptr_array_index (manager->priv->devices_array, i);
1006 
1007                 if (g_strcmp0 (object_path, up_device_get_object_path (device)) == 0) {
1008                         g_ptr_array_remove_index (manager->priv->devices_array, i);
1009                         break;
1010                 }
1011         }
1012         engine_recalculate_state (manager);
1013 }
1014 
1015 #else
1016 
1017 engine_device_removed_cb (UpClient *client, UpDevice *device, CsdPowerManager *manager)
1018 {
1019         gboolean ret;
1020         ret = g_ptr_array_remove (manager->priv->devices_array, device);
1021         if (!ret)
1022                 return;
1023         engine_recalculate_state (manager);
1024 }
1025 #endif
1026 
1027 static void
on_notification_closed(NotifyNotification * notification,gpointer data)1028 on_notification_closed (NotifyNotification *notification, gpointer data)
1029 {
1030     g_object_unref (notification);
1031 }
1032 
1033 static void
create_notification(const char * summary,const char * body,const char * icon,NotifyNotification ** weak_pointer_location)1034 create_notification (const char *summary,
1035                      const char *body,
1036                      const char *icon,
1037                      NotifyNotification **weak_pointer_location)
1038 {
1039         NotifyNotification *notification;
1040 
1041         notification = notify_notification_new (summary, body, icon);
1042         *weak_pointer_location = notification;
1043         g_object_add_weak_pointer (G_OBJECT (notification),
1044                                    (gpointer *) weak_pointer_location);
1045         g_signal_connect (notification, "closed",
1046                           G_CALLBACK (on_notification_closed), NULL);
1047 }
1048 
1049 static void
engine_ups_discharging(CsdPowerManager * manager,UpDevice * device)1050 engine_ups_discharging (CsdPowerManager *manager, UpDevice *device)
1051 {
1052         const gchar *title;
1053         gboolean ret;
1054         gchar *remaining_text = NULL;
1055         gdouble percentage;
1056         GError *error = NULL;
1057         GIcon *icon = NULL;
1058         gint64 time_to_empty;
1059         GString *message;
1060         UpDeviceKind kind;
1061 
1062         /* get device properties */
1063         g_object_get (device,
1064                       "kind", &kind,
1065                       "percentage", &percentage,
1066                       "time-to-empty", &time_to_empty,
1067                       NULL);
1068 
1069         if (kind != UP_DEVICE_KIND_UPS)
1070                 return;
1071 
1072         /* only show text if there is a valid time */
1073         if (time_to_empty > 0)
1074                 remaining_text = gpm_get_timestring (time_to_empty);
1075 
1076         /* TRANSLATORS: UPS is now discharging */
1077         title = _("UPS Discharging");
1078 
1079         message = g_string_new ("");
1080         if (remaining_text != NULL) {
1081                 /* TRANSLATORS: tell the user how much time they have got */
1082                 g_string_append_printf (message, _("%s of UPS backup power remaining"),
1083                                         remaining_text);
1084         } else {
1085                 g_string_append (message, gpm_device_to_localised_string (device));
1086         }
1087         g_string_append_printf (message, " (%.0f%%)", percentage);
1088 
1089         icon = gpm_upower_get_device_icon (device, TRUE);
1090 
1091         /* close any existing notification of this class */
1092         notify_close_if_showing (manager->priv->notification_discharging);
1093 
1094         /* create a new notification */
1095         create_notification (title, message->str,
1096                              get_first_themed_icon_name (icon),
1097                              &manager->priv->notification_discharging);
1098         notify_notification_set_timeout (manager->priv->notification_discharging,
1099                                          CSD_POWER_MANAGER_NOTIFY_TIMEOUT_LONG);
1100         notify_notification_set_urgency (manager->priv->notification_discharging,
1101                                          NOTIFY_URGENCY_NORMAL);
1102         /* TRANSLATORS: this is the notification application name */
1103         notify_notification_set_app_name (manager->priv->notification_discharging, _("Power"));
1104         notify_notification_set_hint (manager->priv->notification_discharging,
1105                                       "transient", g_variant_new_boolean (TRUE));
1106 
1107         /* try to show */
1108         ret = notify_notification_show (manager->priv->notification_discharging,
1109                                         &error);
1110         if (!ret) {
1111                 g_warning ("failed to show notification: %s", error->message);
1112                 g_error_free (error);
1113                 g_object_unref (manager->priv->notification_discharging);
1114         }
1115         g_string_free (message, TRUE);
1116         if (icon != NULL)
1117                 g_object_unref (icon);
1118         g_free (remaining_text);
1119 }
1120 
1121 static CsdPowerActionType
manager_critical_action_get(CsdPowerManager * manager,gboolean is_ups)1122 manager_critical_action_get (CsdPowerManager *manager,
1123                              gboolean         is_ups)
1124 {
1125         CsdPowerActionType policy;
1126 
1127         policy = g_settings_get_enum (manager->priv->settings, "critical-battery-action");
1128         if (policy == CSD_POWER_ACTION_SUSPEND) {
1129                 if (is_ups == FALSE
1130 #if ! UP_CHECK_VERSION(0,99,0)
1131                     && up_client_get_can_suspend (manager->priv->up_client)
1132 #endif
1133                 )
1134                         return policy;
1135                 return CSD_POWER_ACTION_SHUTDOWN;
1136         } else if (policy == CSD_POWER_ACTION_HIBERNATE) {
1137 #if ! UP_CHECK_VERSION(0,99,0)
1138                 if (up_client_get_can_hibernate (manager->priv->up_client))
1139 #endif
1140                         return policy;
1141                 return CSD_POWER_ACTION_SHUTDOWN;
1142         }
1143 
1144         return policy;
1145 }
1146 
1147 static gboolean
manager_critical_action_do(CsdPowerManager * manager,gboolean is_ups)1148 manager_critical_action_do (CsdPowerManager *manager,
1149                             gboolean         is_ups)
1150 {
1151         CsdPowerActionType action_type;
1152 
1153         /* stop playing the alert as it's too late to do anything now */
1154         if (manager->priv->critical_alert_timeout_id > 0)
1155                 play_loop_stop (manager);
1156 
1157         action_type = manager_critical_action_get (manager, is_ups);
1158         do_power_action_type (manager, action_type);
1159 
1160         return FALSE;
1161 }
1162 
1163 static gboolean
manager_critical_action_do_cb(CsdPowerManager * manager)1164 manager_critical_action_do_cb (CsdPowerManager *manager)
1165 {
1166         manager_critical_action_do (manager, FALSE);
1167         return FALSE;
1168 }
1169 
1170 static gboolean
manager_critical_ups_action_do_cb(CsdPowerManager * manager)1171 manager_critical_ups_action_do_cb (CsdPowerManager *manager)
1172 {
1173         manager_critical_action_do (manager, TRUE);
1174         return FALSE;
1175 }
1176 
1177 static gboolean
engine_just_laptop_battery(CsdPowerManager * manager)1178 engine_just_laptop_battery (CsdPowerManager *manager)
1179 {
1180         UpDevice *device;
1181         UpDeviceKind kind;
1182         GPtrArray *array;
1183         gboolean ret = TRUE;
1184         guint i;
1185 
1186         /* find if there are any other device types that mean we have to
1187          * be more specific in our wording */
1188         array = manager->priv->devices_array;
1189         for (i=0; i<array->len; i++) {
1190                 device = g_ptr_array_index (array, i);
1191                 g_object_get (device, "kind", &kind, NULL);
1192                 if (kind != UP_DEVICE_KIND_BATTERY) {
1193                         ret = FALSE;
1194                         break;
1195                 }
1196         }
1197         return ret;
1198 }
1199 
1200 static void
engine_charge_low(CsdPowerManager * manager,UpDevice * device)1201 engine_charge_low (CsdPowerManager *manager, UpDevice *device)
1202 {
1203         const gchar *title = NULL;
1204         gboolean ret;
1205         gchar *message = NULL;
1206         gchar *tmp;
1207         gchar *remaining_text;
1208         gdouble percentage;
1209         GIcon *icon = NULL;
1210         gint64 time_to_empty;
1211         UpDeviceKind kind;
1212         GError *error = NULL;
1213 
1214         /* get device properties */
1215         g_object_get (device,
1216                       "kind", &kind,
1217                       "percentage", &percentage,
1218                       "time-to-empty", &time_to_empty,
1219                       NULL);
1220 
1221         /* check to see if the batteries have not noticed we are on AC */
1222         if (kind == UP_DEVICE_KIND_BATTERY) {
1223                 if (!up_client_get_on_battery (manager->priv->up_client)) {
1224                         g_warning ("ignoring low message as we are not on battery power");
1225                         goto out;
1226                 }
1227         }
1228 
1229         if (kind == UP_DEVICE_KIND_BATTERY) {
1230 
1231                 /* if the user has no other batteries, drop the "Laptop" wording */
1232                 ret = engine_just_laptop_battery (manager);
1233                 if (ret) {
1234                         /* TRANSLATORS: laptop battery low, and we only have one battery */
1235                         title = _("Battery low");
1236                 } else {
1237                         /* TRANSLATORS: laptop battery low, and we have more than one kind of battery */
1238                         title = _("Laptop battery low");
1239                 }
1240                 tmp = gpm_get_timestring (time_to_empty);
1241                 remaining_text = g_strconcat ("<b>", tmp, "</b>", NULL);
1242                 g_free (tmp);
1243 
1244                 /* TRANSLATORS: tell the user how much time they have got */
1245                 message = g_strdup_printf (_("Approximately %s remaining (%.0f%%)"), remaining_text, percentage);
1246                 g_free (remaining_text);
1247 
1248         } else if (kind == UP_DEVICE_KIND_UPS) {
1249                 /* TRANSLATORS: UPS is starting to get a little low */
1250                 title = _("UPS low");
1251                 tmp = gpm_get_timestring (time_to_empty);
1252                 remaining_text = g_strconcat ("<b>", tmp, "</b>", NULL);
1253                 g_free (tmp);
1254 
1255                 /* TRANSLATORS: tell the user how much time they have got */
1256                 message = g_strdup_printf (_("Approximately %s of remaining UPS backup power (%.0f%%)"),
1257                                            remaining_text, percentage);
1258                 g_free (remaining_text);
1259         } else if (kind == UP_DEVICE_KIND_MOUSE) {
1260                 /* TRANSLATORS: mouse is getting a little low */
1261                 title = _("Mouse battery low");
1262 
1263                 /* TRANSLATORS: tell user more details */
1264                 message = g_strdup_printf (_("Wireless mouse is low in power (%.0f%%)"), percentage);
1265 
1266         } else if (kind == UP_DEVICE_KIND_KEYBOARD) {
1267                 /* TRANSLATORS: keyboard is getting a little low */
1268                 title = _("Keyboard battery low");
1269 
1270                 /* TRANSLATORS: tell user more details */
1271                 message = g_strdup_printf (_("Wireless keyboard is low in power (%.0f%%)"), percentage);
1272 
1273         } else if (kind == UP_DEVICE_KIND_PDA) {
1274                 /* TRANSLATORS: PDA is getting a little low */
1275                 title = _("PDA battery low");
1276 
1277                 /* TRANSLATORS: tell user more details */
1278                 message = g_strdup_printf (_("PDA is low in power (%.0f%%)"), percentage);
1279 
1280         } else if (kind == UP_DEVICE_KIND_PHONE) {
1281                 /* TRANSLATORS: cell phone (mobile) is getting a little low */
1282                 title = _("Cell phone battery low");
1283 
1284                 /* TRANSLATORS: tell user more details */
1285                 message = g_strdup_printf (_("Cell phone is low in power (%.0f%%)"), percentage);
1286 
1287         } else if (kind == UP_DEVICE_KIND_MEDIA_PLAYER) {
1288                 /* TRANSLATORS: media player, e.g. mp3 is getting a little low */
1289                 title = _("Media player battery low");
1290 
1291                 /* TRANSLATORS: tell user more details */
1292                 message = g_strdup_printf (_("Media player is low in power (%.0f%%)"), percentage);
1293 
1294         } else if (kind == UP_DEVICE_KIND_TABLET) {
1295                 /* TRANSLATORS: graphics tablet, e.g. wacom is getting a little low */
1296                 title = _("Tablet battery low");
1297 
1298                 /* TRANSLATORS: tell user more details */
1299                 message = g_strdup_printf (_("Tablet is low in power (%.0f%%)"), percentage);
1300 
1301         } else if (kind == UP_DEVICE_KIND_COMPUTER) {
1302                 /* TRANSLATORS: computer, e.g. ipad is getting a little low */
1303                 title = _("Attached computer battery low");
1304 
1305                 /* TRANSLATORS: tell user more details */
1306                 message = g_strdup_printf (_("Attached computer is low in power (%.0f%%)"), percentage);
1307         }
1308 
1309         /* get correct icon */
1310         icon = gpm_upower_get_device_icon (device, TRUE);
1311 
1312         /* close any existing notification of this class */
1313         notify_close_if_showing (manager->priv->notification_low);
1314 
1315         /* create a new notification */
1316         create_notification (title, message,
1317                              get_first_themed_icon_name (icon),
1318                              &manager->priv->notification_low);
1319         notify_notification_set_timeout (manager->priv->notification_low,
1320                                          CSD_POWER_MANAGER_NOTIFY_TIMEOUT_LONG);
1321         notify_notification_set_urgency (manager->priv->notification_low,
1322                                          NOTIFY_URGENCY_NORMAL);
1323         notify_notification_set_app_name (manager->priv->notification_low, _("Power"));
1324         notify_notification_set_hint (manager->priv->notification_low,
1325                                       "transient", g_variant_new_boolean (TRUE));
1326 
1327         /* try to show */
1328         ret = notify_notification_show (manager->priv->notification_low,
1329                                         &error);
1330         if (!ret) {
1331                 g_warning ("failed to show notification: %s", error->message);
1332                 g_error_free (error);
1333                 g_object_unref (manager->priv->notification_low);
1334         }
1335 
1336         /* play the sound, using sounds from the naming spec */
1337         ca_context_play (manager->priv->canberra_context, 0,
1338                          CA_PROP_EVENT_ID, "battery-low",
1339                          /* TRANSLATORS: this is the sound description */
1340                          CA_PROP_EVENT_DESCRIPTION, _("Battery is low"), NULL);
1341 
1342 out:
1343         if (icon != NULL)
1344                 g_object_unref (icon);
1345         g_free (message);
1346 }
1347 
1348 static void
engine_charge_critical(CsdPowerManager * manager,UpDevice * device)1349 engine_charge_critical (CsdPowerManager *manager, UpDevice *device)
1350 {
1351         const gchar *title = NULL;
1352         gboolean ret;
1353         gchar *message = NULL;
1354         gdouble percentage;
1355         GIcon *icon = NULL;
1356         gint64 time_to_empty;
1357         CsdPowerActionType policy;
1358         UpDeviceKind kind;
1359         GError *error = NULL;
1360 
1361         /* get device properties */
1362         g_object_get (device,
1363                       "kind", &kind,
1364                       "percentage", &percentage,
1365                       "time-to-empty", &time_to_empty,
1366                       NULL);
1367 
1368         /* check to see if the batteries have not noticed we are on AC */
1369         if (kind == UP_DEVICE_KIND_BATTERY) {
1370                 if (!up_client_get_on_battery (manager->priv->up_client)) {
1371                         g_warning ("ignoring critically low message as we are not on battery power");
1372                         goto out;
1373                 }
1374         }
1375 
1376         if (kind == UP_DEVICE_KIND_BATTERY) {
1377 
1378                 /* if the user has no other batteries, drop the "Laptop" wording */
1379                 ret = engine_just_laptop_battery (manager);
1380                 if (ret) {
1381                         /* TRANSLATORS: laptop battery critically low, and only have one kind of battery */
1382                         title = _("Battery critically low");
1383                 } else {
1384                         /* TRANSLATORS: laptop battery critically low, and we have more than one type of battery */
1385                         title = _("Laptop battery critically low");
1386                 }
1387 
1388                 /* we have to do different warnings depending on the policy */
1389                 policy = manager_critical_action_get (manager, FALSE);
1390 
1391                 /* use different text for different actions */
1392                 if (policy == CSD_POWER_ACTION_NOTHING) {
1393                         /* TRANSLATORS: tell the use to insert the plug, as we're not going to do anything */
1394                         message = g_strdup (_("Plug in your AC adapter to avoid losing data."));
1395 
1396                 } else if (policy == CSD_POWER_ACTION_SUSPEND) {
1397                         /* TRANSLATORS: give the user a ultimatum */
1398                         message = g_strdup_printf (_("Computer will suspend very soon unless it is plugged in."));
1399 
1400                 } else if (policy == CSD_POWER_ACTION_HIBERNATE) {
1401                         /* TRANSLATORS: give the user a ultimatum */
1402                         message = g_strdup_printf (_("Computer will hibernate very soon unless it is plugged in."));
1403 
1404                 } else if (policy == CSD_POWER_ACTION_SHUTDOWN) {
1405                         /* TRANSLATORS: give the user a ultimatum */
1406                         message = g_strdup_printf (_("Computer will shutdown very soon unless it is plugged in."));
1407                 }
1408 
1409         } else if (kind == UP_DEVICE_KIND_UPS) {
1410                 gchar *remaining_text;
1411                 gchar *tmp;
1412 
1413                 /* TRANSLATORS: the UPS is very low */
1414                 title = _("UPS critically low");
1415                 tmp = gpm_get_timestring (time_to_empty);
1416                 remaining_text = g_strconcat ("<b>", tmp, "</b>", NULL);
1417                 g_free (tmp);
1418 
1419                 /* TRANSLATORS: give the user a ultimatum */
1420                 message = g_strdup_printf (_("Approximately %s of remaining UPS power (%.0f%%). "
1421                                              "Restore AC power to your computer to avoid losing data."),
1422                                            remaining_text, percentage);
1423                 g_free (remaining_text);
1424         } else if (kind == UP_DEVICE_KIND_MOUSE) {
1425                 /* TRANSLATORS: the mouse battery is very low */
1426                 title = _("Mouse battery low");
1427 
1428                 /* TRANSLATORS: the device is just going to stop working */
1429                 message = g_strdup_printf (_("Wireless mouse is very low in power (%.0f%%). "
1430                                              "This device will soon stop functioning if not charged."),
1431                                            percentage);
1432         } else if (kind == UP_DEVICE_KIND_KEYBOARD) {
1433                 /* TRANSLATORS: the keyboard battery is very low */
1434                 title = _("Keyboard battery low");
1435 
1436                 /* TRANSLATORS: the device is just going to stop working */
1437                 message = g_strdup_printf (_("Wireless keyboard is very low in power (%.0f%%). "
1438                                              "This device will soon stop functioning if not charged."),
1439                                            percentage);
1440         } else if (kind == UP_DEVICE_KIND_PDA) {
1441 
1442                 /* TRANSLATORS: the PDA battery is very low */
1443                 title = _("PDA battery low");
1444 
1445                 /* TRANSLATORS: the device is just going to stop working */
1446                 message = g_strdup_printf (_("PDA is very low in power (%.0f%%). "
1447                                              "This device will soon stop functioning if not charged."),
1448                                            percentage);
1449 
1450         } else if (kind == UP_DEVICE_KIND_PHONE) {
1451 
1452                 /* TRANSLATORS: the cell battery is very low */
1453                 title = _("Cell phone battery low");
1454 
1455                 /* TRANSLATORS: the device is just going to stop working */
1456                 message = g_strdup_printf (_("Cell phone is very low in power (%.0f%%). "
1457                                              "This device will soon stop functioning if not charged."),
1458                                            percentage);
1459 
1460         } else if (kind == UP_DEVICE_KIND_MEDIA_PLAYER) {
1461 
1462                 /* TRANSLATORS: the cell battery is very low */
1463                 title = _("Cell phone battery low");
1464 
1465                 /* TRANSLATORS: the device is just going to stop working */
1466                 message = g_strdup_printf (_("Media player is very low in power (%.0f%%). "
1467                                              "This device will soon stop functioning if not charged."),
1468                                            percentage);
1469         } else if (kind == UP_DEVICE_KIND_TABLET) {
1470 
1471                 /* TRANSLATORS: the cell battery is very low */
1472                 title = _("Tablet battery low");
1473 
1474                 /* TRANSLATORS: the device is just going to stop working */
1475                 message = g_strdup_printf (_("Tablet is very low in power (%.0f%%). "
1476                                              "This device will soon stop functioning if not charged."),
1477                                            percentage);
1478         } else if (kind == UP_DEVICE_KIND_COMPUTER) {
1479 
1480                 /* TRANSLATORS: the cell battery is very low */
1481                 title = _("Attached computer battery low");
1482 
1483                 /* TRANSLATORS: the device is just going to stop working */
1484                 message = g_strdup_printf (_("Attached computer is very low in power (%.0f%%). "
1485                                              "The device will soon shutdown if not charged."),
1486                                            percentage);
1487         }
1488 
1489         /* get correct icon */
1490         icon = gpm_upower_get_device_icon (device, TRUE);
1491 
1492         /* close any existing notification of this class */
1493         notify_close_if_showing (manager->priv->notification_low);
1494 
1495         /* create a new notification */
1496         create_notification (title, message,
1497                              get_first_themed_icon_name (icon),
1498                              &manager->priv->notification_low);
1499         notify_notification_set_timeout (manager->priv->notification_low,
1500                                          CSD_POWER_MANAGER_NOTIFY_TIMEOUT_LONG);
1501         notify_notification_set_urgency (manager->priv->notification_low,
1502                                          NOTIFY_URGENCY_CRITICAL);
1503         notify_notification_set_app_name (manager->priv->notification_low, _("Power"));
1504 
1505         /* try to show */
1506         ret = notify_notification_show (manager->priv->notification_low,
1507                                         &error);
1508         if (!ret) {
1509                 g_warning ("failed to show notification: %s", error->message);
1510                 g_error_free (error);
1511                 g_object_unref (manager->priv->notification_low);
1512         }
1513 
1514         switch (kind) {
1515 
1516         case UP_DEVICE_KIND_BATTERY:
1517         case UP_DEVICE_KIND_UPS:
1518                 g_debug ("critical charge level reached, starting sound loop");
1519                 play_loop_start (manager,
1520                                  "battery-caution",
1521                                  _("Battery is critically low"),
1522                                  TRUE,
1523                                  CSD_POWER_MANAGER_CRITICAL_ALERT_TIMEOUT);
1524                 break;
1525 
1526         default:
1527                 /* play the sound, using sounds from the naming spec */
1528                 ca_context_play (manager->priv->canberra_context, 0,
1529                                  CA_PROP_EVENT_ID, "battery-caution",
1530                                  /* TRANSLATORS: this is the sound description */
1531                                  CA_PROP_EVENT_DESCRIPTION, _("Battery is critically low"), NULL);
1532                 break;
1533         }
1534 out:
1535         if (icon != NULL)
1536                 g_object_unref (icon);
1537         g_free (message);
1538 }
1539 
1540 static void
engine_charge_action(CsdPowerManager * manager,UpDevice * device)1541 engine_charge_action (CsdPowerManager *manager, UpDevice *device)
1542 {
1543         const gchar *title = NULL;
1544         gboolean ret;
1545         gchar *message = NULL;
1546         GError *error = NULL;
1547         GIcon *icon = NULL;
1548         CsdPowerActionType policy;
1549         guint timer_id;
1550         UpDeviceKind kind;
1551 
1552         /* get device properties */
1553         g_object_get (device,
1554                       "kind", &kind,
1555                       NULL);
1556 
1557         /* check to see if the batteries have not noticed we are on AC */
1558         if (kind == UP_DEVICE_KIND_BATTERY) {
1559                 if (!up_client_get_on_battery (manager->priv->up_client)) {
1560                         g_warning ("ignoring critically low message as we are not on battery power");
1561                         goto out;
1562                 }
1563         }
1564 
1565         if (kind == UP_DEVICE_KIND_BATTERY) {
1566 
1567                 /* TRANSLATORS: laptop battery is really, really, low */
1568                 title = _("Laptop battery critically low");
1569 
1570                 /* we have to do different warnings depending on the policy */
1571                 policy = manager_critical_action_get (manager, FALSE);
1572 
1573                 /* use different text for different actions */
1574                 if (policy == CSD_POWER_ACTION_NOTHING) {
1575                         /* TRANSLATORS: computer will shutdown without saving data */
1576                         message = g_strdup (_("The battery is below the critical level and "
1577                                               "this computer will <b>power-off</b> when the "
1578                                               "battery becomes completely empty."));
1579 
1580                 } else if (policy == CSD_POWER_ACTION_SUSPEND) {
1581                         /* TRANSLATORS: computer will suspend */
1582                         message = g_strdup (_("The battery is below the critical level and "
1583                                               "this computer is about to suspend.\n"
1584                                               "<b>NOTE:</b> A small amount of power is required "
1585                                               "to keep your computer in a suspended state."));
1586 
1587                 } else if (policy == CSD_POWER_ACTION_HIBERNATE) {
1588                         /* TRANSLATORS: computer will hibernate */
1589                         message = g_strdup (_("The battery is below the critical level and "
1590                                               "this computer is about to hibernate."));
1591 
1592                 } else if (policy == CSD_POWER_ACTION_SHUTDOWN) {
1593                         /* TRANSLATORS: computer will just shutdown */
1594                         message = g_strdup (_("The battery is below the critical level and "
1595                                               "this computer is about to shutdown."));
1596                 }
1597 
1598                 /* wait 20 seconds for user-panic */
1599                 timer_id = g_timeout_add_seconds (20, (GSourceFunc) manager_critical_action_do_cb, manager);
1600                 g_source_set_name_by_id (timer_id, "[CsdPowerManager] battery critical-action");
1601 
1602         } else if (kind == UP_DEVICE_KIND_UPS) {
1603                 /* TRANSLATORS: UPS is really, really, low */
1604                 title = _("UPS critically low");
1605 
1606                 /* we have to do different warnings depending on the policy */
1607                 policy = manager_critical_action_get (manager, TRUE);
1608 
1609                 /* use different text for different actions */
1610                 if (policy == CSD_POWER_ACTION_NOTHING) {
1611                         /* TRANSLATORS: computer will shutdown without saving data */
1612                         message = g_strdup (_("UPS is below the critical level and "
1613                                               "this computer will <b>power-off</b> when the "
1614                                               "UPS becomes completely empty."));
1615 
1616                 } else if (policy == CSD_POWER_ACTION_HIBERNATE) {
1617                         /* TRANSLATORS: computer will hibernate */
1618                         message = g_strdup (_("UPS is below the critical level and "
1619                                               "this computer is about to hibernate."));
1620 
1621                 } else if (policy == CSD_POWER_ACTION_SHUTDOWN) {
1622                         /* TRANSLATORS: computer will just shutdown */
1623                         message = g_strdup (_("UPS is below the critical level and "
1624                                               "this computer is about to shutdown."));
1625                 }
1626 
1627                 /* wait 20 seconds for user-panic */
1628                 timer_id = g_timeout_add_seconds (20, (GSourceFunc) manager_critical_ups_action_do_cb, manager);
1629                 g_source_set_name_by_id (timer_id, "[CsdPowerManager] ups critical-action");
1630         }
1631 
1632         /* not all types have actions */
1633         if (title == NULL) {
1634                 g_free (message);
1635                 return;
1636         }
1637 
1638         /* get correct icon */
1639         icon = gpm_upower_get_device_icon (device, TRUE);
1640 
1641         /* close any existing notification of this class */
1642         notify_close_if_showing (manager->priv->notification_low);
1643 
1644         /* create a new notification */
1645         create_notification (title, message,
1646                              get_first_themed_icon_name (icon),
1647                              &manager->priv->notification_low);
1648         notify_notification_set_timeout (manager->priv->notification_low,
1649                                          CSD_POWER_MANAGER_NOTIFY_TIMEOUT_LONG);
1650         notify_notification_set_urgency (manager->priv->notification_low,
1651                                          NOTIFY_URGENCY_CRITICAL);
1652         notify_notification_set_app_name (manager->priv->notification_low, _("Power"));
1653 
1654         /* try to show */
1655         ret = notify_notification_show (manager->priv->notification_low,
1656                                         &error);
1657         if (!ret) {
1658                 g_warning ("failed to show notification: %s", error->message);
1659                 g_error_free (error);
1660                 g_object_unref (manager->priv->notification_low);
1661         }
1662 
1663         /* play the sound, using sounds from the naming spec */
1664         ca_context_play (manager->priv->canberra_context, 0,
1665                          CA_PROP_EVENT_ID, "battery-caution",
1666                          /* TRANSLATORS: this is the sound description */
1667                          CA_PROP_EVENT_DESCRIPTION, _("Battery is critically low"), NULL);
1668 out:
1669         if (icon != NULL)
1670                 g_object_unref (icon);
1671         g_free (message);
1672 }
1673 
1674 static void
1675 #if UP_CHECK_VERSION(0,99,0)
device_properties_changed_cb(UpDevice * device,GParamSpec * pspec,CsdPowerManager * manager)1676 device_properties_changed_cb (UpDevice *device, GParamSpec *pspec, CsdPowerManager *manager)
1677 #else
1678 engine_device_changed_cb (UpClient *client, UpDevice *device, CsdPowerManager *manager)
1679 #endif
1680 {
1681         UpDeviceKind kind;
1682         UpDeviceState state;
1683         UpDeviceState state_old;
1684         CsdPowerManagerWarning warning_old;
1685         CsdPowerManagerWarning warning;
1686 
1687         /* get device properties */
1688         g_object_get (device,
1689                       "kind", &kind,
1690                       NULL);
1691 
1692         /* if battery then use composite device to cope with multiple batteries */
1693         if (kind == UP_DEVICE_KIND_BATTERY) {
1694                 g_debug ("updating because %s changed", up_device_get_object_path (device));
1695                 device = engine_update_composite_device (manager, device);
1696         }
1697 
1698         /* get device properties (may be composite) */
1699         g_object_get (device,
1700                       "state", &state,
1701                       NULL);
1702 
1703         g_debug ("%s state is now %s", up_device_get_object_path (device), up_device_state_to_string (state));
1704 
1705         /* see if any interesting state changes have happened */
1706         state_old = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(device), "engine-state-old"));
1707         if (state_old != state) {
1708                 if (state == UP_DEVICE_STATE_DISCHARGING) {
1709                         g_debug ("discharging");
1710                         engine_ups_discharging (manager, device);
1711                 } else if (state == UP_DEVICE_STATE_FULLY_CHARGED ||
1712                            state == UP_DEVICE_STATE_CHARGING) {
1713                         g_debug ("fully charged or charging, hiding notifications if any");
1714                         notify_close_if_showing (manager->priv->notification_low);
1715                         notify_close_if_showing (manager->priv->notification_discharging);
1716                 }
1717 
1718                 /* save new state */
1719                 g_object_set_data (G_OBJECT(device), "engine-state-old", GUINT_TO_POINTER(state));
1720         }
1721 
1722         /* check the warning state has not changed */
1723         warning_old = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(device), "engine-warning-old"));
1724         warning = engine_get_warning (manager, device);
1725         if (warning != warning_old) {
1726                 if (warning == WARNING_LOW) {
1727                         g_debug ("** EMIT: charge-low");
1728                         engine_charge_low (manager, device);
1729                 } else if (warning == WARNING_CRITICAL) {
1730                         g_debug ("** EMIT: charge-critical");
1731                         engine_charge_critical (manager, device);
1732                 } else if (warning == WARNING_ACTION) {
1733                         g_debug ("charge-action");
1734                         engine_charge_action (manager, device);
1735                 }
1736                 /* save new state */
1737                 g_object_set_data (G_OBJECT(device), "engine-warning-old", GUINT_TO_POINTER(warning));
1738         }
1739 
1740         engine_recalculate_state (manager);
1741 }
1742 
1743 static UpDevice *
engine_get_primary_device(CsdPowerManager * manager)1744 engine_get_primary_device (CsdPowerManager *manager)
1745 {
1746         guint i;
1747         UpDevice *device = NULL;
1748         UpDevice *device_tmp;
1749         UpDeviceKind kind;
1750         UpDeviceState state;
1751         gboolean is_present;
1752 
1753         for (i=0; i<manager->priv->devices_array->len; i++) {
1754                 device_tmp = g_ptr_array_index (manager->priv->devices_array, i);
1755 
1756                 /* get device properties */
1757                 g_object_get (device_tmp,
1758                               "kind", &kind,
1759                               "state", &state,
1760                               "is-present", &is_present,
1761                               NULL);
1762 
1763                 /* not present */
1764                 if (!is_present)
1765                         continue;
1766 
1767                 /* not discharging */
1768                 if (state != UP_DEVICE_STATE_DISCHARGING)
1769                         continue;
1770 
1771                 /* not battery */
1772                 if (kind != UP_DEVICE_KIND_BATTERY)
1773                         continue;
1774 
1775                 /* use composite device to cope with multiple batteries */
1776                 device = g_object_ref (engine_get_composite_device (manager, device_tmp));
1777                 break;
1778         }
1779         return device;
1780 }
1781 
1782 static void
phone_device_added_cb(GpmPhone * phone,guint idx,CsdPowerManager * manager)1783 phone_device_added_cb (GpmPhone *phone, guint idx, CsdPowerManager *manager)
1784 {
1785         UpDevice *device;
1786         device = up_device_new ();
1787 
1788         g_debug ("phone added %i", idx);
1789 
1790         /* get device properties */
1791         g_object_set (device,
1792                       "kind", UP_DEVICE_KIND_PHONE,
1793                       "is-rechargeable", TRUE,
1794                       "native-path", g_strdup_printf ("dummy:phone_%i", idx),
1795                       "is-present", TRUE,
1796                       NULL);
1797 
1798         /* state changed */
1799         engine_device_add (manager, device);
1800         g_ptr_array_add (manager->priv->devices_array, g_object_ref (device));
1801         engine_recalculate_state (manager);
1802 }
1803 
1804 static void
phone_device_removed_cb(GpmPhone * phone,guint idx,CsdPowerManager * manager)1805 phone_device_removed_cb (GpmPhone *phone, guint idx, CsdPowerManager *manager)
1806 {
1807         guint i;
1808         UpDevice *device;
1809         UpDeviceKind kind;
1810 
1811         g_debug ("phone removed %i", idx);
1812 
1813         for (i=0; i<manager->priv->devices_array->len; i++) {
1814                 device = g_ptr_array_index (manager->priv->devices_array, i);
1815 
1816                 /* get device properties */
1817                 g_object_get (device,
1818                               "kind", &kind,
1819                               NULL);
1820 
1821                 if (kind == UP_DEVICE_KIND_PHONE) {
1822                         g_ptr_array_remove_index (manager->priv->devices_array, i);
1823                         break;
1824                 }
1825         }
1826 
1827         /* state changed */
1828         engine_recalculate_state (manager);
1829 }
1830 
1831 static void
phone_device_refresh_cb(GpmPhone * phone,guint idx,CsdPowerManager * manager)1832 phone_device_refresh_cb (GpmPhone *phone, guint idx, CsdPowerManager *manager)
1833 {
1834         guint i;
1835         UpDevice *device;
1836         UpDeviceKind kind;
1837         UpDeviceState state;
1838         gboolean is_present;
1839         gdouble percentage;
1840 
1841         g_debug ("phone refresh %i", idx);
1842 
1843         for (i=0; i<manager->priv->devices_array->len; i++) {
1844                 device = g_ptr_array_index (manager->priv->devices_array, i);
1845 
1846                 /* get device properties */
1847                 g_object_get (device,
1848                               "kind", &kind,
1849                               "state", &state,
1850                               "percentage", &percentage,
1851                               "is-present", &is_present,
1852                               NULL);
1853 
1854                 if (kind == UP_DEVICE_KIND_PHONE) {
1855                         is_present = gpm_phone_get_present (phone, idx);
1856                         state = gpm_phone_get_on_ac (phone, idx) ? UP_DEVICE_STATE_CHARGING : UP_DEVICE_STATE_DISCHARGING;
1857                         percentage = gpm_phone_get_percentage (phone, idx);
1858                         break;
1859                 }
1860         }
1861 
1862         /* state changed */
1863         engine_recalculate_state (manager);
1864 }
1865 
1866 static void
cinnamon_session_shutdown_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)1867 cinnamon_session_shutdown_cb (GObject *source_object,
1868                            GAsyncResult *res,
1869                            gpointer user_data)
1870 {
1871         GVariant *result;
1872         GError *error = NULL;
1873 
1874         result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object),
1875                                            res,
1876                                            &error);
1877         if (result == NULL) {
1878                 g_warning ("couldn't shutdown using cinnamon-session: %s",
1879                            error->message);
1880                 g_error_free (error);
1881         } else {
1882                 g_variant_unref (result);
1883         }
1884 }
1885 
1886 static void
cinnamon_session_shutdown(void)1887 cinnamon_session_shutdown (void)
1888 {
1889         GError *error = NULL;
1890         GDBusProxy *proxy;
1891 
1892         /* ask cinnamon-session to show the shutdown dialog with a timeout */
1893         proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
1894                                                G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
1895                                                NULL,
1896                                                GNOME_SESSION_DBUS_NAME,
1897                                                GNOME_SESSION_DBUS_PATH,
1898                                                GNOME_SESSION_DBUS_INTERFACE,
1899                                                NULL, &error);
1900         if (proxy == NULL) {
1901                 g_warning ("cannot connect to cinnamon-session: %s",
1902                            error->message);
1903                 g_error_free (error);
1904                 return;
1905         }
1906         g_dbus_proxy_call (proxy,
1907                            "Shutdown",
1908                            NULL,
1909                            G_DBUS_CALL_FLAGS_NONE,
1910                            -1, NULL,
1911                            cinnamon_session_shutdown_cb, NULL);
1912         g_object_unref (proxy);
1913 }
1914 
1915 static void
turn_monitors_off(CsdPowerManager * manager)1916 turn_monitors_off (CsdPowerManager *manager)
1917 {
1918     gboolean ret;
1919     GError *error = NULL;
1920 
1921     ret = gnome_rr_screen_set_dpms_mode (manager->priv->x11_screen,
1922                                          GNOME_RR_DPMS_OFF,
1923                                          &error);
1924     if (!ret) {
1925             g_warning ("failed to turn the panel off for policy action: %s",
1926                        error->message);
1927             g_error_free (error);
1928     }
1929 }
1930 
1931 static void
do_power_action_type(CsdPowerManager * manager,CsdPowerActionType action_type)1932 do_power_action_type (CsdPowerManager *manager,
1933                       CsdPowerActionType action_type)
1934 {
1935         switch (action_type) {
1936         case CSD_POWER_ACTION_SUSPEND:
1937                 if (should_lock_on_suspend (manager)) {
1938                         lock_screensaver (manager);
1939                 }
1940 
1941                 turn_monitors_off (manager);
1942 
1943                 gboolean hybrid = g_settings_get_boolean (manager->priv->settings_cinnamon_session,
1944                                                           "prefer-hybrid-sleep");
1945                 gboolean suspend_then_hibernate = g_settings_get_boolean (manager->priv->settings_cinnamon_session,
1946                                                           "suspend-then-hibernate");
1947 
1948                 csd_power_suspend (hybrid, suspend_then_hibernate);
1949                 break;
1950         case CSD_POWER_ACTION_INTERACTIVE:
1951                 cinnamon_session_shutdown ();
1952                 break;
1953         case CSD_POWER_ACTION_HIBERNATE:
1954                 if (should_lock_on_suspend (manager)) {
1955                         lock_screensaver (manager);
1956                 }
1957 
1958                 turn_monitors_off (manager);
1959                 csd_power_hibernate ();
1960                 break;
1961         case CSD_POWER_ACTION_SHUTDOWN:
1962                 /* this is only used on critically low battery where
1963                  * hibernate is not available and is marginally better
1964                  * than just powering down the computer mid-write */
1965                 csd_power_poweroff ();
1966                 break;
1967         case CSD_POWER_ACTION_BLANK:
1968                 /* Lock first or else xrandr might reconfigure stuff and the ss's coverage
1969                  * may be incorrect upon return. */
1970                 lock_screensaver (manager);
1971                 turn_monitors_off (manager);
1972                 break;
1973         case CSD_POWER_ACTION_NOTHING:
1974                 break;
1975         }
1976 }
1977 
1978 static gboolean
upower_kbd_get_percentage(CsdPowerManager * manager,GError ** error)1979 upower_kbd_get_percentage (CsdPowerManager *manager, GError **error)
1980 {
1981         GVariant *k_now = NULL;
1982 
1983         k_now = g_dbus_proxy_call_sync (manager->priv->upower_kbd_proxy,
1984                                         "GetBrightness",
1985                                         NULL,
1986                                         G_DBUS_CALL_FLAGS_NONE,
1987                                         -1,
1988                                         NULL,
1989                                         error);
1990         if (k_now != NULL) {
1991                 g_variant_get (k_now, "(i)", &manager->priv->kbd_brightness_now);
1992                 g_variant_unref (k_now);
1993                 return TRUE;
1994         }
1995 
1996         return FALSE;
1997 }
1998 
1999 static void
upower_kbd_emit_changed(CsdPowerManager * manager)2000 upower_kbd_emit_changed (CsdPowerManager *manager)
2001 {
2002         /* not yet connected to the bus */
2003         if (manager->priv->keyboard_iface == NULL)
2004                 return;
2005 
2006         csd_keyboard_emit_changed (manager->priv->keyboard_iface);
2007 }
2008 
2009 static gboolean
upower_kbd_set_brightness(CsdPowerManager * manager,guint value,GError ** error)2010 upower_kbd_set_brightness (CsdPowerManager *manager, guint value, GError **error)
2011 {
2012         GVariant *retval;
2013 
2014         /* same as before */
2015         if (manager->priv->kbd_brightness_now == value)
2016                 return TRUE;
2017 
2018         /* update h/w value */
2019         retval = g_dbus_proxy_call_sync (manager->priv->upower_kbd_proxy,
2020                                          "SetBrightness",
2021                                          g_variant_new ("(i)", (gint) value),
2022                                          G_DBUS_CALL_FLAGS_NONE,
2023                                          -1,
2024                                          NULL,
2025                                          error);
2026         if (retval == NULL)
2027                 return FALSE;
2028 
2029         /* save new value */
2030         manager->priv->kbd_brightness_now = value;
2031         g_variant_unref (retval);
2032         upower_kbd_emit_changed(manager);
2033         return TRUE;
2034 }
2035 
2036 static gboolean
upower_kbd_toggle(CsdPowerManager * manager,GError ** error)2037 upower_kbd_toggle (CsdPowerManager *manager,
2038                    GError **error)
2039 {
2040         gboolean ret;
2041 
2042         if (manager->priv->kbd_brightness_old >= 0) {
2043                 g_debug ("keyboard toggle off");
2044                 ret = upower_kbd_set_brightness (manager,
2045                                                  manager->priv->kbd_brightness_old,
2046                                                  error);
2047                 if (ret) {
2048                         /* succeeded, set to -1 since now no old value */
2049                         manager->priv->kbd_brightness_old = -1;
2050                 }
2051         } else {
2052                 g_debug ("keyboard toggle on");
2053                 /* save the current value to restore later when untoggling */
2054                 manager->priv->kbd_brightness_old = manager->priv->kbd_brightness_now;
2055                 ret = upower_kbd_set_brightness (manager, 0, error);
2056                 if (!ret) {
2057                         /* failed, reset back to -1 */
2058                         manager->priv->kbd_brightness_old = -1;
2059                 }
2060         }
2061 
2062         upower_kbd_emit_changed(manager);
2063         return ret;
2064 }
2065 
2066 static void
upower_kbd_handle_changed(GDBusProxy * proxy,gchar * sender_name,gchar * signal_name,GVariant * parameters,gpointer user_data)2067 upower_kbd_handle_changed (GDBusProxy *proxy,
2068                            gchar      *sender_name,
2069                            gchar      *signal_name,
2070                            GVariant   *parameters,
2071                            gpointer    user_data)
2072 {
2073         CsdPowerManager *manager = CSD_POWER_MANAGER (user_data);
2074 
2075         g_debug("keyboard changed signal");
2076 
2077         if (g_strcmp0 (signal_name, "BrightnessChangedWithSource") == 0) {
2078                 g_debug ("Received upower kbdbacklight BrightnessChangedWithSource");
2079                 const gchar *source;
2080                 gint brightness;
2081 
2082                 g_variant_get (parameters, "(i&s)", &brightness, &source);
2083 
2084                 if (g_strcmp0 (source, "external") == 0) {
2085                     return;
2086                 }
2087 
2088                 manager->priv->kbd_brightness_now = brightness;
2089                 upower_kbd_emit_changed(manager);
2090         }
2091 
2092 }
2093 
2094 static gboolean
suspend_on_lid_close(CsdPowerManager * manager)2095 suspend_on_lid_close (CsdPowerManager *manager)
2096 {
2097         CsdXrandrBootBehaviour val;
2098 
2099         if (!external_monitor_is_connected (manager->priv->x11_screen))
2100                 return TRUE;
2101 
2102         val = g_settings_get_enum (manager->priv->settings_xrandr, "default-monitors-setup");
2103         return val == CSD_XRANDR_BOOT_BEHAVIOUR_DO_NOTHING;
2104 }
2105 
2106 static gboolean
inhibit_lid_switch_timer_cb(CsdPowerManager * manager)2107 inhibit_lid_switch_timer_cb (CsdPowerManager *manager)
2108 {
2109         if (suspend_on_lid_close (manager)) {
2110                 g_debug ("no external monitors for a while; uninhibiting lid close");
2111                 uninhibit_lid_switch (manager);
2112                 manager->priv->inhibit_lid_switch_timer_id = 0;
2113                 return G_SOURCE_REMOVE;
2114         }
2115 
2116         g_debug ("external monitor still there; trying again later");
2117         return G_SOURCE_CONTINUE;
2118 }
2119 
2120 /* Sets up a timer to be triggered some seconds after closing the laptop lid
2121  * when the laptop is *not* suspended for some reason.  We'll check conditions
2122  * again in the timeout handler to see if we can suspend then.
2123  */
2124 static void
setup_inhibit_lid_switch_timer(CsdPowerManager * manager)2125 setup_inhibit_lid_switch_timer (CsdPowerManager *manager)
2126 {
2127         if (manager->priv->inhibit_lid_switch_timer_id != 0) {
2128                 g_debug ("lid close safety timer already set up");
2129                 return;
2130         }
2131 
2132         g_debug ("setting up lid close safety timer");
2133 
2134         manager->priv->inhibit_lid_switch_timer_id = g_timeout_add_seconds (CSD_POWER_MANAGER_LID_CLOSE_SAFETY_TIMEOUT,
2135                                                                           (GSourceFunc) inhibit_lid_switch_timer_cb,
2136                                                                           manager);
2137         g_source_set_name_by_id (manager->priv->inhibit_lid_switch_timer_id, "[CsdPowerManager] lid close safety timer");
2138 }
2139 
2140 static void
restart_inhibit_lid_switch_timer(CsdPowerManager * manager)2141 restart_inhibit_lid_switch_timer (CsdPowerManager *manager)
2142 {
2143         if (manager->priv->inhibit_lid_switch_timer_id != 0) {
2144                 g_debug ("restarting lid close safety timer");
2145                 g_source_remove (manager->priv->inhibit_lid_switch_timer_id);
2146                 manager->priv->inhibit_lid_switch_timer_id = 0;
2147                 setup_inhibit_lid_switch_timer (manager);
2148         }
2149 }
2150 
2151 
2152 static gboolean
randr_output_is_on(GnomeRROutput * output)2153 randr_output_is_on (GnomeRROutput *output)
2154 {
2155         GnomeRRCrtc *crtc;
2156 
2157         crtc = gnome_rr_output_get_crtc (output);
2158         if (!crtc)
2159                 return FALSE;
2160         return gnome_rr_crtc_get_current_mode (crtc) != NULL;
2161 }
2162 
2163 static gboolean
external_monitor_is_connected(GnomeRRScreen * screen)2164 external_monitor_is_connected (GnomeRRScreen *screen)
2165 {
2166         GnomeRROutput **outputs;
2167         guint i;
2168 
2169         /* see if we have more than one screen plugged in */
2170         outputs = gnome_rr_screen_list_outputs (screen);
2171         for (i = 0; outputs[i] != NULL; i++) {
2172                 if (randr_output_is_on (outputs[i]) &&
2173                     !gnome_rr_output_is_laptop (outputs[i]))
2174                         return TRUE;
2175         }
2176 
2177         return FALSE;
2178 }
2179 
2180 static void
on_randr_event(GnomeRRScreen * screen,gpointer user_data)2181 on_randr_event (GnomeRRScreen *screen, gpointer user_data)
2182 {
2183         CsdPowerManager *manager = CSD_POWER_MANAGER (user_data);
2184 
2185         if (suspend_on_lid_close (manager)) {
2186                 restart_inhibit_lid_switch_timer (manager);
2187                 return;
2188         }
2189 
2190         /* when a second monitor is plugged in, we take the
2191         * handle-lid-switch inhibitor lock of logind to prevent
2192         * it from suspending.
2193         *
2194         * Uninhibiting is done in the inhibit_lid_switch_timer,
2195         * since we want to give users a few seconds when unplugging
2196         * and replugging an external monitor, not suspend right away.
2197         */
2198         inhibit_lid_switch (manager);
2199         setup_inhibit_lid_switch_timer (manager);
2200 }
2201 
2202 static void
do_lid_open_action(CsdPowerManager * manager)2203 do_lid_open_action (CsdPowerManager *manager)
2204 {
2205         gboolean ret;
2206         GError *error = NULL;
2207 
2208         /* play a sound, using sounds from the naming spec */
2209         ca_context_play (manager->priv->canberra_context, 0,
2210                          CA_PROP_EVENT_ID, "lid-open",
2211                          /* TRANSLATORS: this is the sound description */
2212                          CA_PROP_EVENT_DESCRIPTION, _("Lid has been opened"),
2213                          NULL);
2214 
2215         /* ensure we turn the panel back on after lid open */
2216         ret = gnome_rr_screen_set_dpms_mode (manager->priv->x11_screen,
2217                                              GNOME_RR_DPMS_ON,
2218                                              &error);
2219         if (!ret) {
2220                 g_warning ("failed to turn the panel on after lid open: %s",
2221                            error->message);
2222                 g_clear_error (&error);
2223         }
2224 
2225         /* only toggle keyboard if present and already toggled off */
2226         if (manager->priv->upower_kbd_proxy != NULL &&
2227             manager->priv->kbd_brightness_old != -1) {
2228                 ret = upower_kbd_toggle (manager, &error);
2229                 if (!ret) {
2230                         g_warning ("failed to turn the kbd backlight on: %s",
2231                                    error->message);
2232                         g_error_free (error);
2233                 }
2234         }
2235 
2236         kill_lid_close_safety_timer (manager);
2237 }
2238 
2239 static gboolean
is_on(GnomeRROutput * output)2240 is_on (GnomeRROutput *output)
2241 {
2242 	GnomeRRCrtc *crtc;
2243 
2244 	crtc = gnome_rr_output_get_crtc (output);
2245 	if (!crtc)
2246 		return FALSE;
2247 	return gnome_rr_crtc_get_current_mode (crtc) != NULL;
2248 }
2249 
2250 static gboolean
non_laptop_outputs_are_all_off(GnomeRRScreen * screen)2251 non_laptop_outputs_are_all_off (GnomeRRScreen *screen)
2252 {
2253         GnomeRROutput **outputs;
2254         int i;
2255 
2256         outputs = gnome_rr_screen_list_outputs (screen);
2257         for (i = 0; outputs[i] != NULL; i++) {
2258                 if (gnome_rr_output_is_laptop (outputs[i]))
2259                         continue;
2260 
2261                 if (is_on (outputs[i]))
2262                         return FALSE;
2263         }
2264 
2265         return TRUE;
2266 }
2267 
2268 /* Timeout callback used to check conditions when the laptop's lid is closed but
2269  * the machine is not suspended yet.  We try to suspend again, so that the laptop
2270  * won't overheat if placed in a backpack.
2271  */
2272 static gboolean
lid_close_safety_timer_cb(CsdPowerManager * manager)2273 lid_close_safety_timer_cb (CsdPowerManager *manager)
2274 {
2275         manager->priv->lid_close_safety_timer_id = 0;
2276 
2277         g_debug ("lid has been closed for a while; trying to suspend again");
2278         do_lid_closed_action (manager);
2279 
2280         return FALSE;
2281 }
2282 
2283 /* Sets up a timer to be triggered some seconds after closing the laptop lid
2284  * when the laptop is *not* suspended for some reason.  We'll check conditions
2285  * again in the timeout handler to see if we can suspend then.
2286  */
2287 static void
setup_lid_close_safety_timer(CsdPowerManager * manager)2288 setup_lid_close_safety_timer (CsdPowerManager *manager)
2289 {
2290         if (manager->priv->lid_close_safety_timer_id != 0)
2291                 return;
2292 
2293         manager->priv->lid_close_safety_timer_id = g_timeout_add_seconds (CSD_POWER_MANAGER_LID_CLOSE_SAFETY_TIMEOUT,
2294                                                                           (GSourceFunc) lid_close_safety_timer_cb,
2295                                                                           manager);
2296         g_source_set_name_by_id (manager->priv->lid_close_safety_timer_id, "[CsdPowerManager] lid close safety timer");
2297 }
2298 
2299 static void
kill_lid_close_safety_timer(CsdPowerManager * manager)2300 kill_lid_close_safety_timer (CsdPowerManager *manager)
2301 {
2302         if (manager->priv->lid_close_safety_timer_id != 0) {
2303                 g_source_remove (manager->priv->lid_close_safety_timer_id);
2304                 manager->priv->lid_close_safety_timer_id = 0;
2305         }
2306 }
2307 
2308 static void
suspend_with_lid_closed(CsdPowerManager * manager)2309 suspend_with_lid_closed (CsdPowerManager *manager)
2310 {
2311         gboolean ret;
2312         GError *error = NULL;
2313         CsdPowerActionType action_type;
2314 
2315         /* we have different settings depending on AC state */
2316         if (up_client_get_on_battery (manager->priv->up_client)) {
2317                 action_type = g_settings_get_enum (manager->priv->settings,
2318                                                    "lid-close-battery-action");
2319         } else {
2320                 action_type = g_settings_get_enum (manager->priv->settings,
2321                                                    "lid-close-ac-action");
2322         }
2323 
2324         /* check we won't melt when the lid is closed */
2325         if (action_type != CSD_POWER_ACTION_SUSPEND &&
2326             action_type != CSD_POWER_ACTION_HIBERNATE) {
2327 #if ! UP_CHECK_VERSION(0,99,0)
2328                 if (up_client_get_lid_force_sleep (manager->priv->up_client)) {
2329                         g_warning ("to prevent damage, now forcing suspend");
2330                         do_power_action_type (manager, CSD_POWER_ACTION_SUSPEND);
2331                         return;
2332                 }
2333 #endif
2334         }
2335 
2336         /* only toggle keyboard if present and not already toggled */
2337         if (manager->priv->upower_kbd_proxy &&
2338             manager->priv->kbd_brightness_old == -1) {
2339                 ret = upower_kbd_toggle (manager, &error);
2340                 if (!ret) {
2341                         g_warning ("failed to turn the kbd backlight off: %s",
2342                                    error->message);
2343                         g_error_free (error);
2344                 }
2345         }
2346 
2347         do_power_action_type (manager, action_type);
2348 }
2349 
2350 static void
do_lid_closed_action(CsdPowerManager * manager)2351 do_lid_closed_action (CsdPowerManager *manager)
2352 {
2353         /* play a sound, using sounds from the naming spec */
2354         ca_context_play (manager->priv->canberra_context, 0,
2355                          CA_PROP_EVENT_ID, "lid-close",
2356                          /* TRANSLATORS: this is the sound description */
2357                          CA_PROP_EVENT_DESCRIPTION, _("Lid has been closed"),
2358                          NULL);
2359 
2360         /* refresh RANDR so we get an accurate view of what monitors are plugged in when the lid is closed */
2361         gnome_rr_screen_refresh (manager->priv->x11_screen, NULL); /* NULL-GError */
2362 
2363         /* perform policy action */
2364         if (g_settings_get_boolean (manager->priv->settings, "lid-close-suspend-with-external-monitor")
2365             || non_laptop_outputs_are_all_off (manager->priv->x11_screen)) {
2366                 g_debug ("lid is closed; suspending or hibernating");
2367                 suspend_with_lid_closed (manager);
2368         } else {
2369                 g_debug ("lid is closed; not suspending nor hibernating since some external monitor outputs are still active");
2370                 setup_lid_close_safety_timer (manager);
2371         }
2372 }
2373 
2374 
2375 static void
2376 #if UP_CHECK_VERSION(0,99,0)
lid_state_changed_cb(UpClient * client,GParamSpec * pspec,CsdPowerManager * manager)2377 lid_state_changed_cb (UpClient *client, GParamSpec *pspec, CsdPowerManager *manager)
2378 #else
2379 up_client_changed_cb (UpClient *client, CsdPowerManager *manager)
2380 #endif
2381 {
2382         gboolean lid_is_closed;
2383         gboolean on_battery;
2384 
2385         on_battery = up_client_get_on_battery(client);
2386         if (!on_battery) {
2387             /* if we are playing a critical charge sound loop on AC, stop it */
2388             if (manager->priv->critical_alert_timeout_id > 0) {
2389                  g_debug ("stopping alert loop due to ac being present");
2390                  play_loop_stop (manager);
2391             }
2392             notify_close_if_showing (manager->priv->notification_low);
2393         }
2394 
2395         /* same state */
2396         lid_is_closed = up_client_get_lid_is_closed (manager->priv->up_client);
2397 
2398         if (manager->priv->lid_is_closed == lid_is_closed &&
2399                 manager->priv->on_battery == on_battery)
2400                 return;
2401 
2402         manager->priv->lid_is_closed = lid_is_closed;
2403         manager->priv->on_battery = on_battery;
2404 
2405         /* fake a keypress */
2406         if (lid_is_closed)
2407                 do_lid_closed_action (manager);
2408         else
2409                 do_lid_open_action (manager);
2410 }
2411 
2412 typedef enum {
2413         SESSION_STATUS_CODE_AVAILABLE = 0,
2414         SESSION_STATUS_CODE_INVISIBLE,
2415         SESSION_STATUS_CODE_BUSY,
2416         SESSION_STATUS_CODE_IDLE,
2417         SESSION_STATUS_CODE_UNKNOWN
2418 } SessionStatusCode;
2419 
2420 typedef enum {
2421         SESSION_INHIBIT_MASK_LOGOUT = 1,
2422         SESSION_INHIBIT_MASK_SWITCH = 2,
2423         SESSION_INHIBIT_MASK_SUSPEND = 4,
2424         SESSION_INHIBIT_MASK_IDLE = 8
2425 } SessionInhibitMask;
2426 
2427 static const gchar *
idle_mode_to_string(CsdPowerIdleMode mode)2428 idle_mode_to_string (CsdPowerIdleMode mode)
2429 {
2430         if (mode == CSD_POWER_IDLE_MODE_NORMAL)
2431                 return "normal";
2432         if (mode == CSD_POWER_IDLE_MODE_DIM)
2433                 return "dim";
2434         if (mode == CSD_POWER_IDLE_MODE_BLANK)
2435                 return "blank";
2436         if (mode == CSD_POWER_IDLE_MODE_SLEEP)
2437                 return "sleep";
2438         return "unknown";
2439 }
2440 
2441 static GnomeRROutput *
get_primary_output(CsdPowerManager * manager)2442 get_primary_output (CsdPowerManager *manager)
2443 {
2444         GnomeRROutput *output = NULL;
2445         GnomeRROutput **outputs;
2446         guint i;
2447 
2448         /* search all X11 outputs for the device id */
2449         outputs = gnome_rr_screen_list_outputs (manager->priv->x11_screen);
2450         if (outputs == NULL)
2451                 goto out;
2452 
2453         for (i = 0; outputs[i] != NULL; i++) {
2454                 if (gnome_rr_output_is_connected (outputs[i]) &&
2455                     gnome_rr_output_is_laptop (outputs[i]) &&
2456                     gnome_rr_output_get_backlight_min (outputs[i]) >= 0 &&
2457                     gnome_rr_output_get_backlight_max (outputs[i]) > 0) {
2458                         output = outputs[i];
2459                         break;
2460                 }
2461         }
2462 out:
2463         return output;
2464 }
2465 
2466 static void
backlight_override_settings_refresh(CsdPowerManager * manager)2467 backlight_override_settings_refresh (CsdPowerManager *manager)
2468 {
2469         int i = 0;
2470         /* update all the stored backlight override properties
2471          * this is called on startup and by engine_settings_key_changed_cb */
2472         manager->priv->backlight_helper_force = g_settings_get_boolean
2473                         (manager->priv->settings, "backlight-helper-force");
2474 
2475         /* concatenate all the search preferences into a single argument string */
2476         gchar** backlight_preference_order = g_settings_get_strv
2477                 (manager->priv->settings, "backlight-helper-preference-order");
2478 
2479         gchar* tmp1 = NULL;
2480         gchar* tmp2 = NULL;
2481 
2482         if (backlight_preference_order[0] != NULL) {
2483                 tmp1 = g_strdup_printf("-b %s", backlight_preference_order[0]);
2484         }
2485 
2486         for (i=1; backlight_preference_order[i] != NULL; i++ )
2487         {
2488                 tmp2 = tmp1;
2489                 tmp1 = g_strdup_printf("%s -b %s", tmp2,
2490                                 backlight_preference_order[i]);
2491                 g_free(tmp2);
2492         }
2493 
2494         tmp2 = manager->priv->backlight_helper_preference_args;
2495         manager->priv->backlight_helper_preference_args = tmp1;
2496         g_free(tmp2);
2497         tmp2 = NULL;
2498 
2499         g_free(backlight_preference_order);
2500         backlight_preference_order = NULL;
2501 }
2502 
2503 /**
2504  * backlight_helper_get_value:
2505  *
2506  * Gets a brightness value from the PolicyKit helper.
2507  *
2508  * Return value: the signed integer value from the helper, or -1
2509  * for failure. If -1 then @error is set.
2510  **/
2511 static gint64
backlight_helper_get_value(const gchar * argument,CsdPowerManager * manager,GError ** error)2512 backlight_helper_get_value (const gchar *argument, CsdPowerManager* manager,
2513                 GError **error)
2514 {
2515         gboolean ret;
2516         gchar *stdout_data = NULL;
2517         gint exit_status = 0;
2518         gint64 value = -1;
2519         gchar *command = NULL;
2520         gchar *endptr = NULL;
2521 
2522 #ifndef __linux__
2523         /* non-Linux platforms won't have /sys/class/backlight */
2524         g_set_error_literal (error,
2525                              CSD_POWER_MANAGER_ERROR,
2526                              CSD_POWER_MANAGER_ERROR_FAILED,
2527                              "The sysfs backlight helper is only for Linux");
2528         goto out;
2529 #endif
2530 
2531         /* get the data */
2532         command = g_strdup_printf (LIBEXECDIR "/csd-backlight-helper --%s %s",
2533                                    argument,
2534                                    manager->priv->backlight_helper_preference_args);
2535         ret = g_spawn_command_line_sync (command,
2536                                          &stdout_data,
2537                                          NULL,
2538                                          &exit_status,
2539                                          error);
2540         g_debug ("executed %s retval: %i", command, exit_status);
2541 
2542         if (!ret)
2543                 goto out;
2544 
2545         if (WEXITSTATUS (exit_status) != 0) {
2546                  g_set_error (error,
2547                              CSD_POWER_MANAGER_ERROR,
2548                              CSD_POWER_MANAGER_ERROR_FAILED,
2549                              "csd-backlight-helper failed: %s",
2550                              stdout_data ? stdout_data : "No reason");
2551                 goto out;
2552         }
2553 
2554         /* parse */
2555         value = g_ascii_strtoll (stdout_data, &endptr, 10);
2556 
2557         /* parsing error */
2558         if (endptr == stdout_data) {
2559                 value = -1;
2560                 g_set_error (error,
2561                              CSD_POWER_MANAGER_ERROR,
2562                              CSD_POWER_MANAGER_ERROR_FAILED,
2563                              "failed to parse value: %s",
2564                              stdout_data);
2565                 goto out;
2566         }
2567 
2568         /* out of range */
2569         if (value > G_MAXINT) {
2570                 value = -1;
2571                 g_set_error (error,
2572                              CSD_POWER_MANAGER_ERROR,
2573                              CSD_POWER_MANAGER_ERROR_FAILED,
2574                              "value out of range: %s",
2575                              stdout_data);
2576                 goto out;
2577         }
2578 
2579         /* Fetching the value failed, for some other reason */
2580         if (value < 0) {
2581                 g_set_error (error,
2582                              CSD_POWER_MANAGER_ERROR,
2583                              CSD_POWER_MANAGER_ERROR_FAILED,
2584                              "value negative, but helper did not fail: %s",
2585                              stdout_data);
2586                 goto out;
2587         }
2588 
2589 out:
2590         g_free (command);
2591         g_free (stdout_data);
2592         return value;
2593 }
2594 
2595 /**
2596  * backlight_helper_set_value:
2597  *
2598  * Sets a brightness value using the PolicyKit helper.
2599  *
2600  * Return value: Success. If FALSE then @error is set.
2601  **/
2602 static gboolean
backlight_helper_set_value(const gchar * argument,gint value,CsdPowerManager * manager,GError ** error)2603 backlight_helper_set_value (const gchar *argument,
2604                             gint value,
2605                             CsdPowerManager* manager,
2606                             GError **error)
2607 {
2608         gboolean ret;
2609         gint exit_status = 0;
2610         gchar *command = NULL;
2611 
2612 #ifndef __linux__
2613         /* non-Linux platforms won't have /sys/class/backlight */
2614         g_set_error_literal (error,
2615                              CSD_POWER_MANAGER_ERROR,
2616                              CSD_POWER_MANAGER_ERROR_FAILED,
2617                              "The sysfs backlight helper is only for Linux");
2618         goto out;
2619 #endif
2620 
2621         /* get the data */
2622         command = g_strdup_printf ("pkexec " LIBEXECDIR "/csd-backlight-helper --%s %i %s",
2623                                    argument, value,
2624                                    manager->priv->backlight_helper_preference_args);
2625         ret = g_spawn_command_line_sync (command,
2626                                          NULL,
2627                                          NULL,
2628                                          &exit_status,
2629                                          error);
2630 
2631         g_debug ("executed %s retval: %i", command, exit_status);
2632 
2633         if (!ret || WEXITSTATUS (exit_status) != 0)
2634                 goto out;
2635 
2636 out:
2637         g_free (command);
2638         return ret;
2639 }
2640 
2641 int
backlight_get_output_id(CsdPowerManager * manager)2642 backlight_get_output_id (CsdPowerManager *manager)
2643 {
2644         GnomeRROutput *output = NULL;
2645         GnomeRROutput **outputs;
2646         GnomeRRCrtc *crtc;
2647         GdkScreen *gdk_screen;
2648         gint x, y;
2649         guint i;
2650 
2651         outputs = gnome_rr_screen_list_outputs (manager->priv->x11_screen);
2652         if (outputs == NULL)
2653                 return -1;
2654 
2655         for (i = 0; outputs[i] != NULL; i++) {
2656                 if (gnome_rr_output_is_connected (outputs[i]) &&
2657                     gnome_rr_output_is_laptop (outputs[i])) {
2658                         output = outputs[i];
2659                         break;
2660                 }
2661         }
2662 
2663         if (output == NULL)
2664                 return -1;
2665 
2666         crtc = gnome_rr_output_get_crtc (output);
2667         if (crtc == NULL)
2668                 return -1;
2669 
2670         gdk_screen = gdk_screen_get_default ();
2671         gnome_rr_crtc_get_position (crtc, &x, &y);
2672 
2673         return gdk_screen_get_monitor_at_point (gdk_screen, x, y);
2674 }
2675 
2676 static gint
backlight_get_abs(CsdPowerManager * manager,GError ** error)2677 backlight_get_abs (CsdPowerManager *manager, GError **error)
2678 {
2679         GnomeRROutput *output;
2680 
2681         /* prioritize user override settings */
2682         if (!manager->priv->backlight_helper_force)
2683         {
2684                 /* prefer xbacklight */
2685                 output = get_primary_output (manager);
2686                 if (output != NULL) {
2687                         return gnome_rr_output_get_backlight (output,
2688                                                               error);
2689                 }
2690         }
2691 
2692         /* fall back to the polkit helper */
2693         return backlight_helper_get_value ("get-brightness", manager, error);
2694 }
2695 
2696 static gint
backlight_get_percentage(CsdPowerManager * manager,GError ** error)2697 backlight_get_percentage (CsdPowerManager *manager, GError **error)
2698 {
2699         GnomeRROutput *output;
2700         gint now;
2701         gint value = -1;
2702         gint min = 0;
2703         gint max;
2704 
2705         /* prioritize user override settings */
2706         if (!manager->priv->backlight_helper_force)
2707         {
2708                 /* prefer xbacklight */
2709                 output = get_primary_output (manager);
2710                 if (output != NULL) {
2711 
2712                         min = gnome_rr_output_get_backlight_min (output);
2713                         max = gnome_rr_output_get_backlight_max (output);
2714                         now = gnome_rr_output_get_backlight (output, error);
2715                         if (now < 0)
2716                                 goto out;
2717                         value = ABS_TO_PERCENTAGE (min, max, now);
2718                         goto out;
2719                 }
2720         }
2721 
2722         /* fall back to the polkit helper */
2723         max = backlight_helper_get_value ("get-max-brightness", manager, error);
2724         if (max < 0)
2725                 goto out;
2726         now = backlight_helper_get_value ("get-brightness", manager, error);
2727         if (now < 0)
2728                 goto out;
2729         value = ABS_TO_PERCENTAGE (min, max, now);
2730 out:
2731         return value;
2732 }
2733 
2734 static gint
backlight_get_min(CsdPowerManager * manager)2735 backlight_get_min (CsdPowerManager *manager)
2736 {
2737         /* if we have no xbacklight device, then hardcode zero as sysfs
2738         * offsets everything to 0 as min */
2739 
2740         /* user override means we will be using sysfs */
2741         if (manager->priv->backlight_helper_force)
2742                 return 0;
2743 
2744         GnomeRROutput *output;
2745 
2746         output = get_primary_output (manager);
2747         if (output == NULL)
2748                 return 0;
2749 
2750         /* get xbacklight value, which maybe non-zero */
2751         return gnome_rr_output_get_backlight_min (output);
2752 }
2753 
2754 static gint
backlight_get_max(CsdPowerManager * manager,GError ** error)2755 backlight_get_max (CsdPowerManager *manager, GError **error)
2756 {
2757         gint value;
2758         GnomeRROutput *output;
2759 
2760         /* prioritize user override settings */
2761         if (!manager->priv->backlight_helper_force)
2762         {
2763                 /* prefer xbacklight */
2764                 output = get_primary_output (manager);
2765                 if (output != NULL) {
2766                         value = gnome_rr_output_get_backlight_max (output);
2767                         if (value < 0) {
2768                                 g_set_error (error,
2769                                              CSD_POWER_MANAGER_ERROR,
2770                                              CSD_POWER_MANAGER_ERROR_FAILED,
2771                                              "failed to get backlight max");
2772                         }
2773                         return value;
2774                 }
2775         }
2776 
2777         /* fall back to the polkit helper */
2778         return  backlight_helper_get_value ("get-max-brightness", manager, error);
2779 }
2780 
2781 static void
backlight_emit_changed(CsdPowerManager * manager)2782 backlight_emit_changed (CsdPowerManager *manager)
2783 {
2784         /* not yet connected to the bus */
2785         if (manager->priv->screen_iface == NULL)
2786                 return;
2787 
2788         csd_screen_emit_changed (manager->priv->screen_iface);
2789 }
2790 
2791 static gboolean
backlight_set_percentage(CsdPowerManager * manager,guint value,gboolean emit_changed,GError ** error)2792 backlight_set_percentage (CsdPowerManager *manager,
2793                           guint value,
2794                           gboolean emit_changed,
2795                           GError **error)
2796 {
2797         GnomeRROutput *output;
2798         gboolean ret = FALSE;
2799         gint min = 0;
2800         gint max;
2801         guint discrete;
2802 
2803         /* prioritize user override settings */
2804         if (!manager->priv->backlight_helper_force)
2805         {
2806                 /* prefer xbacklight */
2807                 output = get_primary_output (manager);
2808                 if (output != NULL) {
2809                         min = gnome_rr_output_get_backlight_min (output);
2810                         max = gnome_rr_output_get_backlight_max (output);
2811                         if (min < 0 || max < 0) {
2812                                 g_warning ("no xrandr backlight capability");
2813                                 goto out;
2814                         }
2815                         discrete = PERCENTAGE_TO_ABS (min, max, value);
2816                         ret = gnome_rr_output_set_backlight (output,
2817                                                              discrete,
2818                                                              error);
2819                         goto out;
2820                 }
2821         }
2822 
2823         /* fall back to the polkit helper */
2824         max = backlight_helper_get_value ("get-max-brightness", manager, error);
2825         if (max < 0)
2826                 goto out;
2827         discrete = PERCENTAGE_TO_ABS (min, max, value);
2828         ret = backlight_helper_set_value ("set-brightness",
2829                                           discrete,
2830                                           manager,
2831                                           error);
2832 out:
2833         if (ret && emit_changed)
2834                 backlight_emit_changed (manager);
2835         return ret;
2836 }
2837 
2838 static gint
backlight_step_up(CsdPowerManager * manager,GError ** error)2839 backlight_step_up (CsdPowerManager *manager, GError **error)
2840 {
2841         GnomeRROutput *output;
2842         gboolean ret = FALSE;
2843         gint percentage_value = -1;
2844         gint min = 0;
2845         gint max;
2846         gint now;
2847         gint step;
2848         guint discrete;
2849         GnomeRRCrtc *crtc;
2850 
2851         /* prioritize user override settings */
2852         if (!manager->priv->backlight_helper_force)
2853         {
2854                 /* prefer xbacklight */
2855                 output = get_primary_output (manager);
2856                 if (output != NULL) {
2857 
2858                         crtc = gnome_rr_output_get_crtc (output);
2859                         if (crtc == NULL) {
2860                                 g_set_error (error,
2861                                              CSD_POWER_MANAGER_ERROR,
2862                                              CSD_POWER_MANAGER_ERROR_FAILED,
2863                                              "no crtc for %s",
2864                                              gnome_rr_output_get_name (output));
2865                                 goto out;
2866                         }
2867                         min = gnome_rr_output_get_backlight_min (output);
2868                         max = gnome_rr_output_get_backlight_max (output);
2869                         now = gnome_rr_output_get_backlight (output, error);
2870                         if (now < 0)
2871                                goto out;
2872                         step = BRIGHTNESS_STEP_AMOUNT (max - min + 1);
2873                         discrete = MIN (now + step, max);
2874                         ret = gnome_rr_output_set_backlight (output,
2875                                                              discrete,
2876                                                              error);
2877                         if (ret)
2878                                 percentage_value = ABS_TO_PERCENTAGE (min, max, discrete);
2879                         goto out;
2880                 }
2881         }
2882 
2883         /* fall back to the polkit helper */
2884         now = backlight_helper_get_value ("get-brightness", manager, error);
2885         if (now < 0)
2886                 goto out;
2887         max = backlight_helper_get_value ("get-max-brightness", manager, error);
2888         if (max < 0)
2889                 goto out;
2890         step = BRIGHTNESS_STEP_AMOUNT (max - min + 1);
2891         discrete = MIN (now + step, max);
2892         ret = backlight_helper_set_value ("set-brightness",
2893                                           discrete,
2894                                           manager,
2895                                           error);
2896         if (ret)
2897                 percentage_value = ABS_TO_PERCENTAGE (min, max, discrete);
2898 out:
2899         if (ret)
2900                 backlight_emit_changed (manager);
2901         return percentage_value;
2902 }
2903 
2904 static gint
backlight_step_down(CsdPowerManager * manager,GError ** error)2905 backlight_step_down (CsdPowerManager *manager, GError **error)
2906 {
2907         GnomeRROutput *output;
2908         gboolean ret = FALSE;
2909         gint percentage_value = -1;
2910         gint min = 0;
2911         gint max;
2912         gint now;
2913         gint step;
2914         guint discrete;
2915         GnomeRRCrtc *crtc;
2916 
2917         /* prioritize user override settings */
2918         if (!manager->priv->backlight_helper_force)
2919         {
2920                 /* prefer xbacklight */
2921                 output = get_primary_output (manager);
2922                 if (output != NULL) {
2923 
2924                         crtc = gnome_rr_output_get_crtc (output);
2925                         if (crtc == NULL) {
2926                                 g_set_error (error,
2927                                              CSD_POWER_MANAGER_ERROR,
2928                                              CSD_POWER_MANAGER_ERROR_FAILED,
2929                                              "no crtc for %s",
2930                                              gnome_rr_output_get_name (output));
2931                                 goto out;
2932                         }
2933                         min = gnome_rr_output_get_backlight_min (output);
2934                         max = gnome_rr_output_get_backlight_max (output);
2935                         now = gnome_rr_output_get_backlight (output, error);
2936                         if (now < 0)
2937                                goto out;
2938                         step = BRIGHTNESS_STEP_AMOUNT (max - min + 1);
2939                         discrete = MAX (now - step, 0);
2940                         ret = gnome_rr_output_set_backlight (output,
2941                                                              discrete,
2942                                                              error);
2943                         if (ret)
2944                                 percentage_value = ABS_TO_PERCENTAGE (min, max, discrete);
2945                         goto out;
2946                 }
2947         }
2948 
2949         /* fall back to the polkit helper */
2950         now = backlight_helper_get_value ("get-brightness", manager, error);
2951         if (now < 0)
2952                 goto out;
2953         max = backlight_helper_get_value ("get-max-brightness", manager, error);
2954         if (max < 0)
2955                 goto out;
2956         step = BRIGHTNESS_STEP_AMOUNT (max - min + 1);
2957         discrete = MAX (now - step, 0);
2958         ret = backlight_helper_set_value ("set-brightness",
2959                                           discrete,
2960                                           manager,
2961                                           error);
2962         if (ret)
2963                 percentage_value = ABS_TO_PERCENTAGE (min, max, discrete);
2964 out:
2965         if (ret)
2966                 backlight_emit_changed (manager);
2967         return percentage_value;
2968 }
2969 
2970 static gint
backlight_set_abs(CsdPowerManager * manager,guint value,gboolean emit_changed,GError ** error)2971 backlight_set_abs (CsdPowerManager *manager,
2972                    guint value,
2973                    gboolean emit_changed,
2974                    GError **error)
2975 {
2976         GnomeRROutput *output;
2977         gboolean ret = FALSE;
2978 
2979         /* prioritize user override settings */
2980         if (!manager->priv->backlight_helper_force)
2981         {
2982                 /* prefer xbacklight */
2983                 output = get_primary_output (manager);
2984                 if (output != NULL) {
2985                         ret = gnome_rr_output_set_backlight (output,
2986                                                              value,
2987                                                              error);
2988                         goto out;
2989                 }
2990         }
2991         /* fall back to the polkit helper */
2992         ret = backlight_helper_set_value ("set-brightness",
2993                                           value,
2994                                           manager,
2995                                           error);
2996 out:
2997         if (ret && emit_changed)
2998                 backlight_emit_changed (manager);
2999         return ret;
3000 }
3001 
3002 static gboolean
display_backlight_dim(CsdPowerManager * manager,gint idle_percentage,GError ** error)3003 display_backlight_dim (CsdPowerManager *manager,
3004                        gint idle_percentage,
3005                        GError **error)
3006 {
3007         gint min;
3008         gint max;
3009         gint now;
3010         gint idle;
3011         gboolean ret = FALSE;
3012 
3013         now = backlight_get_abs (manager, error);
3014         if (now < 0) {
3015                 goto out;
3016         }
3017 
3018         /* is the dim brightness actually *dimmer* than the
3019          * brightness we have now? */
3020         min = backlight_get_min (manager);
3021         max = backlight_get_max (manager, error);
3022         if (max < 0) {
3023                 goto out;
3024         }
3025         idle = PERCENTAGE_TO_ABS (min, max, idle_percentage);
3026         if (idle > now) {
3027                 g_debug ("brightness already now %i/%i, so "
3028                          "ignoring dim to %i/%i",
3029                          now, max, idle, max);
3030                 ret = TRUE;
3031                 goto out;
3032         }
3033         ret = backlight_set_abs (manager,
3034                                  idle,
3035                                  FALSE,
3036                                  error);
3037         if (!ret) {
3038                 goto out;
3039         }
3040 
3041         /* save for undim */
3042         manager->priv->pre_dim_brightness = now;
3043 
3044 out:
3045         return ret;
3046 }
3047 
3048 static gboolean
kbd_backlight_dim(CsdPowerManager * manager,gint idle_percentage,GError ** error)3049 kbd_backlight_dim (CsdPowerManager *manager,
3050                    gint idle_percentage,
3051                    GError **error)
3052 {
3053         gboolean ret;
3054         gint idle;
3055         gint max;
3056         gint now;
3057 
3058         if (manager->priv->upower_kbd_proxy == NULL)
3059                 return TRUE;
3060 
3061         now = manager->priv->kbd_brightness_now;
3062         max = manager->priv->kbd_brightness_max;
3063         idle = PERCENTAGE_TO_ABS (0, max, idle_percentage);
3064         if (idle > now) {
3065                 g_debug ("kbd brightness already now %i/%i, so "
3066                          "ignoring dim to %i/%i",
3067                          now, max, idle, max);
3068                 return TRUE;
3069         }
3070         ret = upower_kbd_set_brightness (manager, idle, error);
3071         if (!ret)
3072                 return FALSE;
3073 
3074         /* save for undim */
3075         manager->priv->kbd_brightness_pre_dim = now;
3076         return TRUE;
3077 }
3078 
3079 static void
idle_set_mode(CsdPowerManager * manager,CsdPowerIdleMode mode)3080 idle_set_mode (CsdPowerManager *manager, CsdPowerIdleMode mode)
3081 {
3082         gboolean ret = FALSE;
3083         GError *error = NULL;
3084         gint idle_percentage;
3085         CsdPowerActionType action_type;
3086         CinnamonSettingsSessionState state;
3087 
3088         if (mode == manager->priv->current_idle_mode)
3089                 return;
3090 
3091         /* Ignore attempts to set "less idle" modes */
3092         if (mode < manager->priv->current_idle_mode &&
3093             mode != CSD_POWER_IDLE_MODE_NORMAL)
3094                 return;
3095 
3096         /* ensure we're still on an active console */
3097         state = cinnamon_settings_session_get_state (manager->priv->session);
3098         if (state == CINNAMON_SETTINGS_SESSION_STATE_INACTIVE) {
3099                 g_debug ("ignoring state transition to %s as inactive",
3100                          idle_mode_to_string (mode));
3101                 return;
3102         }
3103 
3104         manager->priv->current_idle_mode = mode;
3105         g_debug ("Doing a state transition: %s", idle_mode_to_string (mode));
3106 
3107         /* don't do any power saving if we're a VM */
3108         if (manager->priv->is_virtual_machine) {
3109                 g_debug ("ignoring state transition to %s as virtual machine",
3110                          idle_mode_to_string (mode));
3111                 return;
3112         }
3113 
3114         /* save current brightness, and set dim level */
3115         if (mode == CSD_POWER_IDLE_MODE_DIM) {
3116 
3117                 /* have we disabled the action */
3118                 if (up_client_get_on_battery (manager->priv->up_client)) {
3119                         ret = g_settings_get_boolean (manager->priv->settings,
3120                                                       "idle-dim-battery");
3121                 } else {
3122                         ret = g_settings_get_boolean (manager->priv->settings,
3123                                                       "idle-dim-ac");
3124                 }
3125                 if (!ret) {
3126                         g_debug ("not dimming due to policy");
3127                         return;
3128                 }
3129 
3130                 /* display backlight */
3131                 idle_percentage = g_settings_get_int (manager->priv->settings,
3132                                                       "idle-brightness");
3133                 ret = display_backlight_dim (manager, idle_percentage, &error);
3134                 if (!ret) {
3135                         g_warning ("failed to set dim backlight to %i%%: %s",
3136                                    idle_percentage,
3137                                    error->message);
3138                         g_clear_error (&error);
3139                 }
3140 
3141                 /* keyboard backlight */
3142                 ret = kbd_backlight_dim (manager, idle_percentage, &error);
3143                 if (!ret) {
3144                         g_warning ("failed to set dim kbd backlight to %i%%: %s",
3145                                    idle_percentage,
3146                                    error->message);
3147                         g_clear_error (&error);
3148                 }
3149 
3150         /* turn off screen and kbd */
3151         } else if (mode == CSD_POWER_IDLE_MODE_BLANK) {
3152 
3153                 ret = gnome_rr_screen_set_dpms_mode (manager->priv->x11_screen,
3154                                                      GNOME_RR_DPMS_OFF,
3155                                                      &error);
3156                 if (!ret) {
3157                         g_warning ("failed to turn the panel off: %s",
3158                                    error->message);
3159                         g_clear_error (&error);
3160                 }
3161 
3162                 /* only toggle keyboard if present and not already toggled */
3163                 if (manager->priv->upower_kbd_proxy &&
3164                     manager->priv->kbd_brightness_old == -1) {
3165                         ret = upower_kbd_toggle (manager, &error);
3166                         if (!ret) {
3167                                 g_warning ("failed to turn the kbd backlight off: %s",
3168                                            error->message);
3169                                 g_error_free (error);
3170                         }
3171                 }
3172 
3173         /* sleep */
3174         } else if (mode == CSD_POWER_IDLE_MODE_SLEEP) {
3175 
3176                 if (up_client_get_on_battery (manager->priv->up_client)) {
3177                         action_type = g_settings_get_enum (manager->priv->settings,
3178                                                            "sleep-inactive-battery-type");
3179                 } else {
3180                         action_type = g_settings_get_enum (manager->priv->settings,
3181                                                            "sleep-inactive-ac-type");
3182                 }
3183                 do_power_action_type (manager, action_type);
3184 
3185         /* turn on screen and restore user-selected brightness level */
3186         } else if (mode == CSD_POWER_IDLE_MODE_NORMAL) {
3187 
3188                 ret = gnome_rr_screen_set_dpms_mode (manager->priv->x11_screen,
3189                                                      GNOME_RR_DPMS_ON,
3190                                                      &error);
3191                 if (!ret) {
3192                         g_warning ("failed to turn the panel on: %s",
3193                                    error->message);
3194                         g_clear_error (&error);
3195                 }
3196 
3197                 /* reset brightness if we dimmed */
3198                 if (manager->priv->pre_dim_brightness >= 0) {
3199                         ret = backlight_set_abs (manager,
3200                                                  manager->priv->pre_dim_brightness,
3201                                                  FALSE,
3202                                                  &error);
3203                         if (!ret) {
3204                                 g_warning ("failed to restore backlight to %i: %s",
3205                                            manager->priv->pre_dim_brightness,
3206                                            error->message);
3207                                 g_clear_error (&error);
3208                         } else {
3209                                 manager->priv->pre_dim_brightness = -1;
3210                         }
3211                 }
3212 
3213                 /* only toggle keyboard if present and already toggled off */
3214                 if (manager->priv->upower_kbd_proxy &&
3215                     manager->priv->kbd_brightness_old != -1) {
3216                         ret = upower_kbd_toggle (manager, &error);
3217                         if (!ret) {
3218                                 g_warning ("failed to turn the kbd backlight on: %s",
3219                                            error->message);
3220                                 g_clear_error (&error);
3221                         }
3222                 }
3223 
3224                 /* reset kbd brightness if we dimmed */
3225                 if (manager->priv->kbd_brightness_pre_dim >= 0) {
3226                         ret = upower_kbd_set_brightness (manager,
3227                                                          manager->priv->kbd_brightness_pre_dim,
3228                                                          &error);
3229                         if (!ret) {
3230                                 g_warning ("failed to restore kbd backlight to %i: %s",
3231                                            manager->priv->kbd_brightness_pre_dim,
3232                                            error->message);
3233                                 g_error_free (error);
3234                         }
3235                         manager->priv->kbd_brightness_pre_dim = -1;
3236                 }
3237 
3238         }
3239 }
3240 
3241 static gboolean
idle_is_session_inhibited(CsdPowerManager * manager,guint mask)3242 idle_is_session_inhibited (CsdPowerManager *manager, guint mask)
3243 {
3244         gboolean ret;
3245         GVariant *retval = NULL;
3246         GError *error = NULL;
3247 
3248         /* not yet connected to cinnamon-session */
3249         if (manager->priv->session_proxy == NULL) {
3250                 g_debug ("session inhibition not available, cinnamon-session is not available");
3251                 return FALSE;
3252         }
3253 
3254         retval = g_dbus_proxy_call_sync (manager->priv->session_proxy,
3255                                          "IsInhibited",
3256                                          g_variant_new ("(u)",
3257                                                         mask),
3258                                          G_DBUS_CALL_FLAGS_NONE,
3259                                          -1, NULL,
3260                                          &error);
3261         if (retval == NULL) {
3262                 /* abort as the DBUS method failed */
3263                 g_warning ("IsInhibited failed: %s", error->message);
3264                 g_error_free (error);
3265                 return FALSE;
3266         }
3267 
3268         g_variant_get (retval, "(b)", &ret);
3269         g_variant_unref (retval);
3270 
3271         return ret;
3272 }
3273 
3274 /**
3275  *  idle_adjust_timeout:
3276  *  @idle_time: Current idle time, in seconds.
3277  *  @timeout: The new timeout we want to set, in seconds.
3278  *
3279  *  On slow machines, or machines that have lots to load duing login,
3280  *  the current idle time could be bigger than the requested timeout.
3281  *  In this case the scheduled idle timeout will never fire, unless
3282  *  some user activity (keyboard, mouse) resets the current idle time.
3283  *  Instead of relying on user activity to correct this issue, we need
3284  *  to adjust timeout, as related to current idle time, so the idle
3285  *  timeout will fire as designed.
3286  *
3287  *  Return value: timeout to set, adjusted acccording to current idle time.
3288  **/
3289 static guint
idle_adjust_timeout(guint idle_time,guint timeout)3290 idle_adjust_timeout (guint idle_time, guint timeout)
3291 {
3292         /* allow 2 sec margin for messaging delay. */
3293         idle_time += 2;
3294 
3295         /* Double timeout until it's larger than current idle time.
3296          * Give up for ultra slow machines. (86400 sec = 24 hours) */
3297         while (timeout < idle_time &&
3298                timeout < 86400 &&
3299                timeout > 0) {
3300                 timeout *= 2;
3301         }
3302         return timeout;
3303 }
3304 
3305 /**
3306  * @timeout: The new timeout we want to set, in seconds
3307  **/
3308 static void
idle_set_timeout_dim(CsdPowerManager * manager,guint timeout)3309 idle_set_timeout_dim (CsdPowerManager *manager, guint timeout)
3310 {
3311         guint idle_time;
3312         gboolean is_idle_inhibited;
3313 
3314         /* are we inhibited from going idle */
3315         is_idle_inhibited = idle_is_session_inhibited (manager,
3316                                                        SESSION_INHIBIT_MASK_IDLE);
3317         if (is_idle_inhibited) {
3318                 g_debug ("inhibited, so using normal state");
3319                 idle_set_mode (manager, CSD_POWER_IDLE_MODE_NORMAL);
3320 
3321                 gpm_idletime_alarm_remove (manager->priv->idletime,
3322                                            CSD_POWER_IDLETIME_DIM_ID);
3323                 return;
3324         }
3325 
3326         idle_time = gpm_idletime_get_time (manager->priv->idletime) / 1000;
3327 
3328         g_debug ("Setting dim idle timeout: %ds", timeout);
3329         if (timeout > 0) {
3330                 gpm_idletime_alarm_set (manager->priv->idletime,
3331                                         CSD_POWER_IDLETIME_DIM_ID,
3332                                         idle_adjust_timeout (idle_time, timeout) * 1000);
3333         } else {
3334                 gpm_idletime_alarm_remove (manager->priv->idletime,
3335                                            CSD_POWER_IDLETIME_DIM_ID);
3336         }
3337         return;
3338 }
3339 
3340 static void
refresh_idle_dim_settings(CsdPowerManager * manager)3341 refresh_idle_dim_settings (CsdPowerManager *manager)
3342 {
3343         gint timeout_dim;
3344         timeout_dim = g_settings_get_int (manager->priv->settings,
3345                                           "idle-dim-time");
3346         g_debug ("idle dim set with timeout %i", timeout_dim);
3347         idle_set_timeout_dim (manager, timeout_dim);
3348 }
3349 
3350 /**
3351  * idle_adjust_timeout_blank:
3352  * @idle_time: current idle time, in seconds.
3353  * @timeout: the new timeout we want to set, in seconds.
3354  *
3355  * Same as idle_adjust_timeout(), but also accounts for the duration
3356  * of the fading animation in the screensaver (so that blanking happens
3357  * exactly at the end of it, if configured with the same timeouts)
3358  */
3359 static guint
idle_adjust_timeout_blank(guint idle_time,guint timeout)3360 idle_adjust_timeout_blank (guint idle_time, guint timeout)
3361 {
3362         return idle_adjust_timeout (idle_time,
3363                                     timeout + SCREENSAVER_FADE_TIME);
3364 }
3365 
3366 static void
idle_configure(CsdPowerManager * manager)3367 idle_configure (CsdPowerManager *manager)
3368 {
3369         gboolean is_idle_inhibited;
3370         guint current_idle_time;
3371         guint timeout_lock;
3372         guint timeout_blank;
3373         guint timeout_sleep;
3374         gboolean on_battery;
3375 
3376         /* are we inhibited from going idle */
3377         is_idle_inhibited = idle_is_session_inhibited (manager,
3378                                                        SESSION_INHIBIT_MASK_IDLE);
3379         if (is_idle_inhibited) {
3380                 g_debug ("inhibited, so using normal state");
3381                 idle_set_mode (manager, CSD_POWER_IDLE_MODE_NORMAL);
3382 
3383                 gpm_idletime_alarm_remove (manager->priv->idletime,
3384                                            CSD_POWER_IDLETIME_LOCK_ID);
3385                 gpm_idletime_alarm_remove (manager->priv->idletime,
3386                                            CSD_POWER_IDLETIME_BLANK_ID);
3387                 gpm_idletime_alarm_remove (manager->priv->idletime,
3388                                            CSD_POWER_IDLETIME_SLEEP_ID);
3389 
3390                 refresh_idle_dim_settings (manager);
3391                 return;
3392         }
3393 
3394         current_idle_time = gpm_idletime_get_time (manager->priv->idletime) / 1000;
3395 
3396         /* set up blank callback even when session is not idle,
3397          * but only if we actually want to blank. */
3398         on_battery = up_client_get_on_battery (manager->priv->up_client);
3399         if (on_battery) {
3400                 timeout_blank = g_settings_get_int (manager->priv->settings,
3401                                                     "sleep-display-battery");
3402         } else {
3403                 timeout_blank = g_settings_get_int (manager->priv->settings,
3404                                                     "sleep-display-ac");
3405         }
3406 
3407         /* set up custom screensaver lock after idle time trigger */
3408         timeout_lock = g_settings_get_uint (manager->priv->settings_desktop_session,
3409                                             "idle-delay");
3410         if (timeout_lock != 0) {
3411                 if (timeout_blank != 0 && timeout_lock > timeout_blank) {
3412                         g_debug ("reducing lock timeout to match blank timeout");
3413                         timeout_lock = timeout_blank;
3414                 }
3415                 g_debug ("setting up lock callback for %is", timeout_lock);
3416 
3417                 gpm_idletime_alarm_set (manager->priv->idletime,
3418                                         CSD_POWER_IDLETIME_LOCK_ID,
3419                                         idle_adjust_timeout (current_idle_time, timeout_lock) * 1000);
3420         } else {
3421                 gpm_idletime_alarm_remove (manager->priv->idletime,
3422                                            CSD_POWER_IDLETIME_LOCK_ID);
3423         }
3424 
3425         if (timeout_blank != 0) {
3426                 g_debug ("setting up blank callback for %is", timeout_blank);
3427 
3428                 gpm_idletime_alarm_set (manager->priv->idletime,
3429                                         CSD_POWER_IDLETIME_BLANK_ID,
3430                                         idle_adjust_timeout_blank (current_idle_time, timeout_blank) * 1000);
3431         } else {
3432                 gpm_idletime_alarm_remove (manager->priv->idletime,
3433                                            CSD_POWER_IDLETIME_BLANK_ID);
3434         }
3435 
3436         gboolean is_sleep_inhibited = idle_is_session_inhibited (manager,
3437                                                                  SESSION_INHIBIT_MASK_SUSPEND);
3438         /* only do the sleep timeout when the session is idle
3439          * and we aren't inhibited from sleeping */
3440         if (on_battery) {
3441                 timeout_sleep = g_settings_get_int (manager->priv->settings,
3442                                                     "sleep-inactive-battery-timeout");
3443         } else {
3444                 timeout_sleep = g_settings_get_int (manager->priv->settings,
3445                                                     "sleep-inactive-ac-timeout");
3446         }
3447         if (!is_sleep_inhibited && timeout_sleep != 0) {
3448                 g_debug ("setting up sleep callback %is", timeout_sleep);
3449 
3450                 gpm_idletime_alarm_set (manager->priv->idletime,
3451                                         CSD_POWER_IDLETIME_SLEEP_ID,
3452                                         idle_adjust_timeout (current_idle_time, timeout_sleep) * 1000);
3453         } else {
3454                 gpm_idletime_alarm_remove (manager->priv->idletime,
3455                                            CSD_POWER_IDLETIME_SLEEP_ID);
3456         }
3457 
3458         refresh_idle_dim_settings (manager);
3459 }
3460 
3461 #if UP_CHECK_VERSION(0,99,0)
3462 static void
up_client_on_battery_cb(UpClient * client,GParamSpec * pspec,CsdPowerManager * manager)3463 up_client_on_battery_cb (UpClient *client,
3464                          GParamSpec *pspec,
3465                          CsdPowerManager *manager)
3466 {
3467         idle_configure (manager);
3468         lid_state_changed_cb(client, pspec, manager);
3469 }
3470 #endif
3471 
3472 static void
csd_power_manager_class_init(CsdPowerManagerClass * klass)3473 csd_power_manager_class_init (CsdPowerManagerClass *klass)
3474 {
3475         GObjectClass   *object_class = G_OBJECT_CLASS (klass);
3476 
3477         object_class->finalize = csd_power_manager_finalize;
3478 
3479         g_type_class_add_private (klass, sizeof (CsdPowerManagerPrivate));
3480 }
3481 
3482 static void
setup_locker_process(gpointer user_data)3483 setup_locker_process (gpointer user_data)
3484 {
3485         /* This function should only contain signal safe code, as it is invoked
3486          * between fork and exec. See signal-safety(7) for more information. */
3487         CsdPowerManager *manager = user_data;
3488 
3489         /* close all FDs except stdin, stdout, stderr and the inhibition fd */
3490         for (gint fd = 3; fd < manager->priv->fd_close_loop_end; fd++)
3491                 if (fd != manager->priv->inhibit_suspend_fd)
3492                         close (fd);
3493 
3494         /* make sure the inhibit fd does not get closed on exec, as it's options
3495          * are not specified in the logind inhibitor interface documentation. */
3496         if (-1 != manager->priv->inhibit_suspend_fd)
3497                 fcntl (manager->priv->inhibit_suspend_fd,
3498                        F_SETFD,
3499                        ~FD_CLOEXEC & fcntl (manager->priv->inhibit_suspend_fd, F_GETFD));
3500 }
3501 
3502 static void
lock_screen_with_custom_saver(CsdPowerManager * manager,gchar * custom_saver,gboolean idle_lock)3503 lock_screen_with_custom_saver (CsdPowerManager *manager,
3504                                gchar *custom_saver,
3505                                gboolean idle_lock)
3506 {
3507         gboolean res;
3508         gchar *fd = NULL;
3509         gchar **argv = NULL;
3510         gchar **env = NULL;
3511         GError *error = NULL;
3512 
3513         /* environment setup */
3514         fd = g_strdup_printf ("%d", manager->priv->inhibit_suspend_fd);
3515         if (!fd) {
3516                 g_warning ("failed to printf inhibit_suspend_fd");
3517                 goto quit;
3518         }
3519         if (!(env = g_get_environ ())) {
3520                 g_warning ("failed to get environment");
3521                 goto quit;
3522         }
3523         env = g_environ_setenv (env, "XSS_SLEEP_LOCK_FD", fd, FALSE);
3524         if (!env) {
3525                 g_warning ("failed to set XSS_SLEEP_LOCK_FD");
3526                 goto quit;
3527         }
3528         env = g_environ_setenv (env,
3529                                 "LOCKED_BY_SESSION_IDLE",
3530                                 idle_lock ? "true" : "false",
3531                                 TRUE);
3532         if (!env) {
3533                 g_warning ("failed to set LOCKED_BY_SESSION_IDLE");
3534                 goto quit;
3535         }
3536 
3537         /* argv setup */
3538         res = g_shell_parse_argv (custom_saver, NULL, &argv, &error);
3539         if (!res) {
3540                 g_warning ("failed to parse custom saver cmd '%s': %s",
3541                            custom_saver,
3542                            error->message);
3543                 goto quit;
3544         }
3545 
3546         /* get the max number of open file descriptors */
3547         manager->priv->fd_close_loop_end = sysconf (_SC_OPEN_MAX);
3548         if (-1 == manager->priv->fd_close_loop_end)
3549                 /* use some sane default */
3550                 manager->priv->fd_close_loop_end = 32768;
3551 
3552         /* spawn the custom screen locker */
3553         res = g_spawn_async (NULL,
3554                              argv,
3555                              env,
3556                              G_SPAWN_LEAVE_DESCRIPTORS_OPEN | G_SPAWN_SEARCH_PATH,
3557                              &setup_locker_process,
3558                              manager,
3559                              NULL,
3560                              &error);
3561         if (!res)
3562                 g_warning ("failed to run custom screensaver '%s': %s",
3563                            custom_saver,
3564                            error->message);
3565 
3566 quit:
3567         g_free (fd);
3568         g_strfreev (argv);
3569         g_strfreev (env);
3570         g_clear_error (&error);
3571 }
3572 
3573 static void
lock_screensaver(CsdPowerManager * manager)3574 lock_screensaver (CsdPowerManager *manager)
3575 {
3576     GError *error;
3577     gboolean ret;
3578     gchar *custom_saver = g_settings_get_string (manager->priv->settings_screensaver,
3579                                                  "custom-screensaver-command");
3580 
3581     g_debug ("Locking screen before sleep/hibernate");
3582 
3583     if (custom_saver && g_strcmp0 (custom_saver, "") != 0) {
3584             lock_screen_with_custom_saver (manager, custom_saver, FALSE);
3585             goto quit;
3586     }
3587 
3588     /* if we fail to get the gsettings entry, or if the user did not select
3589      * a custom screen saver, default to invoking cinnamon-screensaver */
3590     /* do this sync to ensure it's on the screen when we start suspending */
3591     error = NULL;
3592     ret = g_spawn_command_line_sync ("cinnamon-screensaver-command --lock", NULL, NULL, NULL, &error);
3593 
3594     if (!ret) {
3595         g_warning ("Couldn't lock screen: %s", error->message);
3596         g_error_free (error);
3597     }
3598 
3599 quit:
3600     g_free (custom_saver);
3601 }
3602 
3603 static void
idle_dbus_signal_cb(GDBusProxy * proxy,const gchar * sender_name,const gchar * signal_name,GVariant * parameters,gpointer user_data)3604 idle_dbus_signal_cb (GDBusProxy *proxy,
3605                      const gchar *sender_name,
3606                      const gchar *signal_name,
3607                      GVariant *parameters,
3608                      gpointer user_data)
3609 {
3610         CsdPowerManager *manager = CSD_POWER_MANAGER (user_data);
3611 
3612         if (g_strcmp0 (signal_name, "InhibitorAdded") == 0 ||
3613             g_strcmp0 (signal_name, "InhibitorRemoved") == 0) {
3614                 g_debug ("Received gnome session inhibitor change");
3615                 idle_configure (manager);
3616         }
3617         if (g_strcmp0 (signal_name, "StatusChanged") == 0) {
3618                 guint status;
3619 
3620                 g_variant_get (parameters, "(u)", &status);
3621                 g_dbus_proxy_set_cached_property (proxy, "status",
3622                                                   g_variant_new ("u", status));
3623                 g_debug ("Received gnome session status change");
3624                 idle_configure (manager);
3625         }
3626 }
3627 
3628 static void
session_proxy_ready_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)3629 session_proxy_ready_cb (GObject *source_object,
3630                         GAsyncResult *res,
3631                         gpointer user_data)
3632 {
3633         GError *error = NULL;
3634         CsdPowerManager *manager = CSD_POWER_MANAGER (user_data);
3635 
3636         manager->priv->session_proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
3637         if (manager->priv->session_proxy == NULL) {
3638                 g_warning ("Could not connect to cinnamon-session: %s",
3639                            error->message);
3640                 g_error_free (error);
3641         } else {
3642                 g_signal_connect (manager->priv->session_proxy, "g-signal",
3643                                   G_CALLBACK (idle_dbus_signal_cb), manager);
3644         }
3645 
3646         idle_configure (manager);
3647 }
3648 
3649 static void
session_presence_proxy_ready_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)3650 session_presence_proxy_ready_cb (GObject *source_object,
3651                                  GAsyncResult *res,
3652                                  gpointer user_data)
3653 {
3654         GError *error = NULL;
3655         CsdPowerManager *manager = CSD_POWER_MANAGER (user_data);
3656 
3657         manager->priv->session_presence_proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
3658         if (manager->priv->session_presence_proxy == NULL) {
3659                 g_warning ("Could not connect to gnome-sesson: %s",
3660                            error->message);
3661                 g_error_free (error);
3662                 return;
3663         }
3664         g_signal_connect (manager->priv->session_presence_proxy, "g-signal",
3665                           G_CALLBACK (idle_dbus_signal_cb), manager);
3666 }
3667 
3668 static void
power_keyboard_proxy_ready_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)3669 power_keyboard_proxy_ready_cb (GObject             *source_object,
3670                                GAsyncResult        *res,
3671                                gpointer             user_data)
3672 {
3673         GVariant *k_now = NULL;
3674         GVariant *k_max = NULL;
3675         GError *error = NULL;
3676         CsdPowerManager *manager = CSD_POWER_MANAGER (user_data);
3677 
3678         manager->priv->upower_kbd_proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
3679         if (manager->priv->upower_kbd_proxy == NULL) {
3680                 g_warning ("Could not connect to UPower: %s",
3681                            error->message);
3682                 g_error_free (error);
3683                 goto out;
3684         }
3685 
3686         k_now = g_dbus_proxy_call_sync (manager->priv->upower_kbd_proxy,
3687                                         "GetBrightness",
3688                                         NULL,
3689                                         G_DBUS_CALL_FLAGS_NONE,
3690                                         -1,
3691                                         NULL,
3692                                         &error);
3693         if (k_now == NULL) {
3694                 if (error->domain != G_DBUS_ERROR ||
3695                     error->code != G_DBUS_ERROR_UNKNOWN_METHOD) {
3696                         g_warning ("Failed to get brightness: %s",
3697                                    error->message);
3698                 }
3699                 g_error_free (error);
3700                 goto out;
3701         }
3702 
3703         k_max = g_dbus_proxy_call_sync (manager->priv->upower_kbd_proxy,
3704                                         "GetMaxBrightness",
3705                                         NULL,
3706                                         G_DBUS_CALL_FLAGS_NONE,
3707                                         -1,
3708                                         NULL,
3709                                         &error);
3710         if (k_max == NULL) {
3711                 g_warning ("Failed to get max brightness: %s", error->message);
3712                 g_error_free (error);
3713                 goto out;
3714         }
3715 
3716         g_signal_connect (manager->priv->upower_kbd_proxy, "g-signal", G_CALLBACK(upower_kbd_handle_changed), manager);
3717 
3718         g_variant_get (k_now, "(i)", &manager->priv->kbd_brightness_now);
3719         g_variant_get (k_max, "(i)", &manager->priv->kbd_brightness_max);
3720 
3721         /* Set keyboard brightness to zero if the current value is out of valid range.
3722         Unlike display brightness, keyboard backlight brightness should be dim by default.*/
3723         if ((manager->priv->kbd_brightness_now  < 0) || (manager->priv->kbd_brightness_now > manager->priv->kbd_brightness_max)) {
3724                 gboolean ret;
3725                 ret = upower_kbd_set_brightness (manager,
3726                                                  0,
3727                                                  &error);
3728                 if (!ret) {
3729                         g_warning ("failed to initialize kbd backlight to %i: %s",
3730                                    0,
3731                                    error->message);
3732                         g_error_free (error);
3733                 }
3734         }
3735 out:
3736         if (k_now != NULL)
3737                 g_variant_unref (k_now);
3738         if (k_max != NULL)
3739                 g_variant_unref (k_max);
3740 }
3741 
3742 static void
idle_idletime_alarm_expired_cb(GpmIdletime * idletime,guint alarm_id,CsdPowerManager * manager)3743 idle_idletime_alarm_expired_cb (GpmIdletime *idletime,
3744                                 guint alarm_id,
3745                                 CsdPowerManager *manager)
3746 {
3747         g_debug ("idletime alarm: %i", alarm_id);
3748 
3749         switch (alarm_id) {
3750         case CSD_POWER_IDLETIME_DIM_ID:
3751                 idle_set_mode (manager, CSD_POWER_IDLE_MODE_DIM);
3752                 break;
3753         case CSD_POWER_IDLETIME_LOCK_ID:
3754                 /* cinnamon-screensaver has its own lock after some idle delay.
3755                  * If we have a custom screensaver configured, we have to use
3756                  * the idle delay from cinnamon-settings-daemon to trigger the
3757                  * screen lock after the idle timeout */
3758                 ; /* empty statement, because C does not allow a declaration to
3759                    * follow a label */
3760                 gchar *custom_saver = g_settings_get_string (manager->priv->settings_screensaver,
3761                                                              "custom-screensaver-command");
3762                 if (custom_saver && g_strcmp0 (custom_saver, "") != 0)
3763                         lock_screen_with_custom_saver (manager,
3764                                                        custom_saver,
3765                                                        TRUE);
3766                 g_free (custom_saver);
3767 
3768                 break;
3769         case CSD_POWER_IDLETIME_BLANK_ID:
3770                 idle_set_mode (manager, CSD_POWER_IDLE_MODE_BLANK);
3771                 break;
3772         case CSD_POWER_IDLETIME_SLEEP_ID:
3773                 idle_set_mode (manager, CSD_POWER_IDLE_MODE_SLEEP);
3774                 break;
3775         }
3776 }
3777 
3778 static void
idle_idletime_reset_cb(GpmIdletime * idletime,CsdPowerManager * manager)3779 idle_idletime_reset_cb (GpmIdletime *idletime,
3780                         CsdPowerManager *manager)
3781 {
3782         g_debug ("idletime reset");
3783 
3784         idle_set_mode (manager, CSD_POWER_IDLE_MODE_NORMAL);
3785 }
3786 
3787 static void
engine_settings_key_changed_cb(GSettings * settings,const gchar * key,CsdPowerManager * manager)3788 engine_settings_key_changed_cb (GSettings *settings,
3789                                 const gchar *key,
3790                                 CsdPowerManager *manager)
3791 {
3792         /* note: you *have* to check if your key was changed here before
3793          * doing anything here. this gets invoked on module stop, and
3794          * will crash c-s-d if you don't. */
3795         if (g_strcmp0 (key, "use-time-for-policy") == 0) {
3796                 manager->priv->use_time_primary = g_settings_get_boolean (settings, key);
3797                 return;
3798         }
3799         if (g_strcmp0 (key, "idle-dim-time") == 0) {
3800                 refresh_idle_dim_settings (manager);
3801                 return;
3802         }
3803         if (g_str_has_prefix (key, "sleep-inactive") ||
3804             g_str_has_prefix (key, "sleep-display")) {
3805                 idle_configure (manager);
3806                 return;
3807         }
3808 
3809         if (g_str_has_prefix (key, "backlight-helper")) {
3810                 backlight_override_settings_refresh (manager);
3811                 return;
3812         }
3813 }
3814 
3815 static void
engine_session_active_changed_cb(CinnamonSettingsSession * session,GParamSpec * pspec,CsdPowerManager * manager)3816 engine_session_active_changed_cb (CinnamonSettingsSession *session,
3817                                   GParamSpec *pspec,
3818                                   CsdPowerManager *manager)
3819 {
3820         /* when doing the fast-user-switch into a new account,
3821          * ensure the new account is undimmed and with the backlight on */
3822         idle_set_mode (manager, CSD_POWER_IDLE_MODE_NORMAL);
3823 }
3824 
3825 /* This timer goes off every few minutes, whether the user is idle or not,
3826    to try and clean up anything that has gone wrong.
3827 
3828    It calls disable_builtin_screensaver() so that if xset has been used,
3829    or some other program (like xlock) has messed with the XSetScreenSaver()
3830    settings, they will be set back to sensible values (if a server extension
3831    is in use, messing with xlock can cause the screensaver to never get a wakeup
3832    event, and could cause monitor power-saving to occur, and all manner of
3833    heinousness.)
3834 
3835    This code was originally part of cinnamon-screensaver, see
3836    http://git.gnome.org/browse/cinnamon-screensaver/tree/src/gs-watcher-x11.c?id=fec00b12ec46c86334cfd36b37771cc4632f0d4d#n530
3837  */
3838 static gboolean
disable_builtin_screensaver(gpointer unused)3839 disable_builtin_screensaver (gpointer unused)
3840 {
3841         int current_server_timeout, current_server_interval;
3842         int current_prefer_blank,   current_allow_exp;
3843         int desired_server_timeout, desired_server_interval;
3844         int desired_prefer_blank,   desired_allow_exp;
3845 
3846         XGetScreenSaver (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
3847                          &current_server_timeout,
3848                          &current_server_interval,
3849                          &current_prefer_blank,
3850                          &current_allow_exp);
3851 
3852         desired_server_timeout  = current_server_timeout;
3853         desired_server_interval = current_server_interval;
3854         desired_prefer_blank    = current_prefer_blank;
3855         desired_allow_exp       = current_allow_exp;
3856 
3857         desired_server_interval = 0;
3858 
3859         /* I suspect (but am not sure) that DontAllowExposures might have
3860            something to do with powering off the monitor as well, at least
3861            on some systems that don't support XDPMS?  Who know... */
3862         desired_allow_exp = AllowExposures;
3863 
3864         /* When we're not using an extension, set the server-side timeout to 0,
3865            so that the server never gets involved with screen blanking, and we
3866            do it all ourselves.  (However, when we *are* using an extension,
3867            we tell the server when to notify us, and rather than blanking the
3868            screen, the server will send us an X event telling us to blank.)
3869         */
3870         desired_server_timeout = 0;
3871 
3872         if (desired_server_timeout     != current_server_timeout
3873             || desired_server_interval != current_server_interval
3874             || desired_prefer_blank    != current_prefer_blank
3875             || desired_allow_exp       != current_allow_exp) {
3876 
3877                 g_debug ("disabling server builtin screensaver:"
3878                          " (xset s %d %d; xset s %s; xset s %s)",
3879                          desired_server_timeout,
3880                          desired_server_interval,
3881                          (desired_prefer_blank ? "blank" : "noblank"),
3882                          (desired_allow_exp ? "expose" : "noexpose"));
3883 
3884                 XSetScreenSaver (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
3885                                  desired_server_timeout,
3886                                  desired_server_interval,
3887                                  desired_prefer_blank,
3888                                  desired_allow_exp);
3889 
3890                 XSync (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), FALSE);
3891         }
3892 
3893         return TRUE;
3894 }
3895 
3896 static void
inhibit_lid_switch_done(GObject * source,GAsyncResult * result,gpointer user_data)3897 inhibit_lid_switch_done (GObject      *source,
3898                          GAsyncResult *result,
3899                          gpointer      user_data)
3900 {
3901         GDBusProxy *proxy = G_DBUS_PROXY (source);
3902         CsdPowerManager *manager = CSD_POWER_MANAGER (user_data);
3903         GError *error = NULL;
3904         GVariant *res;
3905         GUnixFDList *fd_list = NULL;
3906         gint idx;
3907 
3908         res = g_dbus_proxy_call_with_unix_fd_list_finish (proxy, &fd_list, result, &error);
3909         if (res == NULL) {
3910                 g_warning ("Unable to inhibit lid switch: %s", error->message);
3911                 g_error_free (error);
3912         } else {
3913                 g_variant_get (res, "(h)", &idx);
3914                 manager->priv->inhibit_lid_switch_fd = g_unix_fd_list_get (fd_list, idx, &error);
3915                 if (manager->priv->inhibit_lid_switch_fd == -1) {
3916                         g_warning ("Failed to receive system inhibitor fd: %s", error->message);
3917                         g_error_free (error);
3918                 }
3919                 g_debug ("System inhibitor fd is %d", manager->priv->inhibit_lid_switch_fd);
3920                 g_object_unref (fd_list);
3921                 g_variant_unref (res);
3922         }
3923 }
3924 
3925 static void
inhibit_lid_switch(CsdPowerManager * manager)3926 inhibit_lid_switch (CsdPowerManager *manager)
3927 {
3928         if (!manager->priv->inhibit_lid_switch_enabled)  {
3929                 // The users asks us not to interfere with what logind does
3930                 // w.r.t. handling the lid switch
3931                 g_debug ("inhibiting lid-switch disabled");
3932                 return;
3933         }
3934 
3935         GVariant *params;
3936 
3937         if (manager->priv->inhibit_lid_switch_taken) {
3938                 g_debug ("already inhibited lid-switch");
3939                 return;
3940         }
3941         g_debug ("Adding lid switch system inhibitor");
3942         manager->priv->inhibit_lid_switch_taken = TRUE;
3943 
3944         params = g_variant_new ("(ssss)",
3945                                 "handle-lid-switch",
3946                                 g_get_user_name (),
3947                                 "Multiple displays attached",
3948                                 "block");
3949         g_dbus_proxy_call_with_unix_fd_list (manager->priv->logind_proxy,
3950                                              "Inhibit",
3951                                              params,
3952                                              0,
3953                                              G_MAXINT,
3954                                              NULL,
3955                                              NULL,
3956                                              inhibit_lid_switch_done,
3957                                              manager);
3958 }
3959 
3960 static void
uninhibit_lid_switch(CsdPowerManager * manager)3961 uninhibit_lid_switch (CsdPowerManager *manager)
3962 {
3963         if (manager->priv->inhibit_lid_switch_fd == -1) {
3964                 g_debug ("no lid-switch inhibitor");
3965                 return;
3966         }
3967         g_debug ("Removing lid switch system inhibitor");
3968         close (manager->priv->inhibit_lid_switch_fd);
3969         manager->priv->inhibit_lid_switch_fd = -1;
3970         manager->priv->inhibit_lid_switch_taken = FALSE;
3971 }
3972 
3973 
3974 static void
inhibit_suspend_done(GObject * source,GAsyncResult * result,gpointer user_data)3975 inhibit_suspend_done (GObject      *source,
3976                       GAsyncResult *result,
3977                       gpointer      user_data)
3978 {
3979         GDBusProxy *proxy = G_DBUS_PROXY (source);
3980         CsdPowerManager *manager = CSD_POWER_MANAGER (user_data);
3981         GError *error = NULL;
3982         GVariant *res;
3983         GUnixFDList *fd_list = NULL;
3984         gint idx;
3985 
3986         res = g_dbus_proxy_call_with_unix_fd_list_finish (proxy, &fd_list, result, &error);
3987         if (res == NULL) {
3988                 g_warning ("Unable to inhibit suspend: %s", error->message);
3989                 g_error_free (error);
3990         } else {
3991                 g_variant_get (res, "(h)", &idx);
3992                 manager->priv->inhibit_suspend_fd = g_unix_fd_list_get (fd_list, idx, &error);
3993                 if (manager->priv->inhibit_suspend_fd == -1) {
3994                         g_warning ("Failed to receive system inhibitor fd: %s", error->message);
3995                         g_error_free (error);
3996                 }
3997                 g_debug ("System inhibitor fd is %d", manager->priv->inhibit_suspend_fd);
3998                 g_object_unref (fd_list);
3999                 g_variant_unref (res);
4000         }
4001 }
4002 
4003 /* We take a delay inhibitor here, which causes logind to send a
4004  * PrepareToSleep signal, which gives us a chance to lock the screen
4005  * and do some other preparations.
4006  */
4007 static void
inhibit_suspend(CsdPowerManager * manager)4008 inhibit_suspend (CsdPowerManager *manager)
4009 {
4010         if (manager->priv->inhibit_suspend_taken) {
4011                 g_debug ("already inhibited lid-switch");
4012                 return;
4013         }
4014         g_debug ("Adding suspend delay inhibitor");
4015         manager->priv->inhibit_suspend_taken = TRUE;
4016         g_dbus_proxy_call_with_unix_fd_list (manager->priv->logind_proxy,
4017                                              "Inhibit",
4018                                              g_variant_new ("(ssss)",
4019                                                             "sleep",
4020                                                             g_get_user_name (),
4021                                                             "Cinnamon needs to lock the screen",
4022                                                             "delay"),
4023                                              0,
4024                                              G_MAXINT,
4025                                              NULL,
4026                                              NULL,
4027                                              inhibit_suspend_done,
4028                                              manager);
4029 }
4030 
4031 static void
uninhibit_suspend(CsdPowerManager * manager)4032 uninhibit_suspend (CsdPowerManager *manager)
4033 {
4034         if (manager->priv->inhibit_suspend_fd == -1) {
4035                 g_debug ("no suspend delay inhibitor");
4036                 return;
4037         }
4038         g_debug ("Removing suspend delay inhibitor");
4039         close (manager->priv->inhibit_suspend_fd);
4040         manager->priv->inhibit_suspend_fd = -1;
4041         manager->priv->inhibit_suspend_taken = FALSE;
4042 }
4043 
4044 static void
handle_suspend_actions(CsdPowerManager * manager)4045 handle_suspend_actions (CsdPowerManager *manager)
4046 {
4047         /* Is this even necessary? We lock ahead of the suspend initiation,
4048          * during do_power_action_type().  This is a signal from logind or
4049          * upower that we're about to suspend.  That may have originated in
4050          * this module, or elsewhere (cinnamon-session via menu or user
4051          * applet.  Lock is handled there as well... but just in case I
4052          * suppose.)
4053          */
4054         if (should_lock_on_suspend (manager)) {
4055             lock_screensaver (manager);
4056         }
4057 
4058         /* lift the delay inhibit, so logind can proceed */
4059         uninhibit_suspend (manager);
4060 }
4061 
4062 static void
handle_resume_actions(CsdPowerManager * manager)4063 handle_resume_actions (CsdPowerManager *manager)
4064 {
4065         gboolean ret;
4066         GError *error = NULL;
4067 
4068         /* this displays the unlock dialogue so the user doesn't have
4069          * to move the mouse or press any key before the window comes up */
4070         g_dbus_connection_call (manager->priv->connection,
4071                                 GS_DBUS_NAME,
4072                                 GS_DBUS_PATH,
4073                                 GS_DBUS_INTERFACE,
4074                                 "SimulateUserActivity",
4075                                 NULL, NULL,
4076                                 G_DBUS_CALL_FLAGS_NONE, -1,
4077                                 NULL, NULL, NULL);
4078 
4079         /* close existing notifications on resume, the system power
4080          * state is probably different now */
4081         notify_close_if_showing (manager->priv->notification_low);
4082         notify_close_if_showing (manager->priv->notification_discharging);
4083 
4084         /* ensure we turn the panel back on after resume */
4085         ret = gnome_rr_screen_set_dpms_mode (manager->priv->x11_screen,
4086                                              GNOME_RR_DPMS_ON,
4087                                              &error);
4088         if (!ret) {
4089                 g_warning ("failed to turn the panel on after resume: %s",
4090                            error->message);
4091                 g_error_free (error);
4092         }
4093 
4094         /* set up the delay again */
4095         inhibit_suspend (manager);
4096 }
4097 
4098 #if ! UP_CHECK_VERSION(0,99,0)
4099 static void
upower_notify_sleep_cb(UpClient * client,UpSleepKind sleep_kind,CsdPowerManager * manager)4100 upower_notify_sleep_cb (UpClient *client,
4101                         UpSleepKind sleep_kind,
4102                         CsdPowerManager *manager)
4103 {
4104         handle_suspend_actions (manager);
4105 }
4106 
4107 static void
upower_notify_resume_cb(UpClient * client,UpSleepKind sleep_kind,CsdPowerManager * manager)4108 upower_notify_resume_cb (UpClient *client,
4109                          UpSleepKind sleep_kind,
4110                          CsdPowerManager *manager)
4111 {
4112         handle_resume_actions (manager);
4113 }
4114 #endif
4115 
4116 static void
logind_proxy_signal_cb(GDBusProxy * proxy,const gchar * sender_name,const gchar * signal_name,GVariant * parameters,gpointer user_data)4117 logind_proxy_signal_cb (GDBusProxy  *proxy,
4118                         const gchar *sender_name,
4119                         const gchar *signal_name,
4120                         GVariant    *parameters,
4121                         gpointer     user_data)
4122 {
4123         CsdPowerManager *manager = CSD_POWER_MANAGER (user_data);
4124         gboolean is_about_to_suspend;
4125 
4126         if (g_strcmp0 (signal_name, "PrepareForSleep") != 0)
4127                 return;
4128         g_variant_get (parameters, "(b)", &is_about_to_suspend);
4129         if (is_about_to_suspend) {
4130                 handle_suspend_actions (manager);
4131         } else {
4132                 handle_resume_actions (manager);
4133         }
4134 }
4135 
4136 static gboolean
is_hardware_a_virtual_machine(void)4137 is_hardware_a_virtual_machine (void)
4138 {
4139         const gchar *str;
4140         gboolean ret = FALSE;
4141         GError *error = NULL;
4142         GVariant *inner;
4143         GVariant *variant = NULL;
4144         GDBusConnection *connection;
4145 
4146         connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM,
4147                                      NULL,
4148                                      &error);
4149         if (connection == NULL) {
4150                 g_warning ("system bus not available: %s", error->message);
4151                 g_error_free (error);
4152                 goto out;
4153         }
4154         variant = g_dbus_connection_call_sync (connection,
4155                                                "org.freedesktop.systemd1",
4156                                                "/org/freedesktop/systemd1",
4157                                                "org.freedesktop.DBus.Properties",
4158                                                "Get",
4159                                                g_variant_new ("(ss)",
4160                                                               "org.freedesktop.systemd1.Manager",
4161                                                               "Virtualization"),
4162                                                NULL,
4163                                                G_DBUS_CALL_FLAGS_NONE,
4164                                                -1,
4165                                                NULL,
4166                                                &error);
4167         if (variant == NULL) {
4168                 g_debug ("Failed to get property '%s': %s", "Virtualization", error->message);
4169                 g_error_free (error);
4170                 goto out;
4171         }
4172 
4173         /* on bare-metal hardware this is the empty string,
4174          * otherwise an identifier such as "kvm", "vmware", etc. */
4175         g_variant_get (variant, "(v)", &inner);
4176         str = g_variant_get_string (inner, NULL);
4177         if (str != NULL && str[0] != '\0')
4178                 ret = TRUE;
4179 out:
4180         if (connection != NULL)
4181                 g_object_unref (connection);
4182         if (variant != NULL)
4183                 g_variant_unref (variant);
4184         return ret;
4185 }
4186 
4187 gboolean
csd_power_manager_start(CsdPowerManager * manager,GError ** error)4188 csd_power_manager_start (CsdPowerManager *manager,
4189                          GError **error)
4190 {
4191         gboolean ret;
4192 
4193         g_debug ("Starting power manager");
4194         cinnamon_settings_profile_start (NULL);
4195 
4196         /* coldplug the list of screens */
4197         manager->priv->x11_screen = gnome_rr_screen_new (gdk_screen_get_default (), error);
4198         if (manager->priv->x11_screen == NULL)
4199                 return FALSE;
4200 
4201         /* Set up the logind proxy */
4202         manager->priv->logind_proxy =
4203                 g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
4204                                                0,
4205                                                NULL,
4206                                                LOGIND_DBUS_NAME,
4207                                                LOGIND_DBUS_PATH,
4208                                                LOGIND_DBUS_INTERFACE,
4209                                                NULL,
4210                                                error);
4211         g_signal_connect (manager->priv->logind_proxy, "g-signal",
4212                           G_CALLBACK (logind_proxy_signal_cb),
4213                           manager);
4214 
4215         /* Set up a delay inhibitor to be informed about suspend attempts */
4216         inhibit_suspend (manager);
4217 
4218         /* track the active session */
4219         manager->priv->session = cinnamon_settings_session_new ();
4220         g_signal_connect (manager->priv->session, "notify::state",
4221                           G_CALLBACK (engine_session_active_changed_cb),
4222                           manager);
4223 
4224         manager->priv->kbd_brightness_old = -1;
4225         manager->priv->kbd_brightness_pre_dim = -1;
4226         manager->priv->pre_dim_brightness = -1;
4227         manager->priv->settings = g_settings_new (CSD_POWER_SETTINGS_SCHEMA);
4228         g_signal_connect (manager->priv->settings, "changed",
4229                           G_CALLBACK (engine_settings_key_changed_cb), manager);
4230         manager->priv->settings_screensaver = g_settings_new (CSD_SAVER_SETTINGS_SCHEMA);
4231         manager->priv->settings_xrandr = g_settings_new (CSD_XRANDR_SETTINGS_SCHEMA);
4232         manager->priv->settings_desktop_session = g_settings_new (CSD_SESSION_SETTINGS_SCHEMA);
4233         manager->priv->settings_cinnamon_session = g_settings_new (CSD_CINNAMON_SESSION_SCHEMA);
4234         manager->priv->inhibit_lid_switch_enabled =
4235                           g_settings_get_boolean (manager->priv->settings, "inhibit-lid-switch");
4236 
4237         /* Disable logind's lid handling while g-s-d is active */
4238         inhibit_lid_switch (manager);
4239 
4240         manager->priv->up_client = up_client_new ();
4241 #if ! UP_CHECK_VERSION(0,99,0)
4242         g_signal_connect (manager->priv->up_client, "notify-sleep",
4243                           G_CALLBACK (upower_notify_sleep_cb), manager);
4244         g_signal_connect (manager->priv->up_client, "notify-resume",
4245                           G_CALLBACK (upower_notify_resume_cb), manager);
4246 #endif
4247         manager->priv->lid_is_closed = up_client_get_lid_is_closed (manager->priv->up_client);
4248         manager->priv->on_battery = up_client_get_on_battery(manager->priv->up_client);
4249         g_signal_connect (manager->priv->up_client, "device-added",
4250                           G_CALLBACK (engine_device_added_cb), manager);
4251         g_signal_connect (manager->priv->up_client, "device-removed",
4252                           G_CALLBACK (engine_device_removed_cb), manager);
4253 #if UP_CHECK_VERSION(0,99,0)
4254         g_signal_connect_after (manager->priv->up_client, "notify::lid-is-closed",
4255                                 G_CALLBACK (lid_state_changed_cb), manager);
4256 
4257         g_signal_connect (manager->priv->up_client, "notify::on-battery",
4258                           G_CALLBACK (up_client_on_battery_cb), manager);
4259 #else
4260         g_signal_connect (manager->priv->up_client, "device-changed",
4261                           G_CALLBACK (engine_device_changed_cb), manager);
4262         g_signal_connect_after (manager->priv->up_client, "changed",
4263                                 G_CALLBACK (up_client_changed_cb), manager);
4264 #endif
4265 
4266         /* use the fallback name from gnome-power-manager so the shell
4267          * blocks this, and uses the power extension instead */
4268         manager->priv->status_icon = gtk_status_icon_new ();
4269         gtk_status_icon_set_name (manager->priv->status_icon,
4270                                   "gnome-power-manager");
4271         /* TRANSLATORS: this is the title of the power manager status icon
4272          * that is only shown in fallback mode */
4273         gtk_status_icon_set_title (manager->priv->status_icon, _("Power Manager"));
4274         gtk_status_icon_set_visible (manager->priv->status_icon, FALSE);
4275 
4276         /* connect to UPower for keyboard backlight control */
4277         g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
4278                                   G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
4279                                   NULL,
4280                                   UPOWER_DBUS_NAME,
4281                                   UPOWER_DBUS_PATH_KBDBACKLIGHT,
4282                                   UPOWER_DBUS_INTERFACE_KBDBACKLIGHT,
4283                                   NULL,
4284                                   power_keyboard_proxy_ready_cb,
4285                                   manager);
4286 
4287         /* connect to the session */
4288         g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
4289                                   G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
4290                                   NULL,
4291                                   GNOME_SESSION_DBUS_NAME,
4292                                   GNOME_SESSION_DBUS_PATH,
4293                                   GNOME_SESSION_DBUS_INTERFACE,
4294                                   NULL,
4295                                   session_proxy_ready_cb,
4296                                   manager);
4297         g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
4298                                   0,
4299                                   NULL,
4300                                   GNOME_SESSION_DBUS_NAME,
4301                                   GNOME_SESSION_DBUS_PATH_PRESENCE,
4302                                   GNOME_SESSION_DBUS_INTERFACE_PRESENCE,
4303                                   NULL,
4304                                   session_presence_proxy_ready_cb,
4305                                   manager);
4306 
4307         manager->priv->devices_array = g_ptr_array_new_with_free_func (g_object_unref);
4308         manager->priv->canberra_context = ca_gtk_context_get_for_screen (gdk_screen_get_default ());
4309 
4310         manager->priv->phone = gpm_phone_new ();
4311         g_signal_connect (manager->priv->phone, "device-added",
4312                           G_CALLBACK (phone_device_added_cb), manager);
4313         g_signal_connect (manager->priv->phone, "device-removed",
4314                           G_CALLBACK (phone_device_removed_cb), manager);
4315         g_signal_connect (manager->priv->phone, "device-refresh",
4316                           G_CALLBACK (phone_device_refresh_cb), manager);
4317 
4318         /* create a fake virtual composite battery */
4319         manager->priv->device_composite = up_device_new ();
4320         g_object_set (manager->priv->device_composite,
4321                       "kind", UP_DEVICE_KIND_BATTERY,
4322                       "is-rechargeable", TRUE,
4323                       "native-path", "dummy:composite_battery",
4324                       "power-supply", TRUE,
4325                       "is-present", TRUE,
4326                       NULL);
4327 
4328         /* get backlight setting overrides */
4329         manager->priv->backlight_helper_preference_args = NULL;
4330         backlight_override_settings_refresh (manager);
4331 
4332         /* get percentage policy */
4333         manager->priv->low_percentage = g_settings_get_int (manager->priv->settings,
4334                                                             "percentage-low");
4335         manager->priv->critical_percentage = g_settings_get_int (manager->priv->settings,
4336                                                                  "percentage-critical");
4337         manager->priv->action_percentage = g_settings_get_int (manager->priv->settings,
4338                                                                "percentage-action");
4339 
4340         /* get time policy */
4341         manager->priv->low_time = g_settings_get_int (manager->priv->settings,
4342                                                       "time-low");
4343         manager->priv->critical_time = g_settings_get_int (manager->priv->settings,
4344                                                            "time-critical");
4345         manager->priv->action_time = g_settings_get_int (manager->priv->settings,
4346                                                          "time-action");
4347 
4348         /* we can disable this if the time remaining is inaccurate or just plain wrong */
4349         manager->priv->use_time_primary = g_settings_get_boolean (manager->priv->settings,
4350                                                                   "use-time-for-policy");
4351 
4352         /* create IDLETIME watcher */
4353         manager->priv->idletime = gpm_idletime_new ();
4354         g_signal_connect (manager->priv->idletime, "reset",
4355                           G_CALLBACK (idle_idletime_reset_cb), manager);
4356         g_signal_connect (manager->priv->idletime, "alarm-expired",
4357                           G_CALLBACK (idle_idletime_alarm_expired_cb), manager);
4358 
4359         /* set up the screens */
4360         g_signal_connect (manager->priv->x11_screen, "changed", G_CALLBACK (on_randr_event), manager);
4361         on_randr_event (manager->priv->x11_screen, manager);
4362 
4363         /* ensure the default dpms timeouts are cleared */
4364         ret = gnome_rr_screen_set_dpms_mode (manager->priv->x11_screen,
4365                                              GNOME_RR_DPMS_ON,
4366                                              error);
4367         if (!ret) {
4368                 g_warning ("Failed set DPMS mode: %s", (*error)->message);
4369                 g_clear_error (error);
4370         }
4371 
4372         /* coldplug the engine */
4373         engine_coldplug (manager);
4374 
4375         /* Make sure that Xorg's DPMS extension never gets in our way. The defaults seem to have changed in Xorg 1.14
4376          * being "0" by default to being "600" by default
4377          * https://bugzilla.gnome.org/show_bug.cgi?id=709114
4378          */
4379         gdk_x11_display_error_trap_push (gdk_display_get_default ());
4380         int dummy;
4381         if (DPMSQueryExtension(GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &dummy, &dummy)) {
4382             DPMSSetTimeouts (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), 0, 0, 0);
4383         }
4384         gdk_x11_display_error_trap_pop_ignored (gdk_display_get_default ());
4385 
4386         manager->priv->xscreensaver_watchdog_timer_id = g_timeout_add_seconds (XSCREENSAVER_WATCHDOG_TIMEOUT,
4387                                                                                disable_builtin_screensaver,
4388                                                                                NULL);
4389         /* don't blank inside a VM */
4390         manager->priv->is_virtual_machine = is_hardware_a_virtual_machine ();
4391 
4392         cinnamon_settings_profile_end (NULL);
4393         return TRUE;
4394 }
4395 
4396 void
csd_power_manager_stop(CsdPowerManager * manager)4397 csd_power_manager_stop (CsdPowerManager *manager)
4398 {
4399         g_debug ("Stopping power manager");
4400 
4401         if (manager->priv->bus_cancellable != NULL) {
4402                 g_cancellable_cancel (manager->priv->bus_cancellable);
4403                 g_object_unref (manager->priv->bus_cancellable);
4404                 manager->priv->bus_cancellable = NULL;
4405         }
4406 
4407         kill_lid_close_safety_timer (manager);
4408 
4409         g_signal_handlers_disconnect_by_data (manager->priv->up_client, manager);
4410 
4411         if (manager->priv->connection != NULL) {
4412                 g_object_unref (manager->priv->connection);
4413                 manager->priv->connection = NULL;
4414         }
4415 
4416         if (manager->priv->session != NULL) {
4417                 g_object_unref (manager->priv->session);
4418                 manager->priv->session = NULL;
4419         }
4420 
4421         if (manager->priv->settings != NULL) {
4422                 g_object_unref (manager->priv->settings);
4423                 manager->priv->settings = NULL;
4424         }
4425 
4426         if (manager->priv->settings_screensaver != NULL) {
4427                 g_object_unref (manager->priv->settings_screensaver);
4428                 manager->priv->settings_screensaver = NULL;
4429         }
4430 
4431         if (manager->priv->settings_xrandr != NULL) {
4432                 g_object_unref (manager->priv->settings_xrandr);
4433                 manager->priv->settings_xrandr = NULL;
4434         }
4435 
4436         if (manager->priv->settings_desktop_session != NULL) {
4437                 g_object_unref (manager->priv->settings_desktop_session);
4438                 manager->priv->settings_desktop_session = NULL;
4439         }
4440 
4441         if (manager->priv->settings_cinnamon_session != NULL) {
4442                 g_object_unref (manager->priv->settings_cinnamon_session);
4443                 manager->priv->settings_cinnamon_session = NULL;
4444         }
4445 
4446         if (manager->priv->up_client != NULL) {
4447                 g_object_unref (manager->priv->up_client);
4448                 manager->priv->up_client = NULL;
4449         }
4450 
4451         if (manager->priv->inhibit_lid_switch_fd != -1) {
4452                 close (manager->priv->inhibit_lid_switch_fd);
4453                 manager->priv->inhibit_lid_switch_fd = -1;
4454                 manager->priv->inhibit_lid_switch_taken = FALSE;
4455         }
4456         if (manager->priv->inhibit_suspend_fd != -1) {
4457                 close (manager->priv->inhibit_suspend_fd);
4458                 manager->priv->inhibit_suspend_fd = -1;
4459                 manager->priv->inhibit_suspend_taken = FALSE;
4460         }
4461 
4462         if (manager->priv->logind_proxy != NULL) {
4463                 g_object_unref (manager->priv->logind_proxy);
4464                 manager->priv->logind_proxy = NULL;
4465         }
4466 
4467         g_free (manager->priv->backlight_helper_preference_args);
4468         manager->priv->backlight_helper_preference_args = NULL;
4469 
4470         if (manager->priv->x11_screen != NULL) {
4471                 g_object_unref (manager->priv->x11_screen);
4472                 manager->priv->x11_screen = NULL;
4473         }
4474 
4475         g_ptr_array_unref (manager->priv->devices_array);
4476         manager->priv->devices_array = NULL;
4477 
4478         if (manager->priv->phone != NULL) {
4479                 g_object_unref (manager->priv->phone);
4480                 manager->priv->phone = NULL;
4481         }
4482 
4483         if (manager->priv->device_composite != NULL) {
4484                 g_object_unref (manager->priv->device_composite);
4485                 manager->priv->device_composite = NULL;
4486         }
4487 
4488         if (manager->priv->previous_icon != NULL) {
4489                 g_object_unref (manager->priv->previous_icon);
4490                 manager->priv->previous_icon = NULL;
4491         }
4492 
4493         g_free (manager->priv->previous_summary);
4494         manager->priv->previous_summary = NULL;
4495 
4496         if (manager->priv->session_proxy != NULL) {
4497                 g_object_unref (manager->priv->session_proxy);
4498                 manager->priv->session_proxy = NULL;
4499         }
4500 
4501         if (manager->priv->session_presence_proxy != NULL) {
4502                 g_object_unref (manager->priv->session_presence_proxy);
4503                 manager->priv->session_presence_proxy = NULL;
4504         }
4505 
4506         if (manager->priv->critical_alert_timeout_id > 0) {
4507                 g_source_remove (manager->priv->critical_alert_timeout_id);
4508                 manager->priv->critical_alert_timeout_id = 0;
4509         }
4510         g_signal_handlers_disconnect_by_func (manager->priv->idletime,
4511                                               idle_idletime_reset_cb,
4512                                               manager);
4513         g_signal_handlers_disconnect_by_func (manager->priv->idletime,
4514                                               idle_idletime_alarm_expired_cb,
4515                                               manager);
4516 
4517         if (manager->priv->idletime != NULL) {
4518                 g_object_unref (manager->priv->idletime);
4519                 manager->priv->idletime = NULL;
4520         }
4521 
4522         if (manager->priv->status_icon != NULL) {
4523                 g_object_unref (manager->priv->status_icon);
4524                 manager->priv->status_icon = NULL;
4525         }
4526 
4527         if (manager->priv->xscreensaver_watchdog_timer_id > 0) {
4528                 g_source_remove (manager->priv->xscreensaver_watchdog_timer_id);
4529                 manager->priv->xscreensaver_watchdog_timer_id = 0;
4530         }
4531 
4532         g_clear_object (&manager->priv->power_iface);
4533         g_clear_object (&manager->priv->screen_iface);
4534         g_clear_object (&manager->priv->keyboard_iface);
4535 }
4536 
4537 static void
csd_power_manager_init(CsdPowerManager * manager)4538 csd_power_manager_init (CsdPowerManager *manager)
4539 {
4540         manager->priv = CSD_POWER_MANAGER_GET_PRIVATE (manager);
4541         manager->priv->inhibit_lid_switch_fd = -1;
4542         manager->priv->inhibit_suspend_fd = -1;
4543 }
4544 
4545 static void
csd_power_manager_finalize(GObject * object)4546 csd_power_manager_finalize (GObject *object)
4547 {
4548         CsdPowerManager *manager;
4549 
4550         manager = CSD_POWER_MANAGER (object);
4551 
4552         g_return_if_fail (manager->priv != NULL);
4553 
4554         if (manager->priv->p_name_id != 0)
4555                 g_bus_unown_name (manager->priv->p_name_id);
4556 
4557         if (manager->priv->s_name_id != 0)
4558                 g_bus_unown_name (manager->priv->s_name_id);
4559 
4560         if (manager->priv->k_name_id != 0)
4561                 g_bus_unown_name (manager->priv->k_name_id);
4562 
4563         G_OBJECT_CLASS (csd_power_manager_parent_class)->finalize (object);
4564 }
4565 
4566 #if !UP_CHECK_VERSION(0,99,0)
4567 #define UP_DEVICE_LEVEL_NONE 1
4568 #endif
4569 
4570 static GVariant *
device_to_variant_blob(UpDevice * device)4571 device_to_variant_blob (UpDevice *device)
4572 {
4573         const gchar *object_path, *vendor, *model;
4574         gchar *device_icon;
4575         gdouble percentage;
4576         GIcon *icon;
4577         guint64 time_empty, time_full;
4578         guint64 time_state = 0;
4579         GVariant *value;
4580         UpDeviceKind kind;
4581         UpDeviceState state;
4582         gint battery_level;
4583 
4584         icon = gpm_upower_get_device_icon (device, TRUE);
4585         device_icon = g_icon_to_string (icon);
4586         g_object_get (device,
4587                       "vendor", &vendor,
4588                       "model", &model,
4589                       "kind", &kind,
4590                       "percentage", &percentage,
4591                       "state", &state,
4592                       "time-to-empty", &time_empty,
4593                       "time-to-full", &time_full,
4594                       NULL);
4595 
4596         /* upower < 0.99.5 compatibility */
4597         if (g_object_class_find_property (G_OBJECT_GET_CLASS (device), "battery-level")) {
4598                 g_object_get (device,
4599                               "battery-level", &battery_level,
4600                               NULL);
4601         } else {
4602                 battery_level = UP_DEVICE_LEVEL_NONE;
4603         }
4604 
4605         /* only return time for these simple states */
4606         if (state == UP_DEVICE_STATE_DISCHARGING)
4607                 time_state = time_empty;
4608         else if (state == UP_DEVICE_STATE_CHARGING)
4609                 time_state = time_full;
4610 
4611         /* get an object path, even for the composite device */
4612         object_path = up_device_get_object_path (device);
4613         if (object_path == NULL)
4614                 object_path = CSD_POWER_DBUS_PATH;
4615 
4616         /* format complex object */
4617         value = g_variant_new ("(sssusduut)",
4618                                object_path,
4619                                vendor,
4620                                model,
4621                                kind,
4622                                device_icon,
4623                                percentage,
4624                                state,
4625                                battery_level,
4626                                time_state);
4627         g_free (device_icon);
4628         g_object_unref (icon);
4629         return value;
4630 }
4631 
4632 /* returns new level */
4633 static void
handle_method_call_keyboard(CsdPowerManager * manager,const gchar * method_name,GVariant * parameters,GDBusMethodInvocation * invocation)4634 handle_method_call_keyboard (CsdPowerManager *manager,
4635                              const gchar *method_name,
4636                              GVariant *parameters,
4637                              GDBusMethodInvocation *invocation)
4638 {
4639         gint step;
4640         gint value = -1;
4641         gboolean ret;
4642         guint percentage;
4643         GError *error = NULL;
4644 
4645         if (g_strcmp0 (method_name, "GetPercentage") == 0) {
4646                 g_debug ("keyboard get percentage");
4647                 ret = upower_kbd_get_percentage (manager, &error);
4648                 value = manager->priv->kbd_brightness_now;
4649 
4650         } else if (g_strcmp0 (method_name, "SetPercentage") == 0) {
4651                 g_debug ("keyboard set percentage");
4652 
4653                 guint value_tmp;
4654                 g_variant_get (parameters, "(u)", &percentage);
4655                 value_tmp = PERCENTAGE_TO_ABS (0, manager->priv->kbd_brightness_max, percentage);
4656 
4657                 ret = upower_kbd_set_brightness (manager, value_tmp, &error);
4658                 if (ret)
4659                         value = value_tmp;
4660 
4661         } else if (g_strcmp0 (method_name, "StepUp") == 0) {
4662                 g_debug ("keyboard step up");
4663                 step = BRIGHTNESS_STEP_AMOUNT (manager->priv->kbd_brightness_max);
4664                 value = MIN (manager->priv->kbd_brightness_now + step,
4665                              manager->priv->kbd_brightness_max);
4666                 ret = upower_kbd_set_brightness (manager, value, &error);
4667 
4668         } else if (g_strcmp0 (method_name, "StepDown") == 0) {
4669                 g_debug ("keyboard step down");
4670                 step = BRIGHTNESS_STEP_AMOUNT (manager->priv->kbd_brightness_max);
4671                 value = MAX (manager->priv->kbd_brightness_now - step, 0);
4672                 ret = upower_kbd_set_brightness (manager, value, &error);
4673         } else if (g_strcmp0 (method_name, "GetStep") == 0) {
4674                 g_debug ("keyboard get step");
4675                 value = BRIGHTNESS_STEP_AMOUNT (manager->priv->kbd_brightness_max);
4676                 ret = (value > 0);
4677         } else if (g_strcmp0 (method_name, "Toggle") == 0) {
4678                 ret = upower_kbd_toggle (manager, &error);
4679                 value = manager->priv->kbd_brightness_now;
4680         } else {
4681                 g_assert_not_reached ();
4682         }
4683 
4684         /* return value */
4685         if (!ret) {
4686                 g_dbus_method_invocation_return_gerror (invocation,
4687                                                         error);
4688                 g_error_free (error);
4689         } else {
4690                 percentage = ABS_TO_PERCENTAGE (0,
4691                                                 manager->priv->kbd_brightness_max,
4692                                                 value);
4693                 g_dbus_method_invocation_return_value (invocation,
4694                                                        g_variant_new ("(u)", percentage));
4695         }
4696 }
4697 
4698 static void
handle_method_call_screen(CsdPowerManager * manager,const gchar * method_name,GVariant * parameters,GDBusMethodInvocation * invocation)4699 handle_method_call_screen (CsdPowerManager *manager,
4700                            const gchar *method_name,
4701                            GVariant *parameters,
4702                            GDBusMethodInvocation *invocation)
4703 {
4704         gboolean ret = FALSE;
4705         gint value = -1;
4706         guint value_tmp;
4707         GError *error = NULL;
4708 
4709         if ((g_strcmp0 (method_name, "GetPercentage") == 0) || (g_strcmp0 (method_name, "SetPercentage") == 0)) {
4710                 if (g_strcmp0 (method_name, "GetPercentage") == 0) {
4711                         g_debug ("screen get percentage");
4712                         value = backlight_get_percentage (manager, &error);
4713 
4714                 } else if (g_strcmp0 (method_name, "SetPercentage") == 0) {
4715                         g_debug ("screen set percentage");
4716                         g_variant_get (parameters, "(u)", &value_tmp);
4717                         ret = backlight_set_percentage (manager, value_tmp, TRUE, &error);
4718                         if (ret)
4719                                 value = value_tmp;
4720                 }
4721 
4722                 /* return value */
4723                 if (value < 0) {
4724                         g_dbus_method_invocation_return_gerror (invocation,
4725                                                                 error);
4726                         g_error_free (error);
4727                 } else {
4728                         g_dbus_method_invocation_return_value (invocation,
4729                                                                g_variant_new ("(u)",
4730                                                                               value));
4731                 }
4732         } else if ((g_strcmp0 (method_name, "StepUp") == 0) || (g_strcmp0 (method_name, "StepDown") == 0)) {
4733                 if (g_strcmp0 (method_name, "StepUp") == 0) {
4734                         g_debug ("screen step up");
4735                         value = backlight_step_up (manager, &error);
4736                 } else if (g_strcmp0 (method_name, "StepDown") == 0) {
4737                         g_debug ("screen step down");
4738                         value = backlight_step_down (manager, &error);
4739                 }
4740 
4741                 /* return value */
4742                 if (value < 0) {
4743                         g_dbus_method_invocation_return_gerror (invocation,
4744                                                                 error);
4745                         g_error_free (error);
4746                 } else {
4747                         g_dbus_method_invocation_return_value (invocation,
4748                                                                g_variant_new ("(ui)",
4749                                                                               value,
4750                                                                               backlight_get_output_id (manager)));
4751                 }
4752         } else {
4753                 g_assert_not_reached ();
4754         }
4755 }
4756 
4757 static gboolean
screen_iface_method_cb(CsdScreen * object,GDBusMethodInvocation * invocation,gpointer user_data)4758 screen_iface_method_cb (CsdScreen              *object,
4759                         GDBusMethodInvocation *invocation,
4760                         gpointer               user_data)
4761 {
4762     CsdPowerManager *manager = CSD_POWER_MANAGER (user_data);
4763 
4764     handle_method_call_screen (manager,
4765                                g_dbus_method_invocation_get_method_name (invocation),
4766                                g_dbus_method_invocation_get_parameters (invocation),
4767                                invocation);
4768 
4769     return TRUE;
4770 }
4771 
4772 static gboolean
screen_iface_set_method_cb(CsdScreen * object,GDBusMethodInvocation * invocation,guint percent,gpointer user_data)4773 screen_iface_set_method_cb (CsdScreen              *object,
4774                             GDBusMethodInvocation *invocation,
4775                             guint                  percent,
4776                             gpointer               user_data)
4777 {
4778     CsdPowerManager *manager = CSD_POWER_MANAGER (user_data);
4779 
4780     handle_method_call_screen (manager,
4781                                g_dbus_method_invocation_get_method_name (invocation),
4782                                g_dbus_method_invocation_get_parameters (invocation),
4783                                invocation);
4784 
4785     return TRUE;
4786 }
4787 
4788 static gboolean
keyboard_iface_method_cb(CsdScreen * object,GDBusMethodInvocation * invocation,gpointer user_data)4789 keyboard_iface_method_cb (CsdScreen              *object,
4790                            GDBusMethodInvocation *invocation,
4791                            gpointer               user_data)
4792 {
4793     CsdPowerManager *manager = CSD_POWER_MANAGER (user_data);
4794 
4795     handle_method_call_keyboard (manager,
4796                                  g_dbus_method_invocation_get_method_name (invocation),
4797                                  g_dbus_method_invocation_get_parameters (invocation),
4798                                  invocation);
4799 
4800     return TRUE;
4801 }
4802 
4803 static gboolean
keyboard_iface_set_method_cb(CsdScreen * object,GDBusMethodInvocation * invocation,guint percent,gpointer user_data)4804 keyboard_iface_set_method_cb (CsdScreen              *object,
4805                               GDBusMethodInvocation *invocation,
4806                               guint                  percent,
4807                               gpointer               user_data)
4808 {
4809     CsdPowerManager *manager = CSD_POWER_MANAGER (user_data);
4810 
4811     handle_method_call_keyboard (manager,
4812                                  g_dbus_method_invocation_get_method_name (invocation),
4813                                  g_dbus_method_invocation_get_parameters (invocation),
4814                                  invocation);
4815 
4816     return TRUE;
4817 }
4818 
4819 static gboolean
power_iface_handle_get_primary_device(CsdPower * object,GDBusMethodInvocation * invocation,gpointer user_data)4820 power_iface_handle_get_primary_device (CsdPower              *object,
4821                                        GDBusMethodInvocation *invocation,
4822                                        gpointer               user_data)
4823 {
4824         CsdPowerManager *manager = CSD_POWER_MANAGER (user_data);
4825         UpDevice *device;
4826         GVariant *tuple = NULL;
4827         GVariant *value = NULL;
4828 
4829         g_debug ("Handling Power interface method GetPrimaryDevice");
4830 
4831         /* get the virtual device */
4832         device = engine_get_primary_device (manager);
4833         if (device == NULL) {
4834                 g_dbus_method_invocation_return_dbus_error (invocation,
4835                                                             "org.cinnamon.SettingsDaemon.Power.Failed",
4836                                                             "There is no primary device.");
4837                 return TRUE;
4838         }
4839 
4840         /* return the value */
4841         value = device_to_variant_blob (device);
4842         tuple = g_variant_new_tuple (&value, 1);
4843         g_dbus_method_invocation_return_value (invocation, tuple);
4844         g_object_unref (device);
4845 
4846         return TRUE;
4847 }
4848 
4849 static gboolean
power_iface_handle_get_devices(CsdPower * object,GDBusMethodInvocation * invocation,gpointer user_data)4850 power_iface_handle_get_devices (CsdPower              *object,
4851                                 GDBusMethodInvocation *invocation,
4852                                 gpointer               user_data)
4853 {
4854         CsdPowerManager *manager = CSD_POWER_MANAGER (user_data);
4855         UpDevice *device;
4856         GPtrArray *array;
4857         guint i;
4858         GVariantBuilder *builder;
4859         GVariant *tuple = NULL;
4860         GVariant *value = NULL;
4861 
4862         g_debug ("Handling Power interface method GetDevices");
4863 
4864         /* create builder */
4865         builder = g_variant_builder_new (G_VARIANT_TYPE("a(sssusduut)"));
4866 
4867         /* add each tuple to the array */
4868         array = manager->priv->devices_array;
4869         for (i=0; i<array->len; i++) {
4870                 device = g_ptr_array_index (array, i);
4871                 value = device_to_variant_blob (device);
4872                 g_variant_builder_add_value (builder, value);
4873         }
4874 
4875         /* return the value */
4876         value = g_variant_builder_end (builder);
4877         tuple = g_variant_new_tuple (&value, 1);
4878         g_dbus_method_invocation_return_value (invocation, tuple);
4879         g_variant_builder_unref (builder);
4880 
4881         return TRUE;
4882 }
4883 
4884 static void
power_name_acquired(GDBusConnection * connection,const gchar * name,gpointer user_data)4885 power_name_acquired (GDBusConnection *connection,
4886                      const gchar     *name,
4887                      gpointer         user_data)
4888 {
4889         CsdPower *iface;
4890         CsdPowerManager *manager = CSD_POWER_MANAGER (user_data);
4891 
4892         iface = csd_power_skeleton_new ();
4893 
4894         g_signal_connect (iface,
4895                           "handle-get-primary-device",
4896                           G_CALLBACK (power_iface_handle_get_primary_device),
4897                           manager);
4898 
4899         g_signal_connect (iface,
4900                           "handle-get-devices",
4901                           G_CALLBACK (power_iface_handle_get_devices),
4902                           manager);
4903 
4904         g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (iface),
4905                                           connection,
4906                                           CSD_POWER_DBUS_PATH,
4907                                           NULL);
4908 
4909         manager->priv->power_iface = iface;
4910 
4911         engine_recalculate_state (manager);
4912 }
4913 
4914 static void
screen_name_acquired(GDBusConnection * connection,const gchar * name,gpointer user_data)4915 screen_name_acquired (GDBusConnection *connection,
4916                      const gchar     *name,
4917                      gpointer         user_data)
4918 {
4919         CsdScreen *iface;
4920         CsdPowerManager *manager = CSD_POWER_MANAGER (user_data);
4921 
4922         iface = csd_screen_skeleton_new ();
4923 
4924         g_signal_connect (iface,
4925                           "handle-get-percentage",
4926                           G_CALLBACK (screen_iface_method_cb),
4927                           manager);
4928 
4929         g_signal_connect (iface,
4930                           "handle-set-percentage",
4931                           G_CALLBACK (screen_iface_set_method_cb),
4932                           manager);
4933 
4934         g_signal_connect (iface,
4935                           "handle-step-down",
4936                           G_CALLBACK (screen_iface_method_cb),
4937                           manager);
4938 
4939         g_signal_connect (iface,
4940                           "handle-step-up",
4941                           G_CALLBACK (screen_iface_method_cb),
4942                           manager);
4943 
4944         g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (iface),
4945                                           connection,
4946                                           CSD_POWER_DBUS_PATH,
4947                                           NULL);
4948 
4949         manager->priv->screen_iface = iface;
4950 
4951         backlight_emit_changed (manager);
4952 }
4953 
4954 static void
keyboard_name_acquired(GDBusConnection * connection,const gchar * name,gpointer user_data)4955 keyboard_name_acquired (GDBusConnection *connection,
4956                         const gchar     *name,
4957                         gpointer         user_data)
4958 {
4959         CsdKeyboard *iface;
4960         CsdPowerManager *manager = CSD_POWER_MANAGER (user_data);
4961 
4962         iface = csd_keyboard_skeleton_new ();
4963 
4964         g_signal_connect (iface,
4965                           "handle-get-percentage",
4966                           G_CALLBACK (keyboard_iface_method_cb),
4967                           manager);
4968 
4969         g_signal_connect (iface,
4970                           "handle-set-percentage",
4971                           G_CALLBACK (keyboard_iface_set_method_cb),
4972                           manager);
4973 
4974         g_signal_connect (iface,
4975                           "handle-step-down",
4976                           G_CALLBACK (keyboard_iface_method_cb),
4977                           manager);
4978 
4979         g_signal_connect (iface,
4980                           "handle-step-up",
4981                           G_CALLBACK (keyboard_iface_method_cb),
4982                           manager);
4983 
4984         g_signal_connect (iface,
4985                           "handle-get-step",
4986                           G_CALLBACK (keyboard_iface_method_cb),
4987                           manager);
4988 
4989         g_signal_connect (iface,
4990                           "handle-toggle",
4991                           G_CALLBACK (keyboard_iface_method_cb),
4992                           manager);
4993 
4994         g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (iface),
4995                                           connection,
4996                                           CSD_POWER_DBUS_PATH,
4997                                           NULL);
4998 
4999         manager->priv->keyboard_iface = iface;
5000 
5001         upower_kbd_emit_changed (manager);
5002 }
5003 
5004 static void
on_bus_gotten(GObject * source_object,GAsyncResult * res,CsdPowerManager * manager)5005 on_bus_gotten (GObject             *source_object,
5006                GAsyncResult        *res,
5007                CsdPowerManager     *manager)
5008 {
5009         GDBusConnection *connection;
5010         GError *error = NULL;
5011 
5012         if (manager->priv->bus_cancellable == NULL ||
5013             g_cancellable_is_cancelled (manager->priv->bus_cancellable)) {
5014                 g_warning ("Operation has been cancelled, so not retrieving session bus");
5015                 return;
5016         }
5017 
5018         connection = g_bus_get_finish (res, &error);
5019         if (connection == NULL) {
5020                 g_warning ("Could not get session bus: %s", error->message);
5021                 g_error_free (error);
5022                 return;
5023         }
5024 
5025         manager->priv->connection = connection;
5026 
5027         manager->priv->p_name_id = g_bus_own_name_on_connection (connection,
5028                                                                  CSD_POWER_DBUS_INTERFACE,
5029                                                                  G_BUS_NAME_OWNER_FLAGS_NONE,
5030                                                                  power_name_acquired,
5031                                                                  NULL,
5032                                                                  manager,
5033                                                                  NULL);
5034         manager->priv->s_name_id = g_bus_own_name_on_connection (connection,
5035                                                                  CSD_POWER_DBUS_INTERFACE_SCREEN,
5036                                                                  G_BUS_NAME_OWNER_FLAGS_NONE,
5037                                                                  screen_name_acquired,
5038                                                                  NULL,
5039                                                                  manager,
5040                                                                  NULL);
5041         manager->priv->k_name_id = g_bus_own_name_on_connection (connection,
5042                                                                  CSD_POWER_DBUS_INTERFACE_KEYBOARD,
5043                                                                  G_BUS_NAME_OWNER_FLAGS_NONE,
5044                                                                  keyboard_name_acquired,
5045                                                                  NULL,
5046                                                                  manager,
5047                                                                  NULL);
5048 }
5049 
5050 static void
register_manager_dbus(CsdPowerManager * manager)5051 register_manager_dbus (CsdPowerManager *manager)
5052 {
5053         manager->priv->bus_cancellable = g_cancellable_new ();
5054 
5055         g_bus_get (G_BUS_TYPE_SESSION,
5056                    manager->priv->bus_cancellable,
5057                    (GAsyncReadyCallback) on_bus_gotten,
5058                    manager);
5059 }
5060 
5061 CsdPowerManager *
csd_power_manager_new(void)5062 csd_power_manager_new (void)
5063 {
5064         if (manager_object != NULL) {
5065                 g_object_ref (manager_object);
5066         } else {
5067                 manager_object = g_object_new (CSD_TYPE_POWER_MANAGER, NULL);
5068                 g_object_add_weak_pointer (manager_object,
5069                                            (gpointer *) &manager_object);
5070                 register_manager_dbus (manager_object);
5071         }
5072         return CSD_POWER_MANAGER (manager_object);
5073 }
5074