#!/usr/bin/perl
#
# Programme d'affichage LCD sur 2 lignes x 20 caracteres  
# 
# This code is distributed under the terms of the GPL
#
# (c) 2003 Fisch Bob (bob@fesch.lu)
# http://www.fesch.lu
#
my $VERSION = 'LCD - Version 0.7';
#
# Designed initially for a CFAH2004AYYBJP 20x4 display
#

use strict;
use Fcntl;
use IO::Socket::INET;
use Time::HiRes qw(usleep gettimeofday);

#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  Parallel Port Config
#
#  => This is the 1. part
#     you have to set up.
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

my $BASEPORT 	= 0x378;	# -> Look at the BIOS for this value


#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  LCD Config
#
#  => This is the 2. part
#     you have to set up.
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

my $ROWS 	= 4;			# -> How many rows does your LCD have?
my $COLS	= 20;			# -> How many colums does it have?

## DOES NOT WORK YET !!! 
## ONLY 8 bit mode is supported
my $MODE	= 8; 			# -> In what mode is your LCD? 4 or 8 bits?

### This value is being overridden by the configuration file!
my $CYCLE 	= 100;			# -> Time of a cycle in milli-seconds.


#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  Interface Config
#
#  => This is the 3. part
#     you have to set up.
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

my $IFACE   = 'ppp0';		# -> What is the interface you want to monitor?

my $UPBITS 	= 160;			# -> What's the maximum upload rate in bits/second of your line?
my $DOWNBITS	= 1216;		# -> What's the maximum downlaod rate in bits/second of your line?


#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  Heart Config
#
#  => This is the 4. part
#     you have to set up.
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

my $USE_HEART = 1;			# -> set to 0 if you don't want it
my $HEART_COL = $COLS-1;		# -> column to show the heart
my $HEART_ROW = $ROWS-1;		# -> row to show the heart


#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  Catch signals
#
#  => Don't touch this!
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

$SIG{INT} 	= \&catch_sig;
$SIG{HUP} 	= \&catch_sig;
$SIG{TERM} 	= \&catch_sig;
$SIG{QUIT} 	= \&catch_sig;


#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  Debug Variables
#
#  => Don't touch this!
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

my $DEBUG_BUTTONS = 0;


#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  Bar Constants
#
#  => Don't touch this!
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

my @BAR_FULL  = [(0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F)];
my @BAR_EMPTY = [(0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x1F)];

my @BAR_OPEN   =( 
                 [(0x1F,0x10,0x10,0x10,0x10,0x10,0x10,0x1F)],
                 [(0x1F,0x18,0x18,0x18,0x18,0x18,0x18,0x1F)],
                 [(0x1F,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1F)],
                 [(0x1F,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1F)],
                 [(0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F)]
                );

my @BAR_CLOSE   =(
                  [(0x1F,0x01,0x01,0x01,0x01,0x01,0x01,0x1F)],
                  [(0x1F,0x11,0x11,0x11,0x11,0x11,0x11,0x1F)],
                  [(0x1F,0x19,0x19,0x19,0x19,0x19,0x19,0x1F)],
                  [(0x1F,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D,0x1F)],
                  [(0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F)]
                 );


#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  Cache Variables
#
#  => Don't touch this!
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

my @LINECACHE 	= ('','','','');  
my $CACHE_DOWN 	= '';
my $CACHE_DOWN_BAR	= '';
my $CACHE_UP_BAR 	= '';
my $CACHE_DL 	= -1;
my $CACHE_UL		= -1;
my $CACHE_IXUI	= '';
my $CACHE_HEART	= 1;


#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  Timing Variables
#
#  => Don't touch this!
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

my ($seconds1, $microseconds1);
my ($seconds2, $microseconds2);

my $TIME_DOWN     		= 0;
my $TIME_DONW_BAR 		= 0;
my $TIME_UP_BAR 		= 0;
my $TIME_IXUI		= 0;


#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  Menu Variables
#
#  => Don't touch this!
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

my $MENU_LINE = 0;
my @MENU = (\&get_line,\&get_function,\&get_none,\&get_version);


#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  Line Variables
#
#  => Don't touch this,
#     expect if you are
#     a programmer and want
#     to add a function!
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

### These values are being overridden by the configuration file!
my @LINE_FUNC = ('TIME','IXUI','BAR UP VAL','BAR DOWN VAL');

 ##### ATTENTION
 #
 #  Do not use get_bar_X and get_bar_X_val
 #  at the same time!
 #
 #####

my %FUNCS = (
		'SYSTEM LOAD' 	=> \&get_system_load,
	      	'CPU IDLE' 		=> \&get_cpu_idle,
	      	'NAME' 		=> \&get_name,
            	'UPTIME'		=> \&get_uptime,
 	      	'SWAP'	 		=> \&get_swap,
            	'DOWNUPLOAD'   	=> \&get_downupload,
             	'IPCONNECTED'   	=> \&get_ipconnected,
             	'MACHINES' 		=> \&get_connected_machines,
             	'TIME'  		=> \&get_time,
             	'BAR DOWN'  		=> \&get_bar_down,
             	'BAR UP'  		=> \&get_bar_up,
             	'BAR DOWN VAL'   	=> \&get_bar_down_val,
             	'BAR UP VAL'  	=> \&get_bar_up_val,
             	'BOX DOWN VAL'	=> \&get_bar_down_val_boxed,
             	'BOX UP VAL'  	=> \&get_bar_up_val_boxed,
             	'MOUNT 1' 		=> \&get_mount_1,
             	'MOUNT 2' 		=> \&get_mount_2, 
             	'MOUNT 3' 		=> \&get_mount_3, 
             	'IXUI'	 		=> \&get_ixui,
	      	'NONE'			=> \&get_none
            );


#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  Constants
#
#  => Don't touch this!
#     These are different
#     system constants.
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

my $base_port = $BASEPORT;
my $status_port = $BASEPORT + 1;
my $control_port = $BASEPORT + 2;


### E and RW must be inverted because are 
### attached to control pin 0 and 1 that 
### are logically inverted by parallel port

my $E_HIGH	= 0;
my $E_LOW	= 1;
my $RW_HIGH	= 0;
my $RW_LOW	= 2;
my $RS_HIGH	= 4;
my $RS_LOW	= 0;

my $BACK_ON	= 0x00;
my $BACK_OFF	= 0x08;

my $CLEARDISPLAY 	= 0x01;

my $RETURNHOME	= 0x02;

my $ENTRYMODE	= 0x04;
my $INCREMENT	= 0x02;
my $DECREMENT	= 0x00;
my $SHIFT_ON		= 0x01;
my $SHIFT_OFF	= 0x00;

my $DISPLAY		= 0x08;
my $DISPLAY_ON	= 0x04;
my $DISPLAY_OFF	= 0x00;
my $CURSOR_ON	= 0x02;
my $CURSOR_OFF	= 0x00;
my $BLINK_ON		= 0x01;
my $BLINK_OFF	= 0x00;

my $SHIFTMODE	= 0x10;
my $DISPLAY_SELECT	= 0x08;
my $CURSOR_SELECT	= 0x00;
my $RIGHT		= 0x01;
my $LEFT		= 0x00;

