diff options
Diffstat (limited to 'src/gpm-kbd-backlight.c')
-rw-r--r-- | src/gpm-kbd-backlight.c | 734 |
1 files changed, 734 insertions, 0 deletions
diff --git a/src/gpm-kbd-backlight.c b/src/gpm-kbd-backlight.c new file mode 100644 index 0000000..8c8ba69 --- /dev/null +++ b/src/gpm-kbd-backlight.c @@ -0,0 +1,734 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2010 Alex Launi <alex launi canonical com> + * + * 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 <gio/gio.h> +#include <glib.h> +#include <libupower-glib/upower.h> + +#include "gpm-button.h" +#include "gpm-common.h" +#include "gpm-control.h" +#include "gpm-idle.h" +#include "gpm-kbd-backlight.h" + +static const gchar *kbd_backlight_introspection = "" +"<?xml version=\"1.0\" encoding=\"UTF-8\"?>""<node name=\"/\">" + "<interface name=\"org.gnome.PowerManager.Backlight\">" + "<method name=\"GetBrightness\">" + "<arg type=\"u\" name=\"percentage_brightness\" direction=\"out\"/>" + "</method>" + "<method name=\"SetBrightness\">" + "<arg type=\"u\" name=\"percentage_brightness\" direction=\"in\"/>" + "</method>" + "<signal name=\"BrightnessChanged\">" + "<arg type=\"u\" name=\"percentage_brightness\" direction=\"out\"/>" + "</signal>" + "</interface>" +"</node>"; + +#define GPM_KBD_BACKLIGHT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GPM_TYPE_KBD_BACKLIGHT, GpmKbdBacklightPrivate)) + +struct GpmKbdBacklightPrivate +{ + UpClient *client; + GpmButton *button; + GSettings *settings; + GSettings *settings_gsd; + GpmControl *control; + GpmIdle *idle; + gboolean can_dim; + gboolean system_is_idle; + GTimer *idle_timer; + guint idle_dim_timeout; + guint master_percentage; + guint brightness; + guint max_brightness; + guint brightness_percent; + GDBusProxy *upower_proxy; + GDBusConnection *bus_connection; + guint bus_object_id; +}; + +enum { + BRIGHTNESS_CHANGED, + LAST_SIGNAL +}; + +static guint signals [LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE (GpmKbdBacklight, gpm_kbd_backlight, G_TYPE_OBJECT) + +/** + * gpm_kbd_backlight_error_quark: + * Return value: Our personal error quark. + **/ +GQuark +gpm_kbd_backlight_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("gpm_kbd_backlight_error"); + return quark; +} + +/** + * gpm_kbd_backlight_get_brightness: + * @backlight: + * @brightness: + * @error: + * + * Return value: + */ +gboolean +gpm_kbd_backlight_get_brightness (GpmKbdBacklight *backlight, + guint *brightness, + GError **error) +{ + g_return_val_if_fail (backlight != NULL, FALSE); + g_return_val_if_fail (GPM_IS_KBD_BACKLIGHT (backlight), FALSE); + g_return_val_if_fail (brightness != NULL, FALSE); + + if (backlight->priv->can_dim == FALSE) { + g_set_error_literal (error, gpm_kbd_backlight_error_quark (), + GPM_KBD_BACKLIGHT_ERROR_HARDWARE_NOT_PRESENT, + "Dim capable hardware not present"); + return FALSE; + } + + *brightness = backlight->priv->brightness_percent; + return TRUE; +} + +static gboolean +gpm_kbd_backlight_set (GpmKbdBacklight *backlight, + guint percentage) +{ + gint scale; + guint goal; + + g_return_val_if_fail (GPM_IS_KBD_BACKLIGHT (backlight), FALSE); + /* if we're setting the same we are, don't bother */ + //g_return_val_if_fail (backlight->priv->brightness_percent != percentage, FALSE); + + goal = gpm_discrete_from_percent (percentage, backlight->priv->max_brightness); + scale = percentage > backlight->priv->brightness_percent ? 1 : -1; + + /* step loop down by 1 for a dimming effect */ + while (backlight->priv->brightness != goal) { + backlight->priv->brightness += scale; + backlight->priv->brightness_percent = gpm_discrete_to_percent (backlight->priv->brightness, backlight->priv->max_brightness); + + g_dbus_proxy_call (backlight->priv->upower_proxy, + "SetBrightness", + g_variant_new ("(i)", (gint) backlight->priv->brightness), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + } + + return TRUE; +} + +/** + * gpm_kbd_backlight_brightness_up: + **/ +static gboolean +gpm_kbd_backlight_brightness_up (GpmKbdBacklight *backlight) +{ + guint new; + + new = MIN (backlight->priv->brightness_percent + GPM_KBD_BACKLIGHT_STEP, 100u); + return gpm_kbd_backlight_set (backlight, new); +} + +/** + * gpm_kbd_backlight_brightness_down: + **/ +static gboolean +gpm_kbd_backlight_brightness_down (GpmKbdBacklight *backlight) +{ + guint new; + + // we can possibly go below 0 here, so by converting to a gint we avoid underflow errors. + new = MAX ((gint) backlight->priv->brightness_percent - GPM_KBD_BACKLIGHT_STEP, 0); + return gpm_kbd_backlight_set (backlight, new); +} + +/** + * gpm_kbd_backlight_set_brightness: + * @backlight: + * @percentage: + * @error: + * + * Return value: + **/ +gboolean +gpm_kbd_backlight_set_brightness (GpmKbdBacklight *backlight, + guint percentage, + GError **error) +{ + gboolean ret; + + g_return_val_if_fail (backlight != NULL, FALSE); + g_return_val_if_fail (GPM_IS_KBD_BACKLIGHT (backlight), FALSE); + + if (backlight->priv->can_dim == FALSE) { + g_set_error_literal (error, gpm_kbd_backlight_error_quark (), + GPM_KBD_BACKLIGHT_ERROR_HARDWARE_NOT_PRESENT, + "Dim capable hardware not present"); + return FALSE; + } + + backlight->priv->master_percentage = percentage; + + ret = gpm_kbd_backlight_set (backlight, percentage); + if (!ret) { + g_set_error_literal (error, gpm_kbd_backlight_error_quark (), + GPM_KBD_BACKLIGHT_ERROR_GENERAL, + "Cannot set keyboard backlight brightness"); + } + + return ret; +} + +static void +gpm_kbd_backlight_on_brightness_changed (GpmKbdBacklight *backlight, + guint value) +{ + backlight->priv->brightness = value; + backlight->priv->brightness_percent = gpm_discrete_to_percent (value, backlight->priv->max_brightness); + backlight->priv->master_percentage = backlight->priv->brightness_percent; + g_signal_emit (backlight, signals [BRIGHTNESS_CHANGED], 0, backlight->priv->brightness_percent); +} + +/** + * gpm_kbd_backlight_on_dbus_signal: + **/ +static void +gpm_kbd_backlight_on_dbus_signal (GDBusProxy *proxy, + gchar *sender_name, + gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + guint value; + GpmKbdBacklight *backlight = GPM_KBD_BACKLIGHT (user_data); + + if (g_strcmp0 (signal_name, "BrightnessChanged") == 0) { + g_variant_get (parameters, "(i)", &value); + gpm_kbd_backlight_on_brightness_changed (backlight, value); + return; + } + + g_assert_not_reached (); +} + +/** + * gpm_kbd_backlight_dbus_method_call: + * @connection: + * @object_path: + * @interface_name: + * @method_name: + * @parameters: + * @invocation: + * @user_data: + **/ +static void +gpm_kbd_backlight_dbus_method_call (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + guint value; + gboolean ret; + GError *error = NULL; + GpmKbdBacklight *backlight = GPM_KBD_BACKLIGHT (user_data); + + if (g_strcmp0 (method_name, "GetBrightness") == 0) { + ret = gpm_kbd_backlight_get_brightness (backlight, &value, &error); + if (!ret) { + g_dbus_method_invocation_return_gerror (invocation, error); + g_error_free (error); + } else { + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(u)", value)); + } + return; + } + + if (g_strcmp0 (method_name, "SetBrightness") == 0) { + g_variant_get (parameters, "(u)", &value); + ret = gpm_kbd_backlight_set_brightness (backlight, value, &error); + if (!ret) { + g_dbus_method_invocation_return_gerror (invocation, error); + g_error_free (error); + } else { + g_dbus_method_invocation_return_value (invocation, NULL); + } + return; + } + + g_assert_not_reached (); +} + + +/** + * gpm_kbd_backlight_dbus_property_get: + * @sender: + * @object_path: + * @interface_name: + * @property_name: + * @error: + * @user_data: + * + * Return value: + **/ +static GVariant * +gpm_kbd_backlight_dbus_property_get (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + /* Do nothing, we have no props */ + return NULL; +} + +/** + * gpm_kbd_backlight_dbus_property_set: + * @connection: + * @sender: + * @object_path: + * @interface_name: + * @property_name: + * + * Return value: + **/ +static gboolean +gpm_kbd_backlight_dbus_property_set (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GVariant *value, + GError **error, + gpointer user_data) +{ + /* do nothing, no properties defined */ + return FALSE; +} + +/** + * gpm_kbd_backlight_register_dbus: + * @backlight: + * @connection: + * @error: + **/ +void +gpm_kbd_backlight_register_dbus (GpmKbdBacklight *backlight, + GDBusConnection *connection, + GError **error) +{ + GDBusNodeInfo *node_info; + GDBusInterfaceInfo *interface_info; + GDBusInterfaceVTable interface_vtable = { + gpm_kbd_backlight_dbus_method_call, + gpm_kbd_backlight_dbus_property_get, + gpm_kbd_backlight_dbus_property_set + }; + + node_info = g_dbus_node_info_new_for_xml (kbd_backlight_introspection, NULL); + interface_info = g_dbus_node_info_lookup_interface (node_info, GPM_DBUS_INTERFACE_BACKLIGHT); + + backlight->priv->bus_connection = g_object_ref (connection); + backlight->priv->bus_object_id = + g_dbus_connection_register_object (connection, + GPM_DBUS_PATH_KBD_BACKLIGHT, + interface_info, + &interface_vtable, + backlight, + NULL, + error); + g_dbus_node_info_unref (node_info); +} + +static gboolean +gpm_kbd_backlight_evaluate_power_source_and_set (GpmKbdBacklight *backlight) +{ + gfloat brightness; + gfloat scale; + gboolean on_battery; + gboolean battery_reduce; + guint value; + gboolean ret; + + brightness = backlight->priv->master_percentage; + + g_object_get (backlight->priv->client, + "on-battery", + &on_battery, + NULL); + + battery_reduce = g_settings_get_boolean (backlight->priv->settings, GPM_SETTINGS_KBD_BACKLIGHT_BATT_REDUCE); + + if (on_battery && battery_reduce) { + value = g_settings_get_int (backlight->priv->settings, GPM_SETTINGS_KBD_BRIGHTNESS_DIM_BY_ON_BATT); + + if (value > 100) { + g_warning ("Cannot scale brightness down by more than 100%%. Scaling by 50%%"); + value = 50; + } + + scale = (100 - value) / 100.0f; + brightness *= scale; + + value = (guint) brightness; + + } else { + value = g_settings_get_int (backlight->priv->settings, GPM_SETTINGS_KBD_BRIGHTNESS_ON_AC); + } + + ret = gpm_kbd_backlight_set (backlight, value); + return ret; +} + +/** + * gpm_kbd_backlight_control_resume_cb: + * @control: The control class instance + * @backlight: This backlight class instance + * + * Just make sure that the backlight is back on + **/ +static void +gpm_kbd_backlight_control_resume_cb (GpmControl *control, + GpmControlAction action, + GpmKbdBacklight *backlight) +{ + gboolean ret; + + ret = gpm_kbd_backlight_evaluate_power_source_and_set (backlight); + if (!ret) + g_warning ("Failed to turn kbd brightness back on after resuming"); +} + +/** + * gpm_kbd_backlight_client_changed_cb: + * @client: The up_client class instance + * @backlight: This class instance + * + * Does the actions when the ac power source is inserted/removed. + **/ +static void +gpm_kbd_backlight_client_changed_cb (UpClient *client, + GpmKbdBacklight *backlight) +{ + gpm_kbd_backlight_evaluate_power_source_and_set (backlight); +} + +/** + * gpm_kbd_backlight_button_pressed_cb: + * @power: The power class instance + * @type: The button type, but here we only care about keyboard brightness buttons + * @backlight: This class instance + **/ +static void +gpm_kbd_backlight_button_pressed_cb (GpmButton *button, + const gchar *type, + GpmKbdBacklight *backlight) +{ + static guint saved_brightness; + + saved_brightness = backlight->priv->master_percentage; + + if (g_strcmp0 (type, GPM_BUTTON_KBD_BRIGHT_UP) == 0) { + gpm_kbd_backlight_brightness_up (backlight); + + } else if (g_strcmp0 (type, GPM_BUTTON_KBD_BRIGHT_DOWN) == 0) { + gpm_kbd_backlight_brightness_down (backlight); + + } else if (g_strcmp0 (type, GPM_BUTTON_KBD_BRIGHT_TOGGLE) == 0) { + if (backlight->priv->master_percentage == 0) { + /* backlight is off turn it back on */ + gpm_kbd_backlight_set (backlight, saved_brightness); + } else { + /* backlight is on, turn it off and save current value */ + saved_brightness = backlight->priv->master_percentage; + gpm_kbd_backlight_set (backlight, 0); + } + } +} + +/** + * gpm_kbd_backlight_idle_changed_cb: + * @idle: The idle class instance + * @mode: The idle mode, e.g. GPM_IDLE_MODE_BLANK + * @backlight: This class instance + * + * This callback is called when gnome-screensaver detects that the idle state + * has changed. GPM_IDLE_MODE_BLANK is when the session has become inactive, + * and GPM_IDLE_MODE_SLEEP is where the session has become inactive, AND the + * session timeout has elapsed for the idle action. + **/ +static void +gpm_kbd_backlight_idle_changed_cb (GpmIdle *idle, + GpmIdleMode mode, + GpmKbdBacklight *backlight) +{ + gfloat brightness; + gfloat scale; + guint value; + gboolean lid_closed; + gboolean on_battery; + gboolean enable_action; + + lid_closed = gpm_button_is_lid_closed (backlight->priv->button); + + if (lid_closed) + return; + + g_object_get (backlight->priv->client, + "on-battery", + &on_battery, + NULL); + + enable_action = on_battery + ? g_settings_get_boolean (backlight->priv->settings_gsd, GPM_SETTINGS_IDLE_DIM_BATT) + : g_settings_get_boolean (backlight->priv->settings_gsd, GPM_SETTINGS_IDLE_DIM_AC); + + if (!enable_action) + return; + + if (mode == GPM_IDLE_MODE_NORMAL) { + backlight->priv->master_percentage = 100; + gpm_kbd_backlight_evaluate_power_source_and_set (backlight); + } else if (mode == GPM_IDLE_MODE_DIM) { + brightness = backlight->priv->master_percentage; + value = g_settings_get_int (backlight->priv->settings, GPM_SETTINGS_KBD_BRIGHTNESS_DIM_BY_ON_IDLE); + + if (value > 100) { + g_warning ("Cannot scale brightness down by more than 100%%. Scaling by 50%%"); + value = 50; + } + + scale = (100 - value) / 100.0f; + brightness *= scale; + + value = (guint) brightness; + gpm_kbd_backlight_set (backlight, value); + } else if (mode == GPM_IDLE_MODE_BLANK) { + gpm_kbd_backlight_set (backlight, 0u); + } +} + +/** + * gpm_kbd_backlight_finalize: + * @object: + **/ +static void +gpm_kbd_backlight_finalize (GObject *object) +{ + GpmKbdBacklight *backlight; + + g_return_if_fail (object != NULL); + g_return_if_fail (GPM_IS_KBD_BACKLIGHT (object)); + + backlight = GPM_KBD_BACKLIGHT (object); + + if (backlight->priv->upower_proxy != NULL) { + g_object_unref (backlight->priv->upower_proxy); + } + if (backlight->priv->bus_connection != NULL) { + g_dbus_connection_unregister_object (backlight->priv->bus_connection, + backlight->priv->bus_object_id); + g_object_unref (backlight->priv->bus_connection); + } + + g_timer_destroy (backlight->priv->idle_timer); + + g_object_unref (backlight->priv->control); + g_object_unref (backlight->priv->settings); + g_object_unref (backlight->priv->settings_gsd); + g_object_unref (backlight->priv->client); + g_object_unref (backlight->priv->button); + g_object_unref (backlight->priv->idle); + + g_return_if_fail (backlight->priv != NULL); + G_OBJECT_CLASS (gpm_kbd_backlight_parent_class)->finalize (object); +} + +/** + * gpm_kbd_backlight_class_init: + * @klass: + **/ +static void +gpm_kbd_backlight_class_init (GpmKbdBacklightClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = gpm_kbd_backlight_finalize; + + signals [BRIGHTNESS_CHANGED] = + g_signal_new ("brightness-changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GpmKbdBacklightClass, brightness_changed), + NULL, + NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, + 1, + G_TYPE_UINT); + + g_type_class_add_private (klass, sizeof (GpmKbdBacklightPrivate)); +} + +/** + * gpm_kbd_backlight_init: + * @backlight: This KbdBacklight class instance + * + * Initializes the KbdBacklight class. + **/ +static void +gpm_kbd_backlight_init (GpmKbdBacklight *backlight) +{ + GVariant *u_brightness; + GVariant *u_max_brightness; + GError *error = NULL; + + backlight->priv = GPM_KBD_BACKLIGHT_GET_PRIVATE (backlight); + + backlight->priv->upower_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, + NULL, + "org.freedesktop.UPower", + "/org/freedesktop/UPower/KbdBacklight", + "org.freedesktop.UPower.KbdBacklight", + NULL, + &error); + if (backlight->priv->upower_proxy == NULL) { + g_printerr ("Could not connect to UPower system bus: %s", error->message); + g_error_free (error); + goto err; + } + + g_signal_connect (backlight->priv->upower_proxy, + "g-signal", + G_CALLBACK (gpm_kbd_backlight_on_dbus_signal), + backlight); + + u_brightness = g_dbus_proxy_call_sync (backlight->priv->upower_proxy, + "GetBrightness", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + if (u_brightness == NULL) { + g_warning ("Failed to get brightness: %s", error->message); + g_error_free (error); + goto err; + } + + error = NULL; + u_max_brightness = g_dbus_proxy_call_sync (backlight->priv->upower_proxy, + "GetMaxBrightness", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + if (u_max_brightness == NULL) { + g_warning ("Failed to get max brightness: %s", error->message); + g_error_free (error); + g_variant_unref (u_brightness); + goto err; + } + + g_variant_get (u_brightness, "(i)", &backlight->priv->brightness); + g_variant_get (u_max_brightness, "(i)", &backlight->priv->max_brightness); + + backlight->priv->brightness_percent = gpm_discrete_to_percent (backlight->priv->brightness, + backlight->priv->max_brightness); + + g_variant_unref (u_brightness); + g_variant_unref (u_max_brightness); + goto noerr; + +err: + backlight->priv->brightness = 0; + backlight->priv->brightness_percent = 100; + backlight->priv->max_brightness = 0; + +noerr: + /* Initialize the master to full power. It will get scaled if needed */ + backlight->priv->master_percentage = 100u; + + backlight->priv->idle_timer = g_timer_new (); + backlight->priv->can_dim = backlight->priv->max_brightness > 1; + + /* Use upower for ac changed signal */ + backlight->priv->client = up_client_new (); + g_signal_connect (backlight->priv->client, "changed", + G_CALLBACK (gpm_kbd_backlight_client_changed_cb), backlight); + + backlight->priv->settings = g_settings_new (GPM_SETTINGS_SCHEMA); + //backlight->priv->settings_gsd = g_settings_new (GSD_SETTINGS_SCHEMA); + + /* watch for kbd brightness up and down button presses */ + backlight->priv->button = gpm_button_new (); + g_signal_connect (backlight->priv->button, "button-pressed", + G_CALLBACK (gpm_kbd_backlight_button_pressed_cb), backlight); + + backlight->priv->idle = gpm_idle_new (); + g_signal_connect (backlight->priv->idle, "idle-changed", + G_CALLBACK (gpm_kbd_backlight_idle_changed_cb), backlight); + + /* since gpm is just starting we can pretty safely assume that we're not idle */ + backlight->priv->system_is_idle = FALSE; + backlight->priv->idle_dim_timeout = g_settings_get_int (backlight->priv->settings_gsd, GPM_SETTINGS_IDLE_DIM_TIME); + gpm_idle_set_timeout_dim (backlight->priv->idle, backlight->priv->idle_dim_timeout); + + /* make sure we turn the keyboard backlight back on after resuming */ + backlight->priv->control = gpm_control_new (); + g_signal_connect (backlight->priv->control, "resume", + G_CALLBACK (gpm_kbd_backlight_control_resume_cb), backlight); + + /* set initial values for whether we're on AC or battery*/ + gpm_kbd_backlight_evaluate_power_source_and_set (backlight); +} + +/** + * gpm_kbd_backlight_new: + * Return value: A new GpmKbdBacklight class instance. + **/ +GpmKbdBacklight * +gpm_kbd_backlight_new (void) +{ + GpmKbdBacklight *backlight = g_object_new (GPM_TYPE_KBD_BACKLIGHT, NULL); + return backlight; +} + |