aboutsummaryrefslogtreecommitdiff
path: root/src/brightness.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/brightness.c')
-rw-r--r--src/brightness.c509
1 files changed, 509 insertions, 0 deletions
diff --git a/src/brightness.c b/src/brightness.c
new file mode 100644
index 0000000..5e7c5e5
--- /dev/null
+++ b/src/brightness.c
@@ -0,0 +1,509 @@
+/*
+ * Copyright 2014 Canonical Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+#include "brightness.h"
+
+#include <gio/gio.h>
+
+#define SCHEMA_NAME "com.ubuntu.touch.system"
+#define KEY_AUTO "auto-brightness"
+#define KEY_AUTO_SUPPORTED "auto-brightness-supported"
+#define KEY_BRIGHTNESS "brightness"
+#define KEY_NEED_DEFAULT "brightness-needs-hardware-default"
+
+enum
+{
+ PROP_0,
+ PROP_PERCENTAGE,
+ PROP_AUTO,
+ PROP_AUTO_SUPPORTED,
+ LAST_PROP
+};
+
+static GParamSpec* properties[LAST_PROP];
+
+typedef struct
+{
+ GDBusConnection * system_bus;
+ GCancellable * cancellable;
+
+ GSettings * settings;
+
+ guint powerd_name_tag;
+
+ double percentage;
+
+ /* powerd brightness params */
+ gint powerd_dim;
+ gint powerd_min;
+ gint powerd_max;
+ gint powerd_default_value;
+ gboolean powerd_ab_supported;
+ gboolean have_powerd_params;
+}
+IndicatorPowerBrightnessPrivate;
+
+typedef IndicatorPowerBrightnessPrivate priv_t;
+
+G_DEFINE_TYPE_WITH_PRIVATE(IndicatorPowerBrightness,
+ indicator_power_brightness,
+ G_TYPE_OBJECT)
+
+#define get_priv(o) ((priv_t*)indicator_power_brightness_get_instance_private(o))
+
+/***
+**** GObject virtual functions
+***/
+
+static void
+my_get_property(GObject * o,
+ guint property_id,
+ GValue * value,
+ GParamSpec * pspec)
+{
+ IndicatorPowerBrightness * self = INDICATOR_POWER_BRIGHTNESS(o);
+ priv_t * p = get_priv(self);
+
+ switch (property_id)
+ {
+ case PROP_PERCENTAGE:
+ g_value_set_double(value, indicator_power_brightness_get_percentage(self));
+ break;
+
+ case PROP_AUTO:
+ g_value_set_boolean(value, p->settings ? g_settings_get_boolean(p->settings, KEY_AUTO) : FALSE);
+ break;
+
+ case PROP_AUTO_SUPPORTED:
+ g_value_set_boolean(value, p->powerd_ab_supported);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(o, property_id, pspec);
+ }
+}
+
+static void
+my_set_property(GObject * o,
+ guint property_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+ IndicatorPowerBrightness * self = INDICATOR_POWER_BRIGHTNESS(o);
+ priv_t * p = get_priv(self);
+
+ switch (property_id)
+ {
+ case PROP_PERCENTAGE:
+ indicator_power_brightness_set_percentage(self, g_value_get_double(value));
+ break;
+
+ case PROP_AUTO:
+ if (p->settings != NULL)
+ g_settings_set_boolean (p->settings, KEY_AUTO, g_value_get_boolean(value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(o, property_id, pspec);
+ }
+}
+
+static void
+my_dispose(GObject * o)
+{
+ IndicatorPowerBrightness * self = INDICATOR_POWER_BRIGHTNESS(o);
+ priv_t * p = get_priv(self);
+
+ if (p->cancellable != NULL)
+ {
+ g_cancellable_cancel(p->cancellable);
+ g_clear_object(&p->cancellable);
+ }
+
+ if (p->powerd_name_tag)
+ {
+ g_bus_unwatch_name(p->powerd_name_tag);
+ p->powerd_name_tag = 0;
+ }
+
+ g_clear_object(&p->settings);
+ g_clear_object(&p->system_bus);
+
+ G_OBJECT_CLASS(indicator_power_brightness_parent_class)->dispose(o);
+}
+
+/***
+**** Percentage <-> Brightness Int conversion helpers
+***/
+
+static gdouble
+brightness_to_percentage(IndicatorPowerBrightness * self, int brightness)
+{
+ const priv_t * p;
+ gdouble percentage;
+
+ p = get_priv(self);
+ if (p->have_powerd_params)
+ {
+ const int lo = p->powerd_min;
+ const int hi = p->powerd_max;
+ percentage = (brightness-lo) / (double)(hi-lo);
+ }
+ else
+ {
+ percentage = 0;
+ }
+
+ return percentage;
+}
+
+static int
+percentage_to_brightness(IndicatorPowerBrightness * self, double percentage)
+{
+ const priv_t * p;
+ int brightness;
+
+ p = get_priv(self);
+ if (p->have_powerd_params)
+ {
+ const int lo = p->powerd_min;
+ const int hi = p->powerd_max;
+ brightness = (int)(lo + (percentage*(hi-lo)));
+ }
+ else
+ {
+ brightness = 0;
+ }
+
+ return brightness;
+}
+
+/**
+ * DBus Chatter: com.canonical.powerd
+ *
+ * This is used to get default value, and upper and lower bounds,
+ * of the brightness setting
+ */
+
+static void set_brightness_global(IndicatorPowerBrightness*, int);
+
+static void
+on_powerd_brightness_params_ready(GObject * source,
+ GAsyncResult * res,
+ gpointer gself)
+{
+ GError * error;
+ GVariant * v;
+
+ error = NULL;
+ v = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), res, &error);
+ if (v == NULL)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning("Unable to get system bus: %s", error->message);
+
+ g_error_free(error);
+ }
+ else
+ {
+ IndicatorPowerBrightness * self = INDICATOR_POWER_BRIGHTNESS(gself);
+ priv_t * p = get_priv(self);
+ const gboolean old_ab_supported = p->powerd_ab_supported;
+
+ p->have_powerd_params = TRUE;
+ g_variant_get(v, "((iiiib))", &p->powerd_dim,
+ &p->powerd_min,
+ &p->powerd_max,
+ &p->powerd_default_value,
+ &p->powerd_ab_supported);
+ g_debug("powerd brightness settings: dim=%d, min=%d, max=%d, default=%d, ab_supported=%d",
+ p->powerd_dim,
+ p->powerd_min,
+ p->powerd_max,
+ p->powerd_default_value,
+ (int)p->powerd_ab_supported);
+
+ if (old_ab_supported != p->powerd_ab_supported)
+ g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_AUTO_SUPPORTED]);
+
+ if (p->settings != NULL)
+ {
+ if (g_settings_get_boolean(p->settings, KEY_NEED_DEFAULT))
+ {
+ /* user's first session, so init the schema's default
+ brightness from powerd's hardware-specific params */
+ g_debug("%s is true, so initializing brightness to powerd default '%d'", KEY_NEED_DEFAULT, p->powerd_default_value);
+ set_brightness_global(self, p->powerd_default_value);
+ g_settings_set_boolean(p->settings, KEY_NEED_DEFAULT, FALSE);
+ }
+ else
+ {
+ /* not the first time, so restore the previous session's brightness */
+ set_brightness_global(self, g_settings_get_int(p->settings, KEY_BRIGHTNESS));
+ }
+ }
+
+ /* cleanup */
+ g_variant_unref(v);
+ }
+}
+
+static void
+call_powerd_get_brightness_params(IndicatorPowerBrightness * self)
+{
+ priv_t * p = get_priv(self);
+
+ g_dbus_connection_call(p->system_bus,
+ "com.canonical.powerd",
+ "/com/canonical/powerd",
+ "com.canonical.powerd",
+ "getBrightnessParams",
+ NULL,
+ G_VARIANT_TYPE("((iiiib))"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, /* default timeout */
+ p->cancellable,
+ on_powerd_brightness_params_ready,
+ self);
+}
+
+static void
+on_powerd_appeared(GDBusConnection * connection,
+ const gchar * bus_name G_GNUC_UNUSED,
+ const gchar * name_owner G_GNUC_UNUSED,
+ gpointer gself)
+{
+ IndicatorPowerBrightness * self = INDICATOR_POWER_BRIGHTNESS(gself);
+ priv_t * p = get_priv(self);
+
+ /* keep a handle to the system bus */
+ g_clear_object(&p->system_bus);
+ p->system_bus = g_object_ref(connection);
+
+ /* update our cache of powerd's brightness params */
+ call_powerd_get_brightness_params(self);
+}
+
+static void
+on_powerd_vanished(GDBusConnection * connection G_GNUC_UNUSED,
+ const gchar * bus_name G_GNUC_UNUSED,
+ gpointer gself)
+{
+ priv_t * p = get_priv(INDICATOR_POWER_BRIGHTNESS(gself));
+
+ p->have_powerd_params = FALSE;
+}
+
+/**
+ * DBus Chatter: com.canonical.Unity.Screen
+ *
+ * Used to set the backlight brightness via setUserBrightness
+ */
+
+/* setUserBrightness doesn't return anything,
+ so this function is just to check for bus error messages */
+static void
+on_set_uscreen_user_brightness_result(GObject * system_bus,
+ GAsyncResult * res,
+ gpointer gself G_GNUC_UNUSED)
+{
+ GError * error;
+ GVariant * v;
+
+ error = NULL;
+ v = g_dbus_connection_call_finish(G_DBUS_CONNECTION(system_bus), res, &error);
+ if (error != NULL)
+ {
+ if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning("Unable to call uscreen.setBrightness: %s", error->message);
+
+ g_error_free(error);
+ }
+
+ g_clear_pointer(&v, g_variant_unref);
+}
+
+static void
+set_uscreen_user_brightness(IndicatorPowerBrightness * self,
+ int value)
+{
+ priv_t * p = get_priv(self);
+
+ g_dbus_connection_call(p->system_bus,
+ "com.canonical.Unity.Screen",
+ "/com/canonical/Unity/Screen",
+ "com.canonical.Unity.Screen",
+ "setUserBrightness",
+ g_variant_new("(i)", value),
+ NULL, /* no return args */
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, /* default timeout */
+ p->cancellable,
+ on_set_uscreen_user_brightness_result,
+ self);
+}
+
+/***
+****
+***/
+
+static void
+set_brightness_local(IndicatorPowerBrightness * self, int brightness)
+{
+ priv_t * p = get_priv(self);
+ p->percentage = brightness_to_percentage(self, brightness);
+ g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_PERCENTAGE]);
+}
+
+static void
+on_brightness_changed_in_schema(GSettings * settings,
+ gchar * key,
+ gpointer gself)
+{
+ set_brightness_local(INDICATOR_POWER_BRIGHTNESS(gself),
+ g_settings_get_int(settings, key));
+}
+
+static void
+set_brightness_global(IndicatorPowerBrightness * self, int brightness)
+{
+ priv_t * p = get_priv(self);
+
+ set_uscreen_user_brightness(self, brightness);
+
+ if (p->settings != NULL)
+ g_settings_set_int(p->settings, KEY_BRIGHTNESS, brightness);
+ else
+ set_brightness_local(self, brightness);
+}
+
+static void
+on_auto_changed_in_schema(IndicatorPowerBrightness * self)
+{
+ g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_AUTO]);
+}
+
+
+/***
+**** Instantiation
+***/
+
+static void
+indicator_power_brightness_init(IndicatorPowerBrightness * self)
+{
+ priv_t * p;
+ GSettingsSchema * schema;
+
+ p = get_priv(self);
+ p->cancellable = g_cancellable_new();
+
+ schema = g_settings_schema_source_lookup(g_settings_schema_source_get_default(),
+ SCHEMA_NAME,
+ TRUE);
+
+ /* "brightness" is only spec'ed for the phone profile,
+ so fail gracefully & silently if we don't have the
+ schema for it. */
+ if (schema != NULL)
+ {
+ if (g_settings_schema_has_key(schema, KEY_BRIGHTNESS))
+ {
+ p->settings = g_settings_new(SCHEMA_NAME);
+ g_signal_connect(p->settings, "changed::" KEY_BRIGHTNESS,
+ G_CALLBACK(on_brightness_changed_in_schema), self);
+ g_signal_connect_swapped(p->settings, "changed::" KEY_AUTO,
+ G_CALLBACK(on_auto_changed_in_schema), self);
+ }
+ g_settings_schema_unref(schema);
+ }
+
+ p->powerd_name_tag = g_bus_watch_name(G_BUS_TYPE_SYSTEM,
+ "com.canonical.powerd",
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ on_powerd_appeared,
+ on_powerd_vanished,
+ self,
+ NULL);
+}
+
+static void
+indicator_power_brightness_class_init(IndicatorPowerBrightnessClass * klass)
+{
+ GObjectClass * object_class = G_OBJECT_CLASS(klass);
+
+ object_class->dispose = my_dispose;
+ object_class->get_property = my_get_property;
+ object_class->set_property = my_set_property;
+
+ properties[PROP_0] = NULL;
+
+ properties[PROP_PERCENTAGE] = g_param_spec_double(
+ "percentage",
+ "Percentage",
+ "Brightness percentage",
+ 0.0, /* minimum */
+ 1.0, /* maximum */
+ 0.8,
+ G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_AUTO] = g_param_spec_boolean(
+ "auto-brightness",
+ "Auto-Brightness",
+ "Automatically adjust brightness level",
+ FALSE,
+ G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_AUTO_SUPPORTED] = g_param_spec_boolean(
+ "auto-brightness-supported",
+ "Auto-Brightness Supported",
+ "True if the device can automatically adjust brightness",
+ FALSE,
+ G_PARAM_READABLE|G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(object_class, LAST_PROP, properties);
+}
+
+/***
+**** Public API
+***/
+
+IndicatorPowerBrightness *
+indicator_power_brightness_new(void)
+{
+ gpointer o = g_object_new(INDICATOR_TYPE_POWER_BRIGHTNESS, NULL);
+
+ return INDICATOR_POWER_BRIGHTNESS(o);
+}
+
+void
+indicator_power_brightness_set_percentage(IndicatorPowerBrightness * self,
+ double percentage)
+{
+ g_return_if_fail(INDICATOR_IS_POWER_BRIGHTNESS(self));
+
+ set_brightness_global(self, percentage_to_brightness(self, percentage));
+}
+
+double
+indicator_power_brightness_get_percentage(IndicatorPowerBrightness * self)
+{
+ g_return_val_if_fail(INDICATOR_IS_POWER_BRIGHTNESS(self), 0.0);
+
+ return get_priv(self)->percentage;
+}