my $FUNCTIONSET	= 0x20;
my $EIGHTBITMODE	= 0x10;
my $FOURBITMODE	= 0x00;
my $TWOLINES		= 0x08;
my $ONELINE		= 0x00;
my $FONT5X11		= 0x01;
my $FONT5X8		= 0x00;

my $SETCGRAMADDRESS        	= 0x40;
my $CGRAM_ADDRESS_MASK     	= 0x3f;

my $SETDDRAMADDRESS		= 0x80;
my $DDRAM_ADDRESS_MASK     	= 0x7f;

my $READ_BF_AND_ADDRESS  	= 0x100;
my $BF_MASK     			= 0x80;
my $ADDRESS_MASK     		= 0x7f;

my $WRITE_TO_RAM         	= 0x200;
my $WRITE_TO_RAM_ADDRESS_MASK  = 0xff;

my $READ_FROM_RAM              = 0x300;
my $READ_FROM_RAM_ADDRESS_MASK = 0xff;

my $SECONDLINEOFFSET = 0x40;


#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  Button Constants
#
#  => Don't touch this!
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

my $LAST_BYTE = 0x00;

my $STATE_DOWN = 0;
my $STATE_UP   = 1;

my $STATE_BUSY = $STATE_UP;
my $STATE_ACK  = $STATE_UP;
my $STATE_PE   = $STATE_UP;
my $STATE_SI   = $STATE_UP;
my $STATE_ERR  = $STATE_UP;

my $BUTTON_BUSY = 0x80;
my $BUTTON_ACK  = 0x40;
my $BUTTON_PE   = 0x20;
my $BUTTON_SI   = 0x10;
my $BUTTON_ERR  = 0x08;

my $BUTTON_CHANGED = 0;


#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  Initialisation
#
#  => Here the LCD is being
#     initialised and some
#     operations, depending
#     on the script parameters
#     are executed.
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

if(
   ($ARGV[0] eq '-loop') || 
   ($ARGV[0] eq '-init')
   )
{
 &lcd_init; 			# <- init the LCD

 &generate_bars; 		# <- setup bars (user characters 0-4)
 &generate_heart;		# <- setup hearts (user characters 5-6)
}

if(
   ($ARGV[0] eq '-') || 
   ($ARGV[0] eq '-text') || 
   ($ARGV[0] eq '-line') || 
   ($ARGV[0] eq '-xy')
  )
{
 &clear_display;		# <- clear the display
}

if(
   ($ARGV[0] eq '-init')
   )
{
 for(my $i=6;$i<=7; $i++)	# <- Display two hearts
 {
  &set_data_at($i-6,0,$i);
 }
}

#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  Loop
#
#  => This is the last part
#     you *may* set up.
#     Not recommended!	
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

if($ARGV[0] eq '-loop')
{

 if(fileExists('lcd.conf'))
 {
  &load_LINES;
 }

 my $STATE = $STATE_SI;
 while(1)
 {

  if($STATE_SI==$STATE_UP)
  {

   &set_wait();
   for(my $i=0;$i<$ROWS;$i++)
   {
    &set_line($i,&{$FUNCS{@LINE_FUNC[$i]}}());
   } 

   if($USE_HEART==1)
   {
    if($CACHE_HEART>0) {&set_data_at($HEART_COL,$HEART_ROW,6);}
    else {&set_data_at($HEART_COL,$HEART_ROW,7);}
    $CACHE_HEART=-$CACHE_HEART;
   }
   &make_wait(50);

   &set_wait();
   my $rest = $CYCLE-50;
   while($rest!=0 && $BUTTON_CHANGED==0)
   {
    $rest-=50;
    &make_wait(50);
    &set_wait();
   }

  }
  else
  {

   &set_wait();
   for(my $i=0;$i<$ROWS;$i++)
   {
    &set_line($i,&{@MENU[$i]}());
   } 
   &make_wait(50);

   &set_wait();
   my $rest = $CYCLE-50;
   while($rest!=0 && $BUTTON_CHANGED==0)
   {
    $rest-=50;
    &make_wait(50);
    &set_wait();
   }

  }

  $BUTTON_CHANGED=0;
 }

 if($STATE != $STATE_SI)
 {
  $STATE=$STATE_SI;
  &clear_display;
 }
}


#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  Direct Text Output
#
#  => do not touch
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

if(($ARGV[0] eq '-') || ($ARGV[0] eq '-text') || ($ARGV[0] eq '-line') || ($ARGV[0] eq '-xy'))
{
 my $count = @ARGV;
 my $sentence = '';

 my $row = 0;
 my $start = 1;
 my $col = 0;
 if($ARGV[0] eq '-line')
 {
  $row = $ARGV[1];
  $start = 2;
 }

 if($ARGV[0] eq '-xy')
 {
  $row = $ARGV[1];
  $col = $ARGV[2];
  $start = 3;
 }

 for(my $i = $start; $i<$count; $i++)
 {
  if($i == $start)
  {
   $sentence.=$ARGV[$i];
  }
  else
  {
   $sentence.=" ".$ARGV[$i];
  }
 }
 while(length($sentence)>$COLS-$col)
 {
  &write_string_at($col,$row,trim(substr($sentence,0,$COLS-$col)));
  $row++;
  $sentence=substr($sentence,$COLS-$col,length($sentence)-$COLS+$col);
 }
 &write_string_at($col,$row,trim($sentence));
}

#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  Backlight Commands
#
#  => do not touch
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

if($ARGV[0] eq '-back')
{
 if($ARGV[1] eq 'on') 
 {
  &set_back_on();
 }
 else
 {
  &set_back_off();
 }
}


#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  Help Screen
#
#  => do not touch
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

if($ARGV[0] eq '')
{
 print "$VERSION\n";
 print "by fesch\@ixus.net\n";
 print "-----------------\n";
 print "usage: lcd [-option] [on|off] [row] [col] [text]\n";
 print "\n";
 print "[-option] may be:   -, -text   => simply outputs the [text] on the LCD\n";
 print "                    -back on   => switches the backlight ON\n";
 print "                    -back off  => switches the backlight OFF\n";
 print "                    -line      => outputs [text] at row [row]\n";
 print "                    -xy        => outputs [text] at row [row], colum [col]\n";
 print "                    -init      => only initializes the LCD\n";
 print "                    -loop      => starts the loop\n";
 print "\n";
}


#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  High Resolution Timer
#  Procedure
#
#  -> Do not touch this!
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

sub set_wait()
{
 ($seconds1, $microseconds1) = gettimeofday;
 &checkButtons;
}

sub make_wait()
{
 &checkButtons;
 my $milli = shift;
 ($seconds2, $microseconds2) = gettimeofday;
 my $i = $milli*1000-($seconds2*1000000+$microseconds2)+($seconds1*1000000+$microseconds1);
 if($i<0) {$i=0;}
 usleep($i);
}


#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  Buttons Caption
#
#  -> Do not touch this!
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

