import from Internet Archive master
authorDobrica Pavlinusic <dpavlin@rot13.org>
Sun, 3 Jan 2010 22:32:46 +0000 (23:32 +0100)
committerDobrica Pavlinusic <dpavlin@rot13.org>
Sun, 3 Jan 2010 22:32:46 +0000 (23:32 +0100)
VNC/Codec.pm [new file with mode: 0755]
VNC/Server.pm [new file with mode: 0755]
VNC/ServerConnection.pm [new file with mode: 0755]
life.pl [new file with mode: 0755]
main.pl [new file with mode: 0755]

diff --git a/VNC/Codec.pm b/VNC/Codec.pm
new file mode 100755 (executable)
index 0000000..c3f5d9b
--- /dev/null
@@ -0,0 +1,221 @@
+package VNC::Codec;\r
+\r
+require 5.002;   # becase we use prototypes\r
+\r
+use Socket;\r
+use warnings;\r
+use strict;\r
+use vars qw(@ISA @EXPORT_OK);\r
+require Exporter;\r
+@ISA = qw(Exporter);\r
+@EXPORT_OK = qw(VNC_PIXELFORMAT_32BPP_RGB888 VNC_PIXELFORMAT_16BPP_RGB565 VNC_PIXELFORMAT_8BPP_RGB332);\r
+\r
+\r
+sub buffer2hex($) {\r
+       my ($t) = @_ ;\r
+\r
+       my $result = '';\r
+\r
+       my @b = split (//, $t) ;\r
+\r
+       foreach (@b) {\r
+               $result .= sprintf("%02X ", ord($_)) ;\r
+       }\r
+\r
+       return $result ;\r
+}\r
+\r
+########## \r
+#\r
+# helper function to build the PIXELFORMAT binary struct (please see VNC documentation\r
+#\r
+sub BUILD_PIXEL_FORMAT {\r
+       my ($bits_per_pixel, \r
+               $depth ,\r
+               $big_endian,\r
+               $true_color,\r
+               $red_max,$green_max,$blue_max,\r
+               $red_shift,$green_shift, $blue_shift) = @_ ;\r
+\r
+       my $buffer = pack ("CCCCnnnCCCCn", $bits_per_pixel, \r
+               $depth ,\r
+               $big_endian,\r
+               $true_color,\r
+               $red_max,$green_max,$blue_max,\r
+               $red_shift,$green_shift, $blue_shift ) ;\r
+\r
+       return $buffer;\r
+}\r
+\r
+sub VNC_PIXELFORMAT_32BPP_RGB888 {\r
+       return BUILD_PIXEL_FORMAT(32,24,1,1,255,255,255,16,8,0);\r
+}\r
+\r
+sub VNC_PIXELFORMAT_16BPP_RGB565 {\r
+       return BUILD_PIXEL_FORMAT(16,16,1,1,31,63,31,11,5,0);\r
+}\r
+\r
+sub VNC_PIXELFORMAT_8BPP_RGB332 {\r
+       return BUILD_PIXEL_FORMAT(8,8,1,1,7,7,3,0,3,6);\r
+}\r
+\r
+#######\r
+# Constructs a new VNC Codec object\r
+#\r
+sub new {\r
+       my $proto = shift;\r
+       my $class = $proto;\r
+\r
+       my $self  = {};\r
+       bless ($self, $class);\r
+\r
+       #use default PIXELFORMAT of 32bpp, RGB888\r
+       $self->{RED_MAX}                = 255 ;\r
+       $self->{GREEN_MAX}              = 255 ;\r
+       $self->{BLUE_MAX}               = 255 ;\r
+       $self->{RED_SHIFT}              = 16;\r
+       $self->{GREEN_SHIFT}    = 8 ;\r
+       $self->{BLUE_SHIFT}             = 0 ;\r
+       $self->{BITS_PER_PIXEL} = 32; \r
+       $self->{DEPTH}                  = 24 ;\r
+       $self->{TRUE_COLOR}             = 1 ;\r
+       $self->{BIG_ENDIAN}             = 1 ;\r
+\r
+       \r
+       #This is used for the internal representation o RGB data\r
+       $self->{INTERNAL_RED_MAX} = 255 ;\r
+       $self->{INTERNAL_GREEN_MAX} = 255 ;\r
+       $self->{INTERNAL_BLUE_MAX} = 255 ;\r
+\r
+       return $self;\r
+}\r
+\r
+sub set_pixel_format {\r
+       my ($self, $bits_per_pixel, \r
+               $depth ,\r
+               $big_endian,\r
+               $true_color,\r
+               $red_max,$green_max,$blue_max,\r
+               $red_shift,$green_shift, $blue_shift) = @_ ;\r
+\r
+       $self->{RED_MAX}                = $red_max ;\r
+       $self->{GREEN_MAX}              = $green_max ;\r
+       $self->{BLUE_MAX}               = $blue_max ;\r
+       $self->{RED_SHIFT}              = $red_shift;\r
+       $self->{GREEN_SHIFT}    = $green_shift ;\r
+       $self->{BLUE_SHIFT}             = $blue_shift ;\r
+       $self->{BITS_PER_PIXEL} = $bits_per_pixel; \r
+       $self->{DEPTH}                  = $depth ;\r
+       $self->{TRUE_COLOR}             = $true_color ;\r
+       $self->{BIG_ENDIAN}             = $big_endian ;\r
+\r
+       return 1;\r
+}\r
+\r
+\r
+#############################\r
+#\r
+# This function builds a raw pixel, according to the specified format (bits/pixel,and max/shift values)\r
+# It returns a raw (binary) scalar with the encoded pixel\r
+#\r
+sub encode_pixel {\r
+       my ($self,$r,$g,$b) = @_ ;\r
+\r
+       warn "VNC::Codec::encode_pixel called with self=undef" unless (defined $self);\r
+       warn "VNC::Codec::encode_pixel called with r/g/b=undef" unless (defined $r and defined $g and defined $b);\r
+\r
+       my $red_max = $self->{RED_MAX};\r
+       my $green_max = $self->{GREEN_MAX};\r
+       my $blue_max = $self->{BLUE_MAX};\r
+\r
+       my $red_shift = $self->{RED_SHIFT};\r
+       my $green_shift = $self->{GREEN_SHIFT};\r
+       my $blue_shift = $self->{BLUE_SHIFT};\r
+\r
+       my $bits_per_pixel = $self->{BITS_PER_PIXEL};\r
+\r
+       my $z = ($r * ($red_max))/$self->{INTERNAL_RED_MAX} << $red_shift ;\r
+       $z +=   ($g * ($green_max))/$self->{INTERNAL_GREEN_MAX} << $green_shift;\r
+       $z +=   ($b * ($blue_max))/$self->{INTERNAL_BLUE_MAX} << $blue_shift;\r
+\r
+       my $raw;\r
+\r
+       if ($bits_per_pixel==8) {\r
+               $raw .= pack("C",$z) ;\r
+       } elsif ($bits_per_pixel==16) {\r
+               $raw .= pack("S",$z) ;\r
+       } elsif ($bits_per_pixel==32) {\r
+               $raw .= pack("i",$z) ;\r
+       }\r
+\r
+#      print "encode_pixel($r,$g,$b) = ",buffer2hex($raw),"\n";\r
+\r
+       return $raw;\r
+}\r
+\r
+#############################\r
+#\r
+# This function builds a RAW encoded rectangle, with a singe RGB color\r
+# It returns a raw (binary) scalar with the encoded rect data\r
+#\r
+sub encode_raw_rect {\r
+       my ($self,$raw_pixel,$x,$y,$w,$h) = @_ ;\r
+\r
+       my $i;\r
+       my $j;\r
+       my $raw ;\r
+       \r
+       for ($i=0;$i<$h;$i++) {\r
+               for ($j=0;$j<$w;$j++) {\r
+                       $raw .= $raw_pixel; \r
+               }\r
+       }\r
+\r
+       my $buffer = pack("nnnnN", $x,$y,$w,$h,0 ) . $raw ;\r
+       \r
+#      print "encode_raw_rect = ",buffer2hex($buffer),"\n";\r
+       return $buffer;\r
+}\r
+\r
+#############################\r
+#\r
+# This function builds an RRE sub-rect\r
+# It returns a raw (binary) scalar with the encoded subrect data\r
+#\r
+sub encode_rre_subrect {\r
+       my ($self,$raw_pixel,$x,$y,$w,$h) = @_ ;\r
+\r
+       warn "VNC::Codec::encode_rre_subrect called with self=undef" unless (defined $self);\r
+       warn "VNC::Codec::encode_rre_subrect called with raw_pixel=undef" unless (defined $raw_pixel);\r
+       warn "VNC::Codec::encode_rre_subrect called with x/y/w/h=undef" unless (defined $x and defined $y and defined $w and defined $h);\r
+       \r
+       return $raw_pixel . pack("nnnn",$x,$y,$w,$h);\r
+}\r
+\r
+#############################\r
+#\r
+# This function builds an RRE sub-rect\r
+# It returns a raw (binary) scalar with the encoded subrect data\r
+#\r
+sub encode_rre {\r
+       my ($self,$raw_background_pixel,$x,$y,$w,$h,@subrects) = @_ ;\r
+\r
+       warn "VNC::Codec::encode_rre called with self=undef" unless (defined $self);\r
+       warn "VNC::Codec::encode_rre called with x/y/w/h=undef" unless (defined $x and defined $y and defined $w and defined $h);\r
+       warn "VNC::Codec::encode_rre called with raw_background_pixel=undef" unless (defined $raw_background_pixel);\r
+\r
+       my $count = scalar @subrects;\r
+       \r
+       return pack("nnnnNN",$x,$y,$w,$h,2,$count) . $raw_background_pixel . join('',@subrects); # 2 = RRE encoding\r
+}\r
+\r
+sub encode_framebuffer_update {\r
+       my ($self,@rectangles) = @_ ;\r
+\r
+       my $count = scalar @rectangles;\r
+       my $buffer = pack("CCn",0,0,$count) . join('',@rectangles) ;\r
+\r
+       return $buffer;\r
+}\r
+\r
+1;
\ No newline at end of file
diff --git a/VNC/Server.pm b/VNC/Server.pm
new file mode 100755 (executable)
index 0000000..34b39a8
--- /dev/null
@@ -0,0 +1,149 @@
+package VNC::Server;\r
+\r
+require 5.002;   # becase we use prototypes\r
+\r
+use IO::Select;\r
+use IO::Socket;\r
+use IO::Socket::INET;\r
+use Socket;\r
+use warnings;\r
+use strict;\r
+use VNC::ServerConnection;\r
+use vars qw(@ISA @EXPORT_OK);\r
+require Exporter;\r
+@ISA = qw(Exporter);\r
+@EXPORT_OK = qw();\r
+\r
+sub new {\r
+       my $proto = shift;\r
+       my $class = $proto;\r
+\r
+       my %args = @_ ;\r
+\r
+       my $self  = {};\r
+       bless ($self, $class);\r
+\r
+       # Get the port number\r
+       my $port = $args{Port} ||= 0 ;\r
+       # Port number less than 100 is treated as display number\r
+       if ($port < 100 ) {$port += 5900;}\r
+       $self->{PORT} = $port ;\r
+\r
+       # Get the idle Interval\r
+       $self->{IDLE_INTERVAL} = $args{Idle} || 1 ;\r
+       $self->{IdleHandler} = $args{IdleHandler};\r
+       $self->{KeyEventHandler} = $args{KeyEventHandler};\r
+       $self->{PointerEventHandler} = $args{PointerEventHandler};\r
+       $self->{CutTextHandler} = $args{CutTextHandler};\r
+       $self->{UpdateDisplayHandler} = $args{UpdateDisplayHandler};\r
+       $self->{PixelFormat} = $args{PixelFormat};\r
+       $self->{Caption} = $args{Caption};\r
+       $self->{Width} = $args{Width};\r
+       $self->{Height} = $args{Height};\r
+       $self->{ClientConnectHandler} = $args{ClientConnectHandler};\r
+       $self->{ClientDisconnectHandler} = $args{ClientDisconnectHandler};\r
+       $self->{Password} = $args{Password};\r
+\r
+       return $self;\r
+}\r
+\r
+sub update_all_displays {\r
+       my ($self) = @_ ;\r
+\r
+       my $vncref = $self->{VNC_CONNECTIONS} ;\r
+       my %vncs = %{$vncref} ;\r
+\r
+       foreach (values %vncs) {\r
+               $_->update_display ;\r
+       }\r
+}\r
+\r
+sub disconnect_client {\r
+       my ($self, $vnc, $sock_handle) = @_ ;\r
+\r
+       my $vncref = $self->{VNC_CONNECTIONS} ;\r
+       my %vncs = %{$vncref} ;\r
+\r
+       my $sel = $self->{SELECTOR};\r
+\r
+       shutdown($sock_handle,2);\r
+\r
+       delete $vncs{$sock_handle};\r
+       $sel->remove($sock_handle) ;\r
+       $vnc = undef ;\r
+       $sock_handle = undef ;\r
+\r
+       print "Client Disconnectd\n";\r
+}\r
+\r
+sub vnc_server_loop {\r
+       my ($self) = @_ ;\r
+\r
+       my $listen_sock = new IO::Socket::INET(Listen => 1, LocalPort => $self->{PORT} );\r
+       my $sel    = new IO::Select( $listen_sock );\r
+\r
+       my %vncs = () ;\r
+\r
+       $self->{VNC_CONNECTIONS} = \%vncs;\r
+       $self->{SELECTOR} = $sel ;\r
+\r
+       while (1) {\r
+               my @ready_handles; \r
+               if (@ready_handles = $sel->can_read( $self->{IDLE_INTERVAL} ) ) {\r
+                       foreach my $fh (@ready_handles) {\r
+                               if ($fh == $listen_sock) {\r
+                                       #accept new connection from client\r
+\r
+                                       my $client_sock = $listen_sock->accept;\r
+                                       $client_sock->blocking(1);\r
+                               \r
+                                       my $vnc = new VNC::ServerConnection( Server => $self,\r
+                                                                                                                Caption => $self->{Caption},\r
+                                                                                                                Socket => $client_sock,\r
+                                                                                                                Width => $self->{Width},\r
+                                                                                                                Height => $self->{Height},\r
+                                                                                                                Password => $self->{Password},\r
+                                                                                                                PixelFormat => $self->{PixelFormat},\r
+                                                                                                                KeyEventHandler => $self->{KeyEventHandler},\r
+                                                                                                                PointerEventHandler => $self->{PointerEventHandler},\r
+                                                                                                                CutTextHandler => $self->{CutTextHandler},\r
+                                                                                                                UpdateDisplayHandler => $self->{UpdateDisplayHandler}\r
+                                                                               \r
+                                                                                                          );\r
+                                       print "Accepted new connection from ",$client_sock->sockhost(),"\n" ;\r
+\r
+                                       my $ok = 1 ;\r
+                                       my $handler = $self->{ClientConnectHandler};\r
+                                       if (defined $handler) {\r
+                                               $ok = &$handler($self, $client_sock, $vnc);\r
+                                       }\r
+\r
+                                       if (defined $ok) {\r
+                                               $sel->add($client_sock);\r
+                                               $vnc->start_conversation();\r
+                                               $vncs{$client_sock} = $vnc ;\r
+                                       } else {\r
+                                               $vnc = undef ;\r
+                                               shutdown($client_sock,2) ;\r
+                                               $client_sock = undef; \r
+                                       }\r
+                               } else {\r
+                                       my $vnc = $vncs{$fh} ;\r
+\r
+                                       die "Error! got data from un-connected socket" unless (defined $vnc);\r
+\r
+                                       if (!defined $vnc->handle_client_data()) {\r
+                                                       $vnc->disconnect;\r
+                                       }\r
+                               }\r
+                       }\r
+               } else {\r
+                       my $sub = $self->{IdleHandler};\r
+                       if (defined $sub) {\r
+                               &$sub($self) ;\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+1;
\ No newline at end of file
diff --git a/VNC/ServerConnection.pm b/VNC/ServerConnection.pm
new file mode 100755 (executable)
index 0000000..574b75d
--- /dev/null
@@ -0,0 +1,610 @@
+package VNC::ServerConnection;\r
+\r
+require 5.002;   # becase we use prototypes\r
+\r
+use Socket;\r
+use warnings;\r
+use strict;\r
+use VNC::Codec;\r
+use vars qw(@ISA @EXPORT_OK);\r
+require Exporter;\r
+@ISA = qw(Exporter);\r
+@EXPORT_OK = qw();\r
+\r
+\r
+sub STATE_UNINITIALIZED                                {0};\r
+sub STATE_WAIT_FOR_PROTOCOL                    {1};\r
+sub STATE_WAIT_FOR_AUTHENTICATION      {2};\r
+sub STATE_WAIT_FOR_CLIENT_INIT_MSG     {3};\r
+sub STATE_WAIT_FOR_MSG_HEADER          {4};\r
+sub STATE_WAIT_FOR_FIXED_LEN_MSG       {5};\r
+sub STATE_WAIT_FOR_TEXT_CUT_LENGTH     {6};\r
+sub STATE_WAIT_FOR_TEXT_CUT_CONTENT {7};\r
+sub STATE_WAIT_FOR_ENCODING_COUNT      {8};\r
+sub STATE_WAIT_FOR_ENCODING_DATA       {9};\r
+\r
+sub MSG_SET_PIXEL_FORMAT                       {0};\r
+sub MSG_FIX_COLOR_MAP_ENTRIES          {1};\r
+sub MSG_SET_ENCODING                           {2};\r
+sub MSG_FRAME_BUFFER_UPDATE_REQUEST {3};\r
+sub MSG_KEY_EVENT                                      {4};\r
+sub MSG_POINTER_EVENT                          {5};\r
+sub MSG_CLIENT_CUT_TEXT                                {6};\r
+\r
+my @rfb_message_handlers =( [\&do_set_pixel_format,    20],                    #SetPixelFormat \r
+                                                       [\&do_fix_color_map_entries, 4],                #FixColorMapEntries\r
+                                                       [\&do_set_encoding, -1],                                #SetEncoding\r
+                                                       [\&do_frame_buffer_update_request,10],  #FramebufferUpdateRequest \r
+                                                       [\&do_key_event, 8],                                    #KeyEvent\r
+                                                       [\&do_pointer_event,6],                                 #Pointer Event\r
+                                                       [\&do_client_cut_text,-1]\r
+                                                       );\r
+\r
+######################\r
+#\r
+# Helper function to build the ServerInitMessage binary struct (please see the VNC documentation)\r
+#\r
+sub BUILD_SERVER_INIT_MSG {\r
+       my ($width, $height, $pixel_format, $name) = @_ ;\r
+\r
+       my $buffer = pack ( "nn", $width, $height ) . $pixel_format . pack("N", length($name)) . $name; \r
+\r
+       return $buffer ;\r
+}\r
+\r
+\r
+#######\r
+# Constructs a new, VNC server object, connected to one client using socket handle\r
+#\r
+sub new {\r
+       my $proto = shift;\r
+       my $class = $proto;\r
+\r
+       my %args = @_ ;\r
+\r
+       my $sock_handle = $args{Socket}; \r
+       die "$proto->new called without any socket handle!" unless (defined $sock_handle );\r
+\r
+       my $self  = {};\r
+       bless ($self, $class);\r
+\r
+       $self->{HANDLE} = $sock_handle;\r
+\r
+       $self->{STATE}          = STATE_UNINITIALIZED ;\r
+       $self->{RECV_BUFFER} = '' ;\r
+       $self->{EXPECTED_BUFFER_SIZE} = 0 ;\r
+\r
+       $self->{CODEC} = new VNC::Codec();\r
+\r
+       #if this is defined, a response will be sent immediatly\r
+       #when a FrameBufferUpdateRequest is received\r
+       #if this is UNDEFINED, no response will be sent, and UPDATE_REQUEST_PENDING will be set\r
+       $self->{SEND_UPDATE_RESPONSE} = 1 ;\r
+\r
+       #if this is defined, it means an update request was received, \r
+       #but a response was not sent.\r
+       #so the next time you call udpate_frame_buffer a response will be sent immediately\r
+       $self->{UPDATE_REQUEST_PENDING} = 1 ;\r
+\r
+       $self->{Server} = $args{Server} ;\r
+\r
+       #handlers\r
+       $self->{KeyEventHandler} = $args{KeyEventHandler};\r
+       $self->{PointerEventHandler} = $args{PointerEventHandler};\r
+       $self->{CutTextHandler} = $args{CutTextHandler};\r
+       $self->{UpdateDisplayHandler} = $args{UpdateDisplayHandler};\r
+\r
+       #Get the pixel format\r
+       #if none specified, default to 32Bits/Pixel 24bit depth, RGB888\r
+       my $pixel_format =  $args{PixelFormat} || VNC::Codec::VNC_PIXELFORMAT_32BPP_RGB888;\r
+       $self->{PixelFormat} = $pixel_format;\r
+       \r
+    my (undef, $myaddr) = sockaddr_in(getsockname($sock_handle));\r
+       $self->{Caption} = $args{Caption} || "PerlVNC Server at " . scalar gethostbyaddr($myaddr, AF_INET) . " [" . inet_ntoa($myaddr). "]" ;\r
+\r
+\r
+       $self->{Width} = $args{Width} || 200;\r
+       $self->{Height} = $args{Height} || 200;\r
+\r
+       $self->{PENDING_UPDATE_REQUEST_BUFFER} = pack("CCnnnn",0,0,0,0,10,10);\r
+\r
+       $self->{Password} = $args{Password};\r
+\r
+       return $self;\r
+}\r
+\r
+##########################################3\r
+#\r
+# Helper function to send data to the socket\r
+#\r
+sub send_buffer($$) {\r
+       my $self = shift ;\r
+       my $a = shift ;\r
+       die "send_buffer called, when self is undef"  unless (defined $self);\r
+       return unless (defined $self->{HANDLE});\r
+\r
+       if (!defined $a) {\r
+               warn "send_buffer called, buffer is empty";\r
+               return ;\r
+       }\r
+\r
+       my $count =     send($self->{HANDLE}, $a,0);\r
+\r
+       die "Error sending buffer ($a) " unless ($count == length $a);\r
+\r
+       return $count;\r
+}\r
+\r
+\r
+###############################\r
+# Given a SET_PIXEL_FORMAT buffer (20 bytes long, see VNC documentation)\r
+# Sets the pixel format of this session\r
+sub do_set_pixel_format {\r
+       my $self = shift ;\r
+       my $buffer = shift ;\r
+\r
+       die "do_set_pixel_format called, when self is undef"  unless (defined $self);\r
+       die "do_set_pixel_format, buffer isn't 20 bytes long" unless (length $buffer==20);\r
+\r
+       my (undef,undef,undef,undef,$bits_per_pixel, \r
+               $depth ,\r
+               $big_endian,\r
+               $true_color,\r
+               $red_max,$green_max,$blue_max,\r
+               $red_shift,$green_shift, $blue_shift) = \r
+               unpack("CCCCCCCCnnnCCC", $buffer) ;\r
+\r
+       #\r
+       # TODO: Let's do some sanity checks\r
+       #\r
+\r
+       $self->{CODEC}->set_pixel_format($bits_per_pixel, \r
+                                                       $depth, $big_endian, $true_color,\r
+                                                       $red_max,$green_max,$blue_max,\r
+                                                       $red_shift,$green_shift, $blue_shift);\r
+       \r
+       return ;\r
+}\r
+\r
+sub do_fix_color_map_entries() {\r
+       my $self = shift ;\r
+       die "do_fix_color_map_entries called, when self is undef"  unless (defined $self);\r
+\r
+       my $buffer = shift;\r
+       die "do_set_pixel_format, buffer isn't 6 bytes long" unless (length $buffer==6);\r
+\r
+       die "Client Sent rfbFixColourMapEntriesMsg - UNSUPPORTED\n";\r
+\r
+       my (undef,$pad,$first_color,$n_colors) = unpack("CCnn", $buffer) ;\r
+\r
+       print "color map, first color = $first_color, total colors = $n_colors\n" ;\r
+       return ;\r
+}\r
+\r
+sub do_set_encoding() {\r
+       my $self = shift ;\r
+       die "do_set_encoding called, when self is undef"  unless (defined $self);\r
+       \r
+       my $buffer = shift;\r
+\r
+       my (undef,undef,$nEncoding,@encodings) = unpack("CCnN*", $buffer) ;\r
+\r
+       #\r
+       # Debug Only\r
+       #\r
+       print "Client supports $nEncoding encodings: ",join(' ', @encodings),"\n";\r
+\r
+       return ;\r
+}\r
+\r
+sub update_display {\r
+       my $self = shift ;\r
+       die "update_display called, when self is undef"  unless (defined $self);\r
+\r
+       #if we received an update request but did not send  response, we'll send a response immediately\r
+       if (defined $self->{UPDATE_REQUEST_PENDING}) {\r
+\r
+               my (undef,$incremental, $x,$y,$w,$h) = unpack("CCnnnn", $self->{PENDING_UPDATE_REQUEST_BUFFER}) ;\r
+\r
+               my $handler = $self->{UpdateDisplayHandler};\r
+               if (defined $handler) {\r
+                       my $data = &$handler($self, $incremental, $x, $y, $w, $h);\r
+\r
+                       $self->send_buffer($data);\r
+               }\r
+\r
+               $self->{UPDATE_REQUEST_PENDING} = undef;\r
+               $self->{SEND_UPDATE_RESPONSE} = undef;\r
+       } else {\r
+\r
+               #if now update request is pending, \r
+               #we'll simply signal the next request to be answered immediately\r
+               $self->{SEND_UPDATE_RESPONSE} = 1 ;\r
+       }\r
+}\r
+sub disconnect {\r
+       my $self = shift;\r
+       \r
+       my $server = $self->{Server} ;\r
+\r
+       die "server=undef" unless (defined $server);\r
+\r
+       return unless $self->{HANDLE};\r
+\r
+       $server->disconnect_client($self,$self->{HANDLE}) ;\r
+\r
+       $self->{HANDLE} = undef ;\r
+       $self = undef ;\r
+}\r
+\r
+sub do_frame_buffer_update_request() {\r
+       my $self = shift ;\r
+       die "do_frame_buffer_update_request called, when self is undef"  unless (defined $self);\r
+\r
+       my $buffer = shift;\r
+       die "do_frame_buffer_update_request, buffer isn't 10 bytes long" unless (length $buffer==10);\r
+\r
+       my (undef,$incremental, $x,$y,$w,$h) = unpack("CCnnnn", $buffer) ;\r
+\r
+\r
+#      print "UpdateRequest: Incremental=$incremental, X,Y=($x,$y)  WxH = ($w x $h)\n";\r
+\r
+       if (defined $self->{SEND_UPDATE_RESPONSE}) {\r
+               my $codec = $self->{CODEC};\r
+\r
+       #       my $data = $codec->encode_framebuffer_update($codec->encode_raw_rect($codec->encode_pixel(0,0,255), $x,$y,$w,$h) ) ;\r
+\r
+#              my $data = $codec->encode_framebuffer_update(   $codec->encode_rre($codec->encode_pixel(0,255,0), $x,$y,$w,$h,\r
+#                                                                                                              $codec->encode_rre_subrect( $codec->encode_pixel(255,255,255), 10,10,20,10),\r
+#                                                                                                              $codec->encode_rre_subrect( $codec->encode_pixel(0,128,128), 10,20,10,20)\r
+#                                                                                                      )\r
+#                                                                                              ) ;     \r
+\r
+               my $handler = $self->{UpdateDisplayHandler};\r
+               if (defined $handler) {\r
+                       my $data = &$handler($self, $incremental, $x, $y, $w, $h);\r
+\r
+                       $self->send_buffer($data);\r
+               }\r
+\r
+               $self->{UPDATE_REQUEST_PENDING} = undef;\r
+               $self->{SEND_UPDATE_RESPONSE} = undef;\r
+\r
+       } else {\r
+\r
+               $self->{PENDING_UPDATE_REQUEST_BUFFER} = $buffer ;\r
+\r
+               $self->{UPDATE_REQUEST_PENDING} = 1;\r
+       }\r
+\r
+       return ;\r
+}\r
+\r
+sub do_key_event() {\r
+       my $self = shift ;\r
+       die "do_key_event called, when self is undef"  unless (defined $self);\r
+\r
+       my $buffer = shift;\r
+       die "do_key_event, buffer isn't 8 bytes long" unless (length $buffer==8);\r
+\r
+       my (undef,$down,$pad,$key) = unpack("CCnN", $buffer) ;\r
+\r
+       my $handler = $self->{KeyEventHandler};\r
+       if (defined $handler) {\r
+               &$handler($self, $down, $key);\r
+       }\r
+       return ;\r
+}\r
+\r
+sub do_pointer_event() {\r
+       my $self = shift ;\r
+       die "do_pointer_event called, when self is undef"  unless (defined $self);\r
+\r
+       my $buffer = shift;\r
+       die "do_pointer_event, buffer isn't 6 bytes long" unless (length $buffer==6);\r
+\r
+       my (undef,$mask,$x,$y) = unpack("CCnn", $buffer) ;\r
+\r
+       my $handler = $self->{PointerEventHandler};\r
+       if (defined $handler) {\r
+               &$handler($self, $mask, $x,$y);\r
+       }\r
+       return ;\r
+}\r
+\r
+sub do_client_cut_text() {\r
+       my $self = shift ;\r
+       die "do_client_cut_text called, when self is undef"  unless (defined $self);\r
+\r
+       my $buffer = shift;\r
+       print "Client Sent client cut text\n";\r
+\r
+       my (undef,undef,$length,$text) = unpack("CnNA*", $buffer) ;\r
+\r
+       my $handler = $self->{CutTextHandler};\r
+       if (defined $handler) {\r
+               &$handler($self, $text);\r
+       }\r
+       return ;\r
+}\r
+\r
+sub start_conversation() {\r
+       my $self = shift ;\r
+       die "handle_client_data called, when self is undef"  unless (defined $self);\r
+\r
+       my $fh = $self->{HANDLE};\r
+       die "start_convertsion, handle is undef" unless (defined $fh);\r
+\r
+       $self->send_buffer("RFB 003.003\n");\r
+       $self->{STATE} = STATE_WAIT_FOR_PROTOCOL ;\r
+       $self->{EXPECTED_BUFFER_SIZE} = 12 ;\r
+}\r
+\r
+sub handle_client_data() {\r
+       my $self = shift ;\r
+       die "handle_client_data called, when self is undef"  unless (defined $self);\r
+\r
+       my $fh = $self->{HANDLE};\r
+       die "start_convertsion, handle is undef" unless (defined $fh);\r
+\r
+       #Read data from the socket\r
+       my $data ;\r
+       my $read_rc = $fh->sysread($data, 32786) ;\r
+       \r
+       if (defined $read_rc) {\r
+               if ($read_rc > 0) {\r
+\r
+                       #data read OK\r
+                       #add it to our buffer, check if we got enough data for the current state\r
+                       $self->{RECV_BUFFER} .= $data ;\r
+\r
+                       while ( length $self->{RECV_BUFFER} >= $self->{EXPECTED_BUFFER_SIZE} ) {\r
+                               #got enough data from current state, so handle the state\r
+                                       \r
+                               #debug\r
+#                              print "Handle Buffer: expected size = ",$self->{EXPECTED_BUFFER_SIZE}," recv_buffer size = ", length($self->{RECV_BUFFER}),"\n";\r
+                                       \r
+                               #take just the needed number of byte\r
+                               my $buffer = substr $self->{RECV_BUFFER},0,$self->{EXPECTED_BUFFER_SIZE};\r
+\r
+                               #leave the remaining bytes in the buffe\r
+                               $self->{RECV_BUFFER} = substr ($self->{RECV_BUFFER}, $self->{EXPECTED_BUFFER_SIZE}, length($self->{RECV_BUFFER})-$self->{EXPECTED_BUFFER_SIZE} ); \r
+                               \r
+                               #debug\r
+#                              print "After Buffer Split: buffer size = ", length($buffer), " recv_buffer size = ",length($self->{RECV_BUFFER}),"\n" ;\r
+                               \r
+                               $self->handle_state($buffer);\r
+                       }\r
+               } else {\r
+                       #read_rc == 0\r
+                       #it means the socket was closed by the client\r
+                               \r
+                               \r
+                       return undef;\r
+               }\r
+       } else { \r
+#              die "Errro while reading socket";\r
+               \r
+               return undef;   \r
+       }\r
+       \r
+       return 1;\r
+}\r
+\r
+sub handle_state {\r
+       my $self = shift ;\r
+       die "handle_state called, when self is undef"  unless (defined $self);\r
+       \r
+       my $buffer = shift ;\r
+       die "handle_state called, when buffer is undef"  unless (defined $buffer);\r
+       \r
+       my $current_state = $self->{STATE};\r
+\r
+       #\r
+       # Got protocol version string from the cilent\r
+       #       \r
+       if ($current_state==STATE_WAIT_FOR_PROTOCOL) {\r
+               if (length($buffer) != 12) {\r
+                       warn "Error: WAIT_FOR_PROTOCOL state, buffer != 12 bytes" ;\r
+                       return undef;\r
+               };\r
+               \r
+               my ($major, $minor) = $buffer =~ m/^RFB (\d{3})\.(\d{3})$/ ;\r
+               die "unknown protocol version ($buffer)" unless (defined $major && defined $minor);\r
+               if ($major != 3) {\r
+                       $self->send_buffer("Uknown version. i need major protocol version = 3\nBye Bye\n");\r
+                       return undef;\r
+               }\r
+               print "Client requested protocol version $major.$minor\n";\r
+               \r
+               #\r
+               # Next step:\r
+               # Ask for password, if needed\r
+               #\r
+\r
+               if (defined $self->{Password}) {\r
+                       $self->{STATE} = STATE_WAIT_FOR_AUTHENTICATION ;\r
+                       $self->{EXPECTED_BUFFER_SIZE} = 16 ;\r
+                       \r
+                       $self->send_buffer(pack("N",2));\r
+\r
+                       # TODO:\r
+                       # send a challange and check the resonse using DES\r
+                       $self->send_buffer("0123456789abcdef");\r
+               }\r
+               else {\r
+                       # no password needed\r
+                       $self->{STATE} = STATE_WAIT_FOR_CLIENT_INIT_MSG ;\r
+                       $self->{EXPECTED_BUFFER_SIZE} = 1 ;\r
+                       \r
+                       $self->send_buffer(pack("N",1));\r
+               }\r
+       } \r
+       elsif\r
+       #\r
+       # Got Authentication response from the client\r
+       #\r
+       ($current_state==STATE_WAIT_FOR_AUTHENTICATION) {\r
+               if (length($buffer) != 16) {\r
+                       warn "Error: WAIT_FOR_AUTHENTICATION state, buffer != 16 bytes" ;\r
+                       return undef;\r
+               };\r
+               \r
+               #for now, accept any password\r
+               $self->send_buffer(pack("N",0));\r
+\r
+               #\r
+               # Next Stap:\r
+               # Client Init Msg\r
+               $self->{STATE} = STATE_WAIT_FOR_CLIENT_INIT_MSG;\r
+               $self->{EXPECTED_BUFFER_SIZE} = 1 ;\r
+       }\r
+       elsif\r
+       #\r
+       # Got client Init Message from the client\r
+       #\r
+       ($current_state==STATE_WAIT_FOR_CLIENT_INIT_MSG) {\r
+               if (length($buffer) != 1) {\r
+                       warn "Error: WAIT_FOR_CLIENT_INIT_MSG state, buffer != 1 bytes" ;\r
+                       return undef;\r
+               };\r
+               my $srvr_init_msg = BUILD_SERVER_INIT_MSG( $self->{Width}, \r
+                                                                                                  $self->{Height},\r
+                                                                                                  $self->{PixelFormat},\r
+                                                                                                  $self->{Caption} );\r
+               $self->send_buffer($srvr_init_msg);\r
+               \r
+               #\r
+               # Next Step\r
+               # client normal messages\r
+               $self->{STATE} = STATE_WAIT_FOR_MSG_HEADER;\r
+               $self->{EXPECTED_BUFFER_SIZE} = 1 ;\r
+       }\r
+       elsif\r
+       #\r
+       # Got Message header from the client\r
+       #\r
+       ($current_state==STATE_WAIT_FOR_MSG_HEADER) {\r
+               if (length($buffer) != 1) {\r
+                       warn "Error: STATE_WAIT_FOR_MSG_HEADER state, buffer != 1 bytes" ;\r
+                       return undef;\r
+               };\r
+               \r
+               my $msg = ord($buffer) ;\r
+               \r
+               my ($subroutine, $byte_count) = @{$rfb_message_handlers[$msg]};\r
+               \r
+               die "Error: unknown message $msg" unless (defined $subroutine && defined $byte_count);          \r
+\r
+               # These are FixedLength messages, we can handle them all in the same way\r
+               if ( $msg==MSG_SET_PIXEL_FORMAT\r
+                        or \r
+                        $msg==MSG_FRAME_BUFFER_UPDATE_REQUEST\r
+                        or\r
+                        $msg==MSG_KEY_EVENT\r
+                        or \r
+                        $msg==MSG_POINTER_EVENT ) \r
+               {\r
+                       #\r
+                       # Next Step\r
+                       # client normal messages\r
+                       $self->{STATE} = STATE_WAIT_FOR_FIXED_LEN_MSG;\r
+                       $self->{EXPECTED_BUFFER_SIZE} = $byte_count ;\r
+                       \r
+                       #re-insert this byte into the buffer\r
+                       $self->{RECV_BUFFER} = pack("C",$msg) . $self->{RECV_BUFFER} ;\r
+                       $self->{CURRENT_MSG} = $msg ;\r
+                       \r
+               } elsif \r
+               ($msg==MSG_SET_ENCODING) {\r
+                       $self->{STATE} = STATE_WAIT_FOR_ENCODING_COUNT;\r
+                       $self->{EXPECTED_BUFFER_SIZE} = 3 ;\r
+                       \r
+                       $self->{ENCODING_BUFFER} = $buffer;\r
+               } elsif\r
+               ($msg==MSG_CLIENT_CUT_TEXT) {\r
+                       $self->{STATE} = STATE_WAIT_FOR_TEXT_CUT_LENGTH;\r
+                       $self->{EXPECTED_BUFFER_SIZE} = 7 ;\r
+                       $self->{CUT_TEXT_BUFFER} = $buffer;\r
+               }\r
+       }\r
+       elsif\r
+       #\r
+       # Got Message header from the client\r
+       #\r
+       ($current_state==STATE_WAIT_FOR_FIXED_LEN_MSG) {\r
+               my $msg = $self->{CURRENT_MSG};\r
+               my ($subroutine, $byte_count) = @{$rfb_message_handlers[ $msg ]};\r
+               die "Error: unknown message $msg" unless (defined $subroutine && defined $byte_count);          \r
+\r
+               if (length($buffer) != $byte_count) {\r
+                       warn "Error: STATE_WAIT_FOR_FIXED_LEN_MSG state (msg=$msg), buffer != $byte_count bytes" ;\r
+                       return undef;\r
+               };\r
+\r
+               #\r
+               # Next Step\r
+               # Execute the client's request\r
+               &$subroutine($self,$buffer);\r
+\r
+               $self->{STATE} = STATE_WAIT_FOR_MSG_HEADER;\r
+               $self->{EXPECTED_BUFFER_SIZE} = 1 ;\r
+       } elsif\r
+       #\r
+       # Got Encoding Count\r
+       #\r
+       ($current_state==STATE_WAIT_FOR_ENCODING_COUNT) {\r
+               if (length($buffer) != 3) {\r
+                       warn "Error: STATE_WAIT_FOR_ENCODING_COUNT state, buffer != 3 bytes" ;\r
+                       return undef;\r
+               };\r
+       \r
+               my (undef,$nEncoding) = unpack("Cn", $buffer) ;\r
+               print "Client supports $nEncoding encodings\n" ;\r
+               \r
+               $self->{STATE} = STATE_WAIT_FOR_ENCODING_DATA;\r
+               $self->{EXPECTED_BUFFER_SIZE} = $nEncoding*4 ;\r
+               $self->{ENCODING_BUFFER} .= $buffer;\r
+       } elsif\r
+       #\r
+       # Got Encoding Data\r
+       #\r
+       ($current_state==STATE_WAIT_FOR_ENCODING_DATA) {\r
+               $self->{ENCODING_BUFFER} .= $buffer;\r
+               \r
+               $self->do_set_encoding($self->{ENCODING_BUFFER});\r
+\r
+               $self->{STATE} = STATE_WAIT_FOR_MSG_HEADER;\r
+               $self->{EXPECTED_BUFFER_SIZE} = 1 ;\r
+       } elsif\r
+       #\r
+       # Got Client Cut Text Length\r
+       #\r
+       ($current_state==STATE_WAIT_FOR_TEXT_CUT_LENGTH) {\r
+               if (length($buffer) != 7) {\r
+                       warn "Error: STATE_WAIT_FOR_TEXT_CUT_LENGTH state, buffer != 7 bytes" ;\r
+                       return undef;\r
+               };\r
+       \r
+               my (undef,undef,$length) = unpack("CnN", $buffer) ;\r
+               print "Client Cut Text: $length characters \n" ;\r
+               \r
+               $self->{STATE} = STATE_WAIT_FOR_TEXT_CUT_CONTENT;\r
+               $self->{EXPECTED_BUFFER_SIZE} = $length ;\r
+               $self->{TEXT_CUT_BUFFER} .= $buffer;\r
+       } elsif\r
+       #\r
+       # Got Client Cut text content\r
+       #\r
+       ($current_state==STATE_WAIT_FOR_TEXT_CUT_CONTENT) {\r
+               $self->{TEXT_CUT_BUFFER} .= $buffer;\r
+               \r
+               $self->do_client_cut_text($self->{TEXT_CUT_BUFFER});\r
+               $self->{TEXT_CUT_BUFFER} = undef;\r
+\r
+               $self->{STATE} = STATE_WAIT_FOR_MSG_HEADER;\r
+               $self->{EXPECTED_BUFFER_SIZE} = 1 ;\r
+       } ;\r
+}\r
+\r
+1;
\ No newline at end of file
diff --git a/life.pl b/life.pl
new file mode 100755 (executable)
index 0000000..f0c51bb
--- /dev/null
+++ b/life.pl
@@ -0,0 +1,171 @@
+use strict;\r
+use warnings;\r
+use VNC::Server;\r
+\r
+\r
+my $life_cell_pixel_size = 5 ;\r
+my $life_cells_per_col   = 20 ;\r
+my $life_cells_per_row   = 20;\r
+\r
+my $running = 0;\r
+\r
+#\r
+# Init the life data\r
+#\r
+my @life_data;\r
+\r
+foreach my $i (0..$life_cells_per_row-1) {\r
+       foreach my $j (0..$life_cells_per_col-1) {\r
+               $life_data[$i][$j] = int(rand(2));\r
+       }\r
+}\r
+\r
+sub count_neighbors {\r
+       my ($i,$j) = @_ ;\r
+\r
+       my $count =0 ;\r
+       \r
+       foreach my $q ( ($i-1)..($i+1)) {\r
+               foreach my $w ( ($j-1)..($j+1)) {\r
+                       if ( ($q>=0) && ($q<$life_cells_per_row) && \r
+                                ($w>=0) && ($w<$life_cells_per_col) &&\r
+                                (($q!=$i) || ($w!=$j))\r
+                                ) {\r
+                               if ($life_data[$q][$w]==1) {\r
+                                       $count++ ;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       return $count;\r
+}\r
+\r
+\r
+my $s = new VNC::Server( Port=>2, \r
+                                                Idle=>1, \r
+                                                Caption=>'Game of VNCLife',\r
+                                                Width => $life_cell_pixel_size * $life_cells_per_row, \r
+                                                Height => $life_cell_pixel_size * $life_cells_per_col,\r
+                                                IdleHandler => \&next_turn,\r
+                                                KeyEventHandler => \&keyboard, \r
+                                                PointerEventHandler => \&pointer,\r
+                                                UpdateDisplayHandler => \&updater\r
+                                               );\r
+\r
+$s->vnc_server_loop;\r
+\r
+sub keyboard {\r
+       my ($vnc, $down, $key) = @_ ;\r
+\r
+       #act only on key released\r
+       if ($down==0) {\r
+\r
+               # R = randomize\r
+               if ($key==ord('R') || $key==ord('r')) {\r
+                       foreach my $i (0..$life_cells_per_row-1) {\r
+                               foreach my $j (0..$life_cells_per_col-1) {\r
+                                       $life_data[$i][$j] = int(rand(2));\r
+                               }\r
+                       }\r
+                       $s->update_all_displays;\r
+               }\r
+\r
+               # C = clear\r
+               if ($key==ord('C') || $key==ord('c')) {\r
+                       foreach my $i (0..$life_cells_per_row-1) {\r
+                               foreach my $j (0..$life_cells_per_col-1) {\r
+                                       $life_data[$i][$j] = 0;\r
+                               }\r
+                       }\r
+                       $s->update_all_displays;\r
+               }\r
+\r
+               # Q = Quit\r
+               if ($key==ord('Q') || $key==ord('q')) {\r
+                       $vnc->disconnect();\r
+               }\r
+\r
+               # S = Start/Stop\r
+               if ($key==ord('S') || $key==ord('s')) {\r
+                       if ($running) {\r
+                               print "Stoping!\n";\r
+                               $running = undef ;\r
+                       } else {\r
+                               print "Starting!\n";\r
+                               $running = 1;\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+sub pointer {\r
+       my ($vnc,$mask,$x,$y) = @_ ;\r
+\r
+       if ($mask!=0) {\r
+               my $i = $x / $life_cell_pixel_size ;\r
+               my $j = $y / $life_cell_pixel_size ;\r
+\r
+               $life_data[$i][$j] = ($mask==1) ? 1 : 0 ;\r
+\r
+               $s->update_all_displays;\r
+       }\r
+}\r
+\r
+\r
+sub updater {\r
+       my ($vnc, $inc, $x,$y,$w,$h) = @_ ;\r
+       my $codec = $vnc->{CODEC};\r
+\r
+       my @rects ;\r
+       my $red_pixel = $codec->encode_pixel(255,0,0);\r
+       my $white_pixel = $codec->encode_pixel(255,255,255);\r
+\r
+       foreach my $i (0..$life_cells_per_row-1) {\r
+               foreach my $j (0..$life_cells_per_col-1) {\r
+                       if ($life_data[$i][$j]==1) {\r
+                               push @rects, $codec->encode_rre_subrect( $red_pixel ,$i*$life_cell_pixel_size, $j*$life_cell_pixel_size,$life_cell_pixel_size,$life_cell_pixel_size);\r
+                       }\r
+               }\r
+       }\r
+       my $data = $codec->encode_framebuffer_update( $codec->encode_rre($white_pixel, $x,$y,$w,$h, @rects));\r
+\r
+       return $data ;\r
+}\r
+\r
+sub next_turn {\r
+       #not running, exit immediately\r
+       return unless ($running);\r
+\r
+       #calculate the next life phase\r
+       my @new_life_data;\r
+\r
+       foreach my $i (0..$life_cells_per_row-1) {\r
+               foreach my $j (0..$life_cells_per_col-1) {\r
+                       $new_life_data[$i][$j] = 0;\r
+               }\r
+       }\r
+\r
+       foreach my $i (0..$life_cells_per_row-1) {\r
+               foreach my $j (0..$life_cells_per_col-1) {\r
+                       my $current = $life_data[$i][$j];\r
+                       my $count = count_neighbors($i,$j);\r
+                       \r
+                       if ($current==1){\r
+                               if ($count<2 || $count>3) {\r
+                                       $new_life_data[$i][$j] = 0 ;\r
+                               } else {\r
+                                       $new_life_data[$i][$j] = 1 ;\r
+                               }\r
+                       }else{\r
+                               if ($count==3) {\r
+                                       $new_life_data[$i][$j] = 1 ;\r
+                               } else {\r
+                                       $new_life_data[$i][$j] = 0 ;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       @life_data = @new_life_data;\r
+       \r
+       $s->update_all_displays;\r
+}\r
diff --git a/main.pl b/main.pl
new file mode 100755 (executable)
index 0000000..5c44e5b
--- /dev/null
+++ b/main.pl
@@ -0,0 +1,58 @@
+use strict;\r
+use warnings;\r
+use VNC::Server;\r
+\r
+\r
+my $type = 1;\r
+\r
+\r
+\r
+sub updater {\r
+       my ($vnc, $inc, $x,$y,$w,$h) = @_ ;\r
+\r
+       my $codec = $vnc->{CODEC};\r
+\r
+\r
+       my $data ;\r
+       \r
+       if ($type==1) {\r
+               $data= $codec->encode_framebuffer_update($codec->encode_raw_rect($codec->encode_pixel(0,0,255), $x,$y,$w,$h) ) ;\r
+       } else {\r
+               $data = $codec->encode_framebuffer_update(      $codec->encode_rre($codec->encode_pixel(0,255,0), $x,$y,$w,$h,\r
+                                                                                                       $codec->encode_rre_subrect( $codec->encode_pixel(255,255,255), 10,10,20,10),\r
+                                                                                                       $codec->encode_rre_subrect( $codec->encode_pixel(0,128,128), 10,20,10,20)\r
+                                                                                               )) ;\r
+       }\r
+\r
+       return $data ;\r
+}\r
+\r
+my $s = new VNC::Server( Port=>2, \r
+                                                Idle=>1, \r
+                                                Caption=>'Hello From perlVNC',\r
+                                                Width => 50, Height => 50,\r
+                                                Password => 'dummy',\r
+                                                IdleHandler => sub {print "Nothing happened for 1 second...\n"},\r
+                                                KeyEventHandler => sub { my ($vnc,$d,$k) = @_; print "down = $d, key = $k\n";},\r
+                                                PointerEventHandler => \&pointer,\r
+                                                UpdateDisplayHandler => \&updater\r
+                                               );\r
+\r
+$s->vnc_server_loop;\r
+\r
+sub pointer {\r
+       my ($vnc,$mask,$x,$y) = @_ ;\r
+\r
+       if ($mask==1) {\r
+               $type=1 ;\r
+       };\r
+       if ($mask==4) {\r
+               $type=2;\r
+       }\r
+       if ($mask==2) {\r
+               $vnc->disconnect ;\r
+               return ;\r
+       }\r
+\r
+       $s->update_all_displays;\r
+}\r