/* -*- 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-osd-dialog.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;
	GtkWidget		*popup;
	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 */
	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;
	gboolean ret = FALSE;

	saved_brightness = backlight->priv->master_percentage;

	if (g_strcmp0 (type, GPM_BUTTON_KBD_BRIGHT_UP) == 0) {
		ret = gpm_kbd_backlight_brightness_up (backlight);

	} else if (g_strcmp0 (type, GPM_BUTTON_KBD_BRIGHT_DOWN) == 0) {
		ret = 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 */
			ret = gpm_kbd_backlight_set (backlight, saved_brightness);
		} else {
			/* backlight is on, turn it off and save current value */
			saved_brightness = backlight->priv->master_percentage;
			ret = gpm_kbd_backlight_set (backlight, 0);
		}
	}
	if (ret) {
		gpm_osd_dialog_init (&backlight->priv->popup, "keyboard-brightness-symbolic");
		gsd_media_keys_window_set_volume_level (GSD_MEDIA_KEYS_WINDOW (backlight->priv->popup),
							backlight->priv->brightness_percent);
		gpm_osd_dialog_show (backlight->priv->popup);
	}
}

/**
 * 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, GSD_SETTINGS_IDLE_DIM_BATT)
		: g_settings_get_boolean (backlight->priv->settings_gsd, GSD_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);
	gtk_widget_destroy (backlight->priv->popup);

	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, GSD_SETTINGS_IDLE_DIM_TIME);
	gpm_idle_set_timeout_dim (backlight->priv->idle, backlight->priv->idle_dim_timeout);

	/* use a visual widget */
	gpm_osd_dialog_init(&backlight->priv->popup, "keyboard-brightness-symbolic");

	/* 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;
}