sub checkButtons
{

 my $byte = &read_byte($status_port);

 #print "> ".unpack('B8',chr($byte))."\t".unpack('B8',chr($LAST_BYTE))."\n";
 #print "> ".(($byte ^ $LAST_BYTE))."\t".(($byte & $BUTTON_ACK  ))."\n";

 
 if( (( $byte ^ $LAST_BYTE ) == $BUTTON_BUSY ) && (( $byte & $BUTTON_BUSY ) == $BUTTON_BUSY) ) {$BUTTON_CHANGED=1; $STATE_BUSY=$STATE_DOWN; &doBUSYdown;}
 if( (( $byte ^ $LAST_BYTE ) == $BUTTON_ACK  ) && (( $byte & $BUTTON_ACK  ) == 0x00        ) ) {$BUTTON_CHANGED=1; $STATE_ACK =$STATE_DOWN; &doACKdown; }
 if( (( $byte ^ $LAST_BYTE ) == $BUTTON_PE   ) && (( $byte & $BUTTON_PE   ) == 0x00        ) ) {$BUTTON_CHANGED=1; $STATE_PE  =$STATE_DOWN; &doPEdown;  }
 if( (( $byte ^ $LAST_BYTE ) == $BUTTON_SI   ) && (( $byte & $BUTTON_SI   ) == 0x00        ) ) {$BUTTON_CHANGED=1; $STATE_SI  =$STATE_DOWN; &doSIdown;  }
 if( (( $byte ^ $LAST_BYTE ) == $BUTTON_ERR  ) && (( $byte & $BUTTON_ERR  ) == 0x00        ) ) {$BUTTON_CHANGED=1; $STATE_ERR =$STATE_DOWN; &doERRdown; }

 if( (( $byte ^ $LAST_BYTE ) == $BUTTON_BUSY ) && (( $byte & $BUTTON_BUSY ) == 0x00        ) ) {$BUTTON_CHANGED=1; $STATE_BUSY=$STATE_UP; &doBUSYup;}
 if( (( $byte ^ $LAST_BYTE ) == $BUTTON_ACK  ) && (( $byte & $BUTTON_ACK  ) == $BUTTON_ACK ) ) {$BUTTON_CHANGED=1; $STATE_ACK =$STATE_UP; &doACKup; }
 if( (( $byte ^ $LAST_BYTE ) == $BUTTON_PE   ) && (( $byte & $BUTTON_PE   ) == $BUTTON_PE  ) ) {$BUTTON_CHANGED=1; $STATE_PE  =$STATE_UP; &doPEup;  }
 if( (( $byte ^ $LAST_BYTE ) == $BUTTON_SI   ) && (( $byte & $BUTTON_SI   ) == $BUTTON_SI  ) ) {$BUTTON_CHANGED=1; $STATE_SI  =$STATE_UP; &doSIup;  }
 if( (( $byte ^ $LAST_BYTE ) == $BUTTON_ERR  ) && (( $byte & $BUTTON_ERR  ) == $BUTTON_ERR ) ) {$BUTTON_CHANGED=1; $STATE_ERR =$STATE_UP; &doERRup; }

 $LAST_BYTE=$byte;
}


#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  Buttons Events
#
#  -> This one you may modify.
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

sub doSIdown
{
 if($DEBUG_BUTTONS == 1) {print "SIdown\n";}
}

sub doSIup
{
 if($DEBUG_BUTTONS == 1) {print "SIup\n";}
 $CYCLE=@LINE_FUNC[$ROWS];
 &save_LINES;
 my $found = 0;
 foreach(@LINE_FUNC)
 {
  if(9<= $_ && $_<=12) {$found=1;}
 }
 if($found==1){&generate_bars;}

 $found = 0;
 foreach(@LINE_FUNC)
 {
  if(13<= $_ && $_<=14) {$found=1;}
 }
 if($found==1)
 {
  &generate_char(@BAR_FULL,0);
  &generate_char(@BAR_EMPTY,1);
 }
}

sub doPEdown
{
 if($DEBUG_BUTTONS == 1) {print "PEdown\n";}
 if($STATE_SI==$STATE_DOWN)
 {
  $MENU_LINE++;
  $MENU_LINE=($MENU_LINE % ($ROWS+1));
  if($MENU_LINE==$ROWS)
  {
   @LINE_FUNC[$ROWS]=$CYCLE;
  }
 }
}

sub doPEup
{
 if($DEBUG_BUTTONS == 1) {print "PEup\n";}
}

sub doBUSYdown
{
 if($DEBUG_BUTTONS == 1) {print "BUSYdown\n";}
 if($STATE_SI==$STATE_DOWN)
 {
  if($MENU_LINE!=$ROWS)
  {
   my @cles = sort keys(%FUNCS); 	
   my $funcs = @cles;
   my $coun = @cles;
   my $found = 0;
   for(my $i=0;$i<$coun;$i++)
   {
    if(@LINE_FUNC[$MENU_LINE] eq @cles[$i]) {$found=$i;}
   }
   $found++;
   $found=($found % $funcs);
   @LINE_FUNC[$MENU_LINE]=@cles[$found];

  }
  else
  {
   @LINE_FUNC[$ROWS]+=50;
   if(@LINE_FUNC[$ROWS]>1000) {@LINE_FUNC[$ROWS]=50;}
  }
 }
}

sub doBUSYup
{
 if($DEBUG_BUTTONS == 1) {print "BUSYup\n";}
}

sub doACKdown
{
 if($DEBUG_BUTTONS == 1) {print "ACKdown\n";}
 #`killall lcd | ./lcd -loop &`
 exec('./lcd -loop');
}

sub doACKup
{
 if($DEBUG_BUTTONS == 1) {print "ACKup\n";}
}

sub doERRdown
{
 if($DEBUG_BUTTONS == 1) {print "ERRdown\n";}
 &clear_display;
 &write_string_at(0,0,'Shutting down the');
 &write_string_at(0,1,'system right now ...');
 usleep(1000);
 `killall lcd | halt`;
}

sub doERRup
{
 if($DEBUG_BUTTONS == 1) {print "ERRup\n";}
}


#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  Script Termination  Method
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

sub catch_sig
{
 &save_LINES;
 &clear_display;
 &write_string_at(0,0,'LCD shutdown ...    ');
 print "LCD Shutdown ...\n";
 exit 0;
}


#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  Line caching & writing
#
#  -> do not touch this!
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

sub set_line
{
 my $line  = shift;
 my $title = shift;
 my $LINE = $LINECACHE[$line];

 while(length($title)<$COLS) {$title.=' ';}
 $title=substr($title,0,$COLS);

 if($LINE  ne '')
 {
  for(my $i=0;$i<$COLS;$i++)
  {
   if(substr($LINE,$i,1) ne substr($title,$i,1))
   {
    &write_string_at($i,$line,substr($title,$i,1));
   }
  }
 }
 else 
 { 
  &write_string_at(0,$line,$title);
 }

 $LINECACHE[$line]=$title;
}


#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  Menu Data
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

sub get_line
{
 my $title = "Line: ".($MENU_LINE+1);
 if($MENU_LINE==$ROWS)
 {
  $title = "Refresh intervall";
 }
 while(length($title )<$COLS) {$title .=' ';}
 return substr($title,0,$COLS);
}

