#! /usr/bin/perl -w
#----------------------------------------------------------------------
# gtkremote.pl
#
# A remote desktop front-end of Gtk2/GladeXML
#
# Copyright (C) 2008 Viperii <hominid@tom.com>
# Licensed under the GPL
#
#----------------------------------------------------------------------
use strict;
use warnings;
use Glib qw/TRUE FALSE/;
use Gtk2 '-init';
use Gtk2::Helper;
use Gtk2::GladeXML;
use FindBin qw($Bin);
use Expect;
my $CMD;
my $PID;
my $TAG;
my $CONN = 0;
my $PREFIX = '@PREFIX@';
my $BIN_DIR = (-d $PREFIX ? "$PREFIX/bin" : $Bin);
my $VNC_TMP = "/tmp/vncpasswd.tmp";
$| = 1;
my $glade_data; {local $/ = undef; $glade_data = <DATA>;}
my $gladexml = Gtk2::GladeXML->new_from_buffer($glade_data);
#my $gladexml = Gtk2::GladeXML->new($BIN_DIR.'/gtkremote.glade');
$gladexml->signal_autoconnect_from_package('main');
my $window = $gladexml->get_widget('main');
my $host_entry = $gladexml->get_widget('entry1');
my $port_entry = $gladexml->get_widget('entry2');
my $user_entry = $gladexml->get_widget('entry3');
my $pass_entry = $gladexml->get_widget('entry4');
my $combobox = $gladexml->get_widget('combobox1');
my $button = $gladexml->get_widget('button1');
my $status_icon = $gladexml->get_widget('image2');
my @prot_list = ("VNC", "RDP");
$combobox->append_text($_) foreach (@prot_list);
$combobox->set_active(0);
$window->show_all();
Gtk2->main();
$window->destroy;
exit 0;
sub on_main_delete_event
{
kill 15 => $PID if $PID;
unlink $VNC_TMP if -f $VNC_TMP;
Gtk2->main_quit;
exit;
}
sub on_combobox1_changed
{
$combobox->get_active_text eq "VNC" ? $user_entry->set_sensitive(FALSE) : $user_entry->set_sensitive(TRUE);
}
sub on_button1_clicked
{
unless($CONN)
{
remote_connect();
}
else
{
kill 15 => $PID if $PID;
}
}
sub status_connect
{
$CONN = 1;
$button->child->set_text("Disconnect");
$status_icon->set_from_stock('gtk-connect', 'large-toolbar');
}
sub status_disconnect
{
$CONN = 0;
$button->child->set_text("Connect");
$status_icon->set_from_stock('gtk-disconnect', 'large-toolbar');
}
sub message_dialog_show
{
#$icon can be: a) 'info'
# b) 'warning'
# c) 'error'
# d) 'question'
#$button_type can be: a) 'none'
# b) 'ok'
# c) 'close'
# d) 'cancel'
# e) 'yes-no'
# f) 'ok-cancel'
my ($parent,$icon,$text,$button_type) = @_;
my $dialog = Gtk2::MessageDialog->new_with_markup ($parent,
[qw/modal destroy-with-parent/],
$icon,
$button_type,
sprintf "$text");
my $retval = $dialog->run;
$dialog->destroy;
return $retval;
}
sub remote_connect
{
status_connect();
my $host = $host_entry->get_text;
my $port = $port_entry->get_text;
my $user = $user_entry->get_text;
my $pass = $pass_entry->get_text;
my $prot = $combobox->get_active_text;
if($host)
{
if($prot eq "VNC")
{
unlink $VNC_TMP if -f $VNC_TMP;
$CMD = "vncviewer $host";
$CMD .= "::$port" if $port =~ /^(\d+)$/;
if(length($pass) >= 6)
{
my $sh = Expect->spawn('/bin/sh') or die "Cannot spawn shell: $!\n";
my $exp = new Expect;
my $command = "vncpasswd $VNC_TMP";
$exp->spawn($command) or die "Cannot spawn $command: $!\n";
$exp->expect(10, 'Password:');
$exp->send("$pass\n");
$exp->expect(10, 'Verify:');
$exp->send("$pass\n");
$exp->interact();
$exp->soft_close();
$sh->hard_close();
$CMD .= " -passwd $VNC_TMP";
}
else
{
message_dialog_show($window,
'error',
'VNC password too short.',
'ok'
);
status_disconnect();
return;
}
}
elsif($prot eq "RDP")
{
$CMD = "rdesktop $host";
$CMD .= ":$port" if $port =~ /^(\d+)$/;
$CMD .= " -u $user" if $user;
$CMD .= " -p $pass" if $pass;
}
else
{
status_disconnect();
return;
}
}
else
{
status_disconnect();
return;
}
$PID = open my $pipe, '-|', "$CMD" or die "Failed open pipe: $CMD\n";
$TAG = Gtk2::Helper->add_watch(fileno($pipe), in => sub
{
if(eof($pipe))
{
Gtk2::Helper->remove_watch($TAG);
close($pipe);
unlink $VNC_TMP if -f $VNC_TMP;
status_disconnect();
0;
}
else
{
#my $line = <$pipe>; # stdout string.
#print "OUTPUT: ".$line."\n" if $line;
}
1;
} );
}
__DATA__
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--Generated with glade3 3.4.0 on Wed Feb 6 18:08:05 2008 -->
<glade-interface>
<widget class="GtkWindow" id="main">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="window_position">GTK_WIN_POS_CENTER</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
<signal name="delete_event" handler="on_main_delete_event"/>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkFixed" id="fixed5">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkImage" id="image1">
<property name="width_request">71</property>
<property name="height_request">63</property>
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="stock">gtk-network</property>
<property name="icon_size">6</property>
</widget>
<packing>
<property name="y">53</property>
</packing>
</child>
</widget>
</child>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkFixed" id="fixed2">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkLabel" id="label1">
<property name="width_request">39</property>
<property name="height_request">22</property>
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Host:</property>
</widget>
<packing>
<property name="x">1</property>
<property name="y">10</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="entry1">
<property name="width_request">125</property>
<property name="height_request">25</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
</widget>
<packing>
<property name="x">78</property>
<property name="y">10</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label2">
<property name="width_request">34</property>
<property name="height_request">23</property>
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Port:</property>
</widget>
<packing>
<property name="x">205</property>
<property name="y">10</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="entry2">
<property name="width_request">50</property>
<property name="height_request">25</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="max_length">5</property>
</widget>
<packing>
<property name="x">240</property>
<property name="y">10</property>
</packing>
</child>
</widget>
</child>
<child>
<widget class="GtkFixed" id="fixed4">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkLabel" id="label3">
<property name="width_request">61</property>
<property name="height_request">22</property>
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Protocol:</property>
</widget>
<packing>
<property name="y">7</property>
</packing>
</child>
<child>
<widget class="GtkComboBox" id="combobox1">
<property name="width_request">86</property>
<property name="height_request">27</property>
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="items" translatable="yes"></property>
<signal name="changed" handler="on_combobox1_changed"/>
</widget>
<packing>
<property name="x">79</property>
<property name="y">6</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkFixed" id="fixed3">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkLabel" id="label4">
<property name="width_request">76</property>
<property name="height_request">22</property>
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Username:</property>
</widget>
<packing>
<property name="y">8</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="entry3">
<property name="width_request">115</property>
<property name="height_request">25</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="max_length">128</property>
</widget>
<packing>
<property name="x">79</property>
<property name="y">6</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label5">
<property name="width_request">71</property>
<property name="height_request">21</property>
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Password:</property>
</widget>
<packing>
<property name="y">36</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="entry4">
<property name="width_request">115</property>
<property name="height_request">25</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="max_length">128</property>
<property name="visibility">False</property>
</widget>
<packing>
<property name="x">79</property>
<property name="y">34</property>
</packing>
</child>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkFixed" id="fixed1">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkButton" id="button1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Connect</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_button1_clicked"/>
</widget>
<packing>
<property name="x">77</property>
<property name="y">10</property>
</packing>
</child>
<child>
<widget class="GtkImage" id="image2">
<property name="width_request">32</property>
<property name="height_request">32</property>
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="stock">gtk-disconnect</property>
<property name="icon_size">3</property>
</widget>
<packing>
<property name="x">3</property>
<property name="y">7</property>
</packing>
</child>
<child>
<widget class="GtkLayout" id="layout1">
<property name="width_request">295</property>
<property name="height_request">10</property>
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
</widget>
<packing>
<property name="x">4</property>
<property name="y">43</property>
</packing>
</child>
</widget>
<packing>
<property name="position">3</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>
2008年11月19日星期三
GTK2-Perl程序示例:远程桌面客户端gtkremote
这个例子说明了如何将Glade界面直接放在程序中方便在邮件列表和论坛上直接发布程序,以及如何用Gtk2::Helper调用网络程序防止连接超时导致界面阻塞。程序中用Expect实现了VNC密码输入。
订阅:
博文评论 (Atom)
没有评论:
发表评论