#!/usr/bin/perl
# 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

# 2005 Peter Rustler - qemuctl@chefpro.de

use Gtk2 qw/-init -threads-init/;
use Gtk2::GladeXML;
use IO::Handle;
use IPC::Open3;
use Gtk2::Helper;
use Fcntl;
use Getopt::Long qw(:config pass_through);

$default_floppy_a = "";
$default_floppy_b = "";
$default_cdrom    = "";
$default_serial1    = "";
$default_parport    = "";
$suspend_dir = ".";
$suspend_file = "suspend";
$wakeup=1;
$loadvm=0;
$monitor="xystdio";
$usbhostlog = "";
$usblog = "";
$usb=0;
$usbhost=0;
$dontread = 0;
$usbmenu = 0;
$usbhostmenu = 0;
@usbmenuitems = 0;
@usbhostmenuitems = 0;

GetOptions("suspend-dir=s"=>\$suspend_dir,
           "suspend-file=s"=>\$suspend_file,
           "wakeup!"=>\$wakeup#,
           #"monitor=s"=>\$monitor
);

$command = "qemu '".join( "' '", @ARGV)."' -monitor stdio";

GetOptions("fda=s"=>\$default_floppy_a,
           "fdb=s"=>\$default_floppy_b,
           "cdrom=s"=>\$default_cdrom,
           "serial=s"=>\$default_serial1,
           "parallel=s"=>\$default_parport,
           "loadvm"=>\$loadvm
);

# if monitor != stdio
#if( $monitor ne "stdio" && $monitor != 48 ){
#  print STDERR "Error: Monitor not stdio\n";
#  exit 1;
#}

#print "xxx".$monitor."xxx\n";
#exit;

if( $loadvm == 0 && $wakeup == 1 && -f $suspend_dir."/".$suspend_file.".vm" && -r $suspend_dir."/".$suspend_file.".vm"){
  $command .= " -loadvm '".$suspend_dir."/".$suspend_file.".vm'";
} else {
  $wakeup = 0;
}

#print $command."\n";

$gladexml = Gtk2::GladeXML->new('/usr/share/qemuctl/qemuctl.xml');
#$gladexml = Gtk2::GladeXML->new('qemuctl.xml');

$window = $gladexml->get_widget('mainwindow');
$info = $gladexml->get_widget('info');
$savevm = $gladexml->get_widget('savevm');
$loadvm = $gladexml->get_widget('loadvm');
$openfile = $gladexml->get_widget('openfile');
$savescreen = $gladexml->get_widget('savescreen');
$shellwindow = $gladexml->get_widget('shellwindow');
$logview = $gladexml->get_widget('textview');
$logscroll = $gladexml->get_widget('scrolledwindow1');

# setting default names
$gladexml->get_widget('floppy_a_default')->get_child()->set_label("default (".$default_floppy_a.")");
$gladexml->get_widget('floppy_b_default')->get_child()->set_label("default (".$default_floppy_b.")");
$gladexml->get_widget('cdrom_default')->get_child()->set_label("default (".$default_cdrom.")");
$gladexml->get_widget('serial1_default')->get_child()->set_label("default (".$default_serial1.")");
$gladexml->get_widget('parport_default')->get_child()->set_label("default (".$default_parport.")");

$gladexml->get_widget('usb_connect')->set_submenu( $usbhostmenu=new Gtk2::Menu() );
$gladexml->get_widget('usb_disconnect')->set_submenu( $usbmenu=new Gtk2::Menu() );