sub get_function
{
 #my $title = 'Func: '.@FUNCTIONS[@LINE_FUNC[$MENU_LINE]];
 my $title = 'Func: '.@LINE_FUNC[$MENU_LINE];
 if($MENU_LINE==$ROWS)
 {
  $title = 'Time: '.@LINE_FUNC[$MENU_LINE].' ms';
 }
 while(length($title )<$COLS) {$title .=' ';}
 return substr($title,0,$COLS);
}

#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  Catch Data
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

sub get_none
{
 my $none='';
 for(my $i=0;$i<$COLS;$i++) {$none.=' ';}
 return substr($none,0,$COLS);
}

sub get_connected_machines
{
 # affiche le nombre de machines accdantes 
 # internet via IpCOP (ou autre?). 
 # ATTENTION: Si une machine s'teind, son adresse
 # MAC reste encore un certain temps dans le cache,
 # donc la valeur n'est pas toujours tout  fait 
 # jour. Ce n'est qu'une indication!

 my $name = `arp -a|wc -l`;
 chop($name);
 my $title = 'Online';
 while(length($title)!=$COLS-10) {$title.=' ';}
 $title = sprintf("$title%10s",$name);
 return substr($title,0,$COLS);
}

sub get_name
{
 # by oxy750
 # modified 15/07/2004 by Fesch

 my $name = `/bin/uname -n`;
 chop($name);
 return substr($name,0,$COLS);
}

sub get_uptime 
{ 
 # by oxy750 
 # modified by gysmovoile ... 
 # l'uptime ne fonctionnait pas !!! 
 # modified 15/07/2004 by Fesch

 my ($title,$uptime,$time,$days,$other,$hours,$users,$ld); 
 $uptime = `/usr/bin/uptime`;
 
 #($days,$hours,$other)=split(/,/,$uptime); 
 #($other,$time,$other,$days,$other)=split(/ +/,$days); 
 #if ($hours eq "") 
 #{ 
 # $hours = $days; 
 # $days = 0; 
 #} 
 #my ($h,$m)=split(/:/,$hours); 
 #$title=sprintf("Uptime   %3dd %02dh%02dm",$days,$h,$m); 

 my @some = explode('up',$uptime);
 @some = explode(',',$some[1]);
 $uptime=trim($some[0]); 

 my $title='Uptime';
 while(length($title)<$COLS-length($uptime)) {$title.=' ';}

 return substr($title.$uptime,0,$COLS);
} 

sub get_cpu_idle
{
 my($swp,$title,$total,$used,$free);
 $swp=`top -n 1 | head -n 3 | tail -n 1`;
 chop($swp);
 my @some=explode(' ',$swp);
 my $title="CPU idle";

 while( length($title) != ($COLS-8)) 
 {
  $title.=' ';
 } 
 $title=sprintf("$title%8s",$some[14]);
 return substr($title,0,$COLS);
}

sub get_system_load
{
 my($swp,$title,$total,$used,$free);
 $swp=`/usr/bin/uptime`;
 chop($swp);
 my @some=split(/average/,$swp);
 #print $some[1]." -- ".substr($some[1],2,16)."\n";
 @some=split(/ /,substr($some[1],2,16));
 #substr($some[13].$some[14].$some[15],0,14)
 my $title=sprintf("%-6s%14s","Load",substr($some[0].$some[1].$some[2],0,14));
 return substr($title,0,$COLS);
}

sub get_swap
{
 # by oxy750
 # modified 15/007/2004 by Fesch

 my($swp,$title,$total,$used,$free);
 $swp=`/usr/bin/free | tail -n 1`;
 chop($swp);
 my($title,$total,$used,$free)=split(/ +/,$swp);
 my $sizefree=sprintf("%.f MB",$free/1024);
 my $pourcentuse=sprintf("%3d%%",int($used*100/$total));
 $title="Swap";
 while(length($title) != $COLS-11) {$title.=' ';}
 $title=sprintf("$title%4s %6s",$pourcentuse,$sizefree);

 return substr($title,0,$COLS);
}

sub get_bar_up
{
 my $itIs = time();

 if($TIME_UP_BAR!=-1 && (($itIs-$TIME_UP_BAR)>=2))
 {
  my ($title);
  my ($DspeedByte,$DspeedBits,$DspeedKBits);
  my ($UspeedByte,$UspeedBits,$UspeedKBits);

  # get RX
  my $data = `ifconfig $IFACE | head -n 7 | tail -n 1`;
  $data = substr($data,19);
  my @pieces = split(/ /,$data);
  my $us = substr($pieces[5],6);

  # calculate upload speed
  $UspeedByte = ($us-$CACHE_UL)/($itIs-$TIME_UP_BAR);
  $UspeedBits = int($UspeedByte * 8); 
  $UspeedKBits = ($UspeedBits / 1024);
  my $UP=$UspeedKBits;
  my $UPs = "UP ";
  $UP=($UP/$UPBITS)*(($COLS-3)*5);
  $UP=sprintf("%i",$UP);
  my $ganzUP = sprintf("%i",$UP/5);
  for(my $i=0;$i<$ganzUP;$i++) {$UPs.=chr(4);}
  $UPs.=chr($UP % 5);
  while(length($UPs)<$COLS) {$UPs.=' ';}
  $UPs=substr($UPs,0,$COLS);
  
  $CACHE_UP_BAR=$UPs;
  $TIME_UP_BAR=$itIs;
  $CACHE_UL=$us;
  return substr($UPs,0,$COLS);
 }
 else
 {
  return substr($CACHE_UP_BAR,0,$COLS);
 }
}

sub get_bar_up_val_boxed
{
 # suggestion: Makari

 my $itIs = time();

 if($TIME_UP_BAR!=-1 && (($itIs-$TIME_UP_BAR)>=2))
 {
  my ($title);
  my ($UspeedByte,$UspeedBits,$UspeedKBits,$UspeedBits2,$UspeedKBits2);

  # get RX
  my $data = `ifconfig $IFACE | head -n 7 | tail -n 1`;
  $data = substr($data,19);
  my @pieces = split(/ /,$data);
  my $us = substr($pieces[5],6);

  # calculate upload speed
  $UspeedByte = ($us-$CACHE_UL)/($itIs-$TIME_UP_BAR);
  $UspeedBits = int($UspeedByte * 8); 
  $UspeedBits2 = int($UspeedByte * 1); # on laisse en octet
  $UspeedKBits = ($UspeedBits / 1024);
  $UspeedKBits2 = ($UspeedBits2 / 1024);
  my $UP=$UspeedKBits;
  my $UPs = "UP ";
  $UP=($UP/$UPBITS)*(($COLS-3-7)*5);
  $UP=sprintf("%i",$UP);
  my $ganzUP = sprintf("%i",$UP/5);

  if($ganzUP==0)
  {
   &generate_char(@BAR_OPEN[$UP % 5],4);
   $UPs.=chr(4);
   while(length($UPs)<$COLS-7-1) {$UPs.=chr(1);}
   &generate_char(@BAR_CLOSE[0],5);
   $UPs.=chr(5);
  }
  else  
  {
   for(my $m=0;$m<$ganzUP;$m++) {$UPs.=chr(0);}
   &generate_char(@BAR_OPEN[$UP % 5],4);
   $UPs.=chr(4);
   if(length($UPs)<$COLS-7-1)
   {
    while(length($UPs)<$COLS-7-1) {$UPs.=chr(1);}
   }
   if(length($UPs)==($COLS-7)) {$UPs=substr($UPs,0,$COLS-7-1);}

   if($ganzUP==$COLS-7-1)
   {
    generate_char(@BAR_CLOSE[($UP % 5)+1],5);
    $UPs.=chr(5);
   }
   else
   {
    generate_char(@BAR_CLOSE[0],5);
    $UPs.=chr(5);
   } 
  }
  $UPs=substr($UPs,0,$COLS-7);

  $UPs=substr($UPs,0,$COLS-7);
  if($UspeedKBits2<10) {$UP=sprintf("%.1f",$UspeedKBits2);}
  else {$UP=sprintf("%i",$UspeedKBits2);}
  my $titleUP=sprintf("%4s KB",$UP);
  $UPs=substr($UPs.$titleUP,0,$COLS);
  $CACHE_UP_BAR=$UPs;
  $TIME_UP_BAR=$itIs;
  $CACHE_UL=$us;
  return substr($UPs,0,$COLS);
 }
 else
 {
  return substr($CACHE_UP_BAR,0,$COLS);
 }
}

