#! /usr/bin/perl -w
use strict;

use Getopt::Long qw(:config no_ignore_case bundling);
use Glib qw/TRUE FALSE/;
use Gtk2 '-init';
use POSIX qw(floor);


sub get_gamma;
sub get_backlight;
sub delete_event;
sub set_brightness;
sub set_gamma;


my $num_regexp = qr/[+-]?[0-9]*\.?[0-9]+/;
my $acpi_file = '/proc/acpi/video/C09A/C1AF/brightness';

my $cur_level;
my ($cur_brightness, @levels);

# Options
my ($get, $set, $reset, $help);

GetOptions(
    "set|s=s"	=> \$set,
    "get|g"	=> \$get,
    "reset|r"	=> \$reset,
    "help|h"	=> \$help
) or die( "Error processing options. Use -h for help.\n" );

if( defined( $help ) )
{
    print << 'EOF';
Get / set the kernel brightness, backlight or gamma.

OPTIONS:

    --set <level>, -s <level>
	Set brightness to <level>. If <level> begins with + or - then it is
	treated as an increment/decrement to the current level. If the
	(resulting) level is negative, then the brightness is set to 0, and the
	backlight is reduced.

    --get, -g
	Print the current level

    --reset, -r
	Reset the brightness, by telling the kernel to set the brightness to
	the current level. Some old versions of the intel video driver display
	the incorrect brightness level, and set the kernel brightness to the
	correct value. Resetting the brightness restores correct behaviour.

    --help, -h
	Print help.

With no options a Gtk2 dialogue is opened with sliders that control the kernel
brightness, X backlight and gamma."
EOF
    exit(0);
}


