#!/usr/bin/perl -w
# Created   : Mon 18 Feb 2008 02:57:03 AM PST
# Modified  : Sat 03 Dec 2011 09:37:19 PM EST
# Author    : GI <gi1242@nospam.com> (replace nospam with gmail)

use strict;

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

sub click;
sub expose;
sub debug;
sub quit;

my $window = Gtk2::Window->new('popup');
my $black = Gtk2::Gdk::Color->new( 0, 0, 0 );
my $gc;

my ($click_top_x, $click_top_y, $click_bot_x, $click_bot_y );
my ($tablet_width, $tablet_height);
my @devices;

my $debug_messages = 0;
my $def_width = 100;
my $offset = 100;
my $nosave = 0;
my $only_reset = 0;
my $help = 0;
my $v7rotation = 1;
my $xsetwacom_version = `xsetwacom -V`; chomp( $xsetwacom_version );
my $pre0120 = ($xsetwacom_version =~ /^0\.(1[01]|0?[0-9])\./);

# Process options
GetOptions(
    "window-offset|o=i"	=> \$offset,
    "window-width|w=i"	=> \$def_width,
    "only-reset|r"	=> \$only_reset,
    "v7rotation"	=> \$v7rotation,
    "nosave"		=> \$nosave,
    "help|h"		=> \$help,
    "debug|d"		=> \$debug_messages
) or die( "Error processing options\n" );

if( $help )
{
    print << "EOF" ;
calibrate: Utillity to calibrate your WACOM devices.

OPTIONS:

    --window-offset N, -o N
	Offset from screen edge to draw the calibration windows.
    --window-width N, -w N
	Width of calibration windows
    --only-reset, -r
	Only reset the calibration parameters to default and exit.
    --nosave
	Don't save calibration parameters to ~/.calibrate.X
    --help, -h
	Print this help and exit
    --debug, -d
	Print debugging information.
EOF

    exit(0);
}

# First try getting wacom devices from environment.
@devices=();
for my $dev (keys( %ENV ))
{
    next unless( $dev =~ m/^WACOM_/ );

    push( @devices, $ENV{$dev} );
}

if ( @devices == 0 )
{
    # Now try from xsetwacom
    #@devices = split( /[ \t]+.*\n/m, `xsetwacom list dev` );
    @devices = split(
	/\s+(?:id:\s.*?)?(?:STYLUS|ERASER|CURSOR|PAD|TOUCH)\s*\n/im,
	`xsetwacom list dev` );
}

if ( @devices == 0 )
{
    # Give up.
    die( "Could not get wacom devices from environment/xsetwacom\n" )
}

# Reset calibration parameters
for my $dev (@devices)
{
    system( qw/xsetwacom set/, $dev, $pre0120 ? qw/xyDefault 1/ : "ResetArea" );
}
exit(0) if( $only_reset );

if( $pre0120 )
{
    $tablet_width = `xsetwacom get "$devices[0]" bottomx`;
    $tablet_height = `xsetwacom get "$devices[0]" bottomy`;
}
else
{
    my $area = `xsetwacom get "$devices[0]" Area`;
    my ($x, $y, $width, $height) = split( /\s+/, $area );

    $tablet_width = $width - $x;
    $tablet_height = $height - $y;
}

if( ! $v7rotation )
{
    # 2011-12-03: Recent versions of xsetwacom use a string for the rotation.
    my $rotated = `xsetwacom get "$devices[0]" rotate`;
    if( $rotated == 1 || $rotated == 3 )
    {
	debug( "Swapping width and height\n" );
	($tablet_width, $tablet_height) = ( $tablet_height, $tablet_width );
    }
}

debug( "Devices: ", join(', ', @devices ), "\n" );
debug( "tablet_width=$tablet_width, tablet_height=$tablet_height\n" );
debug( "Requesting window geometry ${def_width}x$def_width+$offset+$offset\n" );

$window->set_events( qw(button-press-mask) );
$window->signal_connect( button_press_event => \&click );
$window->signal_connect( expose_event => \&expose );
$window->parse_geometry( "${def_width}x$def_width+$offset+$offset" );
$window->app_paintable( 1 );
#$window->double_buffered( 0 );

$window->show();

Gtk2->main();