sub get_bar_up_val
{
 my $itIs = time();

 if($TIME_UP_BAR!=-1 && (($itIs-$TIME_UP_BAR)>=2))
 {
  my ($title);
  my ($UspeedByte,$UspeedBits,$UspeedKBits,$UspeedBits2,$UspeedKBits2);

  # get RX
  my $data = `ifconfig $IFACE | head -n 7 | tail -n 1`;
  $data = substr($data,19);
  my @pieces = split(/ /,$data);
  my $us = substr($pieces[5],6);

  # calculate upload speed
  $UspeedByte = ($us-$CACHE_UL)/($itIs-$TIME_UP_BAR);
  $UspeedBits = int($UspeedByte * 8); 
  $UspeedBits2 = int($UspeedByte * 1); # on laisse en octet
  $UspeedKBits = ($UspeedBits / 1024);
  $UspeedKBits2 = ($UspeedBits2 / 1024);
  my $UP=$UspeedKBits;
  my $UPs = "UP ";
  $UP=($UP/$UPBITS)*(($COLS-3-7)*5);
  $UP=sprintf("%i",$UP);
  my $ganzUP = sprintf("%i",$UP/5);
  for(my $i=0;$i<$ganzUP;$i++) {$UPs.=chr(4);}
  $UPs.=chr($UP % 5);
  while(length($UPs)<$COLS-7) {$UPs.=' ';}
  $UPs=substr($UPs,0,$COLS-7);
  if($UspeedKBits2<10) {$UP=sprintf("%.1f",$UspeedKBits2);}
  else {$UP=sprintf("%i",$UspeedKBits2);}
  my $titleUP=sprintf("%4s KB",$UP);
  $UPs=substr($UPs.$titleUP,0,$COLS);
  $CACHE_UP_BAR=$UPs;
  $TIME_UP_BAR=$itIs;
  $CACHE_UL=$us;
  return substr($UPs,0,$COLS);
 }
 else
 {
  return substr($CACHE_UP_BAR,0,$COLS);
 }
}

sub get_bar_down
{
 my $itIs = time();

 if($TIME_DONW_BAR!=-1 && (($itIs-$TIME_DONW_BAR)>=2))
 {
  my ($title);
  my ($DspeedByte,$DspeedBits,$DspeedKBits);

  # get RX
  my $data = `ifconfig $IFACE | head -n 7 | tail -n 1`;
  $data = substr($data,19);
  my @pieces = split(/ /,$data);
  my $ds = $pieces[0]; 

  # calculate download speed
  $DspeedByte = ($ds-$CACHE_DL)/($itIs-$TIME_DONW_BAR);
  $DspeedBits = int($DspeedByte * 8); 
  $DspeedKBits = ($DspeedBits / 1024);


  my $DL=$DspeedKBits;
  my $DLs = "DL ";
  $DL=($DL/$DOWNBITS)*(($COLS-3)*5);
  $DL=sprintf("%i",$DL);
  my $ganzDL = sprintf("%i",$DL/5);
  for(my $i=0;$i<$ganzDL;$i++) {$DLs.=chr(4);}
  $DLs.=chr($DL % 5);
  while(length($DLs)<$COLS) {$DLs.=' ';}
  $DLs=substr($DLs,0,$COLS);
  $CACHE_DL=$ds;
  $CACHE_DOWN_BAR=$DLs;
  $TIME_DONW_BAR=$itIs;
  return substr($DLs,0,$COLS);
 }
 else
 {
  return substr($CACHE_DOWN_BAR,0,$COLS);
 }
}

sub get_bar_down_val_boxed
{
 # suggestion: Makari

 my $itIs = time();

 if($TIME_DONW_BAR!=-1 && (($itIs-$TIME_DONW_BAR)>=2))
 {
  my ($title);
  my ($DspeedByte,$DspeedBits,$DspeedKBits,$DspeedBits2,$DspeedKBits2);

  # get RX
  my $data = `ifconfig $IFACE | head -n 7 | tail -n 1`;
  $data = substr($data,19);
  my @pieces = split(/ /,$data);
  my $ds = $pieces[0]; 

  # calculate download speed
  $DspeedByte = ($ds-$CACHE_DL)/($itIs-$TIME_DONW_BAR);
  $DspeedBits = int($DspeedByte * 8); 
  $DspeedBits2 = int($DspeedByte * 1); # on laisse en octet
  $DspeedKBits = ($DspeedBits / 1024);
  $DspeedKBits2 = ($DspeedBits2 / 1024);

  my $DL=$DspeedKBits;
  my $DLs = "DL ";
  $DL=($DL/$DOWNBITS)*((($COLS-3-7)*5));
  $DL=sprintf("%i",$DL);
  my $ganzDL = sprintf("%i",$DL/5);

  if($ganzDL==0)
  {
   &generate_char(@BAR_OPEN[$DL % 5],2);
   $DLs.=chr(2);
   while(length($DLs)<$COLS-7-1) {$DLs.=chr(1);}
   &generate_char(@BAR_CLOSE[0],3);
   $DLs.=chr(3);
  }
  else  
  {
   for(my $m=0;$m<$ganzDL;$m++) {$DLs.=chr(0);}
   &generate_char(@BAR_OPEN[$DL % 5],2);
   $DLs.=chr(2);
   if(length($DLs)<$COLS-7-1)
   {
    while(length($DLs)<$COLS-7-1) {$DLs.=chr(1);}
   }
   if(length($DLs)==($COLS-7)) {$DLs=substr($DLs,0,$COLS-7-1);}

   if($ganzDL==$COLS-7-1)
   {
    generate_char(@BAR_CLOSE[($DL % 5)+1],3);
    $DLs.=chr(3);
   }
   else
   {
    generate_char(@BAR_CLOSE[0],3);
    $DLs.=chr(3);
   } 
  }
  $DLs=substr($DLs,0,$COLS-7);

  if($DspeedKBits2<10) {$DL=sprintf("%.1f",$DspeedKBits2);}
  else {$DL=sprintf("%i",$DspeedKBits2);}
  my $titleDL=sprintf("%4s KB",$DL);
  $DLs=substr($DLs.$titleDL,0,$COLS);
  $CACHE_DL=$ds;
  $CACHE_DOWN_BAR=$DLs;
  $TIME_DONW_BAR=$itIs;
  return substr($DLs,0,$COLS);
 }
 else
 {
  return substr($CACHE_DOWN_BAR,0,$COLS);
 }
}

