line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
=head1 NAME |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
Term::Graille::IO |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
Allows user interaction in Graille Applications (or perhaps on any |
6
|
|
|
|
|
|
|
pterminal application). Depends on Term::ReadKey and Time::HiRes; |
7
|
|
|
|
|
|
|
When integrated with Term::Graille::Menu allows a modal drop down menu |
8
|
|
|
|
|
|
|
that |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
=head1 SYNOPSIS |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
my $io=Term::Graille::IO->new(); |
13
|
|
|
|
|
|
|
$io->addAction( # add action for key press |
14
|
|
|
|
|
|
|
"Am", # Am is returned for up arrow |
15
|
|
|
|
|
|
|
{note=>"up arrow:cursor up ", # For drawing a menu |
16
|
|
|
|
|
|
|
proc=>sub{my $self.@args)=@_ ...} # the action |
17
|
|
|
|
|
|
|
} ); |
18
|
|
|
|
|
|
|
$io->run($io,@args); # start trapping keypresses |
19
|
|
|
|
|
|
|
... |
20
|
|
|
|
|
|
|
$io->stop(); # stop |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=cut |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
package Term::Graille::IO; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
our $VERSION=0.09; |
27
|
|
|
|
|
|
|
|
28
|
1
|
|
|
1
|
|
821
|
use strict; use warnings; |
|
1
|
|
|
1
|
|
2
|
|
|
1
|
|
|
|
|
24
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
25
|
|
29
|
1
|
|
|
1
|
|
4
|
use Time::HiRes ("sleep"); # allow fractional sleeps |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
30
|
1
|
|
|
1
|
|
71
|
use utf8; # allow utf characters in print |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
3
|
|
31
|
|
|
|
|
|
|
binmode STDOUT, ":utf8"; |
32
|
1
|
|
|
1
|
|
427
|
use Term::ReadKey; # allow reading from keyboard |
|
1
|
|
|
|
|
2007
|
|
|
1
|
|
|
|
|
61
|
|
33
|
1
|
|
|
1
|
|
6
|
use Term::Graille qw/colour paint printAt cursorAt clearScreen border/; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
627
|
|
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=head1 FUNCTIONS |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
=cut |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=head3 Cnew(%params)> |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
Creates a new IO object for user interaction. |
42
|
|
|
|
|
|
|
Three modes are available; C, means the key presses are captured |
43
|
|
|
|
|
|
|
and not echoed, C |
44
|
|
|
|
|
|
|
using C<$io-EaddMenu($menu), and C when the key presses are |
45
|
|
|
|
|
|
|
read normally |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
=cut |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
sub new{ |
50
|
0
|
|
|
0
|
1
|
|
my $class = shift; # |
51
|
0
|
|
|
|
|
|
my $self={}; |
52
|
0
|
|
|
|
|
|
$self->{actions}={}; |
53
|
0
|
|
|
|
|
|
$self->{refreshRate}=20; |
54
|
0
|
|
|
|
|
|
$self->{key}=""; |
55
|
0
|
|
|
|
|
|
$self->{mode}="free";# one of qw/free menu normal/ |
56
|
0
|
|
|
|
|
|
($self->{terminalWidth},$self->{terminalHeight},$self->{terminalXPixels},$self->{terminalYPixels})=GetTerminalSize; |
57
|
0
|
|
|
|
|
|
bless $self,$class; |
58
|
0
|
|
|
|
|
|
return $self; |
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=head3 CaddMenu($menu,$trigger)> |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
Uses a topbar dropdown menu of class Term::Graille::Menu. If C<$trigger> is specified |
64
|
|
|
|
|
|
|
that activates or deactivates the menu; if not specified the 'm' key activates the menu. |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
=cut |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
sub addMenu{ |
69
|
0
|
|
|
0
|
1
|
|
my ($self,$menu,$trigger)=@_; |
70
|
0
|
|
0
|
|
|
|
$self->{menuTrigger}=$trigger//"m"; |
71
|
0
|
|
|
|
|
|
$self->{menu}=$menu; |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
=head3 CaddAction($menu,$key,$actionData)> |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
Determines what happens when a key is pressed in C mode. Functions in the |
78
|
|
|
|
|
|
|
users scripts have to be "fully qualified" e.g. C<&main::function()> |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
$io->addAction("s",{note=>"s key saves sprite",proc=>sub{ |
81
|
|
|
|
|
|
|
my ($self,$canvas,$sprite,$cursor)=@_; # these are the objects passed as parameters |
82
|
|
|
|
|
|
|
&main::saveSprite($sprite); |
83
|
|
|
|
|
|
|
&main::flashCursor($sprite,$cursor); |
84
|
|
|
|
|
|
|
&main::restoreIO();},} ); |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
=cut |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
sub addAction{ |
90
|
0
|
|
|
0
|
1
|
|
my ($self,$key, $actionData)=@_; |
91
|
0
|
|
|
|
|
|
my %args=%$actionData; |
92
|
0
|
|
|
|
|
|
foreach my $k (keys %args){ |
93
|
0
|
|
|
|
|
|
$self->{actions}->{$key}->{$k}=$args{$k}; |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
=head3 Crun($io,@objects)> |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
Iniiating the capture of the key presses may trigger actions. These |
100
|
|
|
|
|
|
|
actions may need parameters including the $io object itself, it is useful |
101
|
|
|
|
|
|
|
to select all possible objects that may need to be passed to the anonymous |
102
|
|
|
|
|
|
|
subroutines added by C above. |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
=cut |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
sub run{ |
107
|
0
|
|
|
0
|
1
|
|
my ($self,@objects)=@_; |
108
|
0
|
|
|
|
|
|
ReadMode 'cbreak'; |
109
|
0
|
|
|
|
|
|
my $n=0; my @modifiers=(); |
|
0
|
|
|
|
|
|
|
110
|
0
|
|
|
|
|
|
while(1){ |
111
|
0
|
|
|
|
|
|
sleep 1/$self->{refreshRate}; |
112
|
0
|
|
|
|
|
|
$self->{key} = ReadKey(-1); # -1 means non-blocking read |
113
|
0
|
0
|
|
|
|
|
if ($self->{key}){ |
114
|
0
|
|
|
|
|
|
my $OrdKey = ord($self->{key}); |
115
|
0
|
0
|
|
|
|
|
if ($OrdKey ==27){push @modifiers, $OrdKey;} |
|
0
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
else{ |
117
|
0
|
0
|
|
|
|
|
my $pressed=chr($OrdKey).(@modifiers?"m":""); |
118
|
0
|
0
|
|
|
|
|
$pressed="enter" if ($OrdKey==10); |
119
|
0
|
|
|
|
|
|
printAt (20,60,"key pressed=$OrdKey $pressed "); |
120
|
0
|
0
|
|
|
|
|
if ($self->{mode} eq "free"){ |
|
|
0
|
|
|
|
|
|
121
|
0
|
0
|
0
|
|
|
|
if (defined $self->{actions}->{$pressed}->{proc}){ |
|
|
0
|
|
|
|
|
|
122
|
0
|
|
|
|
|
|
$self->{actions}->{$pressed}->{proc}->(@objects) |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
elsif((exists $self->{menuTrigger})&&($pressed eq $self->{menuTrigger})){ |
125
|
0
|
|
|
|
|
|
$self->startMenu(); |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
elsif ($self->{mode} eq "menu"){ |
129
|
0
|
0
|
|
|
|
|
if ($pressed eq"Am"){ #up arrow |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
130
|
|
|
|
|
|
|
$self->{menu}->upArrow() |
131
|
0
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
elsif ($pressed eq"Bm"){ #down arrow |
133
|
|
|
|
|
|
|
$self->{menu}->downArrow() |
134
|
0
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
elsif ($pressed eq"Cm"){ #left arrow |
136
|
|
|
|
|
|
|
$self->{menu}->leftArrow() |
137
|
0
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
elsif ($pressed eq"Dm"){ #right arrow |
139
|
|
|
|
|
|
|
$self->{menu}->rightArrow() |
140
|
0
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
elsif ($pressed eq"enter"){ #enter key |
142
|
|
|
|
|
|
|
$self->{menu}->openItem() |
143
|
0
|
|
|
|
|
|
} |
144
|
|
|
|
|
|
|
elsif ($pressed eq $self->{menuTrigger}){ #right arrow |
145
|
|
|
|
|
|
|
$self->{menu}->closeMenu() |
146
|
0
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
else { |
151
|
0
|
|
|
|
|
|
@modifiers=(); |
152
|
|
|
|
|
|
|
} |
153
|
0
|
0
|
|
|
|
|
$self->{actions}->{update}->(@objects) if exists $self->{actions}->{update}; |
154
|
0
|
|
|
|
|
|
$n++; |
155
|
|
|
|
|
|
|
} |
156
|
0
|
|
|
|
|
|
ReadMode 'normal'; |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
=head3 CstartMenu()> |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
Starts a menu as described in Term::Graille::Menu. The $io object enters a "menu" mode |
163
|
|
|
|
|
|
|
when Arrow, Enter and the Trigger key (see above) are passed to the Menu object |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
=cut |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
sub startMenu{ |
168
|
0
|
|
|
0
|
1
|
|
my $self=shift; |
169
|
0
|
0
|
|
|
|
|
if (exists $self->{menu}){ |
170
|
0
|
|
|
|
|
|
$self->{mode}="menu"; |
171
|
0
|
|
|
|
|
|
$self->{menu}->drawMenu(); |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
=head3 CstopMenu()> |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
Stops menu and returns to C mode |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=cut |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
sub stopMenu{ |
182
|
0
|
|
|
0
|
1
|
|
my $self=shift; |
183
|
0
|
|
|
|
|
|
$self->{mode}="free"; |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
=head3 Cstop()> |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
stops capturing key presses and enters normal mode. Useful for exeample, when the |
189
|
|
|
|
|
|
|
user needs to enter data |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=cut |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
sub stop{ |
195
|
0
|
|
|
0
|
1
|
|
my $self=shift; |
196
|
0
|
|
|
|
|
|
ReadMode 'normal'; |
197
|
|
|
|
|
|
|
} |