|  line  | 
 stmt  | 
 bran  | 
 cond  | 
 sub  | 
 pod  | 
 time  | 
 code  | 
| 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  Language::Logo.pm  | 
| 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  An implementation of the Logo programming language which allows  | 
| 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  multiple clients to connect simultaneously.  | 
| 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  Written January 2007, by John C. Norton  | 
| 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  Presented at Boston Perlmongers on January 16th, 2007  | 
| 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  Last update -- 1/30/2007  22:12  | 
| 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
12
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Package header  | 
| 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 package Logo;  | 
| 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 our $VERSION = '1.000';              # Current version  | 
| 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
18
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Strict  | 
| 
19
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
 
 | 
62666
 | 
 use strict;  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
    | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
84
 | 
    | 
| 
20
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
 
 | 
12
 | 
 use warnings;  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
    | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
58
 | 
    | 
| 
21
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Libraries  | 
| 
24
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
 
 | 
365756
 | 
 use Data::Dumper;  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
34098
 | 
    | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
162
 | 
    | 
| 
25
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
 
 | 
3756
 | 
 use IO::Select;  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4768
 | 
    | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
118
 | 
    | 
| 
26
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
 
 | 
1786
 | 
 use IO::Socket;  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
76080
 | 
    | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
    | 
| 
27
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
 
 | 
3482
 | 
 use Sys::Hostname;  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4066
 | 
    | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
190
 | 
    | 
| 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
30
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #################  | 
| 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ### Variables ###  | 
| 
32
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #################  | 
| 
33
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
 
 | 
16
 | 
 use constant PI => (4 * atan2(1, 1));  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
    | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
21082
 | 
    | 