sub get_bar_down_val
{
 my $itIs = time();

 if($TIME_DONW_BAR!=-1 && (($itIs-$TIME_DONW_BAR)>=2))
 {
  my ($title);
  my ($DspeedByte,$DspeedBits,$DspeedKBits,$DspeedBits2,$DspeedKBits2);

  # get RX
  my $data = `ifconfig $IFACE | head -n 7 | tail -n 1`;
  $data = substr($data,19);
  my @pieces = split(/ /,$data);
  my $ds = $pieces[0]; 

  # calculate download speed
  $DspeedByte = ($ds-$CACHE_DL)/($itIs-$TIME_DONW_BAR);
  $DspeedBits = int($DspeedByte * 8); 
  $DspeedBits2 = int($DspeedByte * 1); # on laisse en octet
  $DspeedKBits = ($DspeedBits / 1024);
  $DspeedKBits2 = ($DspeedBits2 / 1024);

  my $DL=$DspeedKBits;
  my $DLs = "DL ";
  $DL=($DL/$DOWNBITS)*(($COLS-3-7)*5);
  $DL=sprintf("%i",$DL);
  my $ganzDL = sprintf("%i",$DL/5);
  for(my $i=0;$i<$ganzDL;$i++) {$DLs.=chr(4);}
  $DLs.=chr($DL % 5);
  while(length($DLs)<$COLS-7) {$DLs.=' ';}
  $DLs=substr($DLs,0,$COLS-7);

  if($DspeedKBits2<10) {$DL=sprintf("%.1f",$DspeedKBits2);}
  else {$DL=sprintf("%i",$DspeedKBits2);}
  my $titleDL=sprintf("%4s KB",$DL);
  $DLs=substr($DLs.$titleDL,0,$COLS);
  $CACHE_DL=$ds;
  $CACHE_DOWN_BAR=$DLs;
  $TIME_DONW_BAR=$itIs;
  return substr($DLs,0,$COLS);
 }
 else
 {
  return substr($CACHE_DOWN_BAR,0,$COLS);
 }
}


sub get_downupload 
{
 # by oxy750
 # Calcul de la vitesse de UP et DL bas
 # sur speedDeamon de Fesch
 # modified 15/07/2004 by Fesch

 my $itIs = time();

 if(($itIs-$TIME_DOWN)>=2)
 {
  my ($title);
  my ($DspeedByte,$DspeedBits,$DspeedKBits);
  my ($UspeedByte,$UspeedBits,$UspeedKBits);  

  # get RX
  my $data = `ifconfig $IFACE | head -n 7 | tail -n 1`;
  $data = substr($data,19);
  my @pieces = split(/ /,$data);
  my $ds = $pieces[0];
  my $us = substr($pieces[5],6); 

  # calculate download speed
  $DspeedByte = ($ds-$CACHE_DL)/($itIs-$TIME_DOWN);
  $DspeedBits = int($DspeedByte * 1); # on laisse en octet
  $DspeedKBits = ($DspeedBits / 1024);  

  # calculate upload speed
  $UspeedByte = ($us-$CACHE_UL)/($itIs-$TIME_DOWN);
  $UspeedBits = int($UspeedByte * 1); # on laisse en octet
  $UspeedKBits = ($UspeedBits / 1024);

  $CACHE_DL=$ds;
  $CACHE_UL=$us;
  $TIME_DOWN=$itIs;

  my $UP=0;
  my $DL=0;

  if($UspeedKBits<10) {$UP=sprintf("%.1f",$UspeedKBits);}
  else {$UP=sprintf("%i",$UspeedKBits);}

  if($DspeedKBits<10) {$DL=sprintf("%.1f",$DspeedKBits);}
  else {$DL=sprintf("%i",$DspeedKBits);}

  $title=sprintf("UP:%4s DL:%4s KB/s",$UP,$DL);

  $CACHE_DOWN=$title;
  return substr($title,0,$COLS);
 }
 else
 {
  return substr($CACHE_DOWN,0,$COLS);
 }
}

sub get_ipconnected 
{
 # by oxy750
 
 my $data = `ifconfig $IFACE | grep -i "inet addr"`;
 my($other,$inet,$addr,$ptp,$mask)=split(/ +/,$data);
 my($other,$ip)=split(/:/,$addr);
 my $title=sprintf("IP %".($COLS-3)."s",$ip);
 return substr($title,0,$COLS);
}

sub get_time 
{
 # by oxy750

 my ($sec,$min,$heure,$mjour,$mois,$annee,$sjour,$ajour,$isdst)=localtime(time);
 $mois++;
 $annee += 1900;
 my $title=sprintf("%02d/%02d/%4d  %02d:%02d:%02d",$mjour,$mois,$annee,$heure,$min,$sec);
 return substr($title,0,$COLS);
}

sub get_mount_1
{
 return &get_disk(1);
}

sub get_mount_2
{
 return &get_disk(2);
}

sub get_mount_3
{
 return &get_disk(3);
}

sub get_disk
{
 # by oxy750
 # modified 15/007/2004 by Fesch

 my ($fs,$size,$used,$avail,$pourcentuse,$mount);
 my $numdisk = shift;
 $numdisk++;
 my $disk=`/bin/df -h | head -n $numdisk | tail -n 1`;
 chop($disk);
 ($fs,$size,$used,$avail,$pourcentuse,$mount)=split(/ +/,$disk);
 my @some = explode('M',$avail);
 my $co = @some;
 if($co = 1)
 {
  my @some = explode('G',$some[0]);
  $avail=$some[0].' GB';
 }
 else
 {
  $avail=$some[0].' MB';
 }
 while(length($mount) != $COLS-11) {$mount.=' ';}
 my $title=sprintf("$mount%4s %6s",$pourcentuse,$avail);

 return substr($title,0,$COLS);
}

sub get_version
{
 my $title=$VERSION;
 while(length($title) != $COLS) {$title.=' ';}
 return substr($title,0,$COLS);
}

