diff options
author | Stefano Karapetsas <[email protected]> | 2011-12-11 12:55:19 +0100 |
---|---|---|
committer | Stefano Karapetsas <[email protected]> | 2011-12-11 12:55:19 +0100 |
commit | 51175189c6d7313a3b84019e39496f957c4e6164 (patch) | |
tree | e4c2c130fa3140bca28685ef900f04a012e53dcd /src/gpm-engine.c | |
download | mate-power-manager-51175189c6d7313a3b84019e39496f957c4e6164.tar.bz2 mate-power-manager-51175189c6d7313a3b84019e39496f957c4e6164.tar.xz |
moved from Mate-Extra
Diffstat (limited to 'src/gpm-engine.c')
-rw-r--r-- | src/gpm-engine.c | 1267 |
1 files changed, 1267 insertions, 0 deletions
diff --git a/src/gpm-engine.c b/src/gpm-engine.c new file mode 100644 index 0000000..1ca78b8 --- /dev/null +++ b/src/gpm-engine.c @@ -0,0 +1,1267 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2007-2008 Richard Hughes <[email protected]> + * + * Licensed under the GNU General Public License Version 2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include <string.h> +#include <glib.h> +#include <glib/gi18n.h> +#include <mateconf/mateconf-client.h> +#include <libupower-glib/upower.h> + +#include "egg-debug.h" + +#include "gpm-common.h" +#include "gpm-upower.h" +#include "gpm-marshal.h" +#include "gpm-engine.h" +#include "gpm-stock-icons.h" +#include "gpm-prefs-server.h" +#include "gpm-phone.h" + +static void gpm_engine_finalize (GObject *object); + +#define GPM_ENGINE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GPM_TYPE_ENGINE, GpmEnginePrivate)) +#define GPM_ENGINE_RESUME_DELAY 2*1000 +#define GPM_ENGINE_WARN_ACCURACY 20 + +struct GpmEnginePrivate +{ + MateConfClient *conf; + UpClient *client; + UpDevice *battery_composite; + GPtrArray *array; + GpmPhone *phone; + GpmIconPolicy icon_policy; + gchar *previous_icon; + gchar *previous_summary; + + gboolean use_time_primary; + gboolean time_is_accurate; + + guint low_percentage; + guint critical_percentage; + guint action_percentage; + guint low_time; + guint critical_time; + guint action_time; +}; + +enum { + ICON_CHANGED, + SUMMARY_CHANGED, + FULLY_CHARGED, + CHARGE_LOW, + CHARGE_CRITICAL, + CHARGE_ACTION, + DISCHARGING, + LOW_CAPACITY, + PERHAPS_RECALL, + LAST_SIGNAL +}; + +static guint signals [LAST_SIGNAL] = { 0 }; +static gpointer gpm_engine_object = NULL; + +G_DEFINE_TYPE (GpmEngine, gpm_engine, G_TYPE_OBJECT) + +static UpDevice *gpm_engine_get_composite_device (GpmEngine *engine, UpDevice *original_device); +static UpDevice *gpm_engine_update_composite_device (GpmEngine *engine, UpDevice *original_device); + +typedef enum { + GPM_ENGINE_WARNING_NONE = 0, + GPM_ENGINE_WARNING_DISCHARGING = 1, + GPM_ENGINE_WARNING_LOW = 2, + GPM_ENGINE_WARNING_CRITICAL = 3, + GPM_ENGINE_WARNING_ACTION = 4 +} GpmEngineWarning; + +/** + * gpm_engine_get_warning_csr: + **/ +static GpmEngineWarning +gpm_engine_get_warning_csr (GpmEngine *engine, UpDevice *device) +{ + gdouble percentage; + + /* get device properties */ + g_object_get (device, "percentage", &percentage, NULL); + + if (percentage < 26.0f) + return GPM_ENGINE_WARNING_LOW; + else if (percentage < 13.0f) + return GPM_ENGINE_WARNING_CRITICAL; + return GPM_ENGINE_WARNING_NONE; +} + +/** + * gpm_engine_get_warning_percentage: + **/ +static GpmEngineWarning +gpm_engine_get_warning_percentage (GpmEngine *engine, UpDevice *device) +{ + gdouble percentage; + + /* get device properties */ + g_object_get (device, "percentage", &percentage, NULL); + + if (percentage <= engine->priv->action_percentage) + return GPM_ENGINE_WARNING_ACTION; + if (percentage <= engine->priv->critical_percentage) + return GPM_ENGINE_WARNING_CRITICAL; + if (percentage <= engine->priv->low_percentage) + return GPM_ENGINE_WARNING_LOW; + return GPM_ENGINE_WARNING_NONE; +} + +/** + * gpm_engine_get_warning_time: + **/ +static GpmEngineWarning +gpm_engine_get_warning_time (GpmEngine *engine, UpDevice *device) +{ + UpDeviceKind kind; + gint64 time_to_empty; + + /* get device properties */ + g_object_get (device, + "kind", &kind, + "time-to-empty", &time_to_empty, + NULL); + + /* this is probably an error condition */ + if (time_to_empty == 0) { + egg_debug ("time zero, falling back to percentage for %s", up_device_kind_to_string (kind)); + return gpm_engine_get_warning_percentage (engine, device); + } + + if (time_to_empty <= engine->priv->action_time) + return GPM_ENGINE_WARNING_ACTION; + if (time_to_empty <= engine->priv->critical_time) + return GPM_ENGINE_WARNING_CRITICAL; + if (time_to_empty <= engine->priv->low_time) + return GPM_ENGINE_WARNING_LOW; + return GPM_ENGINE_WARNING_NONE; +} + +/** + * gpm_engine_get_warning: + * + * This gets the possible engine state for the device according to the + * policy, which could be per-percent, or per-time. + * + * Return value: A GpmEngine state, e.g. GPM_ENGINE_WARNING_DISCHARGING + **/ +static GpmEngineWarning +gpm_engine_get_warning (GpmEngine *engine, UpDevice *device) +{ + UpDeviceKind kind; + UpDeviceState state; + GpmEngineWarning warning_type; + + /* get device properties */ + g_object_get (device, + "kind", &kind, + "state", &state, + NULL); + + /* default to no engine */ + warning_type = GPM_ENGINE_WARNING_NONE; + + /* if the device in question is on ac, don't give a warning */ + if (state == UP_DEVICE_STATE_CHARGING) + goto out; + + if (kind == UP_DEVICE_KIND_MOUSE || + kind == UP_DEVICE_KIND_KEYBOARD) { + + warning_type = gpm_engine_get_warning_csr (engine, device); + + } else if (kind == UP_DEVICE_KIND_UPS || +#if UP_CHECK_VERSION(0,9,5) + kind == UP_DEVICE_KIND_MEDIA_PLAYER || + kind == UP_DEVICE_KIND_TABLET || + kind == UP_DEVICE_KIND_COMPUTER || +#endif + kind == UP_DEVICE_KIND_PDA) { + + warning_type = gpm_engine_get_warning_percentage (engine, device); + + } else if (kind == UP_DEVICE_KIND_PHONE) { + + warning_type = gpm_engine_get_warning_percentage (engine, device); + + } else if (kind == UP_DEVICE_KIND_BATTERY) { + /* only use the time when it is accurate, and MateConf is not disabled */ + if (engine->priv->use_time_primary) + warning_type = gpm_engine_get_warning_time (engine, device); + else + warning_type = gpm_engine_get_warning_percentage (engine, device); + } + + /* If we have no important engines, we should test for discharging */ + if (warning_type == GPM_ENGINE_WARNING_NONE) { + if (state == UP_DEVICE_STATE_DISCHARGING) + warning_type = GPM_ENGINE_WARNING_DISCHARGING; + } + + out: + return warning_type; +} + +/** + * gpm_engine_get_summary: + * @engine: This engine class instance + * @string: The returned string + * + * Returns the complete tooltip ready for display + **/ +gchar * +gpm_engine_get_summary (GpmEngine *engine) +{ + guint i; + GPtrArray *array; + UpDevice *device; + UpDeviceState state; + GString *tooltip = NULL; + gchar *part; + gboolean is_present; + + g_return_val_if_fail (GPM_IS_ENGINE (engine), NULL); + + /* need to get AC state */ + tooltip = g_string_new (""); + + /* do we have specific device types? */ + array = engine->priv->array; + for (i=0;i<array->len;i++) { + device = g_ptr_array_index (engine->priv->array, i); + g_object_get (device, + "is-present", &is_present, + "state", &state, + NULL); + if (!is_present) + continue; + if (state == UP_DEVICE_STATE_EMPTY) + continue; + part = gpm_upower_get_device_summary (device); + if (part != NULL) + g_string_append_printf (tooltip, "%s\n", part); + g_free (part); + } + + /* remove the last \n */ + g_string_truncate (tooltip, tooltip->len-1); + + egg_debug ("tooltip: %s", tooltip->str); + + return g_string_free (tooltip, FALSE); +} + +/** + * gpm_engine_get_icon_priv: + * + * Returns the icon + **/ +static gchar * +gpm_engine_get_icon_priv (GpmEngine *engine, UpDeviceKind device_kind, GpmEngineWarning warning, gboolean use_state) +{ + guint i; + GPtrArray *array; + UpDevice *device; + GpmEngineWarning warning_temp; + UpDeviceKind kind; + UpDeviceState state; + gboolean is_present; + + /* do we have specific device types? */ + array = engine->priv->array; + for (i=0;i<array->len;i++) { + device = g_ptr_array_index (engine->priv->array, i); + + /* get device properties */ + g_object_get (device, + "kind", &kind, + "state", &state, + "is-present", &is_present, + NULL); + + /* if battery then use composite device to cope with multiple batteries */ + if (kind == UP_DEVICE_KIND_BATTERY) + device = gpm_engine_get_composite_device (engine, device); + + warning_temp = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(device), "engine-warning-old")); + if (kind == device_kind && is_present) { + if (warning != GPM_ENGINE_WARNING_NONE) { + if (warning_temp == warning) + return gpm_upower_get_device_icon (device); + continue; + } + if (use_state) { + if (state == UP_DEVICE_STATE_CHARGING || state == UP_DEVICE_STATE_DISCHARGING) + return gpm_upower_get_device_icon (device); + continue; + } + return gpm_upower_get_device_icon (device); + } + } + return NULL; +} + +/** + * gpm_engine_get_icon: + * + * Returns the icon + **/ +gchar * +gpm_engine_get_icon (GpmEngine *engine) +{ + gchar *icon = NULL; + + g_return_val_if_fail (GPM_IS_ENGINE (engine), NULL); + + /* policy */ + if (engine->priv->icon_policy == GPM_ICON_POLICY_NEVER) { + egg_debug ("no icon allowed, so no icon will be displayed."); + return NULL; + } + + /* we try CRITICAL: BATTERY, UPS, MOUSE, KEYBOARD */ + icon = gpm_engine_get_icon_priv (engine, UP_DEVICE_KIND_BATTERY, GPM_ENGINE_WARNING_CRITICAL, FALSE); + if (icon != NULL) + return icon; + icon = gpm_engine_get_icon_priv (engine, UP_DEVICE_KIND_UPS, GPM_ENGINE_WARNING_CRITICAL, FALSE); + if (icon != NULL) + return icon; + icon = gpm_engine_get_icon_priv (engine, UP_DEVICE_KIND_MOUSE, GPM_ENGINE_WARNING_CRITICAL, FALSE); + if (icon != NULL) + return icon; + icon = gpm_engine_get_icon_priv (engine, UP_DEVICE_KIND_KEYBOARD, GPM_ENGINE_WARNING_CRITICAL, FALSE); + if (icon != NULL) + return icon; + + /* policy */ + if (engine->priv->icon_policy == GPM_ICON_POLICY_CRITICAL) { + egg_debug ("no devices critical, so no icon will be displayed."); + return NULL; + } + + /* we try CRITICAL: BATTERY, UPS, MOUSE, KEYBOARD */ + icon = gpm_engine_get_icon_priv (engine, UP_DEVICE_KIND_BATTERY, GPM_ENGINE_WARNING_LOW, FALSE); + if (icon != NULL) + return icon; + icon = gpm_engine_get_icon_priv (engine, UP_DEVICE_KIND_UPS, GPM_ENGINE_WARNING_LOW, FALSE); + if (icon != NULL) + return icon; + icon = gpm_engine_get_icon_priv (engine, UP_DEVICE_KIND_MOUSE, GPM_ENGINE_WARNING_LOW, FALSE); + if (icon != NULL) + return icon; + icon = gpm_engine_get_icon_priv (engine, UP_DEVICE_KIND_KEYBOARD, GPM_ENGINE_WARNING_LOW, FALSE); + if (icon != NULL) + return icon; + + /* policy */ + if (engine->priv->icon_policy == GPM_ICON_POLICY_LOW) { + egg_debug ("no devices low, so no icon will be displayed."); + return NULL; + } + + /* we try (DIS)CHARGING: BATTERY, UPS */ + icon = gpm_engine_get_icon_priv (engine, UP_DEVICE_KIND_BATTERY, GPM_ENGINE_WARNING_NONE, TRUE); + if (icon != NULL) + return icon; + icon = gpm_engine_get_icon_priv (engine, UP_DEVICE_KIND_UPS, GPM_ENGINE_WARNING_NONE, TRUE); + if (icon != NULL) + return icon; + + /* policy */ + if (engine->priv->icon_policy == GPM_ICON_POLICY_CHARGE) { + egg_debug ("no devices (dis)charging, so no icon will be displayed."); + return NULL; + } + + /* we try PRESENT: BATTERY, UPS */ + icon = gpm_engine_get_icon_priv (engine, UP_DEVICE_KIND_BATTERY, GPM_ENGINE_WARNING_NONE, FALSE); + if (icon != NULL) + return icon; + icon = gpm_engine_get_icon_priv (engine, UP_DEVICE_KIND_UPS, GPM_ENGINE_WARNING_NONE, FALSE); + if (icon != NULL) + return icon; + + /* policy */ + if (engine->priv->icon_policy == GPM_ICON_POLICY_PRESENT) { + egg_debug ("no devices present, so no icon will be displayed."); + return NULL; + } + + /* we fallback to the ac_adapter icon */ + egg_debug ("Using fallback"); + return g_strdup (GPM_STOCK_AC_ADAPTER); +} + +/** + * gpm_engine_recalculate_state_icon: + */ +static gboolean +gpm_engine_recalculate_state_icon (GpmEngine *engine) +{ + gchar *icon; + + g_return_val_if_fail (engine != NULL, FALSE); + g_return_val_if_fail (GPM_IS_ENGINE (engine), FALSE); + + /* show a different icon if we are disconnected */ + icon = gpm_engine_get_icon (engine); + if (icon == NULL) { + /* none before, now none */ + if (engine->priv->previous_icon == NULL) + return FALSE; + /* icon before, now none */ + egg_debug ("** EMIT: icon-changed: none"); + g_signal_emit (engine, signals [ICON_CHANGED], 0, NULL); + + g_free (engine->priv->previous_icon); + engine->priv->previous_icon = NULL; + return TRUE; + } + + /* no icon before, now icon */ + if (engine->priv->previous_icon == NULL) { + egg_debug ("** EMIT: icon-changed: %s", icon); + g_signal_emit (engine, signals [ICON_CHANGED], 0, icon); + engine->priv->previous_icon = icon; + return TRUE; + } + + /* icon before, now different */ + if (strcmp (engine->priv->previous_icon, icon) != 0) { + g_free (engine->priv->previous_icon); + engine->priv->previous_icon = icon; + egg_debug ("** EMIT: icon-changed: %s", icon); + g_signal_emit (engine, signals [ICON_CHANGED], 0, icon); + return TRUE; + } + + egg_debug ("no change"); + /* nothing to do */ + g_free (icon); + return FALSE; +} + +/** + * gpm_engine_recalculate_state_summary: + */ +static gboolean +gpm_engine_recalculate_state_summary (GpmEngine *engine) +{ + gchar *summary; + + summary = gpm_engine_get_summary (engine); + if (engine->priv->previous_summary == NULL) { + engine->priv->previous_summary = summary; + egg_debug ("** EMIT: summary-changed(1): %s", summary); + g_signal_emit (engine, signals [SUMMARY_CHANGED], 0, summary); + return TRUE; + } + + if (strcmp (engine->priv->previous_summary, summary) != 0) { + g_free (engine->priv->previous_summary); + engine->priv->previous_summary = summary; + egg_debug ("** EMIT: summary-changed(2): %s", summary); + g_signal_emit (engine, signals [SUMMARY_CHANGED], 0, summary); + return TRUE; + } + egg_debug ("no change"); + /* nothing to do */ + g_free (summary); + return FALSE; +} + +/** + * gpm_engine_recalculate_state: + */ +static void +gpm_engine_recalculate_state (GpmEngine *engine) +{ + + g_return_if_fail (engine != NULL); + g_return_if_fail (GPM_IS_ENGINE (engine)); + + gpm_engine_recalculate_state_icon (engine); + gpm_engine_recalculate_state_summary (engine); +} + +/** + * gpm_engine_conf_key_changed_cb: + **/ +static void +gpm_engine_conf_key_changed_cb (MateConfClient *conf, guint cnxn_id, MateConfEntry *entry, GpmEngine *engine) +{ + MateConfValue *value; + gchar *icon_policy; + + if (entry == NULL) + return; + value = mateconf_entry_get_value (entry); + if (value == NULL) + return; + + if (strcmp (entry->key, GPM_CONF_USE_TIME_POLICY) == 0) { + + engine->priv->use_time_primary = mateconf_value_get_bool (value); + + } else if (strcmp (entry->key, GPM_CONF_UI_ICON_POLICY) == 0) { + + /* do we want to display the icon in the tray */ + icon_policy = mateconf_client_get_string (conf, GPM_CONF_UI_ICON_POLICY, NULL); + engine->priv->icon_policy = gpm_icon_policy_from_string (icon_policy); + g_free (icon_policy); + + /* perhaps change icon */ + gpm_engine_recalculate_state_icon (engine); + } +} + +/** + * gpm_engine_device_check_capacity: + **/ +static gboolean +gpm_engine_device_check_capacity (GpmEngine *engine, UpDevice *device) +{ + gboolean ret; + UpDeviceKind kind; + gdouble capacity; + + /* get device properties */ + g_object_get (device, + "kind", &kind, + "capacity", &capacity, + NULL); + + /* not laptop battery */ + if (kind != UP_DEVICE_KIND_BATTERY) + return FALSE; + + /* capacity okay */ + if (capacity > 50.0f) + return FALSE; + + /* capacity invalid */ + if (capacity < 1.0f) + return FALSE; + + /* only emit this if specified in mateconf */ + ret = mateconf_client_get_bool (engine->priv->conf, GPM_CONF_NOTIFY_LOW_CAPACITY, NULL); + if (ret) { + egg_debug ("** EMIT: low-capacity"); + g_signal_emit (engine, signals [LOW_CAPACITY], 0, device); + } + return TRUE; +} + +/** + * gpm_engine_get_composite_device: + **/ +static UpDevice * +gpm_engine_get_composite_device (GpmEngine *engine, UpDevice *original_device) +{ + guint battery_devices = 0; + GPtrArray *array; + UpDevice *device; + UpDeviceKind kind; + guint i; + + /* find out how many batteries in the system */ + array = engine->priv->array; + for (i=0;i<array->len;i++) { + device = g_ptr_array_index (engine->priv->array, i); + g_object_get (device, + "kind", &kind, + NULL); + if (kind == UP_DEVICE_KIND_BATTERY) + battery_devices++; + } + + /* just use the original device if only one primary battery */ + if (battery_devices <= 1) { + egg_debug ("using original device as only one primary battery"); + device = original_device; + goto out; + } + + /* use the composite device */ + device = engine->priv->battery_composite; +out: + /* return composite device or original device */ + return device; +} + +/** + * gpm_engine_update_composite_device: + **/ +static UpDevice * +gpm_engine_update_composite_device (GpmEngine *engine, UpDevice *original_device) +{ + guint i; + gdouble percentage = 0.0; + gdouble energy = 0.0; + gdouble energy_full = 0.0; + gdouble energy_rate = 0.0; + gdouble energy_total = 0.0; + gdouble energy_full_total = 0.0; + gdouble energy_rate_total = 0.0; + gint64 time_to_empty = 0; + gint64 time_to_full = 0; + guint battery_devices = 0; + gboolean is_charging = FALSE; + gboolean is_discharging = FALSE; + gboolean is_fully_charged = TRUE; + GPtrArray *array; + UpDevice *device; + UpDeviceState state; + UpDeviceKind kind; + gboolean debug; + gchar *text; + + /* are we printing to console? */ + debug = egg_debug_enabled (); + + /* update the composite device */ + array = engine->priv->array; + for (i=0;i<array->len;i++) { + device = g_ptr_array_index (engine->priv->array, i); + g_object_get (device, + "kind", &kind, + "state", &state, + "energy", &energy, + "energy-full", &energy_full, + "energy-rate", &energy_rate, + NULL); + if (kind != UP_DEVICE_KIND_BATTERY) + continue; + + if (debug) { + text = up_device_to_text (device); + egg_debug ("printing device %i:\n%s", i, text); + g_free (text); + } + + /* one of these will be charging or discharging */ + if (state == UP_DEVICE_STATE_CHARGING) + is_charging = TRUE; + if (state == UP_DEVICE_STATE_DISCHARGING) + is_discharging = TRUE; + if (state != UP_DEVICE_STATE_FULLY_CHARGED) + is_fully_charged = FALSE; + + /* sum up composite */ + energy_total += energy; + energy_full_total += energy_full; + energy_rate_total += energy_rate; + battery_devices++; + } + + /* just use the original device if only one primary battery */ + if (battery_devices == 1) { + egg_debug ("using original device as only one primary battery"); + device = original_device; + goto out; + } + + /* use percentage weighted for each battery capacity */ + percentage = 100.0 * energy_total / energy_full_total; + + /* set composite state */ + if (is_charging) + state = UP_DEVICE_STATE_CHARGING; + else if (is_discharging) + state = UP_DEVICE_STATE_DISCHARGING; + else if (is_fully_charged) + state = UP_DEVICE_STATE_FULLY_CHARGED; + else + state = UP_DEVICE_STATE_UNKNOWN; + + /* calculate a quick and dirty time remaining value */ + if (energy_rate_total > 0) { + if (state == UP_DEVICE_STATE_DISCHARGING) + time_to_empty = 3600 * (energy_total / energy_rate_total); + else if (state == UP_DEVICE_STATE_CHARGING) + time_to_full = 3600 * ((energy_full_total - energy_total) / energy_rate_total); + } + + /* okay, we can use the composite device */ + device = engine->priv->battery_composite; + + egg_debug ("printing composite device"); + g_object_set (device, + "energy", energy, + "energy-full", energy_full, + "energy-rate", energy_rate, + "time-to-empty", time_to_empty, + "time-to-full", time_to_full, + "percentage", percentage, + "state", state, + NULL); + if (debug) { + text = up_device_to_text (device); + egg_debug ("composite:\n%s", text); + g_free (text); + } + + /* force update of icon */ + gpm_engine_recalculate_state_icon (engine); +out: + /* return composite device or original device */ + return device; +} + +/** + * gpm_engine_device_add: + **/ +static void +gpm_engine_device_add (GpmEngine *engine, UpDevice *device) +{ + GpmEngineWarning warning; + UpDeviceState state; + UpDeviceKind kind; + UpDevice *composite; + + /* assign warning */ + warning = gpm_engine_get_warning (engine, device); + g_object_set_data (G_OBJECT(device), "engine-warning-old", GUINT_TO_POINTER(warning)); + + /* check capacity */ + gpm_engine_device_check_capacity (engine, device); + + /* get device properties */ + g_object_get (device, + "kind", &kind, + "state", &state, + NULL); + + /* add old state for transitions */ + egg_debug ("adding %s with state %s", up_device_get_object_path (device), up_device_state_to_string (state)); + g_object_set_data (G_OBJECT(device), "engine-state-old", GUINT_TO_POINTER(state)); + + if (kind == UP_DEVICE_KIND_BATTERY) { + egg_debug ("updating because we added a device"); + composite = gpm_engine_update_composite_device (engine, device); + + /* get the same values for the composite device */ + warning = gpm_engine_get_warning (engine, composite); + g_object_set_data (G_OBJECT(composite), "engine-warning-old", GUINT_TO_POINTER(warning)); + g_object_get (composite, "state", &state, NULL); + g_object_set_data (G_OBJECT(composite), "engine-state-old", GUINT_TO_POINTER(state)); + } +} + +/** + * gpm_engine_check_recall: + **/ +static gboolean +gpm_engine_check_recall (GpmEngine *engine, UpDevice *device) +{ + UpDeviceKind kind; + gboolean recall_notice = FALSE; + gchar *recall_vendor = NULL; + gchar *recall_url = NULL; + + /* get device properties */ + g_object_get (device, + "kind", &kind, + "recall-notice", &recall_notice, + "recall-vendor", &recall_vendor, + "recall-url", &recall_url, + NULL); + + /* not battery */ + if (kind != UP_DEVICE_KIND_BATTERY) + goto out; + + /* no recall data */ + if (!recall_notice) + goto out; + + /* emit signal for manager */ + egg_debug ("** EMIT: perhaps-recall"); + g_signal_emit (engine, signals [PERHAPS_RECALL], 0, device, recall_vendor, recall_url); +out: + g_free (recall_vendor); + g_free (recall_url); + return recall_notice; +} + +/** + * gpm_engine_coldplug_idle_cb: + **/ +static gboolean +gpm_engine_coldplug_idle_cb (GpmEngine *engine) +{ + guint i; + GPtrArray *array; + gboolean has_battery = FALSE; + gboolean has_ups = FALSE; + GpmPrefsServer *prefs_server; + UpDevice *device; + UpDeviceKind kind; + gboolean ret; + GError *error = NULL; + + g_return_val_if_fail (engine != NULL, FALSE); + g_return_val_if_fail (GPM_IS_ENGINE (engine), FALSE); + + /* get devices from UPower */ + ret = up_client_enumerate_devices_sync (engine->priv->client, NULL, &error); + if (!ret) { + egg_error ("failed to get device list: %s", error->message); + g_error_free (error); + goto out; + } + engine->priv->array = up_client_get_devices (engine->priv->client); + + /* do we have specific device types? */ + array = engine->priv->array; + for (i=0;i<array->len;i++) { + device = g_ptr_array_index (engine->priv->array, i); + + /* get device properties */ + g_object_get (device, + "kind", &kind, + NULL); + + if (kind == UP_DEVICE_KIND_BATTERY) + has_battery = TRUE; + else if (kind == UP_DEVICE_KIND_UPS) + has_ups = TRUE; + } + + /* only show the battery prefs section if we have batteries */ + prefs_server = gpm_prefs_server_new (); + if (has_battery) + gpm_prefs_server_set_capability (prefs_server, GPM_PREFS_SERVER_BATTERY); + if (has_ups) + gpm_prefs_server_set_capability (prefs_server, GPM_PREFS_SERVER_UPS); + g_object_unref (prefs_server); + + /* connected mobile phones */ + gpm_phone_coldplug (engine->priv->phone); + + gpm_engine_recalculate_state (engine); + + /* add to database */ + for (i=0;i<array->len;i++) { + device = g_ptr_array_index (engine->priv->array, i); + gpm_engine_device_add (engine, device); + gpm_engine_check_recall (engine, device); + } +out: + /* never repeat */ + return FALSE; +} + +/** + * gpm_engine_device_added_cb: + **/ +static void +gpm_engine_device_added_cb (UpClient *client, UpDevice *device, GpmEngine *engine) +{ + /* add to list */ + g_ptr_array_add (engine->priv->array, g_object_ref (device)); + gpm_engine_check_recall (engine, device); + + gpm_engine_recalculate_state (engine); +} + +/** + * gpm_engine_device_removed_cb: + **/ +static void +gpm_engine_device_removed_cb (UpClient *client, UpDevice *device, GpmEngine *engine) +{ + gboolean ret; + ret = g_ptr_array_remove (engine->priv->array, device); + if (!ret) + return; + gpm_engine_recalculate_state (engine); +} + + +/** + * gpm_engine_device_changed_cb: + **/ +static void +gpm_engine_device_changed_cb (UpClient *client, UpDevice *device, GpmEngine *engine) +{ + UpDeviceKind kind; + UpDeviceState state; + UpDeviceState state_old; + GpmEngineWarning warning_old; + GpmEngineWarning warning; + + /* get device properties */ + g_object_get (device, + "kind", &kind, + NULL); + + /* if battery then use composite device to cope with multiple batteries */ + if (kind == UP_DEVICE_KIND_BATTERY) { + egg_debug ("updating because %s changed", up_device_get_object_path (device)); + device = gpm_engine_update_composite_device (engine, device); + } + + /* get device properties (may be composite) */ + g_object_get (device, + "state", &state, + NULL); + + egg_debug ("%s state is now %s", up_device_get_object_path (device), up_device_state_to_string (state)); + + /* see if any interesting state changes have happened */ + state_old = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(device), "engine-state-old")); + if (state_old != state) { + if (state == UP_DEVICE_STATE_DISCHARGING) { + egg_debug ("** EMIT: discharging"); + g_signal_emit (engine, signals [DISCHARGING], 0, device); + } else if (state == UP_DEVICE_STATE_FULLY_CHARGED) { + egg_debug ("** EMIT: fully charged"); + g_signal_emit (engine, signals [FULLY_CHARGED], 0, device); + } + + /* save new state */ + g_object_set_data (G_OBJECT(device), "engine-state-old", GUINT_TO_POINTER(state)); + } + + /* check the warning state has not changed */ + warning_old = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(device), "engine-warning-old")); + warning = gpm_engine_get_warning (engine, device); + if (warning != warning_old) { + if (warning == GPM_ENGINE_WARNING_LOW) { + egg_debug ("** EMIT: charge-low"); + g_signal_emit (engine, signals [CHARGE_LOW], 0, device); + } else if (warning == GPM_ENGINE_WARNING_CRITICAL) { + egg_debug ("** EMIT: charge-critical"); + g_signal_emit (engine, signals [CHARGE_CRITICAL], 0, device); + } else if (warning == GPM_ENGINE_WARNING_ACTION) { + egg_debug ("** EMIT: charge-action"); + g_signal_emit (engine, signals [CHARGE_ACTION], 0, device); + } + /* save new state */ + g_object_set_data (G_OBJECT(device), "engine-warning-old", GUINT_TO_POINTER(warning)); + } + + gpm_engine_recalculate_state (engine); +} + +/** + * gpm_engine_get_devices: + * + * Return value: the UpDevice array, free with g_ptr_array_unref() + **/ +GPtrArray * +gpm_engine_get_devices (GpmEngine *engine) +{ + return g_ptr_array_ref (engine->priv->array); +} + +/** + * phone_device_added_cb: + **/ +static void +phone_device_added_cb (GpmPhone *phone, guint idx, GpmEngine *engine) +{ + UpDevice *device; + device = up_device_new (); + + egg_debug ("phone added %i", idx); + + /* get device properties */ + g_object_set (device, + "kind", UP_DEVICE_KIND_PHONE, + "is-rechargeable", TRUE, + "native-path", g_strdup_printf ("dummy:phone_%i", idx), + "is-present", TRUE, + NULL); + + /* state changed */ + gpm_engine_device_add (engine, device); + g_ptr_array_add (engine->priv->array, g_object_ref (device)); + gpm_engine_recalculate_state (engine); +} + +/** + * phone_device_removed_cb: + **/ +static void +phone_device_removed_cb (GpmPhone *phone, guint idx, GpmEngine *engine) +{ + guint i; + UpDevice *device; + UpDeviceKind kind; + + egg_debug ("phone removed %i", idx); + + for (i=0; i<engine->priv->array->len; i++) { + device = g_ptr_array_index (engine->priv->array, i); + + /* get device properties */ + g_object_get (device, + "kind", &kind, + NULL); + + if (kind == UP_DEVICE_KIND_PHONE) { + g_ptr_array_remove_index (engine->priv->array, i); + break; + } + } + + /* state changed */ + gpm_engine_recalculate_state (engine); +} + +/** + * phone_device_refresh_cb: + **/ +static void +phone_device_refresh_cb (GpmPhone *phone, guint idx, GpmEngine *engine) +{ + guint i; + UpDevice *device; + UpDeviceKind kind; + UpDeviceState state; + gboolean is_present; + gdouble percentage; + + egg_debug ("phone refresh %i", idx); + + for (i=0; i<engine->priv->array->len; i++) { + device = g_ptr_array_index (engine->priv->array, i); + + /* get device properties */ + g_object_get (device, + "kind", &kind, + "state", &state, + "percentage", &percentage, + "is-present", &is_present, + NULL); + + if (kind == UP_DEVICE_KIND_PHONE) { + is_present = gpm_phone_get_present (phone, idx); + state = gpm_phone_get_on_ac (phone, idx) ? UP_DEVICE_STATE_CHARGING : UP_DEVICE_STATE_DISCHARGING; + percentage = gpm_phone_get_percentage (phone, idx); + break; + } + } + + /* state changed */ + gpm_engine_recalculate_state (engine); +} + +/** + * gpm_engine_init: + * @engine: This class instance + **/ +static void +gpm_engine_init (GpmEngine *engine) +{ + gchar *icon_policy; + + engine->priv = GPM_ENGINE_GET_PRIVATE (engine); + + engine->priv->array = g_ptr_array_new_with_free_func (g_object_unref); + engine->priv->client = up_client_new (); + g_signal_connect (engine->priv->client, "device-added", + G_CALLBACK (gpm_engine_device_added_cb), engine); + g_signal_connect (engine->priv->client, "device-removed", + G_CALLBACK (gpm_engine_device_removed_cb), engine); + g_signal_connect (engine->priv->client, "device-changed", + G_CALLBACK (gpm_engine_device_changed_cb), engine); + + engine->priv->conf = mateconf_client_get_default (); + mateconf_client_notify_add (engine->priv->conf, GPM_CONF_DIR, + (MateConfClientNotifyFunc) gpm_engine_conf_key_changed_cb, + engine, NULL, NULL); + + engine->priv->phone = gpm_phone_new (); + g_signal_connect (engine->priv->phone, "device-added", + G_CALLBACK (phone_device_added_cb), engine); + g_signal_connect (engine->priv->phone, "device-removed", + G_CALLBACK (phone_device_removed_cb), engine); + g_signal_connect (engine->priv->phone, "device-refresh", + G_CALLBACK (phone_device_refresh_cb), engine); + + /* create a fake virtual composite battery */ + engine->priv->battery_composite = up_device_new (); + g_object_set (engine->priv->battery_composite, + "kind", UP_DEVICE_KIND_BATTERY, + "is-rechargeable", TRUE, + "native-path", "dummy:composite_battery", + "power-supply", TRUE, + "is-present", TRUE, + NULL); + + engine->priv->previous_icon = NULL; + engine->priv->previous_summary = NULL; + + /* do we want to display the icon in the tray */ + icon_policy = mateconf_client_get_string (engine->priv->conf, GPM_CONF_UI_ICON_POLICY, NULL); + engine->priv->icon_policy = gpm_icon_policy_from_string (icon_policy); + g_free (icon_policy); + + /* get percentage policy */ + engine->priv->low_percentage = mateconf_client_get_int (engine->priv->conf, GPM_CONF_THRESH_PERCENTAGE_LOW, NULL); + engine->priv->critical_percentage = mateconf_client_get_int (engine->priv->conf, GPM_CONF_THRESH_PERCENTAGE_CRITICAL, NULL); + engine->priv->action_percentage = mateconf_client_get_int (engine->priv->conf, GPM_CONF_THRESH_PERCENTAGE_ACTION, NULL); + + /* get time policy */ + engine->priv->low_time = mateconf_client_get_int (engine->priv->conf, GPM_CONF_THRESH_TIME_LOW, NULL); + engine->priv->critical_time = mateconf_client_get_int (engine->priv->conf, GPM_CONF_THRESH_TIME_CRITICAL, NULL); + engine->priv->action_time = mateconf_client_get_int (engine->priv->conf, GPM_CONF_THRESH_TIME_ACTION, NULL); + + /* we can disable this if the time remaining is inaccurate or just plain wrong */ + engine->priv->use_time_primary = mateconf_client_get_bool (engine->priv->conf, GPM_CONF_USE_TIME_POLICY, NULL); + if (engine->priv->use_time_primary) + egg_debug ("Using per-time notification policy"); + else + egg_debug ("Using percentage notification policy"); + + g_idle_add ((GSourceFunc) gpm_engine_coldplug_idle_cb, engine); +} + +/** + * gpm_engine_class_init: + * @engine: This class instance + **/ +static void +gpm_engine_class_init (GpmEngineClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = gpm_engine_finalize; + g_type_class_add_private (klass, sizeof (GpmEnginePrivate)); + + signals [ICON_CHANGED] = + g_signal_new ("icon-changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GpmEngineClass, icon_changed), + NULL, NULL, g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); + signals [SUMMARY_CHANGED] = + g_signal_new ("summary-changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GpmEngineClass, summary_changed), + NULL, NULL, g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); + signals [LOW_CAPACITY] = + g_signal_new ("low-capacity", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GpmEngineClass, low_capacity), + NULL, NULL, g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + signals [PERHAPS_RECALL] = + g_signal_new ("perhaps-recall", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GpmEngineClass, perhaps_recall), + NULL, NULL, gpm_marshal_VOID__POINTER_STRING_STRING, + G_TYPE_NONE, + 3, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_STRING); + signals [FULLY_CHARGED] = + g_signal_new ("fully-charged", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GpmEngineClass, fully_charged), + NULL, NULL, g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + signals [DISCHARGING] = + g_signal_new ("discharging", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GpmEngineClass, discharging), + NULL, NULL, g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + signals [CHARGE_ACTION] = + g_signal_new ("charge-action", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GpmEngineClass, charge_action), + NULL, NULL, g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + signals [CHARGE_LOW] = + g_signal_new ("charge-low", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GpmEngineClass, charge_low), + NULL, NULL, g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + signals [CHARGE_CRITICAL] = + g_signal_new ("charge-critical", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GpmEngineClass, charge_critical), + NULL, NULL, g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); +} + +/** + * gpm_engine_finalize: + * @object: This class instance + **/ +static void +gpm_engine_finalize (GObject *object) +{ + GpmEngine *engine; + + g_return_if_fail (object != NULL); + g_return_if_fail (GPM_IS_ENGINE (object)); + + engine = GPM_ENGINE (object); + engine->priv = GPM_ENGINE_GET_PRIVATE (engine); + + g_ptr_array_unref (engine->priv->array); + g_object_unref (engine->priv->client); + g_object_unref (engine->priv->phone); + g_object_unref (engine->priv->battery_composite); + + g_free (engine->priv->previous_icon); + g_free (engine->priv->previous_summary); + + G_OBJECT_CLASS (gpm_engine_parent_class)->finalize (object); +} + +/** + * gpm_engine_new: + * Return value: new class instance. + **/ +GpmEngine * +gpm_engine_new (void) +{ + if (gpm_engine_object != NULL) { + g_object_ref (gpm_engine_object); + } else { + gpm_engine_object = g_object_new (GPM_TYPE_ENGINE, NULL); + g_object_add_weak_pointer (gpm_engine_object, &gpm_engine_object); + } + return GPM_ENGINE (gpm_engine_object); + +} + |