| 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # User-defined  | 
| 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $iam           = "Language::Logo";     # Module identifier  | 
| 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $d_title       = "$iam version $VERSION";  | 
| 
38
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
39
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $max_connect   = 16;         # Maximum client connections  | 
| 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $retry_timeout = 10;         # Client connection timeout after N seconds  | 
| 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
42
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Defaults  | 
| 
43
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $d_port       = "8220";       # Default socket port  | 
| 
44
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $d_update     = 10;           # Default gui update rate  | 
| 
45
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $d_bg         = "black";      # Default canvas background color  | 
| 
46
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $d_width      = 512;          # Default canvas width  | 
| 
47
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $d_height     = 512;          # Default canvas height  | 
| 
48
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $d_color      = 'white';      # Default pen/turtle color  | 
| 
49
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $d_psize      = '1';          # default pen size (thickness)  | 
| 
50
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $d_txdim      = '6';          # Default turtle x-dimension  | 
| 
51
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $d_tydim      = '9';          # Default turtle y-dimension  | 
| 
52
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
53
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my @switches     = qw( verbose name title bg width height update host port );  | 
| 
54
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my %switches     = map { $_ => 1 } @switches;  | 
| 
55
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
56
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Global (server-specific) variables  | 
| 
57
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $pserver_vars  = [qw( nticks verbose count total )];  | 
| 
58
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
59
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Client-specific top-level variables (with initial values)  | 
| 
60
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $pclient_vars = {  | 
| 
61
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'debug' => 0,  | 
| 
62
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'step'  => 0,  | 
| 
63
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 };  | 
| 
64
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
65
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Turtle state info passed back from server to client  | 
| 
66
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $pinfo = [qw( x y angle pen color size show wrap )];  | 
| 
67
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
68
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Command aliases and descriptions  | 
| 
69
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $palias = {  | 
| 
70
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'fd' => [ 'forward',     'Moves forward the given number of pixels' ],  | 
| 
71
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'bk' => [ 'backward',    'Moves backward the given number of pixels' ],  | 
| 
72
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'rt' => [ 'right',       'Rotates clockwise the given angle' ],  | 
| 
73
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'lt' => [ 'left',        'Rotates counter-clockwise the given angle' ],  | 
| 
74
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'sh' => [ 'seth',        'Sets the turtle heading to the given angle' ],  | 
| 
75
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'pu' => [ 'penup',       'Stops drawing' ],  | 
| 
76
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'pd' => [ 'pendown',     'Starts drawing' ],  | 
| 
77
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'ps' => [ 'pensize',     'Specifies the line width to draw with' ],  | 
| 
78
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'co' => [ 'color',       'Specifies the color to draw with' ],  | 
| 
79
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'cs' => [ 'clear',       'Clears the screen' ],  | 
| 
80
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'hm' => [ 'home',        'Homes the turtle to the starting position' ],  | 
| 
81
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'sx' => [ 'setx',        'Sets the x-coordinate' ],  | 
| 
82
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'sy' => [ 'sety',        'Sets the y-coordinate' ],  | 
| 
83
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'xy' => [ 'setxy',       'Sets the x and y coordinates' ],  | 
| 
84
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'ht' => [ 'hideturtle',  'Makes the turtle invisible' ],  | 
| 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'st' => [ 'showturtle',  'Makes the turtle visible' ],  | 
| 
86
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'w'  => [ 'width',       'Specifies the width of the screen (global)' ],  | 
| 
87
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'h'  => [ 'height',      'Specifies the height of the screen (global)' ],  | 
| 
88
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'bg' => [ 'background',  'Sets the screen background color (global)' ],  | 
| 
89
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'ud' => [ 'update',      'Changes the Tk update interval (global)' ],  | 
| 
90
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'wr' => [ 'wrap',        'Sets wrap (0=normal, 1=torus, 2=reflective)' ],  | 
| 
91
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 };  | 
| 
92
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
93
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
94
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $pmethods = {  | 
| 
95
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'forward'    => 'move_turtle',   | 
| 
96
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'backward'   => 'move_turtle',  | 
| 
97
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'right'      => 'turn_turtle',  | 
| 
98
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'left'       => 'turn_turtle',  | 
| 
99
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'seth'       => 'turn_turtle',  | 
| 
100
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'penup'      => 'change_pen_state',  | 
| 
101
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'pendown'    => 'change_pen_state',  | 
| 
102
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'pensize'    => 'change_pen_size',  | 
| 
103
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'color'      => 'change_color',  | 
| 
104
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'clear'      => 'modify_canvas',  | 
| 
105
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'width'      => 'modify_canvas',  | 
| 
106
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'height'     => 'modify_canvas',  | 
| 
107
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'background' => 'modify_canvas',  | 
| 
108
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'home'       => 'reset_turtle',  | 
| 
109
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'setx'       => 'move_turtle',  | 
| 
110
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'sety'       => 'move_turtle',  | 
| 
111
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'setxy'      => 'move_turtle',  | 
| 
112
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'hideturtle' => 'show_turtle',  | 
| 
113
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'showturtle' => 'show_turtle',  | 
| 
114
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'update'     => 'change_update',  | 
| 
115
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     'wrap'       => 'set_wrap_value',  | 
| 
116
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 };  | 
| 
117
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
118
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
119
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ###################  | 
| 
120
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ### Subroutines ###  | 
| 
121
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ###################  | 
| 
122
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
123
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #===================  | 
| 
124
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #=== Client code ===  | 
| 
125
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #===================  | 
| 
126
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub new {  | 
| 
127
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
 
 | 
64
 | 
     my ($class, @args) = @_;  | 
| 
128
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
10
 | 
     (ref $class) and $class = ref $class;  | 
| 
129
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
130
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Create blessed reference  | 
| 
131
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
     my $self = { };  | 
| 
132
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
     bless $self, $class;  | 
| 
133
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
134
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Parse optional arguments  | 
| 
135
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
     while (@args) {  | 
| 
136
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
         my $arg = shift @args;  | 
| 
137
 | 
8
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
62
 | 
         if ($arg =~ /^sig(.+)$/) {  | 
| 
 
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
138
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # Trap specified signals  | 
| 
139
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             my $sig = uc $1;  | 
| 
140
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             $SIG{$sig} = shift @args;  | 
| 
141
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } elsif (defined($switches{$arg}) and @args > 0) {  | 
| 
142
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # Assign all valid parameters  | 
| 
143
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
44
 | 
             $self->{$arg} = shift @args;  | 
| 
144
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
145
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
146
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
147
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Startup a new server locally if 'host' was not defined.  | 
| 
148
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
14
 | 
     if (!defined($self->{'host'})) {  | 
| 
149
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
         $self->fork_server();  | 
| 
150
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
151
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
152
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Connect to the server  | 
| 
153
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
70
 | 
     $self->connect_to_server();  | 
| 
154
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
155
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Return the object  | 
| 
156
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     return $self;  | 
| 
157
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
158
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
159
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
160
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub disconnect {  | 
| 
161
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
     my ($self, $msg) = @_;  | 
| 
162
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
0
 | 
     if ($msg || 0) {  | 
| 
163
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         print "$msg";  | 
| 
164
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         ;  | 
| 
165
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
166
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my $sock = $self->{'socket'};  | 
| 
167
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
0
 | 
     if ($sock || 0) {  | 
| 
168
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         close($sock);  | 
| 
169
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
170
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
171
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
172
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
173
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub connect_to_server {  | 
| 
174
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
 
 | 
21
 | 
     my ($self) = @_;  | 
| 
175
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
176
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Return if socket is already connected  | 
| 
177
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
     my $sock = $self->{'socket'};  | 
| 
178
 | 
1
 | 
  
 50
  
 | 
  
 50
  
 | 
 
 | 
 
 | 
60
 | 
     ($sock || 0) and return $sock;  | 
| 
179
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
180
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # If hostname is ':', use local host  | 
| 
181
 | 
1
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
149
 | 
     my $host = $self->{'host'} || ':';  | 
| 
182
 | 
1
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
43
 | 
     ($host eq ':') and $host = hostname();  | 
| 
183
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
184
 | 
1
 | 
 
 | 
  
 33
  
 | 
 
 | 
 
 | 
30
 | 
     my $port = $self->{'port'} || $d_port;  | 
| 
185
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
34
 | 
     my %params = (  | 
| 
186
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'PeerAddr'  => $host,  | 
| 
187
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'PeerPort'  => $port,  | 
| 
188
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'Proto'     => 'tcp',  | 
| 
189
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'ReuseAddr' => 0,  | 
| 
190
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     );  | 
| 
191
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
192
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Keep retrying until $retry_timeout is exceeded  | 
| 
193
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
     my $start = time;  | 
| 
194
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
     while (1) {  | 
| 
195
 | 
60
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
2181
 | 
         ($sock = new IO::Socket::INET(%params)) and last;   # Success!  | 
| 
196
 | 
60
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
8976940
 | 
         if (time - $start > $retry_timeout) {  | 
| 
197
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1626
 | 
             die "$iam:  Failed client socket connection\n";  | 
| 
198
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
199
 | 
59
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5922337
 | 
         select(undef, undef, undef, 0.1);  | 
| 
200
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
201
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
202
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Save socket  | 
| 
203
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     $self->{'socket'} = $sock;  | 
| 
204
 | 
0
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
0
 | 
     my $name = $self->{'name'}   || "";  | 
| 
205
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     print $sock ":$name\n";  | 
| 
206
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     chomp(my $ans = <$sock>);  | 
| 
207
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     if ($ans !~ /^(\d+):(.+)$/) {  | 
| 
208
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         die "$iam:  expected 'id:name', got '$ans'\n";  | 
| 
209
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
210
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my ($id, $newname) = ($1, $2);  | 
| 
211
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     $self->{'id'}   = $id;  | 
| 
212
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     $self->{'name'} = $newname;  | 
| 
213
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     $self->{'host'} = $host;  | 
| 
214
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     return $sock;  | 
| 
215
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
216
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
217
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
218
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub host {  | 
| 
219
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
     my ($self) = @_;  | 
| 
220
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     return $self->{'host'};  | 
| 
221
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
222
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
223
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
224
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub interact {  | 
| 
225
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
     my ($self) = @_;  | 
| 
226
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
227
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     print "Type '?' for help\n";  | 
| 
228
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     while (1) {  | 
| 
229
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         print "$iam> ";  | 
| 
230
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         my $cmd = ;  | 
| 
231
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         defined($cmd) or return;  | 
| 
232
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         chomp $cmd;  | 
| 
233
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         $cmd =~ s/^\s*(.*)\s*$/$1/;     # Trim whitespace  | 
| 
234
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         next if ($cmd eq "");  | 
| 
235
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
236
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
0
 | 
         if ($cmd eq 'quit' or $cmd eq 'bye' or $cmd eq 'exit') {  | 
| 
 
 | 
 
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
237
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # Exit interactive mode  | 
| 
238
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             return 0;  | 
| 
239
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
240
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
241
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         if ($cmd eq "?") {  | 
| 
242
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             $self->interactive_help();  | 
| 
243
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } else {  | 
| 
244
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             $self->interactive_command($cmd);  | 
| 
245
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
246
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
247
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
248
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
249
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
250
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub interactive_help {  | 
| 
251
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
     printf "    Command Abbr  Description\n";  | 
| 
252
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     print  "-" x 79, "\n";  | 
| 
253
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my @keys = keys %$palias;  | 
| 
254
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my @sort = sort { $palias->{$a}->[0] cmp $palias->{$b}->[0] } @keys;  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
255
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     foreach my $alias (@sort) {  | 
| 
256
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         my $pcmd = $palias->{$alias};  | 
| 
257
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         my ($full, $desc) = @$pcmd;  | 
| 
258
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         printf  " %10.10s  %3.3s  %s\n", $full, $alias, $desc;  | 
| 
259
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
260
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
261
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
262
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
263
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub interactive_command {  | 
| 
264
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
     my ($self, $cmd) = @_;  | 
| 
265
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
266
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Send a Logo command  | 
| 
267
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my $preply = $self->command($cmd);  | 
| 
268
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my $err = $preply->{'error'};  | 
| 
269
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     if (defined($err)) {  | 
| 
270
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         print "ERROR:  $err\n";  | 
| 
271
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
272
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         my $text = "";  | 
| 
273
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         foreach my $param (@$pinfo) {  | 
| 
274
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             my $val = $preply->{$param};  | 
| 
275
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             $text and $text .= ",";  | 
| 
276
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             $text .= "$param=$val";  | 
| 
277
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
278
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         print "[$text]\n";  | 
| 
279
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
280
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
281
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
282
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
283
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub query {  | 
| 
284
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
     my ($self, @params) = @_;  | 
| 
285
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my $sock = $self->connect_to_server();  | 
| 
286
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my $preply = $self->client_send($sock, "?");  | 
| 
287
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     defined($preply->{'error'}) and return $preply;  | 
| 
288
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my @values = ( );  | 
| 
289
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     foreach my $param (@params) {  | 
| 
290
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         if (!defined($preply->{$param})) {  | 
| 
291
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             $preply->{'error'} = "Server parameter '$param' undefined";  | 
| 
292
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             return $preply;  | 
| 
293
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
294
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         my $value = $preply->{$param};  | 
| 
295
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         push @values, $value;  | 
| 
296
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
297
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     return wantarray? (@values): $values[0];  | 
| 
298
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
299
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
300
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
301
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub command {  | 
| 
302
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
     my ($self, $cmdstr) = @_;  | 
| 
303
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my $sock = $self->connect_to_server();  | 
| 
304
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     $sock or return 0;  | 
| 
305
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my @commands = split(';', $cmdstr);  | 
| 
306
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my $preply = { };  | 
| 
307
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     foreach my $cmd (@commands) {  | 
| 
308
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         $cmd =~ s/^\s*//;    # Trim leading whitespace  | 
| 
309
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         $cmd =~ s/\s*$//;    # Trim trailing whitespace  | 
| 
310
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         $preply = $self->client_send($sock, "=$cmd");  | 
| 
311
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         defined($preply->{'error'}) and return $preply;  | 
| 
312
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
313
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     return $preply;  | 
| 
314
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
315
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
316
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
317
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub cmd {  | 
| 
318
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
     my $self = shift;  | 
| 
319
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     return $self->command(@_);  | 
| 
320
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
321
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
322
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
323
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub client_send {  | 
| 
324
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
     my ($self, $sock, $text) = @_;  | 
| 
325
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     print $sock $text, "\n";  | 
| 
326
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my $answer = <$sock>;  | 
| 
327
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     $answer or die "$iam:  server socket went away\n";  | 
| 
328
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     chomp $answer;  | 
| 
329
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my $preply = { };  | 
| 
330
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     if ($answer =~ s/^!//) {  | 
| 
331
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         $preply->{'error'} = $answer;  | 
| 
332
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         return $preply;  | 
| 
333
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
334
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     $answer =~ s/^(.)//;  | 
| 
335
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my @params = split(',', $answer);  | 
| 
336
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     foreach my $param (@params) {  | 
| 
337
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         my ($param, $val) = ($param =~ /^(.*)=(.*)$/);  | 
| 
338
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         $preply->{$param} = $val;  | 
| 
339
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
340
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     return $preply;  | 
| 
341
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
342
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
343
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
344
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #===================  | 
| 
345
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #=== Server code ===  | 
| 
346
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #===================  | 
| 
347
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub fork_server {  | 
| 
348
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
 
 | 
6
 | 
     my ($self) = @_;  | 
| 
349
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
350
 | 
2
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
28
 | 
     my $verbose = $self->{'verbose'} || 0;  | 
| 
351
 | 
2
 | 
 
 | 
  
 33
  
 | 
 
 | 
 
 | 
18
 | 
     my $title   = $self->{'title'}   || $d_title;  | 
| 
352
 | 
2
 | 
 
 | 
  
 33
  
 | 
 
 | 
 
 | 
12
 | 
     my $w       = $self->{'width'}   || $d_width;  | 
| 
353
 | 
2
 | 
 
 | 
  
 33
  
 | 
 
 | 
 
 | 
8
 | 
     my $h       = $self->{'height'}  || $d_height;  | 
| 
354
 | 
2
 | 
 
 | 
  
 33
  
 | 
 
 | 
 
 | 
16
 | 
     my $bg      = $self->{'bg'}      || $d_bg;  | 
| 
355
 | 
2
 | 
 
 | 
  
 33
  
 | 
 
 | 
 
 | 
10
 | 
     my $update  = $self->{'update'}  || $d_update;  | 
| 
356
 | 
2
 | 
 
 | 
  
 33
  
 | 
 
 | 
 
 | 
20
 | 
     my $host    = $self->{'host'}    || hostname();  | 
| 
357
 | 
2
 | 
 
 | 
  
 33
  
 | 
 
 | 
 
 | 
44
 | 
     my $port    = $self->{'port'}    || $d_port;  | 
| 
358
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      | 
| 
359
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5365
 | 
     my $fork = fork();  | 
| 
360
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
146
 | 
     defined($fork) or die "$iam:  failed to fork server\n";  | 
| 
361
 | 
2
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
160
 | 
     $fork and return;  | 
| 
362
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
60
 | 
     Logo->server_init($verbose, $title, $w, $h, $bg, $update, $host, $port);  | 
| 
363
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
364
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
365
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
366
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub server_init {  | 
| 
367
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
 
 | 
26
 | 
     my ($class, $verbose, $title, $w, $h, $bg, $update, $host, $port) = @_;  | 
| 
368
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
369
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Create a blessed object  | 
| 
370
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
94
 | 
     my $self = {  | 
| 
371
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'nticks'   => 0,        # Tracks number of GUI updates  | 
| 
372
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'verbose'  => $verbose, # Verbose flag  | 
| 
373
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'count'    => 0,        # Current number of connections  | 
| 
374
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'total'    => 0,        # Total number of connections  | 
| 
375
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'clients'  => { },      # The client hash  | 
| 
376
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'names'    => { },      # The clients by name  | 
| 
377
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     };  | 
| 
378
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
36
 | 
     bless $self, $class;  | 
| 
379
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
380
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Open a socket connection at the desired port  | 
| 
381
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
128
 | 
     my %params = (  | 
| 
382
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'LocalHost' => $host,  | 
| 
383
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'LocalPort' => $port,  | 
| 
384
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'Proto'     => 'tcp',  | 
| 
385
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'Listen'    => $max_connect,  | 
| 
386
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'ReuseAddr' => 0,  | 
| 
387
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     );  | 
| 
388
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
389
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Create socket object  | 
| 
390
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
52
 | 
     my $sock = new IO::Socket::INET(%params);  | 
| 
391
 | 
1
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
1600
 | 
     if (!$sock) {  | 
| 
392
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # Port is already in use -- client will connect to it instead  | 
| 
393
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         $verbose and print "[Port $port already in use]\n";  | 
| 
394
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         exit;  | 
| 
395
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
396
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
     $self->{'socket'} = $sock;  | 
| 
397
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
398
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Create select set for reading  | 
| 
399
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
57
 | 
     $self->{'select'} = new IO::Select($sock);  | 
| 
400
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
401
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Create the GUI  | 
| 
402
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1337
 | 
     require Tk;  | 
| 
403
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $verbose and print "[Logo server v$VERSION on '$host']\n";  | 
| 
404
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $mw = Tk::MainWindow->new(-title => $title);  | 
| 
405
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->{'mw'} = $mw;  | 
| 
406
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
407
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Allow easy dismissal of the GUI  | 
| 
408
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     $mw->bind("" => sub { $self->server_exit });  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
409
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
410
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Create a new canvas  | 
| 
411
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->clear_screen($w, $h, $bg);  | 
| 
412
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
413
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Manage the GUI  | 
| 
414
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->{'repid'} = $self->set_update($update);  | 
| 
415
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     Tk::MainLoop();  | 
| 
416
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
417
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
418
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
419
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub server_exit {  | 
| 
420
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self) = @_;  | 
| 
421
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $mw = $self->{'mw'};  | 
| 
422
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
423
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $sel      = $self->{'select'};  | 
| 
424
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $sock     = $self->{'socket'};  | 
| 
425
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $pclients = $self->{'clients'};  | 
| 
426
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $pnames   = $self->{'names'};  | 
| 
427
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
428
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     close $sock;  | 
| 
429
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
430
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     foreach my $name (keys %$pnames) {  | 
| 
431
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $pclient = $pnames->{$name};  | 
| 
432
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $fh = $pclient->{'fh'};  | 
| 
433
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $self->server_remove_client($pclients, $sel, $fh);  | 
| 
434
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
435
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
436
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Shouldn't ever get here, since when the last client exited,  | 
| 
437
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # the server should have already gone away. But just in case ...  | 
| 
438
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     #  | 
| 
439
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $mw->destroy();  | 
| 
440
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     exit;  | 
| 
441
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
442
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
443
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
444
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub set_update {  | 
| 
445
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $update) = @_;  | 
| 
446
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     ($update < 1)    and $update = 1;  | 
| 
447
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     ($update > 1000) and $update = 1000;  | 
| 
448
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->{'update'} = $update;  | 
| 
449
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $mw = $self->{'mw'};  | 
| 
450
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my $id = $mw->repeat($update => sub { $self->server_loop() });  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
451
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return $id;  | 
| 
452
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
453
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
454
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
455
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub server_loop {  | 
| 
456
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self) = @_;  | 
| 
457
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
458
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Increment tick count  | 
| 
459
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     ++$self->{'nticks'};  | 
| 
460
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
461
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Get data from the object  | 
| 
462
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $sel      = $self->{'select'};  | 
| 
463
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $sock     = $self->{'socket'};  | 
| 
464
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $pclients = $self->{'clients'};  | 
| 
465
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
466
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Handle each pending socket  | 
| 
467
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my @readable = $sel->can_read(0);  | 
| 
468
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     foreach my $rh (@readable) {  | 
| 
469
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         if ($rh == $sock) {  | 
| 
470
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # The main socket means a new incoming connection.  | 
| 
471
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $self->server_add_client($rh, $pclients);  | 
| 
472
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } else {  | 
| 
473
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # Service the socket  | 
| 
474
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             my $text = <$rh>;  | 
| 
475
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
476
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             if (defined($text)) {  | 
| 
477
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 # Process command  | 
| 
478
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 chomp $text;  | 
| 
479
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 my $pc = $pclients->{$rh};  | 
| 
480
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 if ($text eq '?') {  | 
| 
 
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
481
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                     $self->server_query($pc);  | 
| 
482
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 } elsif ($text =~ s/^=//) {  | 
| 
483
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                     $self->server_command($pc, $text);  | 
| 
484
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 }  | 
| 
485
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             } else {  | 
| 
486
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 # Socket was closed -- remove the client  | 
| 
487
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $self->server_remove_client($pclients, $sel, $rh);  | 
| 
488
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
489
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
490
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
491
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
492
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
493
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
494
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub server_add_client {  | 
| 
495
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $rh, $pclients) = @_;  | 
| 
496
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
497
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Accept the client connect and add the new socket  | 
| 
498
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $sel = $self->{'select'};  | 
| 
499
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $ns  = $rh->accept();  | 
| 
500
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $sel->add($ns);  | 
| 
501
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
502
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $verbose = $self->{'verbose'};  | 
| 
503
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $peer = getpeername($ns);  | 
| 
504
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my ($port, $iaddr) = unpack_sockaddr_in($peer);  | 
| 
505
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $remote = inet_ntoa($iaddr);  | 
| 
506
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
507
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Get the client handshake, and send back its unique ID  | 
| 
508
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     chomp(my $text = <$ns>);  | 
| 
509
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     ($text =~ /^:(.*)$/) or die "Bad header, expected ':[name]', got '$text'";  | 
| 
510
 | 
0
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     my $name = $1 || "";  | 
| 
511
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
512
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $id = $self->{'total'} + 1;  | 
| 
513
 | 
0
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     $name ||= "CLIENT$id";  | 
| 
514
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     print $ns "$id:$name\n";  | 
| 
515
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
516
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $pc = $pclients->{$ns} = {  | 
| 
517
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'id'      => $id,  | 
| 
518
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'fh'      => $ns,  | 
| 
519
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'name'    => $name,  | 
| 
520
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'remote'  => $remote,  | 
| 
521
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     };  | 
| 
522
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
523
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Assign defaults to client-specific variables  | 
| 
524
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     map { $pc->{$_} = $pclient_vars->{$_} } (keys %$pclient_vars);  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
525
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
526
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Create the 'turtle' object  | 
| 
527
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->create_turtle($pc);  | 
| 
528
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
529
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Increment the number of connections and the total connection count  | 
| 
530
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     ++$self->{'count'};  | 
| 
531
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     ++$self->{'total'};  | 
| 
532
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
533
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Add the client's name  | 
| 
534
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $verbose and print "[Added socket $id => '$name']\n";  | 
| 
535
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->{'names'}->{$name} = $pclients->{$ns};  | 
| 
536
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
537
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
538
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
539
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub server_remove_client {  | 
| 
540
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pclients, $sel, $fh) = @_;;  | 
| 
541
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $verbose = $self->{'verbose'};  | 
| 
542
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $pc = $pclients->{$fh};  | 
| 
543
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $name = $pc->{'name'};  | 
| 
544
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $id   = $pc->{'id'};  | 
| 
545
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $sel->remove($fh);  | 
| 
546
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     close($fh);  | 
| 
547
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     delete $pclients->{$fh};  | 
| 
548
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
549
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Remove the client's name  | 
| 
550
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $pnames = $self->{'names'};  | 
| 
551
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     delete $pnames->{$name};  | 
| 
552
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
553
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Remove the client's turtle  | 
| 
554
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $cv    = $self->{'canvas'};  | 
| 
555
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $ptids = $pc->{'turtle'}->{'tids'};  | 
| 
556
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     ($ptids || 0) and map { $cv->delete($_) } @$ptids;  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
557
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
558
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Decrement the global client count  | 
| 
559
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     --$self->{'count'};  | 
| 
560
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $verbose and print "[Closed socket $id '$name']\n";  | 
| 
561
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
562
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Exit the server if this is the last connection  | 
| 
563
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     if (0 == $self->{'count'} and $self->{'total'} > 0) {  | 
| 
564
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $verbose and print "[Final client closed -- exiting]\n";  | 
| 
565
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $self->{'mw'}->destroy();  | 
| 
566
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         exit;  | 
| 
567
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
568
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
569
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
570
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
571
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub server_query {  | 
| 
572
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pc) = @_;  | 
| 
573
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $text = "";  | 
| 
574
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     foreach my $param (@$pserver_vars) {  | 
| 
575
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $text and $text .= ",";  | 
| 
576
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $val = $self->{$param};  | 
| 
577
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $text .= "$param=$val";  | 
| 
578
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
579
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $fh = $pc->{'fh'};  | 
| 
580
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     printf $fh "?$text\n";  | 
| 
581
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
582
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
583
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
584
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub server_command {  | 
| 
585
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pc, $cmdstr) = @_;  | 
| 
586
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $id = $pc->{'id'};  | 
| 
587
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $pc->{'lastcmd'} = $cmdstr;  | 
| 
588
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
589
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $debug = $pc->{'debug'};  | 
| 
590
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $debug and print "Command<$id>: '$cmdstr'\n";  | 
| 
591
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
592
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my @args = split(/\s+/, $cmdstr);  | 
| 
593
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $cmd = shift @args;  | 
| 
594
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
595
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Allow "noop" command to just query current client parameters  | 
| 
596
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     if ($cmdstr eq 'noop') {  | 
| 
597
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         return $self->server_reply($pc);  | 
| 
598
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
599
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
600
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Resolve any command alias  | 
| 
601
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     while (defined($palias->{$cmd})) {  | 
| 
602
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $pcmd = $palias->{$cmd};  | 
| 
603
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $newcmd = $pcmd->[0];  | 
| 
604
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $cmd = $newcmd;  | 
| 
605
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
606
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     unshift @args, $cmd;  | 
| 
607
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
608
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Execute one command if single-stepping is on  | 
| 
609
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     if ($pc->{'step'}) {  | 
| 
610
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $go = $self->server_single_step($pc, $cmd, [ @args ]);  | 
| 
611
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $go or return $self->server_reply($pc);  | 
| 
612
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
613
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
614
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Client variables  | 
| 
615
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     if (defined($pclient_vars->{$cmd})) {  | 
| 
616
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         return $self->server_set_variable($pc, @args);  | 
| 
617
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
618
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
619
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Find command in dispatch table  | 
| 
620
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $method = $pmethods->{$cmd};  | 
| 
621
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     defined($method) and return $self->$method($pc, @args);  | 
| 
622
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
623
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Return acknowledgment  | 
| 
624
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->server_error($pc, "Unknown command '$cmd'");  | 
| 
625
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
626
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
627
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
628
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub server_set_variable {  | 
| 
629
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pc, $param, $val) = @_;  | 
| 
630
 | 
0
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     $pc->{$param} = $val || 0;  | 
| 
631
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $pc->{'debug'} and print "Variable '$param' set to '$val'\n";  | 
| 
632
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->server_reply($pc);  | 
| 
633
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
634
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
635
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
636
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub server_single_step {  | 
| 
637
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pc, $cmd, $pargs) = @_;  | 
| 
638
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $cmdstr = join(" ", @$pargs);  | 
| 
639
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     print "Step>  [$cmdstr]  Execute {y|n|c}? [y]";  | 
| 
640
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     chomp(my $ans = );  | 
| 
641
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     ($ans =~ /^[cC]/) and $pc->{'step'} = 0;  | 
| 
642
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return ($ans =~ /^[nN]/)? 0: 1;  | 
| 
643
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
644
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
645
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
646
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub server_reply {  | 
| 
647
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pc) = @_;  | 
| 
648
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $fh = $pc->{'fh'};  | 
| 
649
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $turtle = $pc->{'turtle'};  | 
| 
650
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $text = "";  | 
| 
651
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     foreach my $param (@$pinfo) {  | 
| 
652
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $val = $turtle->{$param};  | 
| 
653
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $text and $text .= ",";  | 
| 
654
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $text .= "$param=$val";  | 
| 
655
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
656
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     printf $fh "=$text\n";  | 
| 
657
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
658
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
659
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
660
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub server_error {  | 
| 
661
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pc, $msg) = @_;  | 
| 
662
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $fh = $pc->{'fh'};  | 
| 
663
 | 
0
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     $msg ||= "";  | 
| 
664
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     print $fh "!$msg\n";  | 
| 
665
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
666
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
667
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
668
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub create_turtle {  | 
| 
669
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pc, $from) = @_;  | 
| 
670
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
671
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $turtle = {  | 
| 
672
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'pen'    => 0,          # Pen state:  0 = 'up', 1 = 'down'  | 
| 
673
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'color'  => $d_color,   # Pen color (also turtle color)  | 
| 
674
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'size'   => $d_psize,   # Pen size (thickness)  | 
| 
675
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'xdim'   => $d_txdim,   # Turtle x-dimension  | 
| 
676
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'ydim'   => $d_tydim,   # Turtle y-dimension  | 
| 
677
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'dist'   => 0,          # Last distance traveled (used as default)  | 
| 
678
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'show'   => 1,          # Turtle starts out visible  | 
| 
679
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'wrap'   => 0,          # Normal wrap (= no wrap)  | 
| 
680
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     };  | 
| 
681
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
682
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Use old turtle as a reference  | 
| 
683
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     if ($from || 0) {  | 
| 
684
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         map { $turtle->{$_} = $from->{$_} } (keys %$from);  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
685
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
686
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
687
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->home_turtle($pc, $turtle);  | 
| 
688
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->draw_turtle($pc, $turtle);  | 
| 
689
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
690
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
691
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
692
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub home_turtle {  | 
| 
693
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pc, $turtle) = @_;  | 
| 
694
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $cv     = $self->{'canvas'};  | 
| 
695
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $width  = $cv->cget(-width);  | 
| 
696
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $height = $cv->cget(-height);  | 
| 
697
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
698
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $x = int($width  / 2);  | 
| 
699
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $y = int($height / 2);  | 
| 
700
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
701
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $turtle->{'x'}     = $x;  | 
| 
702
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $turtle->{'y'}     = $y;  | 
| 
703
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $turtle->{'angle'} = 0;  | 
| 
704
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
705
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
706
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
707
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub reset_turtle {  | 
| 
708
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pc, $cmd) = @_;  | 
| 
709
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $turtle = $pc->{'turtle'};  | 
| 
710
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->home_turtle($pc, $turtle);  | 
| 
711
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->draw_turtle($pc, $turtle);  | 
| 
712
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->server_reply($pc);  | 
| 
713
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
714
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
715
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
716
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub draw_turtle {  | 
| 
717
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pc, $turtle) = @_;  | 
| 
718
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
719
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Erase old turtle if one exists  | 
| 
720
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $cv    = $self->{'canvas'};  | 
| 
721
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $ptids = $pc->{'turtle'}->{'tids'};  | 
| 
722
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     if ($ptids || 0) {  | 
| 
723
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         map { $cv->delete($_) } @$ptids;  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
724
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $pc->{'turtle'}->{'tids'} = 0;  | 
| 
725
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
726
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
727
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Create turtle parameters  | 
| 
728
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $cvbg   = $cv->cget(-bg);  | 
| 
729
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $x      = $turtle->{'x'};  | 
| 
730
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $y      = $turtle->{'y'};  | 
| 
731
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $angle  = $turtle->{'angle'};  | 
| 
732
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $color  = $turtle->{'color'};  | 
| 
733
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $show   = $turtle->{'show'};  | 
| 
734
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $xdim   = $turtle->{'xdim'};  | 
| 
735
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $ydim   = $turtle->{'ydim'};  | 
| 
736
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
737
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     if ($turtle->{'show'}) {  | 
| 
738
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # Assign points, rotate them, and plot the turtle  | 
| 
739
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $ppts = [ $x, $y, $x-$xdim, $y, $x, $y-2*$ydim, $x+$xdim, $y ];  | 
| 
740
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $ppts = $self->rotate($x, $y, $angle, $ppts);  | 
| 
741
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my @args = (-fill => $cvbg, -outline => $color);  | 
| 
742
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $tid = $cv->createPolygon(@$ppts, @args);  | 
| 
743
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $turtle->{'tids'} = [ $tid ];  | 
| 
744
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $pc->{'turtle'} = $turtle;  | 
| 
745
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
746
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # If the pen is down, draw a circle around the current point  | 
| 
747
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $ppts = [ ];  | 
| 
748
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         if ($turtle->{'pen'}) {  | 
| 
749
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $ppts = [ $x-3, $y-3, $x+3, $y+3 ];  | 
| 
750
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $tid = $cv->createOval(@$ppts, -outline => $color);  | 
| 
751
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             push @{$turtle->{'tids'}}, $tid;  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
752
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
753
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
754
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
755
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Save the turtle to this client's data  | 
| 
756
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $pc->{'turtle'} = $turtle;  | 
| 
757
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
758
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
759
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
760
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub change_update {  | 
| 
761
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pc, $cmd, $update) = @_;  | 
| 
762
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $repid = $self->{'repid'};  | 
| 
763
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     ($repid || 0) and $repid->cancel();  | 
| 
764
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->{'repid'} = $self->set_update($update);  | 
| 
765
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->server_reply($pc);  | 
| 
766
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
767
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
768
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
769
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub set_wrap_value {  | 
| 
770
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pc, $cmd, $wrap) = @_;  | 
| 
771
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     defined($wrap) or return $self->syntax_error($pc);  | 
| 
772
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $wrap = int($wrap);  | 
| 
773
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     if ($wrap < 0 || $wrap > 2) {  | 
| 
774
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         return $self->server_error($pc, "Invalid wrap value '$wrap'");  | 
| 
775
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
776
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $turtle = $pc->{'turtle'};  | 
| 
777
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $turtle->{'wrap'} = $wrap;  | 
| 
778
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->server_reply($pc);  | 
| 
779
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
780
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
781
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
782
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub modify_canvas {  | 
| 
783
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pc, $cmd, $val) = @_;  | 
| 
784
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
785
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $cv = $self->{'canvas'};  | 
| 
786
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     ($cmd eq 'clear')      and $self->clear_screen();  | 
| 
787
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     ($cmd eq 'width')      and eval {$cv->configure('-wi', $val || $d_width)};  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
788
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     ($cmd eq 'height')     and eval {$cv->configure('-he', $val || $d_height)};  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
789
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     ($cmd eq 'background') and eval {$cv->configure('-bg', $val || $d_bg)};  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
790
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
791
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $pnames = $self->{'names'};  | 
| 
792
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     foreach my $name (keys %$pnames) {  | 
| 
793
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $pclient = $pnames->{$name};  | 
| 
794
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $turtle = $pclient->{'turtle'};  | 
| 
795
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
         if ($cmd eq 'w' or $cmd eq 'h') {  | 
| 
 
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
796
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # Have to recreate the turtle  | 
| 
797
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $self->create_turtle($pclient);  | 
| 
798
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } elsif ($cmd eq 'bg') {  | 
| 
799
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # Have to redraw the turtle  | 
| 
800
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $self->draw_turtle($pclient, $turtle);  | 
| 
801
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
802
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
803
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
804
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->server_reply($pc);  | 
| 
805
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
806
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
807
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
808
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub clear_screen {  | 
| 
809
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $width, $height, $bg) = @_;  | 
| 
810
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
811
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Clear any old canvas  | 
| 
812
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $oldcv = $self->{'canvas'};  | 
| 
813
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     if ($oldcv || 0) {  | 
| 
814
 | 
0
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
         $width  ||= $oldcv->cget(-width);  | 
| 
815
 | 
0
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
         $height ||= $oldcv->cget(-height);  | 
| 
816
 | 
0
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
         $bg     ||= $oldcv->cget(-bg);  | 
| 
817
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $oldcv->packForget();  | 
| 
818
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
819
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       | 
| 
820
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Create a new canvas  | 
| 
821
 | 
0
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     $width  ||= $d_width;  | 
| 
822
 | 
0
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     $height ||= $d_height;  | 
| 
823
 | 
0
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     $bg     ||= $d_bg;  | 
| 
824
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $mw = $self->{'mw'};  | 
| 
825
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my @opts = (-bg => $bg, -width => $width, -height => $height);  | 
| 
826
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $cv = $mw->Canvas(@opts);  | 
| 
827
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $cv->pack(-expand => 1, -fill => 'both');  | 
| 
828
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->{'canvas'} = $cv;  | 
| 
829
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
830
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # For each client, draw its turtle  | 
| 
831
 | 
0
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     my $pclients = $self->{'clients'} || { };  | 
| 
832
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     foreach my $pc (values %$pclients) {  | 
| 
833
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $turtle = $pc->{'turtle'};  | 
| 
834
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $self->create_turtle($pc, $turtle);  | 
| 
835
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
836
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
837
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
838
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
839
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub rotate {  | 
| 
840
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $x, $y, $angle, $ppoints) = @_;  | 
| 
841
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     for (my $i = 0; $i < @$ppoints; $i += 2) {  | 
| 
842
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $ppoints->[$i]   -= $x;  | 
| 
843
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $ppoints->[$i+1] -= $y;  | 
| 
844
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
845
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $ppolar = $self->rect_to_polar($ppoints);  | 
| 
846
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     for (my $i = 1; $i <= @$ppolar; $i += 2) {  | 
| 
847
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $ppolar->[$i] = ($ppolar->[$i] + $angle) % 360;  | 
| 
848
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
849
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $ppoints = $self->polar_to_rect($ppolar);  | 
| 
850
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     for (my $i = 0; $i < @$ppoints; $i += 2) {  | 
| 
851
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $ppoints->[$i]   += $x;  | 
| 
852
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $ppoints->[$i+1] += $y;  | 
| 
853
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
854
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return $ppoints;  | 
| 
855
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
856
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
857
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
858
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub calculate_endpoint {  | 
| 
859
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $x, $y, $angle, $dist) = @_;  | 
| 
860
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $prect = $self->polar_to_rect([ $dist, $angle ]);  | 
| 
861
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my ($x1, $y1) = @$prect;  | 
| 
862
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $x1 += $x;  | 
| 
863
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $y1 += $y;  | 
| 
864
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return ($x1, $y1);  | 
| 
865
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
866
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
867
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
868
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub rect_to_polar {  | 
| 
869
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $ppoints) = @_;  | 
| 
870
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $ppolar = ( );  | 
| 
871
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     while (@$ppoints > 1) {  | 
| 
872
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $x = shift @$ppoints;  | 
| 
873
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $y = shift @$ppoints;  | 
| 
874
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $r = sqrt($x ** 2 + $y ** 2);  | 
| 
875
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $t = $self->rad_to_deg(atan2($y, $x));  | 
| 
876
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         push @$ppolar, $r, $t;  | 
| 
877
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
878
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return $ppolar;  | 
| 
879
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
880
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
881
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
882
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub polar_to_rect {  | 
| 
883
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $ppoints) = @_;  | 
| 
884
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $prect = [ ];  | 
| 
885
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     while (@$ppoints > 1) {  | 
| 
886
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $r = shift @$ppoints;  | 
| 
887
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $t = $self->deg_to_rad(shift @$ppoints);  | 
| 
888
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $x = $r * cos($t);  | 
| 
889
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $y = $r * sin($t);  | 
| 
890
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         push @$prect, $x, $y;  | 
| 
891
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
892
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return $prect;  | 
| 
893
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
894
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
895
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
896
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub deg_to_rad {  | 
| 
897
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $degrees) = @_;  | 
| 
898
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $radians = $degrees * PI / 180;  | 
| 
899
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     ($radians < 0) and $radians += 6.283185307;  | 
| 
900
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return $radians;  | 
| 
901
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
902
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
903
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
904
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub rad_to_deg {  | 
| 
905
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $radians) = @_;  | 
| 
906
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $degrees = $radians * 180 / PI;  | 
| 
907
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     ($degrees < 0) and $degrees += 360;  | 
| 
908
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return $degrees;  | 
| 
909
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
910
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
911
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
912
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub show_turtle {  | 
| 
913
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pc, $cmd) = @_;  | 
| 
914
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $b_show = ($cmd eq 'st')? 1: 0;  | 
| 
915
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $turtle = $pc->{'turtle'};  | 
| 
916
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $turtle->{'show'} = $b_show;  | 
| 
917
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->draw_turtle($pc, $turtle);  | 
| 
918
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->server_reply($pc);  | 
| 
919
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
920
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
921
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
922
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub change_color {  | 
| 
923
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pc, $cmd, $color) = @_;  | 
| 
924
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     defined($color) or return $self->syntax_error($pc);  | 
| 
925
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
926
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Allow a random color  | 
| 
927
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     if (($color || "") eq 'random') {  | 
| 
928
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $color = sprintf "#%02x%02x%02x", rand 256, rand 256, rand 256;  | 
| 
929
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
930
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
931
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $turtle = $pc->{'turtle'};  | 
| 
932
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $turtle->{'color'} = $color;  | 
| 
933
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->draw_turtle($pc, $turtle);  | 
| 
934
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->server_reply($pc);  | 
| 
935
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
936
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
937
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
938
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub change_pen_state {  | 
| 
939
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pc, $cmd) = @_;  | 
| 
940
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $state = ($cmd eq 'pendown')? 1: 0;  | 
| 
941
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $turtle = $pc->{'turtle'};  | 
| 
942
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $turtle->{'pen'} = $state;  | 
| 
943
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->draw_turtle($pc, $turtle);  | 
| 
944
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->server_reply($pc);  | 
| 
945
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
946
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
947
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
948
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub change_pen_size {  | 
| 
949
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pc, $cmd, $size, @args) = @_;  | 
| 
950
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $turtle = $pc->{'turtle'};  | 
| 
951
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
952
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Allow a random pen size  | 
| 
953
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     if (($size || "") eq "random") {  | 
| 
954
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $min = $args[0];  | 
| 
955
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $max = $args[1];  | 
| 
956
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         defined($min) or return $self->syntax_error($pc);  | 
| 
957
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         defined($max) or return $self->syntax_error($pc);  | 
| 
958
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $size = $min + rand($max - $min);  | 
| 
959
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
960
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
961
 | 
0
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     $size ||= $d_psize;  | 
| 
962
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $turtle->{'size'} = $size;  | 
| 
963
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->server_reply($pc);  | 
| 
964
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
965
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
966
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
967
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub syntax_error {  | 
| 
968
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pc) = @_;  | 
| 
969
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $cmd = $pc->{'lastcmd'};  | 
| 
970
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->server_error($pc, "Syntax error in '$cmd'");  | 
| 
971
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
972
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
973
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
974
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub turn_turtle {  | 
| 
975
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pc, $cmd, $newang, $arg0, $arg1) = @_;  | 
| 
976
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
977
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $turtle = $pc->{'turtle'};  | 
| 
978
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $angle  = $turtle->{'angle'};  | 
| 
979
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
980
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Allow a random angle of turn  | 
| 
981
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     if (($newang || "") eq 'random') {  | 
| 
982
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         defined($arg0) or return $self->syntax_error($pc);  | 
| 
983
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         defined($arg1) or return $self->syntax_error($pc);  | 
| 
984
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $newang = $arg0 + rand($arg1 - $arg0);  | 
| 
985
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
986
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
987
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Make angles default to right angles  | 
| 
988
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     defined($newang) or $newang = 90;  | 
| 
989
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
990
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Assign the angle  | 
| 
991
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     ($cmd eq 'left')  and $angle = $angle - $newang;  | 
| 
992
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     ($cmd eq 'right') and $angle = $angle + $newang;  | 
| 
993
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     ($cmd eq 'seth')  and $angle = $newang;  | 
| 
994
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
995
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Normalize the angle  | 
| 
996
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     while ($angle < 0)   { $angle += 360 }  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
997
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     while ($angle > 360) { $angle -= 360 }  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
998
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
999
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $turtle->{'angle'} = $angle;  | 
| 
1000
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->draw_turtle($pc, $turtle);  | 
| 
1001
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->server_reply($pc);  | 
| 
1002
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
1003
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1004
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1005
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub move_turtle {  | 
| 
1006
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pc, $cmd, $dist, $arg0, $arg1) = @_;  | 
| 
1007
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $turtle = $pc->{'turtle'};  | 
| 
1008
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $angle  = $turtle->{'angle'};  | 
| 
1009
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $wrap   = $turtle->{'wrap'};  | 
| 
1010
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1011
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Allow a random distance  | 
| 
1012
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     if (($dist || "") eq 'random') {  | 
| 
1013
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         defined($arg0) or return $self->syntax_error($pc);  | 
| 
1014
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         defined($arg1) or return $self->syntax_error($pc);  | 
| 
1015
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $dist = $arg0 + rand($arg1 - $arg0);  | 
| 
1016
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
1017
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1018
 | 
0
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     $dist ||= $turtle->{'dist'};  | 
| 
1019
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     (0 == $dist) and return $self->syntax_error($pc);  | 
| 
1020
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $turtle->{'dist'} = $dist;  | 
| 
1021
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     ($cmd eq 'forward')  and $angle = ($angle + 270) % 360;  | 
| 
1022
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     ($cmd eq 'backward') and $angle = ($angle + 90)  % 360;  | 
| 
1023
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my ($x0, $y0) = ($turtle->{'x'}, $turtle->{'y'});  | 
| 
1024
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my ($x1, $y1);  | 
| 
1025
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     if ($cmd eq 'setx' or $cmd eq 'sety' or $cmd eq 'setxy') {  | 
| 
 
 | 
 
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1026
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         if ($cmd eq 'setxy') {  | 
| 
1027
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             defined($dist) or return $self->syntax_error($pc);  | 
| 
1028
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             defined($arg0) or return $self->syntax_error($pc);  | 
| 
1029
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             ($x1, $y1) = ($dist, $arg0);  | 
| 
1030
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } else {  | 
| 
1031
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             defined($dist) or return $self->syntax_error($pc);  | 
| 
1032
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             ($x1, $y1) = ($x0, $y0);  | 
| 
1033
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             ($x1, $y1) = ($cmd eq 'setx')? ($dist, $y0): ($x0, $dist);  | 
| 
1034
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
1035
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
1036
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         ($x1, $y1) = $self->calculate_endpoint($x0, $y0, $angle, $dist);  | 
| 
1037
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
1038
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1039
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my @args = ($pc, $x0, $y0, $x1, $y1);  | 
| 
1040
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return $self->move_turtle_reflect(@args) if (2 == $wrap);  | 
| 
1041
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return $self->move_turtle_torus(@args)   if (1 == $wrap);  | 
| 
1042
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return $self->move_turtle_normal(@args); # Assume wrap == 0  | 
| 
1043
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
1044
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1045
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1046
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub move_turtle_normal {  | 
| 
1047
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pc, $x0, $y0, $x1, $y1) = @_;  | 
| 
1048
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $turtle = $pc->{'turtle'};  | 
| 
1049
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $pen    = $turtle->{'pen'};  | 
| 
1050
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $size   = $turtle->{'size'};  | 
| 
1051
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $color  = $turtle->{'color'};  | 
| 
1052
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1053
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->line($pen, $x0, $y0, $x1, $y1, $color, $size);  | 
| 
1054
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->move($pc, $x1, $y1);  | 
| 
1055
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->server_reply($pc);  | 
| 
1056
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
1057
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1058
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1059
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub move_turtle_torus {  | 
| 
1060
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pc, $x0, $y0, $x1, $y1) = @_;  | 
| 
1061
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $turtle = $pc->{'turtle'};  | 
| 
1062
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $pen    = $turtle->{'pen'};  | 
| 
1063
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $size   = $turtle->{'size'};  | 
| 
1064
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $color  = $turtle->{'color'};  | 
| 
1065
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1066
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Calculate (dx, dy), which don't change for torus behavior  | 
| 
1067
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my ($dx, $dy) = ($x1 - $x0, $y1 - $y0);  | 
| 
1068
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1069
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     while (!$self->contained($x1, $y1)) {  | 
| 
1070
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $height = $self->{'height'};  | 
| 
1071
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $width  = $self->{'width'};  | 
| 
1072
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         if (abs($dx) < 0.0000001) {  | 
| 
 
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1073
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # Vertical line  | 
| 
1074
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             my $yb = ($y1 < $y0)? 0: $height;  | 
| 
1075
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $self->line($pen, $x0, $y0, $x0, $yb, $color, $size);  | 
| 
1076
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             ($y0, $y1) = $yb? (0, $y1-$height): ($height, $y1+$height);  | 
| 
1077
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $self->move($pc, $x0, $y0);  | 
| 
1078
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } elsif (abs($dy) < 0.0000001) {  | 
| 
1079
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # Horizontal line  | 
| 
1080
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             my $xb = ($x1 < $x0)? 0: $width;  | 
| 
1081
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $self->line($pen, $x0, $y0, $xb, $y0, $color, $size);  | 
| 
1082
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             ($x0, $x1) = $xb? (0, $x1-$width): ($width, $x1+$width);  | 
| 
1083
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $self->move($pc, $x0, $y0);  | 
| 
1084
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } else {  | 
| 
1085
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # Diagonal line  | 
| 
1086
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             my $m = $dy / $dx;  | 
| 
1087
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             my $b = $y1 - ($m * $x1);  | 
| 
1088
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             my $xb = ($y1 > $y0)? (($height - $b) / $m): (-$b / $m);  | 
| 
1089
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             my $yb = ($x1 > $x0)? (($m * $width) + $b):  $b;  | 
| 
1090
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             my ($xn, $yn) = ($xb, $yb);  | 
| 
1091
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
             my $crossx = ($xb > 0 and $xb < $width)?  1: 0;  | 
| 
1092
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
             my $crossy = ($yb > 0 and $yb < $height)? 1: 0;  | 
| 
1093
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
             if ($crossx and !$crossy) {  | 
| 
 
 | 
 
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1094
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 # Line intercepts x-axis  | 
| 
1095
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $yb = ($y1 > $y0)? $height: 0;  | 
| 
1096
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $yn = $height - $yb;  | 
| 
1097
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $y1 = ($y1 > $y0)? $y1 - $height: $y1 + $height;  | 
| 
1098
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             } elsif ($crossy and !$crossx) {  | 
| 
1099
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 # Line intercepts y-axis  | 
| 
1100
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $xb = ($x1 > $x0)? $width: 0;  | 
| 
1101
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $xn = $width - $xb;  | 
| 
1102
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $x1 = ($x1 > $x0)? $x1 - $width: $x1 + $width;  | 
| 
1103
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             } else {  | 
| 
1104
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 # Line intercepts both axes  | 
| 
1105
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $xb = ($x1 > $x0)? $width:  0;  | 
| 
1106
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $yb = ($y1 > $y0)? $height: 0;  | 
| 
1107
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 ($xn, $yn) = ($width - $xb, $height - $yb);  | 
| 
1108
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $x1 = ($x1 > $x0)? $x1 - $width:  $x1 + $width;  | 
| 
1109
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $y1 = ($y1 > $y0)? $y1 - $height: $y1 + $height;  | 
| 
1110
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
1111
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1112
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $self->line($pen, $x0, $y0, $xb, $yb, $color, $size);  | 
| 
1113
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             ($x0, $y0) = ($xn, $yn);  | 
| 
1114
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $self->move($pc, $x0, $y0);  | 
| 
1115
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
1116
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
1117
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1118
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Back within canvas  | 
| 
1119
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return $self->move_turtle_normal($pc, $x0, $y0, $x1, $y1);  | 
| 
1120
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
1121
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1122
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1123
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub move_turtle_reflect {  | 
| 
1124
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pc, $x0, $y0, $x1, $y1) = @_;  | 
| 
1125
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $turtle = $pc->{'turtle'};  | 
| 
1126
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $angle  = $turtle->{'angle'};  | 
| 
1127
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $pen    = $turtle->{'pen'};  | 
| 
1128
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $size   = $turtle->{'size'};  | 
| 
1129
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $color  = $turtle->{'color'};  | 
| 
1130
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1131
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     while (!$self->contained($x1, $y1)) {  | 
| 
1132
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # Calculate (dx, dy), which change for reflection behavior  | 
| 
1133
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my ($dx, $dy) = ($x1 - $x0, $y1 - $y0);  | 
| 
1134
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1135
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $height = $self->{'height'};  | 
| 
1136
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $width  = $self->{'width'};  | 
| 
1137
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         if (abs($dx) < 0.0000001) {  | 
| 
 
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1138
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # Vertical line  | 
| 
1139
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             my $yb = ($y1 < $y0)? 0: $height;  | 
| 
1140
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $self->line($pen, $x0, $y0, $x0, $yb, $color, $size);  | 
| 
1141
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $y0 = $yb;  | 
| 
1142
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $y1 = ($y1 < $y0)? (- $y1): (2 * $height) - $y1;  | 
| 
1143
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $self->move($pc, $x0, $y0);  | 
| 
1144
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $angle = $self->adjust_angle($pc, 180 - $angle);  | 
| 
1145
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } elsif (abs($dy) < 0.0000001) {  | 
| 
1146
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # Horizontal line  | 
| 
1147
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             my $xb = ($x1 < $x0)? 0: $width;  | 
| 
1148
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $self->line($pen, $x0, $y0, $xb, $y0, $color, $size);  | 
| 
1149
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $x0 = $xb;  | 
| 
1150
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $x1 = ($x1 < $x0)? (- $x1): (2 * $width) - $x1;  | 
| 
1151
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $self->move($pc, $x0, $y0);  | 
| 
1152
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $angle = $self->adjust_angle($pc, 360 - $angle);  | 
| 
1153
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } else {  | 
| 
1154
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # Diagonal line  | 
| 
1155
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             my $m = $dy / $dx;  | 
| 
1156
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             my $b = $y1 - ($m * $x1);  | 
| 
1157
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             my $xb = ($y1 > $y0)? (($height - $b) / $m): (-$b / $m);  | 
| 
1158
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             my $yb = ($x1 > $x0)? (($m * $width) + $b):  $b;  | 
| 
1159
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
             my $crossx = ($xb > 0 and $xb < $width)?  1: 0;  | 
| 
1160
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
             my $crossy = ($yb > 0 and $yb < $height)? 1: 0;  | 
| 
1161
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
             if ($crossx and !$crossy) {  | 
| 
 
 | 
 
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1162
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 # Line intercepts x-axis  | 
| 
1163
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $yb = ($y1 > $y0)? $height: 0;  | 
| 
1164
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $y1 = ($y1 > $y0)? (2 * $height - $y1): (- $y1);  | 
| 
1165
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             } elsif ($crossy and !$crossx) {  | 
| 
1166
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 # Line intercepts y-axis  | 
| 
1167
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $xb = ($x1 > $x0)? $width: 0;  | 
| 
1168
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $x1 = ($x1 > $x0)? (2 * $width - $x1): (- $x1);  | 
| 
1169
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             } else {  | 
| 
1170
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 # Line intercepts both axes  | 
| 
1171
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $xb = ($x1 > $x0)? $width:  0;  | 
| 
1172
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $yb = ($y1 > $y0)? $height: 0;  | 
| 
1173
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $x1 = ($x1 > $x0)? (2 * $width  - $x1): (- $x1);  | 
| 
1174
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $y1 = ($y1 > $y0)? (2 * $height - $y1): (- $y1);  | 
| 
1175
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
1176
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1177
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $self->line($pen, $x0, $y0, $xb, $yb, $color, $size);  | 
| 
1178
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             ($x0, $y0) = ($xb, $yb);  | 
| 
1179
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $self->move($pc, $x0, $y0);  | 
| 
1180
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $angle = $self->adjust_angle($pc, 180 - $angle);  | 
| 
1181
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
1182
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
1183
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1184
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Back within canvas  | 
| 
1185
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return $self->move_turtle_normal($pc, $x0, $y0, $x1, $y1);  | 
| 
1186
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
1187
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1188
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1189
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub adjust_angle {  | 
| 
1190
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pc, $newang) = @_;  | 
| 
1191
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $turtle = $pc->{'turtle'};  | 
| 
1192
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     while ($newang >= 360) {  | 
| 
1193
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $newang -= 360;  | 
| 
1194
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
1195
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     while ($newang < 0) {  | 
| 
1196
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $newang += 360;  | 
| 
1197
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
1198
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $turtle->{'angle'} = $newang;  | 
| 
1199
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->draw_turtle($pc, $turtle);  | 
| 
1200
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return $newang;  | 
| 
1201
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
1202
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1203
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1204
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub line {  | 
| 
1205
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pen, $x0, $y0, $x1, $y1, $color, $size) = @_;  | 
| 
1206
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1207
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Pen is up; no need to draw  | 
| 
1208
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return unless $pen;  | 
| 
1209
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1210
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Get canvas and draw line  | 
| 
1211
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $cv = $self->{'canvas'};  | 
| 
1212
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my @points = ($x0, $y0, $x1, $y1, -fill => $color, -width => $size);  | 
| 
1213
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $cv->createLine(@points);  | 
| 
1214
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
1215
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1216
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1217
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub move {  | 
| 
1218
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $pc, $x, $y) = @_;  | 
| 
1219
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1220
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Set new turtle coordinates and redraw turtle  | 
| 
1221
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $turtle = $pc->{'turtle'};  | 
| 
1222
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $turtle->{'x'} = $x;  | 
| 
1223
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $turtle->{'y'} = $y;  | 
| 
1224
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->draw_turtle($pc, $turtle);  | 
| 
1225
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
1226
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1227
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1228
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub contained {  | 
| 
1229
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
     my ($self, $x1, $y1) = @_;  | 
| 
1230
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1231
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $cv     = $self->{'canvas'};  | 
| 
1232
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $width  = $cv->cget(-width);  | 
| 
1233
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $height = $cv->cget(-height);  | 
| 
1234
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1235
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->{'width'}  = $width;  | 
| 
1236
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->{'height'} = $height;  | 
| 
1237
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1238
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
     return ($x1 < 0 or $x1 > $width or $y1 < 0 or $y1 > $height)? 0: 1;  | 
| 
1239
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
1240
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1241
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1242
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 1;  | 
| 
1243
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1244
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 __END__  |