$gladexml->signal_autoconnect_all(
"gtk_main_quit"				=> gtk_main_quit,
"on_save_screenshot_activate"		=> on_save_screenshot_activate,
"on_save_vm_state_activate"		=> on_save_vm_state_activate,
"on_load_vm_state_activate"		=> on_load_vm_state_activate,
"on_quit_activate"			=> on_quit_activate,

"on_floppy_a_default_activate"		=> on_floppy_a_default_activate,
"on_floppy_a_fd0_activate"		=> on_floppy_a_fd0_activate,
"on_floppy_a_null_activate"		=> on_floppy_a_null_activate,
"on_floppy_a_other_activate"		=> on_floppy_a_other_activate,
"on_eject_a_activate"			=> on_eject_a_activate,

"on_floppy_b_default_activate"		=> on_floppy_b_default_activate,
"on_floppy_b_fd0_activate"		=> on_floppy_b_fd0_activate,
"on_floppy_b_null_activate"		=> on_floppy_b_null_activate,
"on_floppy_b_other_activate"		=> on_floppy_b_other_activate,
"on_eject_b_activate"			=> on_eject_b_activate,

"on_cdrom_default_activate"		=> on_cdrom_default_activate,
"on_cdrom_cdrom_activate"		=> on_cdrom_cdrom_activate,
"on_cdrom_null_activate"		=> on_cdrom_null_activate,
"on_cdrom_other_activate"		=> on_cdrom_other_activate,
"on_eject_cdrom_activate"		=> on_eject_cdrom_activate,

"on_usb_connect_activate"		=> on_usb_connect_activate,
"on_usb_disconnect_activate"		=> on_usb_disconnect_activate,

"on_serial1_default_activate"		=> on_serial1_default_activate,
"on_serial1_ttyS0_activate"		=> on_serial1_ttyS0_activate,
"on_serial1_null_activate"		=> on_serial1_null_activate,
"on_serial1_other_activate"		=> on_serial1_other_activate,

"on_parport_default_activate"		=> on_parport_default_activate,
"on_parport_parport0_activate"		=> on_parport_parport0_activate,
"on_parport_null_activate"		=> on_parport_null_activate,
"on_parport_other_activate"		=> on_parport_other_activate,

"on_suspend_activate"			=> on_suspend_activate,
"on_pause_activate"			=> on_pause_activate,
"on_continue_activate"			=> on_continue_activate,
"on_power_off_activate"			=> on_power_off_activate,
"on_reset_activate"			=> on_reset_activate,
"on_commit_activate"			=> on_commit_activate,
"on_quit_qemu_activate"			=> on_quit_qemu_activate,

"on_help_activate"			=> on_help_activate,
"on_info_activate"			=> on_info_activate,

"on_sendkeys_ctrl-alt-delete_activate"	=> on_sendkeys_ctrl_alt_delete_activate,
"on_sendkeys_ctrl-alt-f1_activate"	=> on_sendkeys_ctrl_alt_f1_activate,
"on_sendkeys_ctrl-alt-f2_activate"	=> on_sendkeys_ctrl_alt_f2_activate,
"on_sendkeys_ctrl-alt-f3_activate"	=> on_sendkeys_ctrl_alt_f3_activate,
"on_sendkeys_ctrl-alt-f4_activate"	=> on_sendkeys_ctrl_alt_f4_activate,
"on_sendkeys_ctrl-alt-f5_activate"	=> on_sendkeys_ctrl_alt_f5_activate,
"on_sendkeys_ctrl-alt-f6_activate"	=> on_sendkeys_ctrl_alt_f6_activate,
"on_sendkeys_ctrl-alt-f7_activate"	=> on_sendkeys_ctrl_alt_f7_activate,
"on_sendkeys_ctrl-alt-f8_activate"	=> on_sendkeys_ctrl_alt_f8_activate,
"on_sendkeys_ctrl-alt-f9_activate"	=> on_sendkeys_ctrl_alt_f9_activate,
"on_sendkeys_ctrl-alt-f10_activate"	=> on_sendkeys_ctrl_alt_f10_activate,
"on_sendkeys_ctrl-alt-f11_activate"	=> on_sendkeys_ctrl_alt_f11_activate,
"on_sendkeys_ctrl-alt-f12_activate"	=> on_sendkeys_ctrl_alt_f12_activate,

"on_shell_activate"			=> on_shell_activate,
"on_shellwindow_delete_event"		=> on_shellwindow_delete_event,
"on_sendbutton_key_press_event"		=> on_sendbutton_key_press_event,
);

