line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Roku::LCD;
|
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
24421
|
use v5.10.1;
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
60
|
|
4
|
1
|
|
|
1
|
|
13
|
use strict;
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
38
|
|
5
|
1
|
|
|
1
|
|
7
|
use warnings;
|
|
1
|
|
|
|
|
15
|
|
|
1
|
|
|
|
|
41
|
|
6
|
1
|
|
|
1
|
|
1054
|
use Time::HiRes qw(sleep);
|
|
1
|
|
|
|
|
1953
|
|
|
1
|
|
|
|
|
5
|
|
7
|
1
|
|
|
1
|
|
1079
|
use Readonly;
|
|
1
|
|
|
|
|
3666
|
|
|
1
|
|
|
|
|
63
|
|
8
|
1
|
|
|
1
|
|
10
|
use Carp qw(croak);
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
163
|
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
# Constants
|
11
|
|
|
|
|
|
|
Readonly::Scalar our $EMPTY => q{};
|
12
|
|
|
|
|
|
|
Readonly::Scalar our $SPACE => q{ };
|
13
|
|
|
|
|
|
|
Readonly::Scalar our $M400 => 400; # Model type
|
14
|
|
|
|
|
|
|
Readonly::Scalar our $M500 => 500; # Model type
|
15
|
|
|
|
|
|
|
Readonly::Scalar our $M400WIDTH => 16; # M400 screen width
|
16
|
|
|
|
|
|
|
Readonly::Scalar our $M500WIDTH => 40; # M500 screen width
|
17
|
|
|
|
|
|
|
Readonly::Scalar our $LETTERPAUSE => 0.25; # Time to pause between printing characters
|
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
require Roku::RCP;
|
20
|
|
|
|
|
|
|
|
21
|
1
|
|
|
1
|
|
943
|
use parent qw(Roku::RCP);
|
|
1
|
|
|
|
|
301
|
|
|
1
|
|
|
|
|
7
|
|
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
our $VERSION = '0.05';
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=head1 NAME
|
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
Roku::LCD - M400 & M500 Display Functions made more accessible than via the Roku::RCP module
|
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
=head1 VERSION
|
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=over
|
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
=item Version 0.05 May 27, 2014 - continuing to modernize the code
|
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=back
|
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
=head1 SYNOPSIS
|
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
use Roku::LCD;
|
41
|
|
|
|
|
|
|
my $display = Roku::LCD->new($rokuIP);
|
42
|
|
|
|
|
|
|
if (! display) { die("Could not connect to Roku Soundbridge"); }
|
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
my($rv) = $display->marquee(text => "This allows easy access to the marquee function - timings for M400 only");
|
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
$display->ticker(text => "An alternative to the marquee function that can cope with large quantities of text", pause => 5);
|
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
open (INFILE, "a_text_file.txt");
|
49
|
|
|
|
|
|
|
@slurp_file = ;
|
50
|
|
|
|
|
|
|
close(INFILE);
|
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
$display->teletype(text => "@slurp_file", pause => 2, linepause => 1);
|
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
$display->Quit;
|
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
=head1 DESCRIPTION
|
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
Roku::LCD was written because the RokuUI module appeared a bit too high level, so I put together some simplified display
|
59
|
|
|
|
|
|
|
routines into a single easy-to-use object.
|
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
It has now been moved to using the Roku::RCP module which is easily available from CPAN.
|
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
It inherits all the methods from the standard Roku::RCP module.
|
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=head1 METHODS
|
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
=head2 new(host => I [, port => I] [, model => I<400 or 500>])
|
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
If not given, the port number is assumed to be 4444, and the model will be determined from the displaytype
|
70
|
|
|
|
|
|
|
command (if that fails, the model type will be set to M400).
|
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
=cut
|
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
sub new {
|
75
|
0
|
|
|
0
|
1
|
|
my ( $class, %args ) = @_;
|
76
|
0
|
0
|
|
|
|
|
if (! $args{Host}) { croak "No soundbridge host to control"; }
|
|
0
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
# Test model type before attempting to connect
|
79
|
0
|
0
|
0
|
|
|
|
if ( ( $args{model} ) && ( $args{model} != $M500 ) && ( $args{model} != $M400 ) ) {
|
|
|
|
0
|
|
|
|
|
80
|
0
|
|
|
|
|
|
croak 'Unrecognised model type, ', $args{model}, "\n";
|
81
|
|
|
|
|
|
|
}
|
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
# Roku::RCP really ought to take host within the %args list...
|
84
|
0
|
|
0
|
|
|
|
my $self = $class->SUPER::new( $args{Host}, Port => $args{Port} || '4444' );
|
85
|
|
|
|
|
|
|
|
86
|
0
|
0
|
|
|
|
|
if (! defined $self) { return; }
|
|
0
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
|
88
|
0
|
0
|
|
|
|
|
if ( $args{model} ) {
|
89
|
0
|
0
|
|
|
|
|
if ( $args{model} == $M500 ) {
|
|
|
0
|
|
|
|
|
|
90
|
|
|
|
|
|
|
# prefer arrow notation to typeglobs used in Roku::RCP
|
91
|
|
|
|
|
|
|
# ${*$self}{display_length} = $M500WIDTH ;
|
92
|
0
|
|
|
|
|
|
${$self}->{display_length} = $M500WIDTH ;
|
|
0
|
|
|
|
|
|
|
93
|
0
|
|
|
|
|
|
${$self}->{model} = $args{model};
|
|
0
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
}
|
95
|
|
|
|
|
|
|
elsif ( $args{model} == $M400 ) {
|
96
|
0
|
|
|
|
|
|
${$self}->{display_length} = $M400WIDTH ;
|
|
0
|
|
|
|
|
|
|
97
|
0
|
|
|
|
|
|
${$self}->{model} = $args{model};
|
|
0
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
}
|
99
|
|
|
|
|
|
|
}
|
100
|
|
|
|
|
|
|
else {
|
101
|
0
|
|
|
|
|
|
my $result = $self->_determine_model;
|
102
|
|
|
|
|
|
|
|
103
|
0
|
0
|
|
|
|
|
if (! ${$self}->{model}) {
|
|
0
|
|
|
|
|
|
|
104
|
0
|
|
|
|
|
|
croak "Unrecognised display type - unknown model type. Try setting manually.\n";
|
105
|
|
|
|
|
|
|
}
|
106
|
|
|
|
|
|
|
}
|
107
|
|
|
|
|
|
|
|
108
|
0
|
|
|
|
|
|
print " ref \$self = '", ref $self ,"'\n ref \*\$self = '", ref *$self ,"'\n ref \${\$self} = '", ref ${$self} , "'\n ref \${\*\$self} = '", ref ${*$self}, "'\n";
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
|
110
|
0
|
0
|
|
|
|
|
if ( ${$self}->{debug} ) {
|
|
0
|
|
|
|
|
|
|
111
|
0
|
|
|
|
|
|
print "DEBUG display length = ${$self}->{display_length}; model = M${$self}->{model}\n";
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
}
|
113
|
|
|
|
|
|
|
|
114
|
0
|
|
|
|
|
|
return bless $self, $class;
|
115
|
|
|
|
|
|
|
} # end new
|
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=head2 marquee(text => I [, clear => I<0/1>])
|
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
This allows quick access to the standard sketch marquee function - timings are for text sized to
|
120
|
|
|
|
|
|
|
the M400 display as I do not have access to an M500.
|
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
If 1 is passed to clear, it forces the display to clear first (default 0)
|
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
=cut
|
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
sub marquee {
|
127
|
0
|
|
|
0
|
1
|
|
my ( $self, %args ) = @_;
|
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
# only take over if on standby
|
130
|
0
|
0
|
|
|
|
|
if (! $self->onstandby ) {
|
131
|
0
|
|
|
|
|
|
return ("Soundbridge running");
|
132
|
|
|
|
|
|
|
}
|
133
|
0
|
|
0
|
|
|
|
my $text = $args{'text'} || $EMPTY;
|
134
|
0
|
|
0
|
|
|
|
my $clear = $args{'clear'} || 0;
|
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
# duration is a magic number - time to wait before releasing display.
|
137
|
0
|
|
|
|
|
|
my $duration = ( int( ( ( length($text) ) + 24 ) / 25 ) ) * 5;
|
138
|
|
|
|
|
|
|
|
139
|
0
|
0
|
|
|
|
|
if ( ${$self}->{debug} ) {
|
|
0
|
|
|
|
|
|
|
140
|
0
|
|
|
|
|
|
print "DEBUG text length = ", length($text),
|
141
|
|
|
|
|
|
|
" duration = $duration\n";
|
142
|
|
|
|
|
|
|
}
|
143
|
|
|
|
|
|
|
|
144
|
0
|
0
|
|
|
|
|
if ($clear) { $self->_clear; }
|
|
0
|
|
|
|
|
|
|
145
|
0
|
|
|
|
|
|
$self->command("sketch -c marquee -start \"$text\"");
|
146
|
0
|
|
|
|
|
|
sleep($duration);
|
147
|
0
|
|
|
|
|
|
$self->command('sketch -c quit');
|
148
|
0
|
|
|
|
|
|
$self->command('sketch -c exit');
|
149
|
|
|
|
|
|
|
|
150
|
0
|
|
|
|
|
|
return ($self->sb_response);
|
151
|
|
|
|
|
|
|
} # end marquee
|
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
sub _blank_line {
|
155
|
|
|
|
|
|
|
# clears a single line
|
156
|
0
|
|
|
0
|
|
|
my ( $self, $line ) = @_;
|
157
|
0
|
|
|
|
|
|
my $rc = $self->_text(
|
158
|
|
|
|
|
|
|
text => $self->_spacefill(text => $SPACE),
|
159
|
|
|
|
|
|
|
duration => 0,
|
160
|
|
|
|
|
|
|
y => $line
|
161
|
|
|
|
|
|
|
);
|
162
|
0
|
|
|
|
|
|
return $rc;
|
163
|
|
|
|
|
|
|
} # end _blank_line
|
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
sub _clear {
|
167
|
|
|
|
|
|
|
# clear the display
|
168
|
0
|
|
|
0
|
|
|
my $self = shift;
|
169
|
0
|
|
|
|
|
|
$self->command('sketch -c clear');
|
170
|
0
|
|
|
|
|
|
my $rc = $self->sb_response;
|
171
|
0
|
|
|
|
|
|
return ($rc);
|
172
|
|
|
|
|
|
|
}
|
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
sub _determine_model {
|
175
|
|
|
|
|
|
|
# determine the soundbridge model from the display size
|
176
|
|
|
|
|
|
|
# M400 returns "16x2 LCD" - I assume M500 returns "40x2 LCD"
|
177
|
0
|
|
|
0
|
|
|
my $self = shift;
|
178
|
0
|
|
|
|
|
|
$self->command("displaytype");
|
179
|
|
|
|
|
|
|
|
180
|
0
|
|
|
|
|
|
my @responses = $self->sb_response();
|
181
|
0
|
|
|
|
|
|
foreach my $response (@responses) {
|
182
|
|
|
|
|
|
|
|
183
|
0
|
0
|
|
|
|
|
if ( ${$self}->{debug} ) {
|
|
0
|
|
|
|
|
|
|
184
|
0
|
|
|
|
|
|
print "DEBUG display type returned '$response'\n";
|
185
|
|
|
|
|
|
|
}
|
186
|
0
|
0
|
|
|
|
|
if ($response =~ /^(\d{2})x/) {
|
187
|
0
|
|
|
|
|
|
${$self}->{display_length} = $1;
|
|
0
|
|
|
|
|
|
|
188
|
0
|
0
|
|
|
|
|
if (${$self}->{display_length} == $M500WIDTH) {
|
|
0
|
|
|
|
|
|
|
189
|
0
|
|
|
|
|
|
${$self}->{model} = $M500 ;
|
|
0
|
|
|
|
|
|
|
190
|
0
|
|
|
|
|
|
return "model $M500";
|
191
|
|
|
|
|
|
|
}
|
192
|
|
|
|
|
|
|
else { # assume it's 16
|
193
|
0
|
|
|
|
|
|
${$self}->{model} = $M400 ;
|
|
0
|
|
|
|
|
|
|
194
|
0
|
|
|
|
|
|
return "model $M400";
|
195
|
|
|
|
|
|
|
}
|
196
|
|
|
|
|
|
|
}
|
197
|
|
|
|
|
|
|
}
|
198
|
0
|
|
|
|
|
|
return; # nothing appeared - return empty handed
|
199
|
|
|
|
|
|
|
} # end _determine_model
|
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
sub _spacefill {
|
203
|
|
|
|
|
|
|
# pad line with spaces - used to overwrite previous lines
|
204
|
|
|
|
|
|
|
# WARNING! This is an internal function, and likely to change
|
205
|
0
|
|
|
0
|
|
|
my ( $self, %args ) = @_;
|
206
|
0
|
|
0
|
|
|
|
my $text = $args{'text'} || $EMPTY;
|
207
|
0
|
|
|
|
|
|
my $tl = length($text);
|
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
# how many spaces do we need ?
|
210
|
0
|
|
|
|
|
|
my $spc = ${$self}->{display_length} - $tl;
|
|
0
|
|
|
|
|
|
|
211
|
0
|
0
|
|
|
|
|
if ($spc < 1) {
|
212
|
|
|
|
|
|
|
# no padding required
|
213
|
0
|
|
|
|
|
|
return $text;
|
214
|
|
|
|
|
|
|
}
|
215
|
|
|
|
|
|
|
else {
|
216
|
0
|
|
|
|
|
|
my $pattern = "%${tl}s%${spc}s";
|
217
|
0
|
|
|
|
|
|
return sprintf $pattern, $text, $SPACE;
|
218
|
|
|
|
|
|
|
}
|
219
|
|
|
|
|
|
|
} # end _spacefill
|
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
sub _text {
|
222
|
|
|
|
|
|
|
# internal function allowing easy access to the sketch "text" command
|
223
|
|
|
|
|
|
|
# usage:
|
224
|
|
|
|
|
|
|
# _text(text => I , duration => I [, clear => I<0/1>], x => I, y => I<0/1>)
|
225
|
0
|
|
|
0
|
|
|
my ( $self, %args ) = @_;
|
226
|
|
|
|
|
|
|
|
227
|
0
|
|
0
|
|
|
|
my $text = $args{'text'} || $SPACE;
|
228
|
0
|
|
0
|
|
|
|
my $x = $args{'x'} || 0;
|
229
|
0
|
|
0
|
|
|
|
my $y = $args{'y'} || 0;
|
230
|
0
|
|
|
|
|
|
my $duration = $args{'duration'};
|
231
|
|
|
|
|
|
|
|
232
|
0
|
|
|
|
|
|
$self->command("text $x $y \"$text\"");
|
233
|
0
|
|
|
|
|
|
sleep($duration);
|
234
|
0
|
|
|
|
|
|
return 1;
|
235
|
|
|
|
|
|
|
} # end _text
|
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
sub _print_current_line {
|
239
|
|
|
|
|
|
|
# An internal function for the teletype method
|
240
|
|
|
|
|
|
|
# clears, then prints the current line
|
241
|
0
|
|
|
0
|
|
|
my ( $self, $text, $y ) = @_;
|
242
|
0
|
|
|
|
|
|
my $rc = $self->_blank_line($y);
|
243
|
0
|
|
|
|
|
|
$rc = $self->_ticker(
|
244
|
|
|
|
|
|
|
text => $text,
|
245
|
|
|
|
|
|
|
y => $y,
|
246
|
|
|
|
|
|
|
pause => $LETTERPAUSE
|
247
|
|
|
|
|
|
|
);
|
248
|
0
|
|
|
|
|
|
return $rc;
|
249
|
|
|
|
|
|
|
} # end _print_current_line
|
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
sub _print_last_line {
|
253
|
|
|
|
|
|
|
# An internal function for the teletype method
|
254
|
|
|
|
|
|
|
# prints the last line on the top line
|
255
|
0
|
|
|
0
|
|
|
my ( $self, $text ) = @_;
|
256
|
0
|
|
|
|
|
|
my $rc = $self->_text(
|
257
|
|
|
|
|
|
|
text => $text,
|
258
|
|
|
|
|
|
|
duration => 0,
|
259
|
|
|
|
|
|
|
y => 0
|
260
|
|
|
|
|
|
|
);
|
261
|
0
|
|
|
|
|
|
return $rc;
|
262
|
|
|
|
|
|
|
} # end _print_last_line
|
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
sub _ttparagraph {
|
266
|
|
|
|
|
|
|
# An internal method which processes individual paragraphs for the teletype method
|
267
|
0
|
|
|
0
|
|
|
my ( $self, $text, $last_line_ref, $y_ref ) = @_;
|
268
|
0
|
|
|
|
|
|
my $dlength = ${$self}->{display_length}; # width of display
|
|
0
|
|
|
|
|
|
|
269
|
0
|
|
|
|
|
|
my $current_line;
|
270
|
0
|
|
|
|
|
|
my $current_line_length = 0;
|
271
|
0
|
|
|
|
|
|
my $rc;
|
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
# is the paragraph small enough to be printed on one line?
|
274
|
0
|
0
|
|
|
|
|
if (length($text) <= $dlength) {
|
275
|
0
|
0
|
|
|
|
|
if (${$last_line_ref}) {
|
|
0
|
|
|
|
|
|
|
276
|
0
|
|
|
|
|
|
$rc = $self->_print_last_line(${$last_line_ref});
|
|
0
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
}
|
278
|
0
|
|
|
|
|
|
$rc = $self->_print_current_line($text, ${$y_ref});
|
|
0
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
# start next line
|
280
|
0
|
|
|
|
|
|
${$y_ref} = 1;
|
|
0
|
|
|
|
|
|
|
281
|
0
|
|
|
|
|
|
${$last_line_ref} = $self->_spacefill(text => $text);
|
|
0
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
}
|
283
|
|
|
|
|
|
|
else {
|
284
|
|
|
|
|
|
|
# process the paragraph - break it into words (split on space)
|
285
|
0
|
|
|
|
|
|
my @string = split(/ /, $text);
|
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
# work through each word in the array (ary_inx holds the current word's position)
|
288
|
0
|
|
|
|
|
|
foreach my $word (@string) {
|
289
|
|
|
|
|
|
|
|
290
|
0
|
0
|
0
|
|
|
|
if ( ( length( $word ) + $current_line_length ) < $dlength ) {
|
|
|
0
|
|
|
|
|
|
291
|
|
|
|
|
|
|
# if the word will fit on the current line
|
292
|
|
|
|
|
|
|
# (note less than as a space needs to be accomodated too)
|
293
|
0
|
0
|
|
|
|
|
$current_line .= $SPACE if ($current_line);
|
294
|
0
|
|
|
|
|
|
$current_line .= $word;
|
295
|
0
|
|
|
|
|
|
$current_line_length = length($current_line);
|
296
|
|
|
|
|
|
|
}
|
297
|
|
|
|
|
|
|
# elsif the word will not fit on the current line but contains a non-word character - split on that (add one to the length because there's a space)
|
298
|
|
|
|
|
|
|
elsif ( ( $word =~ /^(\S+\W)(\S+)$/ )
|
299
|
|
|
|
|
|
|
&& ( ( length($1) + $current_line_length + 1 ) < $dlength ) )
|
300
|
|
|
|
|
|
|
{
|
301
|
0
|
0
|
|
|
|
|
if ($current_line) { $current_line .= $SPACE; }
|
|
0
|
|
|
|
|
|
|
302
|
0
|
|
|
|
|
|
$current_line .= $1;
|
303
|
|
|
|
|
|
|
# print the line
|
304
|
0
|
0
|
|
|
|
|
if (${$last_line_ref}) {
|
|
0
|
|
|
|
|
|
|
305
|
0
|
|
|
|
|
|
$rc = $self->_print_last_line(${$last_line_ref});
|
|
0
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
}
|
307
|
0
|
|
|
|
|
|
$rc = $self->_print_current_line($current_line, ${$y_ref});
|
|
0
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
# start next line
|
309
|
0
|
|
|
|
|
|
${$y_ref} = 1;
|
|
0
|
|
|
|
|
|
|
310
|
0
|
|
|
|
|
|
${$last_line_ref} = $self->_spacefill(text => $current_line);
|
|
0
|
|
|
|
|
|
|
311
|
0
|
|
|
|
|
|
$current_line = $2;
|
312
|
0
|
|
|
|
|
|
$current_line_length = length($current_line);
|
313
|
|
|
|
|
|
|
}
|
314
|
|
|
|
|
|
|
else {
|
315
|
|
|
|
|
|
|
# too big for line, so print the line
|
316
|
0
|
0
|
|
|
|
|
if (${$last_line_ref}) {
|
|
0
|
|
|
|
|
|
|
317
|
0
|
|
|
|
|
|
$rc = $self->_print_last_line(${$last_line_ref});
|
|
0
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
}
|
319
|
0
|
|
|
|
|
|
$rc = $self->_print_current_line($current_line, ${$y_ref});
|
|
0
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
# start next line
|
321
|
0
|
|
|
|
|
|
${$y_ref} = 1;
|
|
0
|
|
|
|
|
|
|
322
|
0
|
|
|
|
|
|
${$last_line_ref} = $self->_spacefill(text => $current_line);
|
|
0
|
|
|
|
|
|
|
323
|
0
|
|
|
|
|
|
$current_line = $word;
|
324
|
0
|
|
|
|
|
|
$current_line_length = length($current_line);
|
325
|
|
|
|
|
|
|
}
|
326
|
|
|
|
|
|
|
} # end foreach @string loop
|
327
|
|
|
|
|
|
|
# we've run out of words, but we haven't printed the line yet!
|
328
|
0
|
0
|
|
|
|
|
if (${$last_line_ref}) {
|
|
0
|
|
|
|
|
|
|
329
|
0
|
|
|
|
|
|
$rc = $self->_print_last_line(${$last_line_ref});
|
|
0
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
}
|
331
|
0
|
|
|
|
|
|
$rc = $self->_print_current_line($current_line, ${$y_ref});
|
|
0
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
# fill last line for next paragraph call
|
333
|
0
|
|
|
|
|
|
${$y_ref} = 1;
|
|
0
|
|
|
|
|
|
|
334
|
0
|
|
|
|
|
|
${$last_line_ref} = $self->_spacefill(text => $current_line);
|
|
0
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
} # end paragraph processing
|
336
|
0
|
|
|
|
|
|
return $rc;
|
337
|
|
|
|
|
|
|
} # end _ttparagraph
|
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
=head2 ticker(text => I [, y => I<0/1>] [, pause => I])
|
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
An alternative to the marquee that can be displayed on either the top or bottom line.
|
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
=cut
|
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
sub ticker { # an alternative to marquee
|
347
|
0
|
|
|
0
|
1
|
|
my ( $self, %args ) = @_;
|
348
|
|
|
|
|
|
|
# only take over if on standby
|
349
|
0
|
0
|
|
|
|
|
if (! $self->onstandby ) {
|
350
|
0
|
|
|
|
|
|
return ('Soundbridge running');
|
351
|
|
|
|
|
|
|
}
|
352
|
|
|
|
|
|
|
|
353
|
0
|
|
|
|
|
|
$self->command('sketch');
|
354
|
|
|
|
|
|
|
|
355
|
0
|
|
|
|
|
|
$self->_ticker(%args);
|
356
|
|
|
|
|
|
|
|
357
|
0
|
|
|
|
|
|
$self->command('quit');
|
358
|
0
|
|
|
|
|
|
my $rc = $self->sb_response;
|
359
|
0
|
|
|
|
|
|
return ($rc);
|
360
|
|
|
|
|
|
|
} # end ticker
|
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
sub _ticker { # the real function - also used by teletype
|
364
|
0
|
|
|
0
|
|
|
my ( $self, %args ) = @_;
|
365
|
0
|
|
0
|
|
|
|
my $text = $args{'text'} || $EMPTY;
|
366
|
0
|
|
0
|
|
|
|
my $pause = $args{'pause'} || 5;
|
367
|
0
|
|
0
|
|
|
|
my $y = $args{'y'} || 0;
|
368
|
0
|
|
|
|
|
|
my $dlength = ${$self}->{display_length};
|
|
0
|
|
|
|
|
|
|
369
|
0
|
|
|
|
|
|
my $offset = 0; # offset for taking a substring
|
370
|
0
|
|
|
|
|
|
my $dtext = '0'; # currently displayed text
|
371
|
0
|
|
|
|
|
|
my $tlength = 0; # length of currently displayed text
|
372
|
0
|
|
|
|
|
|
my $dur = 0;
|
373
|
0
|
|
|
|
|
|
my $spc = 0;
|
374
|
|
|
|
|
|
|
|
375
|
0
|
|
|
|
|
|
my $length = 0;
|
376
|
0
|
|
|
|
|
|
while(++$length < ( length($text) ) ) {
|
377
|
0
|
|
|
|
|
|
$spc++;
|
378
|
0
|
0
|
|
|
|
|
if ( $tlength != $dlength ) {
|
379
|
|
|
|
|
|
|
# current text length != display width
|
380
|
0
|
|
|
|
|
|
$tlength++;
|
381
|
|
|
|
|
|
|
}
|
382
|
|
|
|
|
|
|
|
383
|
0
|
0
|
|
|
|
|
if ( length($dtext) == $dlength ) {
|
384
|
|
|
|
|
|
|
# increase the offset if the displayed text is the same length as the screen width
|
385
|
0
|
|
|
|
|
|
$offset++;
|
386
|
|
|
|
|
|
|
}
|
387
|
|
|
|
|
|
|
|
388
|
0
|
|
|
|
|
|
$dtext = substr( $text, $offset, $tlength );
|
389
|
0
|
0
|
|
|
|
|
if ( substr( $dtext, -1, 1 ) eq $SPACE ) { $spc = 0; }
|
|
0
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
|
391
|
0
|
0
|
0
|
|
|
|
if ( ( length($text) > $dlength ) && ( ++$dur == $dlength ) ) {
|
392
|
|
|
|
|
|
|
# print "length > dlength && dur == dlength\n";
|
393
|
0
|
|
|
|
|
|
$self->_text( text => $dtext, duration => $LETTERPAUSE, y => $y );
|
394
|
0
|
0
|
|
|
|
|
if ( ${$self}->{debug} ) {
|
|
0
|
|
|
|
|
|
|
395
|
0
|
|
|
|
|
|
print "DEBUG dtext='$dtext' dur='$dur' spc='$spc'\n";
|
396
|
|
|
|
|
|
|
}
|
397
|
0
|
|
|
|
|
|
$dur = $spc;
|
398
|
0
|
0
|
|
|
|
|
if ( $dur > $dlength ) { $dur = 0; }
|
|
0
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
}
|
400
|
|
|
|
|
|
|
else {
|
401
|
|
|
|
|
|
|
# print "length <= dlength || dur != dlength\n";
|
402
|
0
|
|
|
|
|
|
$self->_text( text => $dtext, duration => $LETTERPAUSE, y => $y );
|
403
|
0
|
0
|
|
|
|
|
if ( ${$self}->{debug} ) {
|
|
0
|
|
|
|
|
|
|
404
|
0
|
|
|
|
|
|
print "DEBUG dtext='$dtext' dur='$dur' spc='$spc'\n";
|
405
|
|
|
|
|
|
|
}
|
406
|
|
|
|
|
|
|
}
|
407
|
|
|
|
|
|
|
}
|
408
|
0
|
|
|
|
|
|
$dtext = substr( $text, -$dlength, $dlength );
|
409
|
0
|
|
|
|
|
|
$self->_text( text => $dtext, duration => $pause, y => $y );
|
410
|
0
|
|
|
|
|
|
return 1;
|
411
|
|
|
|
|
|
|
} # end _ticker
|
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
=head2 teletype(text => I [, pause => I] [, [linepause => I])
|
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
An alternative to using marquee to display large quantities of text, scrolling the display upwards rather than from
|
416
|
|
|
|
|
|
|
the right.
|
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
The length of time to pause after each line of text is given by I, wheras I holds the
|
419
|
|
|
|
|
|
|
length of time to pause at the end of the text.
|
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
=cut
|
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
sub teletype {
|
424
|
0
|
|
|
0
|
1
|
|
my ( $self, %args ) = @_;
|
425
|
0
|
|
0
|
|
|
|
my $text = $args{'text'} || $EMPTY; # default text is blank
|
426
|
0
|
|
0
|
|
|
|
my $linepause = $args{'linepause'} || 1; # length of time to wait in seconds before next line
|
427
|
0
|
|
0
|
|
|
|
my $pause = $args{'pause'} || 1; # length of additional time to wait in seconds after message
|
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
# only take over if on standby
|
430
|
0
|
0
|
|
|
|
|
if (! $self->onstandby ) {
|
431
|
0
|
|
|
|
|
|
return ("Soundbridge running");
|
432
|
|
|
|
|
|
|
}
|
433
|
|
|
|
|
|
|
|
434
|
0
|
|
|
|
|
|
$self->command('sketch'); # put the command session into sketch mode
|
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
# Clear display first
|
437
|
0
|
|
|
|
|
|
$self->_clear;
|
438
|
|
|
|
|
|
|
|
439
|
0
|
|
|
|
|
|
my @string;
|
440
|
|
|
|
|
|
|
my $rc; # message returned by method
|
441
|
0
|
|
|
|
|
|
my $dlength = ${$self}->{display_length}; # width of display
|
|
0
|
|
|
|
|
|
|
442
|
0
|
|
|
|
|
|
my $line_length = 0; # current length of line
|
443
|
0
|
|
|
|
|
|
my $y = 0; # start at the top
|
444
|
0
|
|
|
|
|
|
my $last_string = undef; # last string printed
|
445
|
|
|
|
|
|
|
|
446
|
0
|
|
|
|
|
|
my (@paras) = split( /\n/, $text ); # break the text into paragraphs
|
447
|
0
|
|
|
|
|
|
foreach my $paragraph (@paras) {
|
448
|
0
|
|
|
|
|
|
$self->_ttparagraph($paragraph, \$last_string, \$y)
|
449
|
|
|
|
|
|
|
}
|
450
|
0
|
|
|
|
|
|
$rc = $self->_print_last_line($last_string);
|
451
|
0
|
|
|
|
|
|
$rc = $self->_text(
|
452
|
|
|
|
|
|
|
text => $self->_spacefill( text => $SPACE ),
|
453
|
|
|
|
|
|
|
duration => 0,
|
454
|
|
|
|
|
|
|
y => 1
|
455
|
|
|
|
|
|
|
);
|
456
|
0
|
|
|
|
|
|
sleep($pause);
|
457
|
0
|
|
|
|
|
|
$self->command('quit');
|
458
|
0
|
|
|
|
|
|
$rc = $self->sb_response;
|
459
|
0
|
|
|
|
|
|
return ($rc);
|
460
|
|
|
|
|
|
|
} # end teletype
|
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
=head2 onstandby
|
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
Checks whether the Soundbridge is on standby (returns true) or in use (returns false)
|
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
=cut
|
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
sub onstandby {
|
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
# an almost direct lift of RokuUI's ison function
|
471
|
|
|
|
|
|
|
# this is used to see whether the radio is in use
|
472
|
0
|
|
|
0
|
1
|
|
my $self = shift;
|
473
|
0
|
|
|
|
|
|
$self->command("ps");
|
474
|
|
|
|
|
|
|
|
475
|
0
|
|
|
|
|
|
for my $ps ( $self->sb_response ) {
|
476
|
0
|
0
|
|
|
|
|
return 1 if $ps =~ /StandbyApp/;
|
477
|
|
|
|
|
|
|
}
|
478
|
0
|
|
|
|
|
|
return 0;
|
479
|
|
|
|
|
|
|
} # end onstandby
|
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
=head2 sb_response
|
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
Used to return any command responses; filtering out prompts
|
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
=cut
|
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
sub sb_response {
|
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
# this is used to return any command responses, but filter out prompts
|
490
|
0
|
|
|
0
|
1
|
|
my $self = shift;
|
491
|
|
|
|
|
|
|
return map {
|
492
|
0
|
0
|
0
|
|
|
|
if ( ( !/^SoundBridge\>/ ) && ( !/^Sketch>/ ) ) { $_; }
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
} $self->response();
|
494
|
|
|
|
|
|
|
} # end sb_response
|
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
1;
|
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
# end of module, additional documentation below
|
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
__END__
|