This example shows two instances of a module for interfacing to a TV remote control; one is for the real remote, which in this case is connected to a serial port on a set-top box, and the other is simulated for testing programs running on a regular operating system. The techniques of special interest are the dynamic use of modules and the communication using a channel.
The module is used by creating a channel and passing it to the module's init function, which returns a success/error indicator and starts an asynchronous process to read the remote control. The user of the module executes a receive on the channel whenever it wishes to accept a button-push.
The (abridged) module declaration is
Ir: module
{
# Codes buttons on IR remote control
Zero: con 0;
One: con 1;
. . .
Mute: con 23;
Error: con 9999;
init: fn(chan of int): int;
PATH: con "/dis/ir.dis";
SIMPATH: con "/dis/irsim.h";
};
implement Ir;
include "ir.m";
include "sys.m";
FD, Dir: import Sys;
sys: Sys;
init(keys: chan of int): int
{
cfd, dfd: ref FD;
sys = load Sys Sys->PATH;
cfd = sys->open("/dev/eia1ctl", sys->OWRITE);
if(cfd == nil)
return -1;
sys->fprint(cfd, "b9600");
dfd = sys->open("/dev/eia1", sys->OREAD);
cfd = nil;
spawn reader(keys, dfd);
return 0;
}
reader(keys: chan of int, dfd: ref FD)
{
n, ta, tb: int;
dir: Dir;
b1:= array[1] of byte;
b2:= array[1] of byte;
# find the number of bytes already
# queued and flush that many
(n, dir) = sys->fstat(dfd);
if(n >= 0 && dir.length > 0) {
while(dir.length) {
n = sys->read(dfd,
array[dir.length] of byte,
dir.length);
if(n < 0)
break;
dir.length -= n;
}
}
loop: for(;;) {
n = sys->read(dfd, b1, len b1);
if(n <= 0)
break;
ta = sys->millisec();
# Button pushes are pairs of characters
# that arrive closer together than
# 200 ms. Longer than that is likely
# to be noise.
for(;;) {
n = sys->read(dfd, b2, 1);
if(n <= 0)
break loop;
tb = sys->millisec();
if(tb - ta <= 200)
break;
ta = tb;
b1[0] = b2[0];
}
# map the character pair; the significant
# bits are the lowest 5.
case ((int b1[0]&16r1f)<<5) | (int b2[0]&16r1f) {
975 => n = Ir->Zero;
479 => n = Ir->One;
. . .
791 => n = Ir->Mute;
* => n = Ir->Error;
}
# found a button-push; send the value
keys <-= n;
}
keys <-= Ir->Error;
}
Here is another implementation of the same interface. Its init function performs the same kind of initialization as the other version, but using the operating system's keyboard files /dev/cons and /dev/consctl. In the Inferno environment, operations corresponding to the Unix `stty' primitive are accomplished by writing messages to a control file associated with the file that handles the data.
implement Ir;
include "ir.m";
include "sys.m";
FD: import Sys;
sys: Sys;
cctlfd: ref FD;
init(keys: chan of int): int
{
dfd: ref FD;
sys = load Sys Sys->PATH;
cctlfd = sys->open("/dev/consctl", sys->OWRITE);
if(cctlfd == nil)
return -1;
sys->write(cctlfd, array of byte "rawon", 5);
dfd = sys->open("/dev/cons", sys->OREAD);
if(dfd == nil)
return -1;
spawn reader(keys, dfd);
return 0;
}
The reader function for this module has the same structure as the first example, but doesn't have to worry about a noisy infrared detector:
reader(keys: chan of int, dfd: ref FD)
{
n: int;
b:= array[1] of byte;
for(;;) {
n = sys->read(dfd, b, 1);
if(n != 1)
break;
case int b[0] {
'0' => n = Ir->Zero;
'1' => n = Ir->One;
. . .
16r7f => n = Ir->Mute;
* => n = Ir->Error;
}
keys <-= n;
}
keys <-= Ir->Error;
}
implement Irtest;
include "sys.m";
include "draw.m";
FD: import Sys;
include "ir.m";
Irtest: module
{
init: fn(nil: ref Draw->Context, nil: list of string);
};
ir: Ir;
sys: Sys;
init(nil: ref Draw->Context, nil: list of string)
{
c: int;
stderr: ref FD;
irchan := chan of int;
sys = load Sys Sys->PATH;
stderr = sys->fildes(2);
# If the real IR remote application can
# be found, use it, otherwise use the simulator:
ir = load Ir Ir->PATH;
if(ir == nil)
ir = load Ir Ir->SIMPATH;
if(ir == nil) {
# %r format code means the last system error string
sys->fprint(stderr, "load ir: %r\n");
return;
}
if(ir->init(irchan) != 0) {
sys->fprint(stderr, "Ir.init: %r\n");
return;
}
names := array[] of {
"Zero",
"One",
. . .
"Mute",
};
for(;;) {
c = <-irchan;
if(c == ir->Error)
sys->print("Error %d\n", c);
else
sys->print("%s\n", names[c]);
}
}
movie(entry: ref Dbinfo, cc: chan of int)
{
i: int;
m: Mpeg;
b: ref Image;
m = load Mpeg Mpeg->PATH;
if (m == nil)
return;
# make a place on the screen
w := screen.window(screen.image.r);
mr := chan of string;
s := m->play(w, 1, w.r, entry.movie, mr);
if(s != "")
return;
# wait for the end of the movie
# while watching for button pushes
for(;;) {
alt {
<-mr =>
return;
i = <-cc =>
case i {
Ir->Select =>
m->ctl("stop");
Ir->Up or Ir->Dn =>
m->ctl("pause");
}
}
}
}