summaryrefslogtreecommitdiff
path: root/appl/examples/minitel/keyb.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/examples/minitel/keyb.b')
-rw-r--r--appl/examples/minitel/keyb.b367
1 files changed, 367 insertions, 0 deletions
diff --git a/appl/examples/minitel/keyb.b b/appl/examples/minitel/keyb.b
new file mode 100644
index 00000000..aba5485d
--- /dev/null
+++ b/appl/examples/minitel/keyb.b
@@ -0,0 +1,367 @@
+#
+# Copyright © 1998 Vita Nuova Limited. All rights reserved.
+#
+
+# special keyboard operations
+Extend, # enable cursor and editing keys and control chars
+C0keys, # cursor keys send BS,HT,LF and VT
+Invert # case inversion
+ : con 1 << iota;
+
+Keyb: adt {
+ m: ref Module; # common attributes
+ in: chan of ref Event;
+
+ cmd: chan of string; # from Tk (keypresses and focus)
+ spec: int; # special keyboard extensions
+
+ init: fn(k: self ref Keyb, toplevel: ref Tk->Toplevel);
+ reset: fn(k: self ref Keyb);
+ run: fn(k: self ref Keyb);
+ quit: fn(k: self ref Keyb);
+ map: fn(k: self ref Keyb, key:int): array of byte;
+};
+
+Keyb.init(k: self ref Keyb, toplevel: ref Tk->Toplevel)
+{
+ k.in = chan of ref Event;
+ k.cmd = chan of string;
+ tk->namechan(toplevel, k.cmd, "keyb"); # Tk -> keyboard
+ k.reset();
+}
+
+Keyb.reset(k: self ref Keyb)
+{
+ k.m = ref Module(Pmodem|Psocket, 0);
+}
+
+ask(in: chan of string, out: chan of string)
+{
+ keys: string;
+
+ T.mode = Videotex;
+ S.setmode(Videotex);
+# clear(S);
+ prompt: con "Numéroter: ";
+ number := M.lastdialstr;
+ S.msg(prompt);
+
+Input:
+ for(;;) {
+ n := len prompt + len number;
+ # guard length must be > len prompt
+ if (n > 30)
+ n -= 30;
+ else
+ n = 0;
+ S.msg(prompt + number[n:]);
+ keys = <- in;
+ if (keys == nil)
+ return;
+
+ keys = canoncmd(keys);
+
+ case keys {
+ "connect" or "send" =>
+ break Input;
+ "correct" =>
+ if(len number > 0)
+ number = number[0: len number -1];
+ "cancel" =>
+ number = "";
+ break Input;
+ "repeat" or "index" or "guide" or "next" or "previous" =>
+ ;
+ * =>
+ number += keys;
+ }
+ }
+
+ S.msg(nil);
+ for (;;) alt {
+ out <- = number =>
+ return;
+ keys = <- in =>
+ if (keys == nil)
+ return;
+ }
+}
+
+Keyb.run(k: self ref Keyb)
+{
+ dontask := chan of string;
+ askchan := dontask;
+ askkeys := chan of string;
+Runloop:
+ for(;;){
+ alt {
+ ev := <- k.in =>
+ pick e := ev {
+ Equit =>
+ break Runloop;
+ Eproto =>
+ case e.cmd {
+ Creset =>
+ k.reset();
+ Cproto =>
+ case e.a0 {
+ START =>
+ case e.a1 {
+ LOWERCASE =>
+ k.spec |= Invert;
+ }
+ STOP =>
+ case e.a1 {
+ LOWERCASE =>
+ k.spec &= ~Invert;
+ }
+ }
+ * => break;
+ }
+ }
+ cmd := <- k.cmd =>
+ if(debug['k'] > 0) {
+ fprint(stderr, "Tk %s\n", cmd);
+ }
+ (n, args) := sys->tokenize(cmd, " ");
+ if(n >0)
+ case hd args {
+ "key" =>
+ (key, nil) := toint(hd tl args, 16);
+ if(askchan != dontask) {
+ s := minikey(key);
+ if (s == nil)
+ s[0] = key;
+ askkeys <-= s;
+ break;
+ }
+ keys := k.map(key);
+ if(keys != nil) {
+ send(ref Event.Edata(k.m.path, Mkeyb, keys));
+ }
+ "skey" => # minitel key hit (soft key)
+ if(hd tl args == "Exit") {
+ if(askchan != dontask) {
+ askchan = dontask;
+ askkeys <-= nil;
+ }
+ if(T.state == Online || T.state == Connecting) {
+ seq := keyseq("connect");
+ if(seq != nil) {
+ send(ref Event.Edata(k.m.path, Mkeyb, seq));
+ send(ref Event.Edata(k.m.path, Mkeyb, seq));
+ }
+ send(ref Event.Eproto(Pmodem, Mkeyb, Cdisconnect, "", 0,0,0));
+ }
+ send(ref Event.Equit(0, 0));
+ break;
+ }
+ if(askchan != dontask) {
+ askkeys <-= hd tl args;
+ break;
+ }
+ case hd tl args {
+ "Connect" =>
+ case T.state {
+ Local =>
+ if(M.connect == Network)
+ send(ref Event.Eproto(Pmodem, Mkeyb, Cconnect, "", 0,0,0));
+ else {
+ askchan = chan of string;
+ spawn ask(askkeys, askchan);
+ }
+ Connecting =>
+ send(ref Event.Eproto(Pmodem, Mkeyb, Cdisconnect, "", 0,0,0));
+ Online =>
+ seq := keyseq("connect");
+ if(seq != nil)
+ send(ref Event.Edata(k.m.path, Mkeyb, seq));
+ }
+ * =>
+ seq := keyseq(hd tl args);
+ if(seq != nil)
+ send(ref Event.Edata(k.m.path, Mkeyb, seq));
+ }
+ "click" => # fetch a word from the display
+ x := int hd tl args;
+ y := int hd tl tl args;
+ word := disp->GetWord(Point(x, y));
+ if(word != nil) {
+ if (askchan != dontask) {
+ askkeys <- = word;
+ break;
+ }
+ if (T.state == Local) {
+ if (canoncmd(word) == "connect") {
+ if(M.connect == Network)
+ send(ref Event.Eproto(Pmodem, Mkeyb, Cconnect, "", 0,0,0));
+ else {
+ askchan = chan of string;
+ spawn ask(askkeys, askchan);
+ }
+ break;
+ }
+ }
+ seq := keyseq(word);
+ if(seq != nil)
+ send(ref Event.Edata(k.m.path, Mkeyb, seq));
+ else {
+ send(ref Event.Edata(k.m.path, Mkeyb, array of byte word ));
+ send(ref Event.Edata(k.m.path, Mkeyb, keyseq("send")));
+ }
+ }
+
+ }
+ dialstr := <-askchan =>
+ askchan = dontask;
+ if(dialstr != nil) {
+ M.dialstr = dialstr;
+ send(ref Event.Eproto(Pmodem, Mkeyb, Cconnect, "", 0,0,0));
+ }
+ }
+ }
+ send(nil);
+}
+
+
+# Perform mode specific key translation
+# returns nil on invalid keypress,
+Keyb.map(nil: self ref Keyb, key: int): array of byte
+{
+ # hardware to minitel keyboard mapping
+ cmd := minikey(key);
+ if (cmd != nil) {
+ seq := keyseq(cmd);
+ if(seq != nil)
+ return seq;
+ }
+
+ # alphabetic (with case mapping)
+ case T.mode {
+ Videotex =>
+ if(key >= 'A' && key <= 'Z')
+ return array [] of { byte ('a' + (key - 'A'))};
+ if(key >= 'a' && key <= 'z')
+ return array [] of {byte ('A' + (key - 'a'))};
+ Mixed or Ascii =>
+ if(key >= 'A' && key <= 'Z' || key >= 'a' && key <= 'z')
+ return array [] of {byte key};
+ };
+
+ # Numeric
+ if(key >= '0' && key <= '9')
+ return array [] of {byte key};
+
+ # Control-A -> Control-Z, Esc - columns 0 and 1
+ if(key >= 16r00 && key <=16r1f)
+ case T.mode {
+ Videotex =>
+ return nil;
+ Mixed or Ascii =>
+ return array [] of {byte key};
+ }
+
+ # miscellaneous key mapping
+ case key {
+ 16r20 => ; # space
+ 16ra3 => return array [] of { byte 16r19, byte 16r23 }; # pound
+ '!' or '"' or '#' or '$'
+ or '%' or '&' or '\'' or '(' or ')'
+ or '*' or '+' or ',' or '-'
+ or '.' or ':' or ';' or '<'
+ or '=' or '>' or '?' or '@' => ;
+ KF13 => # request for error correction - usually Fnct M + C
+ if((M.spec&Ecp) == 0 && T.state == Online && T.connect == Direct) {
+fprint(stderr, "requesting Ecp\n");
+ return array [] of { byte SEP, byte 16r4a };
+ }
+ return nil;
+ * => return nil;
+ }
+ return array [] of {byte key};
+}
+
+Keyb.quit(k: self ref Keyb)
+{
+ if(k==nil);
+}
+
+canoncmd(s : string) : string
+{
+ s = tolower(s);
+ case s {
+ "connect" or "cx/fin" or
+ "connexion" or "fin" => return "connect";
+ "send" or "envoi" => return "send";
+ "repeat" or "repetition" => return "repeat";
+ "index" or "sommaire" or "somm"
+ => return "index";
+ "guide" => return "guide";
+ "correct" or "correction" => return "correct";
+ "cancel" or "annulation" or "annul" or "annu"
+ => return "cancel";
+ "next" or "suite" => return "next";
+ "previous" or "retour" or "retou"
+ => return "previous";
+ }
+ return s;
+}
+
+# map softkey names to the appropriate byte sequences
+keyseq(skey: string): array of byte
+{
+ b2 := 0;
+ asterisk := 0;
+ if(skey == nil || len skey == 0)
+ return nil;
+ if(skey[0] == '*') {
+ asterisk = 1;
+ skey = skey[1:];
+ }
+ skey = canoncmd(skey);
+ case skey {
+ "connect" => b2 = 16r49;
+ "send" => b2 = 16r41;
+ "repeat" => b2 = 16r43;
+ "index" => b2 = 16r46;
+ "guide" => b2 = 16r44;
+ "correct" => b2 = 16r47;
+ "cancel" => b2 = 16r45;
+ "next" => b2 = 16r48;
+ "previous" => b2 = 16r42;
+ }
+ if(b2) {
+ if(asterisk)
+ return array [] of { byte '*', byte SEP, byte b2};
+ else
+ return array [] of { byte SEP, byte b2};
+ } else
+ return nil;
+}
+
+# map hardware or software keyboard presses to minitel functions
+minikey(key: int): string
+{
+ case key {
+ Kup or KupPC =>
+ return"previous";
+ Kdown or KdownPC =>
+ return "next";
+ Kenter =>
+ return "send";
+ Kback =>
+ return "correct";
+ Kesc =>
+ return "cancel";
+ KF1 =>
+ return "guide";
+ KF2 =>
+ return "connect";
+ KF3 =>
+ return "repeat";
+ KF4 =>
+ return "index";
+ * =>
+ return nil;
+ }
+} \ No newline at end of file