这个例子说明了如何将Glade界面直接放在程序中方便在邮件列表和论坛上直接发布程序,以及如何用Gtk2::Helper调用网络程序防止连接超时导致界面阻塞。程序中 用Expect实现了VNC密码输入。
- #! /usr/bin/perl -w
- #----------------------------------------------------------------------
- # gtkremote.pl
- #
- # A remote desktop frontend of Gtk2/GladeXML
- #
- # Copyright (C) 2008 Viperii (hominid@tom.com)
- #
- # This library is free software; you can redistribute it and/or
- # modify it under the terms of the GNU Lesser General Public
- # License as published by the Free Software Foundation; either
- # version 2 of the License, or (at your option) any later version.
- #
- # This library 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
- # Lesser General Public License for more details.
- #
- # You should have received a copy of the GNU Lesser General Public
- # License along with this library; if not, write to the
- # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- # Boston, MA 02111-1307, USA.
- #
- #----------------------------------------------------------------------
- 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('p_w_picpath2');
- 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="p_w_picpath1">
- <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="p_w_picpath2">
- <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>