# Init the brightness levels.
open( BRIGHTNESS, "<$acpi_file") or die( "Could not open $acpi_file\n" );
while( <BRIGHTNESS> )
{
    chomp;

    m/^current: ($num_regexp)/ && do
    {
	$cur_brightness = $1;
	next;
    };

    m/^levels: / && do
    {
	s/^[^0-9]+//;
	my @nulevels = sort {$a <=> $b} split( /[^0-9]+/ ) ;

	foreach my $i (@nulevels)
	{
	    @levels = (@levels, $i)
		if( !scalar(@levels) || $levels[$#levels] != $i);
	}
	next;
    };

    die( "Unrecognized line: $_" );
}
die( "Could not get levels / brightness\n" )
    if( !scalar( @levels ) || !defined( $cur_brightness ) );

for($cur_level=0; $cur_level < scalar(@levels); $cur_level++)
{
    last if( $levels[$cur_level] == $cur_brightness );
}

if( defined( $get ) )
{
    print( $cur_level, "\n" );
}

elsif( defined( $set ) )
{
    # In this case interpret negative values of brightness level to mean
    # reduced backlight
    my $new_level;
    my $backlight = get_backlight();

    die( "$set is not a number" ) unless( $set =~ /^$num_regexp$/ );

    if( $cur_level == 0 )
    {
	$cur_level = floor($backlight / 10.0+.5) - 10;
    }
    
    if( $set =~ /^[+-]/ )
    {
	$new_level = $cur_level + $set;
    }
    else
    {
	$new_level = $set;
    }

    if( $new_level < -8 ) # Cap min brightness at 20
    {
	$new_level = -8
    }
    elsif( $new_level > $#levels )
    {
	$new_level = $#levels
    }

    #printf( "Old level: $cur_level, New Level: $new_level\n" );
    if( $new_level < 0 )
    {
	set_brightness( 0 ) if( $cur_level > 0 );
	set_backlight( floor($new_level+.5) * 10 + 100);
    }
    else
    {
	set_backlight( 100 ) if( $backlight < 100);
	set_brightness( $new_level );
    }
}

elsif( defined( $reset ) )
{
    set_brightness( $cur_level );
}

else
{
    # Pop up a slider to select the brightness.
    my $gamma = get_gamma();
    my $backlight = get_backlight();

    my $window = Gtk2::Window->new('toplevel');
    my $box = Gtk2::VBox->new( 0, 5 );
    my $brightnessScale = Gtk2::HScale->new_with_range(0, 10, 1);
    my $brightnessLabel = Gtk2::Label->new( "Brightness" );
    my $gammaScale = Gtk2::HScale->new_with_range(.5, 1.5, .01);
    my $gammaLabel = Gtk2::Label->new( "Gamma" );
    my $backlightScale = Gtk2::HScale->new_with_range(20, 100, 1);
    my $backlightLabel = Gtk2::Label->new( "Backlight" );

    my $okButton = Gtk2::Button->new_from_stock( 'gtk-ok' );

    $window->signal_connect(delete_event => \&delete_event);
    $window->signal_connect(destroy => sub { Gtk2->main_quit; });
    $okButton->signal_connect(clicked => sub { Gtk2->main_quit; });

    # Sets the border width of the window.
    $window->set_border_width(10);

    $cur_level = 5 unless( defined( $cur_level ));
    $brightnessScale->set_value( $cur_level );
    #$brightnessScale->set_draw_value( 0 );
    $brightnessScale->set_value_pos( 'right' );
    $brightnessScale->set_size_request( 200, -1 );
    $brightnessScale->signal_connect( value_changed => \&set_brightness,
	$window );
    $brightnessScale->set_update_policy( 'delayed' );

    $gamma = 1 unless( defined( $gamma ));
    $gammaScale->set_value( $gamma );
    #$gammaScale->set_draw_value( 0 );
    $gammaScale->set_value_pos( 'right' );
    $gammaScale->set_size_request( 200, -1 );
    $gammaScale->signal_connect( value_changed => \&set_gamma, $window );
    $gammaScale->set_update_policy( 'delayed' );

    $backlight = 100 unless( defined( $backlight ));
    $backlightScale->set_value( $backlight );
    #$backlightScale->set_draw_value( 0 );
    $backlightScale->set_value_pos( 'right' );
    $backlightScale->set_size_request( 200, -1 );
    $backlightScale->signal_connect( value_changed => \&set_backlight,
	$window );
    $backlightScale->set_update_policy( 'delayed' );

    $box->pack_start( $brightnessScale, TRUE, FALSE, 0 );
    $box->pack_start( $brightnessLabel, TRUE, FALSE, 0 );
    $box->pack_start( $backlightScale, TRUE, FALSE, 0 );
    $box->pack_start( $backlightLabel, TRUE, FALSE, 0 );
    $box->pack_start( $gammaScale, TRUE, FALSE, 0 );
    $box->pack_start( $gammaLabel, TRUE, FALSE, 0 );
    $box->pack_start( $okButton, TRUE, FALSE, 0 );

    $window->add( $box );

    $window->show_all();

    # All GTK applications must have a call to the main() method. Control ends
    # here and waits for an event to occur (like a key press or a mouse event).
    Gtk2->main;
}

sub get_backlight
{
    my $xbacklightOutput = `xbacklight -get 2>&1`;

    return $xbacklightOutput =~ /^$num_regexp$/ ? $xbacklightOutput : undef;
}

sub get_gamma
{
    my $xgammaOutput = `xgamma 2>&1`;
    $xgammaOutput =~ s/[^0-9.,]//g;

    my ($r, $g, $b) = split( /,/, $xgammaOutput);

    for ($r, $g, $b)
    {
	return undef unless( defined($_) && $_ =~ /^$num_regexp$/ );
    }

    return ($r + $g + $b) / 3;
}

sub set_backlight
{
    my ($widget, $window) = @_;
    my $newlevel = defined( $window ) ? $widget->get_value : $widget;

    #print( "Set backlight $newlevel\n" );
    system( qw(xbacklight -set), $newlevel );
}

sub set_gamma
{
    my ($widget, $window) = @_;

    system( qw(xgamma -q -gamma), $widget->get_value );
}

sub set_brightness
{
    #my ($widget, $window) = @_;
    my ($a, $b) = @_;
    my $new_level = defined( $b ) ? floor($a->get_value + .5) : $a;

    system( '/root/sbin/set-brightness', $levels[$new_level] );
    #system( qw(sudo /root/sbin/set_brightness),
}

sub delete_event
{
    return FALSE;
}

0;