sub expose
{
    my ($widget, $event) = @_;
    my ($win_width, $win_height) = $widget->get_size();
    my $radius = 3;

    if( !defined( $gc ) )
    {
	$gc = Gtk2::Gdk::GC->new( $window->window );
	$gc->set_foreground( $black );
    }

    $widget->window->draw_line( $gc, $win_width/2, 0, $win_width/2,
	$win_height );
    $widget->window->draw_line( $gc, 0, $win_height/2, $win_width,
	$win_height/2 );
    $widget->window->draw_arc( $gc, 0, $win_width/2 - $radius,
	$win_height/2 - $radius, 2*$radius, 2*$radius, 0, 360*64 );
}

sub click
{
    my ($widget, $event) = @_;
    my ($win_x, $win_y) = $widget->get_position();
    my $filename;

    if( !defined( $click_top_x ) )
    {
	$click_top_x = $win_x + $event->x;
	$click_top_y = $win_y + $event->y;
	debug( "click_top_x=$click_top_x, click_top_y=$click_top_y\n" );

	$widget->parse_geometry( "-$offset-$offset" );
    }

    else
    {
	my ($topx, $topy, $botx, $boty);
	my ($left_xoff, $left_yoff, $right_xoff, $right_yoff);
	my ($screen_width, $screen_height, $screen);

	$click_bot_x = $win_x + $event->x;
	$click_bot_y = $win_y + $event->y;

	debug( "click_bot_x=$click_bot_x, click_bot_y=$click_bot_y\n" );

	$screen = $widget->get_screen();
	$screen_width = $screen->get_width();
	$screen_height = $screen->get_height();

	$left_xoff = $offset + $def_width / 2;
	$left_yoff = $offset + $def_width / 2;
	$right_xoff = $offset + $def_width / 2;
	$right_yoff = $offset + $def_width / 2;
	
	#$padw = ($click_bot_x - $click_top_x) / ($screen_width - $win_width)
	#	* $screen_width;
	#$padh = ($click_bot_y - $click_top_y) / ($screen_height - $win_height)
	#	* $screen_height;

	$topx = ($click_top_x - $left_xoff) * $tablet_width / $screen_width;
	$botx = ($click_bot_x + $right_xoff) * $tablet_width / $screen_width;

	$topy = ($click_top_y - $left_yoff) * $tablet_height / $screen_height;
	$boty = ($click_bot_y + $right_yoff) * $tablet_height / $screen_height;

	debug( "topx=$topx, topy=$topy, botx=$botx, boty=$boty\n" );

	for my $dev (@devices)
	{
	    if( $pre0120 )
	    {
		system( qw/xsetwacom set/, $dev, 'topx', int( $topx ) );
		system( qw/xsetwacom set/, $dev, 'topy', int( $topy ) );
		system( qw/xsetwacom set/, $dev, 'bottomx', int( $botx ) );
		system( qw/xsetwacom set/, $dev, 'bottomy', int( $boty ) );
	    }
	    else
	    {
		system( qw/xsetwacom set/, $dev, 'Area',
		    int( $topx ), int( $topy ), int( $botx ), int( $boty ) );
	    }
	}

	if( $nosave )
	{
	    Gtk2->main_quit();
	    return FALSE;
	}

	$filename = "$ENV{HOME}/.calibrate." . 
	    `xsetwacom get "$devices[0]" rotate`;
	chomp($filename);
	debug( "Writing parameters to $filename\n" );
	open( FILE, ">$filename" )
	    or quit( "Could not open $filename\n" );
	print FILE "#!/bin/bash\n";

	for my $dev (@devices)
	{
	    if( $pre0120 )
	    {
		print FILE
		    "\n",
		    "xsetwacom set \"$dev\" topx ", int($topx), "\n",
		    "xsetwacom set \"$dev\" topy ", int($topy), "\n",
		    "xsetwacom set \"$dev\" bottomx ", int($botx), "\n",
		    "xsetwacom set \"$dev\" bottomy ", int($boty), "\n";
	    }
	    else
	    {
		print FILE "xsetwacom set \"$dev\" Area ",
		    int( $topx ), int( $topy ), int( $botx ), int( $boty ), "\n";
	    }
	}

	close(FILE);
	chmod( (stat( $filename ))[2] | 0100, $filename );
	Gtk2->main_quit();
    }

    return FALSE;
}

sub debug
{
    print STDERR @_
	if( $debug_messages );
}

sub quit
{
    print STDERR @_;
    Gtk2->main_quit();
}
