/* * Copyright 2014 Canonical Ltd. * Copyright 2021 Robert Tari * * 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 . * * Authors: * Charles Kerr * Robert Tari */ #include "brightness.h" #include "dbus-repowerd.h" #include #define SCHEMA_NAME "com.lomiri.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; DbusRepowerd * powerd_proxy; char * powerd_name_owner; 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_proxy != NULL) { g_signal_handlers_disconnect_by_data(p->powerd_proxy, o); g_clear_object(&p->powerd_proxy); } g_clear_object(&p->settings); g_clear_object(&p->system_bus); g_clear_pointer(&p->powerd_name_owner, g_free); 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.lomiri.Repowerd * * 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 set_brightness_local(IndicatorPowerBrightness*, int); static void on_powerd_brightness_params_ready(GObject * oproxy, GAsyncResult * res, gpointer gself) { GError * error; GVariant * v; v = NULL; error = NULL; if (dbus_repowerd_call_get_brightness_params_finish(DBUS_REPOWERD(oproxy), &v, res, &error)) { 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); } else if (error != 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); } } static void on_powerd_name_owner_changed(GDBusProxy * powerd_proxy, GParamSpec * pspec G_GNUC_UNUSED, gpointer gself) { priv_t * p = get_priv(INDICATOR_POWER_BRIGHTNESS(gself)); gchar * owner = g_dbus_proxy_get_name_owner(powerd_proxy); if (g_strcmp0(p->powerd_name_owner, owner)) { p->have_powerd_params = FALSE; if (owner != NULL) { dbus_repowerd_call_get_brightness_params(DBUS_REPOWERD(powerd_proxy), p->cancellable, on_powerd_brightness_params_ready, gself); } g_free(p->powerd_name_owner); p->powerd_name_owner = owner; } } static void on_powerd_brightness_changed(DbusRepowerd * powerd_proxy, GParamSpec * pspec G_GNUC_UNUSED, gpointer gself) { set_brightness_local(gself, dbus_repowerd_get_brightness(powerd_proxy)); } static void on_powerd_proxy_ready(GObject * source_object G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) { GError * error; DbusRepowerd * powerd_proxy; error = NULL; powerd_proxy = dbus_repowerd_proxy_new_for_bus_finish(res, &error); if (powerd_proxy != NULL) { priv_t * p; p = get_priv(INDICATOR_POWER_BRIGHTNESS(gself)); /* keep a handle to the system bus */ g_clear_object(&p->system_bus); p->system_bus = g_object_ref(g_dbus_proxy_get_connection(G_DBUS_PROXY(powerd_proxy))); /* keep the proxy and listen to owner changes */ p->powerd_proxy = powerd_proxy; g_signal_connect(p->powerd_proxy, "notify::g-name-owner", G_CALLBACK(on_powerd_name_owner_changed), gself); g_signal_connect(p->powerd_proxy, "notify::brightness", G_CALLBACK(on_powerd_brightness_changed), gself); on_powerd_name_owner_changed(G_DBUS_PROXY(powerd_proxy), NULL, gself); } else if (error != NULL) { if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning("Unable to get powerd proxy: %s", error->message); g_error_free(error); } } /** * 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); } dbus_repowerd_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES, "com.lomiri.Repowerd", "/com/lomiri/Repowerd", p->cancellable, on_powerd_proxy_ready, self); } 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; }