sub get_ixui
{
 # get time
 my $itIs = time();

 if(($itIs-$TIME_IXUI)>30)
 {
  $TIME_IXUI=$itIs;

  my $peer = 'www.ixus.net';
  my $peerport = 80;
  my $sock;
  unless(($sock = new IO::Socket::INET (PeerAddr => $peer, PeerPort => $peerport, Proto => 'tcp', Timeout => 5)))
  {
   my $some = 'www.ixus.net down?';
   while(length($some)<$COLS) {$some.=' ';}
   $CACHE_IXUI=$some;
   return substr($some,0,$COLS);
  }
  else
  { 
   my $GET_CMD;
   $GET_CMD  = "GET http://www.ixus.net/ixui.html HTTP/1.1\r\n";
   $GET_CMD .= "Host: www.ixus.net\r\n";
   $GET_CMD .= "Cache-Control: no-cache\r\n";
   $GET_CMD .= "Connection: close\r\n\r\n";
   print $sock "$GET_CMD";
   my $ret = '';
   while (<$sock>) 
   {
    $ret .= $_; 
   }
   close($sock);
   my @lines = explode("\n",$ret);
   @lines = explode('<br>',$lines[10]);
   # 1 => time
   # 3 => title
   # 4 => forum
   # 5 => user
   my $title = $lines[1].': '.$lines[3].' ('.$lines[5].') ';
   $title = &strReplace('','e',$title);
   $title = &strReplace('','e',$title);
   $title = &strReplace('','e',$title);
   $title = &strReplace('','e',$title);
   $title = &strReplace('','a',$title);
   $title = &strReplace('','a',$title);
   $title = &strReplace('','a',$title);
   $title = &strReplace('','a',$title);
   $title = &strReplace('','i',$title);
   $title = &strReplace('','i',$title);
   $title = &strReplace('','i',$title);
   $title = &strReplace('','i',$title);
   $title = &strReplace('','u',$title);
   $title = &strReplace('','u',$title);
   $title = &strReplace('','u',$title);
   $title = &strReplace('','u',$title);
   $title = &strReplace('','o',$title);
   $title = &strReplace('','o',$title);
   $title = &strReplace('','o',$title);
   $title = &strReplace('','o',$title);
   $title = &strReplace('','c',$title);
   $title = &strReplace('&gt;','>',$title);
   $title = &strReplace('&lt;','<',$title);
   $title = &strReplace('&quot;','"',$title);
   $title = &strReplace('&amp;','&',$title);
   if($CACHE_IXUI ne $title)
   {
    $CACHE_IXUI=$title;
    return substr($CACHE_IXUI,0,$COLS);
   }
  }
 }
 else
 {
   my $title = substr($CACHE_IXUI,0,$COLS);
   $CACHE_IXUI=substr($CACHE_IXUI,1,length($CACHE_IXUI)).substr($CACHE_IXUI,0,1);
   return substr($title,0,$COLS);
 }
}

#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  LCD User Character
#  Generation Methods
#
#  => Don't touch this!
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

sub generate_char
{
 my $shift = shift;
 my @data = @{$shift};
 my $c = shift;

 &set_instruction($SETDDRAMADDRESS + $c);

 for(my $i=0;$i<8;$i++)
 {
  &set_instruction($SETCGRAMADDRESS + $c*8 + $i);
  &mem(@data[$i]);
 }
}

sub generate_heart
{
 my $c = 6;

 &set_instruction($SETDDRAMADDRESS + $c);

 &set_instruction($SETCGRAMADDRESS + $c*8 + 0);
 &mem(0x08 | 0x02);
 &set_instruction($SETCGRAMADDRESS + $c*8 + 1);
 &mem(0x10 | 0x04 | 0x01);
 &set_instruction($SETCGRAMADDRESS + $c*8 + 2);
 &mem(0x10 | 0x04 | 0x01);
 &set_instruction($SETCGRAMADDRESS + $c*8 + 3);
 &mem(0x10 | 0x01);
 &set_instruction($SETCGRAMADDRESS + $c*8 + 4);
 &mem(0x10 | 0x01);
 &set_instruction($SETCGRAMADDRESS + $c*8 + 5);
 &mem(0x08 | 0x02);
 &set_instruction($SETCGRAMADDRESS + $c*8 + 6);
 &mem(0x08 | 0x02);
 &set_instruction($SETCGRAMADDRESS + $c*8 + 7);
 &mem(0x04);

 $c = 7;

 &set_instruction($SETDDRAMADDRESS + $c);

 &set_instruction($SETCGRAMADDRESS + $c*8 + 0);
 &mem(0x08 | 0x02);
 &set_instruction($SETCGRAMADDRESS + $c*8 + 1);
 &mem(0x10 | 0x08 | 0x04 | 0x02 | 0x01);
 &set_instruction($SETCGRAMADDRESS + $c*8 + 2);
 &mem(0x10 | 0x08 | 0x04 | 0x02 | 0x01);
 &set_instruction($SETCGRAMADDRESS + $c*8 + 3);
 &mem(0x10 | 0x08 | 0x04 | 0x02 | 0x01);
 &set_instruction($SETCGRAMADDRESS + $c*8 + 4);
 &mem(0x10 | 0x08 | 0x04 | 0x02 | 0x01);
 &set_instruction($SETCGRAMADDRESS + $c*8 + 5);
 &mem(0x08 | 0x04 | 0x02);
 &set_instruction($SETCGRAMADDRESS + $c*8 + 6);
 &mem(0x08 | 0x04 | 0x02);
 &set_instruction($SETCGRAMADDRESS + $c*8 + 7);
 &mem(0x04);
}

sub generate_bars
{
 for(my $c=0;$c<6;$c++)
 {
  &set_instruction($SETDDRAMADDRESS + $c);
 
  my $sim = 0;
  if($c==0) {$sim=0x10;}
  if($c==1) {$sim=0x18;}
  if($c==2) {$sim=0x1C;}
  if($c==3) {$sim=0x1E;}
  if($c==4) {$sim=0x1F;}
 
  for(my $l=0;$l<8;$l++)
  {
   &set_instruction($SETCGRAMADDRESS + $c*8 + $l);
   &mem($sim);
  }
 }
}


#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  LCD Control Procedure
#
#  => Don't touch this!
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

sub lcd_init
{
 &set_instruction($FUNCTIONSET | $EIGHTBITMODE);
 &set_instruction($FUNCTIONSET | $EIGHTBITMODE);
 &set_instruction($FUNCTIONSET | $EIGHTBITMODE);
 if($MODE == 4)
 {
  &set_instruction($FUNCTIONSET | $FOURBITMODE );
  &set_instruction( 
		 	$FUNCTIONSET | 
	 		$FOURBITMODE |  
 			$TWOLINES |  
			$FONT5X8
			);
 }
 else
 {
  &set_instruction( 
		 	$FUNCTIONSET | 
	 		$EIGHTBITMODE |  
 			$TWOLINES |  
			$FONT5X8
			);
 }
 ##### display off
 &set_instruction($DISPLAY | $DISPLAY_OFF);
 ##### display clear
 &set_instruction($CLEARDISPLAY);
 ##### entry mode
 &set_instruction(	$ENTRYMODE |
			$INCREMENT |
			$SHIFT_OFF
		);
 ##### end of initialization
 &set_instruction($RETURNHOME);
 ##### turn on display
 &set_instruction(	$DISPLAY | 
			$DISPLAY_ON |
			$CURSOR_OFF |
			$BLINK_OFF
		);
}

sub set_data_at
{
 my $offset = shift;
 my $line = shift;
 my $c = shift;

 my $lineIs = $line;

 if($line == 1) {$lineIs=1*$SECONDLINEOFFSET;}
 if($line == 2) {$lineIs=$COLS;}
 if($line == 3) {$lineIs=1*$SECONDLINEOFFSET + $COLS;}
 
 &set_instruction($SETDDRAMADDRESS + $offset + $lineIs);
 &set_data($c);
}

