Here is a basic Gtk2 based interactive chat client. It could be a bit simpler, if you didn't care about warning messages concerning various errors such as: printing to a closed socket, reconnecting after a disconnect, etc. So there are some comments describing the minimal error checking to avoid scary messages/warnings. Also some extra color adding code. There is a server at
Simple threaded chat server to test this client with. It is basically a Gtk2 version of the Tk client in the above node.
As always, thanks to muppet(Perl/Gtk2 maillist guru) for some boiler-plate code for watching a filehandle.
#!/usr/bin/perl
use warnings;
use strict;
use Glib qw(TRUE FALSE);
use Gtk2 -init;
use IO::Socket;
my $name = shift || 'anon';
my $host = 'localhost';
my $port = 12345;
my $socket;
# make entry widget larger, colored text
Gtk2::Rc->parse_string(<<__);
style "my_entry" {
font_name ="arial 30"
text[NORMAL] = "#FF0000"
}
style "my_text" {
font_name ="sans 12"
text[NORMAL] = "#FFAA00"
base[NORMAL] = "#000000"
GtkTextView::cursor-color = "red"
}
style "my_cursor"{
fg[NORMAL] = "#FF0000"
}
widget "*Text*" style "my_text"
widget "*Entry*" style "my_entry"
__
my $window = Gtk2::Window->new;
$window->signal_connect( delete_event => sub { exit } );
$window->set_default_size( 400, 300 );
my $vbox = Gtk2::VBox->new;
$window->add($vbox);
my $scroller = Gtk2::ScrolledWindow->new;
$vbox->add($scroller);
my $textview = Gtk2::TextView->new;
$textview ->set_editable (0); #read-only text
$textview ->can_focus(0); #
my $buffer = $textview->get_buffer;
$buffer->create_mark( 'end', $buffer->get_end_iter, FALSE );
$buffer->signal_connect(
insert_text => sub {
$textview->scroll_to_mark( $buffer->get_mark('end'), 0.0, TRUE
+, 0,
0.5 );
}
);
$scroller->add($textview);
my $entry = Gtk2::Entry->new();
$vbox->pack_start( $entry, FALSE, FALSE, 0 );
$vbox->set_focus_child ($entry); # keeps cursor in entry
$window->set_focus_child ($entry); # keeps cursor in entry
# allows for sending each line with an enter keypress
my $send_sig = $entry->signal_connect ('key-press-event' => sub {
my ($widget,$event)= @_;
if( $event->keyval() == 65293){ # a return key press
my $text = $entry->get_text;
if(defined $socket){ print $socket $name.'->'. $text, "\n";}
$entry->set_text('');
$entry->set_position(0);
}
});
#If you store the ID returned by signal_connect, you can temporarily
#block your signal handler with
# $object->signal_handler_block ($handler_id)
# and unblock it again when you're done with
## $object->signal_handler_unblock ($handler_id).
# we want to block/unblock the enter keypress depending
# on the state of the socket
$entry->signal_handler_block($send_sig); #not connected yet
$entry->set_editable(0);
my $button = Gtk2::Button->new('Connect');
$button->signal_connect( clicked => \&init_connect );
$vbox->pack_start( $button, FALSE, FALSE, 0 );
$window->show_all;
Gtk2->main;
exit;
sub init_connect{
$socket = IO::Socket::INET->new(
PeerAddr => $host,
PeerPort => $port,
Proto => 'tcp',
);
if( ! defined $socket){
my $buffer = $textview->get_buffer;
$buffer->insert( $buffer->get_end_iter,
"ERROR: Can't connect to port $port on $host: $!\n" );
return;
}
#if we have a socket
$button->set_label('Connected');
$button->set_state('insensitive');
# install an io watch for this stream and
# return immediately to the main caller, who will return
# immediately to the event loop. the callback will be
# invoked whenever something interesting happens.
Glib::IO->add_watch( fileno $socket, [qw/in hup err/], \&watch
+_callback, $socket );
#turn on entry widget
$entry->set_editable(1);
$entry->grab_focus;
$entry->signal_handler_unblock ($send_sig);
Gtk2->main_iteration while Gtk2->events_pending;
}
sub watch_callback {
my ( $fd, $condition, $fh ) = @_;
if ( $condition >= 'in' ) {
# there's data available for reading. we have no
my $bytes = sysread( $fh, my $data, 1024);
if ( defined $data ) {
# do something useful with the text.
my $buffer = $textview->get_buffer;
$buffer->insert( $buffer->get_end_iter, $data );
}
}
if ( $condition >= 'hup' or $condition >= 'err' ) {
# End Of File, Hang UP, or ERRor. that means
# we're finished.
# stop ability to send
$entry->set_editable(0);
$entry->signal_handler_block ($send_sig);
my $buffer = $textview->get_buffer;
$buffer->insert( $buffer->get_end_iter, "Server connection los
+t !!\n" );
#close socket
$fh->close;
$fh = undef;
#allow for new connection
$button->set_label('Connect');
$button->set_sensitive(1);
$button->grab_focus;
Gtk2->main_iteration while Gtk2->events_pending;
}
if ($fh) {
# the file handle is still open, so return TRUE to
# stay installed and be called again.
# print "still connected\n";
# possibly have a "connection alive" indicator
return TRUE;
}
else {
# we're finished with this job. start another one,
# if there are any, and uninstall ourselves.
return FALSE;
}
}
__END__