$log="";
$pid = open3( QEMUIN, QEMUOUT, QEMUERR, $command);
QEMUOUT->autoflush(1);
QEMUIN->autoflush(1);
QEMUERR->autoflush(1);
fcntl( QEMUIN  , F_SETFL, O_NONBLOCK);
fcntl( QEMUOUT , F_SETFL, O_NONBLOCK);
fcntl( QEMUERR , F_SETFL, O_NONBLOCK);
fcntl( STDERR  , F_SETFL, O_NONBLOCK);

if( $monitor eq "stdio" ){
  STDIN->autoflush(1);
  fcntl( STDIN   , F_SETFL, O_NONBLOCK);
  fcntl( STDOUT  , F_SETFL, O_NONBLOCK);
}

$stdqemuout = Gtk2::Helper->add_watch( fileno( QEMUOUT ), in => sub {
    while( $dontread == 1 ){
      ;
    }
    $dontread == 1;
    if ( eof( QEMUOUT ) ) {
        Gtk2::Helper->remove_watch( $stdqemuout );
        close( QEMUOUT );
        $dontread == 0;
	gtk_main_quit();
    }
    else {
        #print "Lese QEMUOUT begin\n";
        sysread( QEMUOUT, $line, 8000);
        #print "Lese QEMUOUT end\n";
        #my $line = <QEMUOUT>;
        #if( $line =~/\n/ ){
          if( $monitor eq "stdio" ){
            print STDOUT $line;
          }
          $line =~s/\(qemu\).+\[D/\(qemu\)/;
          $log = $log . $line;
          $usbhostlog = $usbhostlog . $line;
          $usblog = $usblog . $line;
	  if( $usb == 1 and $usblog =~/qemu/s ){
	    $usb = 0;
	    usb_disconnect_act();
	  }
	  if( $usbhost == 1 and $usbhostlog =~/qemu/s ){
	    $usbhost = 0;
	    usb_connect_act();
	  }
          ($start, $end) = $logview->get_buffer->get_bounds();
	  #print "buffer-chars: ".$logview->get_buffer->get_char_count." buffer-lines: ".$logview->get_buffer->get_line_count."\n";
          Gtk2::TextBuffer::insert($logview->get_buffer,$end,$line);
          #$logview->get_buffer->set_text($log);
          $gladexml->get_widget('scrolledwindow1')->get_vadjustment->set_value(
            $gladexml->get_widget('scrolledwindow1')->get_vadjustment->upper -
            $gladexml->get_widget('scrolledwindow1')->get_vadjustment->page_size - 1);
        #}
    }
    $dontread == 0;

    return 1;
} );

$stdqemuerr = Gtk2::Helper->add_watch( fileno( QEMUERR ), in => sub {
    while( $dontread == 1 ){
      ;
    }
    $dontread == 1;
    if ( eof( QEMUERR ) ) {
        Gtk2::Helper->remove_watch( $stdqemuerr );
        close( QEMUERR );
        $dontread == 0;
        gtk_main_quit();
    }
    else {
        #print "Lese QEMUERR begin\n";
        sysread( QEMUERR, $line, 8000);
        #print "Lese QEMUERR end\n";
        #my $line = <QEMUERR>;
        #if( $line =~/\n/ ){
          $log = $log . $line;
          print STDERR $line;
          #($start, $end) = $logview->get_buffer->get_bounds();
          Gtk2::TextBuffer::insert($logview->get_buffer,$end,$line);
          #$logview->get_buffer->set_text($log);
          $gladexml->get_widget('scrolledwindow1')->get_vadjustment->set_value(
            $gladexml->get_widget('scrolledwindow1')->get_vadjustment->upper -
            $gladexml->get_widget('scrolledwindow1')->get_vadjustment->page_size - 1);
        #}
    }
    $dontread == 0;

    return 1;
} );

if( $monitor eq "stdio" ){
$stdin = Gtk2::Helper->add_watch( fileno( STDIN ), in => sub {
    if ( eof( STDIN ) ) {
        Gtk2::Helper->remove_watch( $stdin );
        close( STDIN );
        gtk_main_quit();
    }
    else {
        #print "Lese STDIN begin\n";
        sysread( STDIN, $line, 8000);
        #print "Lese STDIN end\n";
        #$line = STDIN;
        #if( $line =~ s/\n// ){
          sendcomandraw( $line );
        #}
    }

    return 1;
} );
}
$window->show;
Gtk2->main;

#waitpid($pid);

sub sendcomand {
  print QEMUIN $_[0]."\n";
}

sub sendcomandraw {
  print QEMUIN $_[0];
}

sub gtk_main_quit {
  sendcomand( "quit" );
  Gtk2->main_quit;
  exit(0);
}

sub on_save_screenshot_activate {
  $savescreen->show;
  my $status = $savescreen->run;
  $savescreen->hide;
  if( $status eq "ok" ){
    my $filename = $savescreen->get_filename();
    sendcomand( "screendump \"".$filename.".ppm\"" );
  }
}

sub on_save_vm_state_activate {
  $savevm->show;
  my $status = $savevm->run;
  $savevm->hide;
  if( $status eq "ok" ){
    my $filename = $savevm->get_filename();
    sendcomand( "savevm \"".$filename."\"" );
  }
}

sub on_load_vm_state_activate {
  $loadvm->show;
  my $status = $loadvm->run;
  $loadvm->hide;
  if( $status eq "ok" ){
    my $filename = $loadvm->get_filename();
    sendcomand( "loadvm \"".$filename."\"" );
  }
}

sub on_quit_activate {
  gtk_main_quit();
}

sub on_floppy_a_default_activate {
  sendcomand( "change fda \"".$default_floppy_a."\"" );
}

sub on_floppy_a_fd0_activate {
  sendcomand( "change fda \"/dev/fd0\"" );
}

sub on_floppy_a_null_activate {
  sendcomand( "change fda \"/dev/null\"" );
}

sub on_floppy_a_other_activate {
  $openfile->show;
  my $status = $openfile->run;
  $openfile->hide;
  if( $status eq "ok" ){
    my $filename = $openfile->get_filename();
    sendcomand( "change fda \"".$filename."\"" );
  }
}

sub on_eject_a_activate {
  sendcomand( "eject -f fda" );
}

sub on_floppy_b_default_activate {
  sendcomand( "change fdb \"".$default_floppy_b."\"" );
}

sub on_floppy_b_fd0_activate {
  sendcomand( "change fdb \"/dev/fd0\"" );
}

sub on_floppy_b_null_activate {
  sendcomand( "change fdb \"/dev/null\"" );
}

sub on_floppy_b_other_activate {
  $openfile->show;
  my $status = $openfile->run;
  $openfile->hide;
  if( $status eq "ok" ){
    my $filename = $openfile->get_filename();
    sendcomand( "change fdb \"".$filename."\"" );
  }
}

sub on_eject_b_activate {
  sendcomand( "eject -f fdb" );
}

sub on_cdrom_default_activate {
  sendcomand( "change cdrom \"".$default_cdrom."\"" );
}

sub on_cdrom_cdrom_activate {
  sendcomand( "change cdrom \"/dev/cdrom\"" );
}

sub on_cdrom_null_activate {
  sendcomand( "change cdrom \"/dev/null\"" );
}

sub on_cdrom_other_activate {
  $openfile->show;
  my $status = $openfile->run;
  $openfile->hide;
  if( $status eq "ok" ){
    my $filename = $openfile->get_filename();
    sendcomand( "change cdrom \"".$filename."\"" );
  }
}

sub on_eject_cdrom_activate {
  sendcomand( "eject -f cdrom" );
}

sub on_usb_connect_activate{
  if( $usbhost == 0 and $usb == 0 ){
    $usbhostlog="";
    sendcomand( "info usbhost" );
    $usbhost = 1;
  }
}

sub usb_connect_act{
  #print "starting usb_connect_act\n";
  my @temp = split( /\n/, $usbhostlog);
  #$usbhostmenu->destroy();
  #$usbhostmenu = new Gtk2::Menu();    # Don't need to show menus
  $i=0;
  foreach( @temp ){
    if( $_=~/Class/ ){
      $_=~s/.+USB device (.+), (.+)/\1\*\2/;
      @tmp = split( /\*/, $_ );
      if( not $usbhostmenuitems[$i] ){
        $usbhostmenuitems[$i] = new Gtk2::MenuItem( $tmp[0]." -> ".$tmp[1] );
        $usbhostmenuitems[$i]->signal_connect( 'activate', usb_connect );
        $usbhostmenu->append($usbhostmenuitems[$i]);
        $usbhostmenuitems[$i]->show();
      } else {
        $usbhostmenuitems[$i]->get_child->set_label( $tmp[0]." -> ".$tmp[1] );
        $usbhostmenuitems[$i]->show();
      }
      $i++;
      #print "menu ".$tmp[0]." ".$tmp[1]."\n";
    }
  }
  while( $usbhostmenuitems[$i] ){
    $usbhostmenuitems[$i]->get_child->set_label( "" );
    $usbhostmenuitems[$i]->hide();
    $i++;
  }
  #$gladexml->get_widget('usb_connect')->set_submenu( $usb_menu );
  #print "stoping usb_connect_act\n";
}

sub on_usb_disconnect_activate{
  if( $usbhost == 0 and $usb == 0 ){
    $usblog="";
    sendcomand( "info usb" );
    $usb = 1;
  }
}

sub usb_disconnect_act{
  #print "starting usb_disconnect_act\n";
  my @temp = split( /\n/, $usblog);
  #$usbmenu->destroy();
  #$usbmenu = new Gtk2::Menu();    # Don't need to show menus
  $i=0;
  foreach( @temp ){
    if( $_=~/Device/ ){
      $_=~s/.+Device (.+), .+/\1/;
      if( not $usbmenuitems[$i] ){
        $usbmenuitems[$i] = new Gtk2::MenuItem( "Device ".$_ );
        $usbmenuitems[$i]->signal_connect( 'activate', usb_disconnect );
        $usbmenu->append($usbmenuitems[$i]);
        $usbmenuitems[$i]->show();
      } else {
        $usbmenuitems[$i]->get_child->set_label( "Device ".$_ );
        $usbmenuitems[$i]->show();
      }
      $i++;      
    }
  }
  while( $usbmenuitems[$i] ){
    $usbmenuitems[$i]->get_child->set_label( "" );
    $usbmenuitems[$i]->hide();
    $i++;
  }
  #$gladexml->get_widget('usb_disconnect')->set_submenu( $usb_menu );
  #print "stoping usb_disconnect_act\n";
}

sub on_serial1_default_activate {
  sendcomand( "change serial \"".$default_serial1."\"" );
}

sub on_serial1_ttys0_activate {
  sendcomand( "change serial \"/dev/ttyS0\"" );
}

sub on_serial1_null_activate {
  sendcomand( "change serial \"/dev/null\"" );
}

sub on_serial1_other_activate {
  $openfile->show;
  my $status = $openfile->run;
  $openfile->hide;
  if( $status eq "ok" ){
    my $filename = $openfile->get_filename();
    sendcomand( "change serial \"".$filename."\"" );
  }
}

sub on_parport_default_activate {
  sendcomand( "change parport \"".$default_parport."\"" );
}

sub on_parport_parport0_activate {
  sendcomand( "change parport \"/dev/parport0\"" );
}

sub on_parport_null_activate {
  sendcomand( "change parport \"/dev/null\"" );
}

sub on_parport_other_activate {
  $openfile->show;
  my $status = $openfile->run;
  $openfile->hide;
  if( $status eq "ok" ){
    my $filename = $openfile->get_filename();
    sendcomand( "change parport \"".$filename."\"" );
  }
}

sub on_suspend_activate {
  sendcomand( "stop" );
  sendcomand( "screendump \"".$suspend_dir."/".$suspend_file.".ppm\"" );
  sendcomand( "savevm \"".$suspend_dir."/".$suspend_file.".vm\"" );
  sendcomand( "commit" );
  sendcomand( "quit" );
  gtk_main_quit();
}

sub on_pause_activate {
  sendcomand( "stop" );
}

sub on_continue_activate {
  sendcomand( "c" );
}

sub on_power_off_activate {
  sendcomand( "system_powerdown" );
}

sub on_reset_activate {
  sendcomand( "system_reset" );
}

sub on_commit_activate {
  sendcomand( "commit" );
}

sub on_quit_qemu_activate {
  sendcomand( "quit" );
  gtk_main_quit();
}

sub on_help_activate {
  $info->show;
  $info->run;
  $info->hide;
}

sub on_info_activate {
  $info->show;
  $info->run;
  $info->hide;
}

sub on_sendkeys_ctrl_alt_delete_activate {
  sendcomand( "sendkey ctrl-alt-delete" );
}

sub on_sendkeys_ctrl_alt_f1_activate {
  sendcomand( "sendkey ctrl-alt-f1" );
}

sub on_sendkeys_ctrl_alt_f2_activate {
  sendcomand( "sendkey ctrl-alt-f2" );
}

sub on_sendkeys_ctrl_alt_f3_activate {
  sendcomand( "sendkey ctrl-alt-f3" );
}

sub on_sendkeys_ctrl_alt_f4_activate {
  sendcomand( "sendkey ctrl-alt-f4" );
}

sub on_sendkeys_ctrl_alt_f5_activate {
  sendcomand( "sendkey ctrl-alt-f5" );
}

sub on_sendkeys_ctrl_alt_f6_activate {
  sendcomand( "sendkey ctrl-alt-f6" );
}

sub on_sendkeys_ctrl_alt_f7_activate {
  sendcomand( "sendkey ctrl-alt-f7" );
}

sub on_sendkeys_ctrl_alt_f8_activate {
  sendcomand( "sendkey ctrl-alt-f8" );
}

sub on_sendkeys_ctrl_alt_f9_activate {
  sendcomand( "sendkey ctrl-alt-f9" );
}

sub on_sendkeys_ctrl_alt_f10_activate {
  sendcomand( "sendkey ctrl-alt-f10" );
}

sub on_sendkeys_ctrl_alt_f11_activate {
  sendcomand( "sendkey ctrl-alt-f11" );
}

sub on_sendkeys_ctrl_alt_f12_activate {
  sendcomand( "sendkey ctrl-alt-f12" );
}

sub on_shell_activate {
  $shellwindow->show;
}

sub on_shellwindow_delete_event {
  $shellwindow->hide;
  return TRUE;
}

sub on_sendbutton_key_press_event {
  sendcomand( $gladexml->get_widget('sendtext')->get_text() );
  $gladexml->get_widget('sendtext')->set_text("");
}

sub usb_connect{
  $tmp = $_[0]->get_child()->get_label();
  $tmp =~s/(.+) -> .+/\1/;
  #print( "usb_add host:".$tmp."\n" );
  sendcomand( "usb_add host:".$tmp );
}

sub usb_disconnect{
  $tmp = $_[0]->get_child()->get_label();
  $tmp =~s/Device (.+)/\1/;
  #print( "usb_del ".$tmp."\n" );
  sendcomand( "usb_del ".$tmp );
}