sub set_data
{
 my $data = shift;
 write_byte($data,$base_port);
 write_byte($RS_HIGH | $RW_LOW | $E_HIGH,$control_port);
# usleep(0);
 write_byte($RS_HIGH | $RW_LOW | $E_LOW,$control_port);
}

sub mem
{
 my $data = shift;
 write_byte($data,$base_port);
 write_byte($RS_HIGH | $RW_LOW | $E_HIGH,$control_port);
 write_byte($RS_HIGH | $RW_LOW | $E_LOW,$control_port);
}

sub set_instruction
{
 my $data = shift;
 write_byte($data,$base_port);
 write_byte($RS_LOW | $RW_LOW | $E_HIGH,$control_port);
 usleep(0);
# usleep(0.0000002000);
 write_byte($RS_LOW | $RW_LOW | $E_LOW,$control_port);
}

sub read_byte
{
 my $port = shift;
 my $show = 0;

 sysopen (PORT,"/dev/port",O_RDWR) || die "Datei: $!";
 binmode (PORT);
 sysseek (PORT,$port,0)|| die "seek_err\n";
 my $test = sysread(PORT,$show,1)  || print "write_err while reading from port\n";
 close (PORT);
 $show=ord($show);
 return $show;
}

sub write_byte
{
 my $data = shift;
 my $show = chr($data);
 my $port = shift;

 if($MODE==4 && $port==$base_port)
 {
  my $first = $data;
  $first = ($first >> 4);
  my $second = $data;
  $second = (($second << 4) >> 4) % 256;

  sysopen (PORT,"/dev/port",O_RDWR) || die "Datei: $!";
  binmode (PORT);

  $show = chr($first);
  sysseek (PORT,$port,0)|| die "seek_err\n";
  syswrite(PORT,$show)  || print "write_err while writing $data \n";

  $show = chr($second);
  sysseek (PORT,$port,0)|| die "seek_err\n";
  syswrite(PORT,$show)  || print "write_err while writing $data \n";

  close (PORT);
 }
 else
 {
  sysopen (PORT,"/dev/port",O_RDWR) || die "Datei: $!";
  binmode (PORT);
  sysseek (PORT,$port,0)|| die "seek_err\n";
  syswrite(PORT,$show)  || print "write_err while writing $data \n";
  close (PORT);
 }
}

sub write_string
{
 my $str = shift;
 for(my $i=0;$i<length($str);$i++)
 {
  set_data(ord(substr($str,$i,1)));
 } 
}

sub write_string_at
{
 my $off = shift;
 my $line = shift;
 my $str = shift;

 for(my $i=0;$i<length($str);$i++)
 {
  set_data_at($off+$i,$line,ord(substr($str,$i,1)));
  &checkButtons;
 } 
}

sub clear_display
{
 @LINECACHE = ('','','','');
 &set_instruction($CLEARDISPLAY);
}


sub set_back_on
{
 &write_byte($BACK_ON, $control_port);
}

sub set_back_off
{
 &write_byte($BACK_OFF, $control_port);
}


#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  Save & Load Procedures
#
#  -> Do not touch!
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

sub load_LINES
{
 open (LINES, "lcd.conf") || die "couldn't open the file!";
 my $i = 0;
 while (my $record = <LINES>)
 {
  if(trim($record) ne '')
  {
   @LINE_FUNC[$i] = trim($record);
   $i=$i+1;
  }
 }
 if(@LINE_FUNC[$ROWS] ne '')
 {
  $CYCLE = @LINE_FUNC[$ROWS];
 }
 close(LINES);

 my $found = 0;
 foreach(@LINE_FUNC)
 {
  if(9<= $_ && $_<=12) {$found=1;}
 }
 if($found==1){&generate_bars;}

 $found = 0;
 foreach(@LINE_FUNC)
 {
  if(13<= $_ && $_<=14) {$found=1;}
 }
 if($found==1)
 {
  &generate_char(@BAR_FULL,0);
  &generate_char(@BAR_EMPTY,1);
 }
}

sub save_LINES
{
 open (LINES, ">lcd.conf") or die "no such file";
 for(my $f=0;$f<$ROWS;$f++)
 {
  print LINES @LINE_FUNC[$f]."\n";
 }
 print LINES $CYCLE."\n";
 close(LINES);
}


#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  General functions
#
#  -> Do not touch!
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

sub fileExists
{
 return (-f shift);
}

sub trim
{
  my $temp = shift;
  $temp =~ s/^\s*//;
  $temp =~ s/\s*$//;
  return $temp;
}

sub explode
{
 my $splitter = shift;
 my $who = shift;
 my $tmp;
 my @result;
 my $i = 0;
 my $count = 0;

 while($i<length($who))
 {
  if(substr($who,$i,length($splitter)) eq $splitter)
  {
   $result[$count]=$tmp;
   $tmp='';
   $count++;
   $i+=length($splitter);
  }
  else
  {
   $tmp=$tmp.substr($who,$i,1);
   $i++;
  }
 }
 $result[$count]=$tmp;
 return @result;
}

sub strReplace
{
 my $what = shift;
 my $with = shift;
 my $who = shift;
 $who =~ s/$what/$with/g;
 return $who;
}

sub file
{
 open (FILE, shift) || die "couldn't open the file!";
 my @LINES;
 my $i = 0;
 while (my $record = <FILE>)
 {
  @LINES[$i] = trim($record);
  $i=$i+1;
 }
 close(FILE);
 return @LINES;
}


#/\/\/\/\/\/\/\/\/\/\/\/\/\
# 
#  Not used !!!
#
#\/\/\/\/\/\/\/\/\/\/\/\/\/

sub write_char_X{
 my $show = shift;
 my $port = shift;

 ##### to modify !

 open (PORT,">/dev/port");
 binmode (PORT);
 seek (PORT,$port,0)|| die "seek_err\n";
 print PORT "$show" ||die "write_err while writing $show\n";
 close (PORT);
}

sub set_instruction_4bit
{
 my $data = shift;
 my $first = $data;
 $first = ($first >> 4) << 4;
 my $second = $data;
 $second = $data << 4;
 &set_instruction($first);
 &set_instruction($second);
}

sub lcd_init_4bit
{
 &set_instruction( $FUNCTIONSET | $EIGHTBITMODE );
 &set_instruction( $FUNCTIONSET | $EIGHTBITMODE );
 &set_instruction( $FUNCTIONSET | $EIGHTBITMODE );
 &set_instruction( $FUNCTIONSET | $FOURBITMODE );
 &set_instruction( $FUNCTIONSET | $FOURBITMODE );
 &set_instruction( $FUNCTIONSET | $FOURBITMODE | $TWOLINES | $FONT5X8 );
 ##### display off
 &set_instruction( $DISPLAY | $DISPLAY_OFF );
 ##### display clear
 &set_instruction( $DISPLAY | $CLEARDISPLAY );
 ##### entry mode
 &set_instruction( $ENTRYMODE | $INCREMENT | $SHIFT_OFF );
 ##### end of initialization
 &set_instruction( $RETURNHOME );
 ##### turn on display
 &set_instruction( $DISPLAY | $DISPLAY_ON | $CURSOR_OFF | $BLINK_OFF );
}

__END__ 

