diff options
| author | Charles Forsyth <charles.forsyth@gmail.com> | 2015-04-29 15:19:07 +0100 |
|---|---|---|
| committer | Charles Forsyth <charles.forsyth@gmail.com> | 2015-04-29 15:19:07 +0100 |
| commit | c714c442442ef137f20ca4ff9707d5480cb9ba7a (patch) | |
| tree | d0d285f05cb4292fa8f1f3c0bc70bec1251e956e /appl/wm | |
| parent | 1ac9729e9325d84db36c04b5cda3b5b1bc0d041f (diff) | |
remove obsolete minitel, but leave source as example
Diffstat (limited to 'appl/wm')
| -rw-r--r-- | appl/wm/minitel/README | 209 | ||||
| -rw-r--r-- | appl/wm/minitel/event.b | 19 | ||||
| -rw-r--r-- | appl/wm/minitel/event.m | 19 | ||||
| -rw-r--r-- | appl/wm/minitel/keyb.b | 367 | ||||
| -rw-r--r-- | appl/wm/minitel/mdisplay.b | 799 | ||||
| -rw-r--r-- | appl/wm/minitel/mdisplay.m | 115 | ||||
| -rw-r--r-- | appl/wm/minitel/miniterm.b | 1187 | ||||
| -rw-r--r-- | appl/wm/minitel/miniterm.m | 120 | ||||
| -rw-r--r-- | appl/wm/minitel/mkfile | 24 | ||||
| -rw-r--r-- | appl/wm/minitel/modem.b | 620 | ||||
| -rw-r--r-- | appl/wm/minitel/screen.b | 1610 | ||||
| -rw-r--r-- | appl/wm/minitel/socket.b | 49 | ||||
| -rw-r--r-- | appl/wm/minitel/swkeyb.b | 370 | ||||
| -rw-r--r-- | appl/wm/minitel/swkeyb.m | 21 |
14 files changed, 0 insertions, 5529 deletions
diff --git a/appl/wm/minitel/README b/appl/wm/minitel/README deleted file mode 100644 index 82f3202e..00000000 --- a/appl/wm/minitel/README +++ /dev/null @@ -1,209 +0,0 @@ -Minitel Emulation for Inferno - -This directory contains the source of `miniterm', a minitel emulator -for Inferno. Miniterm is written in Limbo. The main components are: - - miniterm.m - common constants - miniterm.b - terminal emulator, messaging and Minitel `protocol` - event.[mb] - inter-module message format - keyb.b - Minitel keyboard module - modem.b - Minitel modem module - screen.b - Minitel screen module - socket.b - Minitel socket module - arg.m - basic command line argument handling - mdisplay.[mb] - Videotex display module - swkeyb.[mb] - Minitel aware software keyboard - - fonts.tgz which expands into: - - fonts/minitel - external and subfont directory (`bind -b' into /fonts) - fonts/minitel/f40x25 - 40 column external font - fonts/minitel/14x17 - fonts/minitel/14x17xoe - fonts/minitel/14x17arrow - fonts/minitel/f40x25g1 - 40 column semigraphic external font - fonts/minitel/vid14x17 - fonts/minitel/f40x25h - 40 column double height external font - fonts/minitel/14x34 - fonts/minitel/14x34xoe - fonts/minitel/14x34arrow - fonts/minitel/f40x25w - 40 column double width external font - fonts/minitel/28x17 - fonts/minitel/28x17xoe - fonts/minitel/28x17arrow - fonts/minitel/f40x25s - 40 column double size external font - fonts/minitel/28x34xoe - fonts/minitel/28x34arrow - fonts/minitel/f80x25 - 80 column external font - fonts/minitel/8x12 - fonts/minitel/8x12xoe - fonts/minitel/8x12arrow - -The fonts subdirectory should be bound into /fonts: - bind -b fonts /fonts -or the directory fonts/minitel copied to /fonts/minitel before invoking the emulator. -The names of the external fonts are -known to the Videotex display module. Similarly, the files: - /dev/modem - /dev/modemctl -are known to the modem module, but you can ignore them if -(as is almost certain) you are using the Internet-minitel gateway -and you haven't got appropriate modem hardware anyway. - -To build - mkdir /usr/inferno/dis/wm/minitel - mk install - -The code models the structure outlined in the Minitel 1B specification -provided by France Telecom. However, much more interpretation was -required to display the majority of screens currently seen on Minitel. -Additional information (although sketchy) was found on the Internet by -searching for Minitel or Videotex and also by examination of the codes -sent by minitel servers and experimenting with replies. There must be -some more up to date information somewhere! - -We don't support downloadable fonts, but correctly filter them out. - -The file miniterm.b contains the code for the minitel `terminal' with -which the other modules communicate. The keyboard, modem, socket, -screen and terminal are run as separate threads which communicate by -calling: - send(e: ref Event) -The clue to the intermodule communication is in Terminal.run which -does something like: - for(;;) { - ev =<- t.in => - eva := protocol(ev); - while(len eva > 0) { - post(eva[0]); - eva = eva[1:]; - } - # then deliver any `posted' messages (without blocking) - } -An Event `ev' may typically be an Edata type (say from the modem) or -an Eproto type for internal interpretation. In the call: - eva := protocol(ev) -The function protocol() dissects Edata messages to produce an inline -sequence of Edata and Eproto messages. The function post() queues -messages for delivery to the appropriate modules. For example, data -from the modem might be destined for the screen and the socket module. -Messages are queued until they can be delivered. That way the line: - ev =<- t.in -is executed in a timely way and the other modules can be written to -make blocking writes (via send()) and to service reads when they are -ready. - -In many places in the code lines appear with comments like: - if(p.skip < 1 || p.skip > 127) # 5.0 -These refer to sections of the Minitel specification which explain the -code. - -The mdisplay code provides a Videotex display using Inferno -primitives. The screen, keyboard and modem modules interpret data as -described in the equivalent section of the Minitel specification. The -socket module has not been implemented but currently performs a `null' -function and could easily be added if required. - - -- Namespace -We always expect the fonts to appear in /fonts and the softmodem -to appear as /dev/modem and /dev/modemctl. - -- Invocation -If invoked with no argument, miniterm uses the France Telecom -internet gateway by default (tcp!193.252.252.250!513). -If the argument starts with `modem' then -a direct connection through /dev/modem will be established. - -An argument beginning with anything other than `modem' will -be assumed to be an address suitable for dial(). For example: - - wm/minitel/miniterm tcp!193.252.252.250!513 - -will connect to the current France Telecom internet server. - -For direct connections a modem `init' string and an optional -phone number can follow the modem prefix, as in: - - wm/minitel/miniterm modem!F3!3615 - -or - - wm/minitel/miniterm modem!F3!01133836431414 - -The `F3' is the code which instructs the softmodem to enable V.23 -and needs to be passed when connecting to the FT servers. -To use pulse dialing instead of tone dialing the phone number -can be prefixed with a 'P' as in: - - wm/minitel/miniterm modem!F3!P3614 - -If the parameter specifies a network connection or a direct connection -with a phone number the software will attempt to connect immediately. -If Cx/Fin is used to disconnect and then re-connect it will use the -same IP address for a network connection or prompt for a new -phone number in the case of a direct connection. When prompting -for a new number the top row of the screen is used to allow the user -to edit the last used number. Simple editing is available, and the minitel -keys do the obvious things. - - - -** Notes on the 15th December 1998 Release ** - -- Software keyboard -A version of the software keyboard which understands some of -the minitel keyboard mappings is included. For example, hitting 'A' results -in a capital 'A' on the screen in spite of the Videotex case mapping. - -- Minitel function keys -The minitel keys are displayed on the right hand side of the screen -in 40 column mode on a network connection -and can be swapped to the left hand side by hitting the <- key. -In direct dial mode and 80 column network mode the keys are -displayed at the bottom of the screen. -In network mode they are re-displayed as appropriate on 40 to 80 -column mode changes. - - -Known Omission -------------- -- Error Correction (direct dial only) -There is no screen button to enable error correction in the release. -If a server asks for error correction it will be enabled. It looks as though -we need to include a key to enable it. Without it direct dial screens are -occasionally corrupted. - -- Software Keyboard Handling -We need to add some code to update the software keyboard and -bring it to the foreground on a mode change. - -- Full 80 column support -I am aware of some screens which don't look correct in 80 column -mode (and others that do). See `EMAIL' then choose USENET and -press SUITE a few times. I believe it behaves as specified but as we -have seen with the 40 column Videotex mode the specification -is not sufficient to display most of the minitel screens correctly. -80 column support needs just a little more work. -It may be, too, that the 80 column font could be made much more -readable by utilising a few more pixels on the screen now that we -are able to cover the toolbar. - -- Full toolbar integration -Experimentation will show whether there needs to be more -integration with the toolbar. - -Known Bugs ----------- -- Softmodem disconnection -Often, the modem does not hangup correctly. - -- Choose `USA' from a network connection -USA (from a network connection) gives an `iC' in bottom left hand -corner of screen. Possibly a server issue. Doesn't occur when -connecting directly. The server is really sending this sequence. -Both the FT emulator and their explorer plug-in suffer from it too. - - -John Bates -Vita Nuova Limited diff --git a/appl/wm/minitel/event.b b/appl/wm/minitel/event.b deleted file mode 100644 index f751f55b..00000000 --- a/appl/wm/minitel/event.b +++ /dev/null @@ -1,19 +0,0 @@ -# -# Copyright © 1998 Vita Nuova Limited. All rights reserved. -# - -Event.str(ev: self ref Event) : string -{ - s := "?"; - pick e := ev { - Edata => - s = sprint("Edata %d = ", len e.data); - for(i:=0; i<len e.data; i++) - s += hex(int e.data[i], 2) + " "; - Equit => - s = "Equit"; - Eproto => - s = sprint("Eproto %ux (%s)", e.cmd, e.s); - } - return s; -} diff --git a/appl/wm/minitel/event.m b/appl/wm/minitel/event.m deleted file mode 100644 index 1b524363..00000000 --- a/appl/wm/minitel/event.m +++ /dev/null @@ -1,19 +0,0 @@ -# -# Copyright © 1998 Vita Nuova Limited. All rights reserved. -# - -Event: adt { - path: int; # path for delivery - from: int; # sending module (for reply) - pick { - Edata => - data: array of byte; - Eproto => - cmd: int; - s: string; - a0, a1, a2: int; # parameters - Equit => - } - - str: fn(e: self ref Event) : string; # convert to readable form -}; diff --git a/appl/wm/minitel/keyb.b b/appl/wm/minitel/keyb.b deleted file mode 100644 index aba5485d..00000000 --- a/appl/wm/minitel/keyb.b +++ /dev/null @@ -1,367 +0,0 @@ -# -# 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 diff --git a/appl/wm/minitel/mdisplay.b b/appl/wm/minitel/mdisplay.b deleted file mode 100644 index b3c629f9..00000000 --- a/appl/wm/minitel/mdisplay.b +++ /dev/null @@ -1,799 +0,0 @@ -implement MDisplay; - -# -# Copyright © 1998 Vita Nuova Limited. All rights reserved. -# -# - best viewed with acme! - -include "sys.m"; -include "draw.m"; -include "mdisplay.m"; - -sys : Sys; -draw : Draw; - -Context, Point, Rect, Font, Image, Display, Screen : import draw; - - -# len cell == number of lines -# len cell[0] == number of cellmap cells per char -# (x,y)*cellsize == font glyph clipr - -cellS := array [] of {array [] of {(0, 0)}}; -cellW := array [] of {array [] of {(0, 0), (1, 0)}}; -cellH := array [] of {array [] of {(0, 1)}, array [] of {(0, 0)}}; -cellWH := array [] of {array [] of {(0, 1), (1, 1)}, array [] of {(0, 0), (1, 0)}}; - -Cellinfo : adt { - font : ref Font; - ch, attr : int; - clipmod : (int, int); -}; - - -# current display attributes -display : ref Display; -window : ref Image; -frames := array [2] of ref Image; -update : chan of int; - -colours : array of ref Image; -bright : ref Image; - -# current mode attributes -cellmap : array of Cellinfo; -nrows : int; -ncols : int; -ulheight : int; -curpos : Point; -winoff : Point; -cellsize : Point; -modeattr : con fgWhite | bgBlack; -showC := 0; -delims := 0; -modbbox := Rect((0,0),(0,0)); -blankrow : array of Cellinfo; - -ctxt : ref Context; -font : ref Font; # g0 videotex font - extended with unicode g2 syms -fonth : ref Font; # double height version of font -fontw : ref Font; # double width -fonts : ref Font; # double size -fontg1 : ref Font; # semigraphic videotex font (ch+128=separated) -fontfr : ref Font; # french character set -fontusa : ref Font; # american character set - - -Init(c : ref Context) : string -{ - sys = load Sys Sys->PATH; - draw = load Draw Draw->PATH; - - if (c == nil || c.display == nil) - return "no display context"; - - ctxt = c; - disp := ctxt.display; - - black := disp.rgb2cmap(0, 0, 0); - blue := disp.rgb2cmap(0, 0, 255); - red := disp.rgb2cmap(255, 0, 0); - magenta := disp.rgb2cmap(255, 0, 255); - green := disp.rgb2cmap(0, 255, 0); - cyan := disp.rgb2cmap(0, 255, 255); - yellow := disp.rgb2cmap(255, 255, 0); - white := disp.rgb2cmap(240, 240, 240); - - iblack := disp.color(black); - iblue := disp.color(blue); - ired := disp.color(red); - imagenta := disp.color(magenta); - igreen := disp.color(green); - icyan := disp.color(cyan); - iyellow := disp.color(yellow); - iwhite := disp.color(white); - - colours = array [] of { iblack, iblue, ired, imagenta, - igreen, icyan, iyellow, iwhite}; - bright = disp.color(disp.rgb2cmap(255, 255, 255)); - - update = chan of int; - spawn Update(update); - display = disp; - return nil; -} - -Quit() -{ - if (update != nil) - update <- = QuitUpdate; - update = nil; - window = nil; - frames[0] = nil; - frames[1] = nil; - cellmap = nil; - display = nil; -} - -Mode(r : Draw->Rect, w, h, ulh, d : int, fontpath : string) : (string, ref Draw->Image) -{ - if (display == nil) - # module not properly Init()'d - return ("not initialized", nil); - - curpos = Point(-1, -1); - if (window != nil) - update <- = Pause; - - cellmap = nil; - window = nil; - (dx, dy) := (r.dx(), r.dy()); - if (dx == 0 || dy == 0) { - return (nil, nil); - } - - black := display.rgb2cmap(0, 0, 0); - window = ctxt.screen.newwindow(r, Draw->Refbackup, black); - if (window == nil) - return ("cannot create window", nil); - - window.origin(Point(0,0), r.min); - winr := Rect((0,0), (dx, dy)); - frames[0] = display.newimage(winr, window.chans, 0, black); - frames[1] = display.newimage(winr, window.chans, 0, black); - - if (window == nil || frames[0] == nil || frames[1] == nil) { - window = nil; - return ("cannot allocate display resources", nil); - } - - ncols = w; - nrows = h; - ulheight = ulh; - delims = d; - showC = 0; - - cellmap = array [ncols * nrows] of Cellinfo; - - font = Font.open(display, fontpath); - fontw = Font.open(display, fontpath + "w"); - fonth = Font.open(display, fontpath + "h"); - fonts = Font.open(display, fontpath + "s"); - fontg1 = Font.open(display, fontpath + "g1"); - fontfr = Font.open(display, fontpath + "fr"); - fontusa = Font.open(display, fontpath + "usa"); - - if (font != nil) - cellsize = Point(font.width(" "), font.height); - else - cellsize = Point(dx/ncols, dy / nrows); - - winoff.x = (dx - (cellsize.x * ncols)) / 2; - winoff.y = (dy - (cellsize.y * nrows)) /2; - if (winoff.x < 0) - winoff.x = 0; - if (winoff.y < 0) - winoff.y = 0; - - blankrow = array [ncols] of {* => Cellinfo(font, ' ', modeattr | fgWhite, (0,0))}; - for (y := 0; y < nrows; y++) { - col0 := y * ncols; - cellmap[col0:] = blankrow; - } - -# frames[0].clipr = frames[0].r; -# frames[1].clipr = frames[1].r; -# frames[0].draw(frames[0].r, colours[0], nil, Point(0,0)); -# frames[1].draw(frames[1].r, colours[0], nil, Point(0,0)); -# window.draw(window.r, colours[0], nil, Point(0,0)); - update <- = Continue; - return (nil, window); -} - -Cursor(pt : Point) -{ - if (update == nil || cellmap == nil) - # update thread (cursor/character flashing) not running - return; - - # normalize pt - pt.x--; - - curpos = pt; - update <- = CursorSet; -} - -Put(str : string, pt : Point, charset, attr, insert : int) -{ - if (cellmap == nil || str == nil) - # nothing to do - return; - - # normalize pt - pt.x--; - - f : ref Font; - cell := cellS; - - case charset { - videotex => - if (!(attr & attrD)) - attr &= (fgMask | attrF | attrH | attrW | attrP); - if (attr & attrW && attr & attrH) { - cell = cellWH; - f = fonts; - } else if (attr & attrH) { - cell = cellH; - f = fonth; - } else if (attr & attrW) { - cell = cellW; - f = fontw; - } else { - f = font; - } - - semigraphic => - f = fontg1; - if (attr & attrL) { - # convert to "separated" - newstr := ""; - for (ix := 0; ix < len str; ix++) - newstr[ix] = str[ix] + 16r80; - str = newstr; - } - # semigraphic charset does not support size / polarity attributes - # attrD always set later once field attr established - attr &= ~(attrD | attrH | attrW | attrP | attrL); - - french => f = fontfr; - american => f = fontusa; - * => f = font; - } - - update <- = Pause; - - txty := pt.y - (len cell - 1); - for (cellix := len cell - 1; cellix >= 0; cellix--) { - y := pt.y - cellix; - - if (y < 0) - continue; - if (y >= nrows) - break; - - col0 := y * ncols; - colbase := pt.y * ncols; - - if (delims && !(attr & attrD)) { - # seek back for a delimiter - mask : int; - delimattr := modeattr; - - # semigraphics only inherit attrC from current field - if (charset == semigraphic) - mask = attrC; - else - mask = bgMask | attrC | attrL; - - for (ix := pt.x-1; ix >= 0; ix--) { - cix := ix + col0; - if (cellmap[cix].attr & attrD) { - if (cellmap[cix].font == fontg1 && f != fontg1) - # don't carry over attrL from semigraphic field - mask &= ~attrL; - - delimattr = cellmap[cix].attr; - break; - } - } - attr = (attr & ~mask) | (delimattr & mask); - - # semigraphics validate background colour - if (charset == semigraphic) - attr |= attrD; - } - - strlen := len cell[0] * len str; - gfxwidth := cellsize.x * strlen; - srco := Point(pt.x*cellsize.x, y*cellsize.y); - - if (insert) { - # copy existing cells and display to new position - if (pt.x + strlen < ncols) { - for (destx := ncols -1; destx > pt.x; destx--) { - srcx := destx - strlen; - if (srcx < 0) - break; - cellmap[col0 + destx] = cellmap[col0 + srcx]; - } - - # let draw() do the clipping for us - dsto := Point(srco.x + gfxwidth, srco.y); - dstr := Rect((dsto.x, srco.y), (ncols * cellsize.x, srco.y + cellsize.y)); - - frames[0].clipr = frames[0].r; - frames[1].clipr = frames[1].r; - frames[0].draw(dstr, frames[0], nil, srco); - frames[1].draw(dstr, frames[1], nil, srco); - if (modbbox.dx() == 0) - modbbox = dstr; - else - modbbox = boundingrect(modbbox, dstr); - } - } - - # copy-in new string - x := pt.x; - for (strix := 0; x < ncols && strix < len str; strix++) { - for (clipix := 0; clipix < len cell[cellix]; (x, clipix) = (x+1, clipix+1)) { - if (x < 0) - continue; - if (x >= ncols) - break; - cmix := col0 + x; - cellmap[cmix].font = f; - cellmap[cmix].ch = str[strix]; - cellmap[cmix].attr = attr; - cellmap[cmix].clipmod = cell[cellix][clipix]; - } - } - - # render the new string - txto := Point(srco.x, txty * cellsize.y); - strr := Rect(srco, (srco.x + gfxwidth, srco.y + cellsize.y)); - if (strr.max.x > ncols * cellsize.x) - strr.max.x = ncols * cellsize.x; - - drawstr(str, f, strr, txto, attr); - - # redraw remainder of line until find cell not needing redraw - - # this could be optimised by - # spotting strings with same attrs, font and clipmod pairs - # and write out whole string rather than processing - # a char at a time - - attr2 := attr; - mask := bgMask | attrC | attrL; - s := ""; - for (; delims && x < ncols; x++) { - if (x < 0) - continue; - newattr := cellmap[col0 + x].attr; - - if (cellmap[col0 + x].font == fontg1) { - # semigraphics act as bg colour delimiter - attr2 = (attr2 & ~bgMask) | (newattr & bgMask); - mask &= ~attrL; - } else - if (newattr & attrD) - break; - - if ((attr2 & mask) == (newattr & mask)) - break; - newattr = (newattr & ~mask) | (attr2 & mask); - cellmap[col0 + x].attr = newattr; - s[0] = cellmap[col0 + x].ch; - (cx, cy) := cellmap[col0 + x].clipmod; - f2 := cellmap[col0 + x].font; - - cellpos := Point(x * cellsize.x, y * cellsize.y); - clipr := Rect(cellpos, cellpos.add(Point(cellsize.x, cellsize.y))); - drawpt := cellpos.sub(Point(cx*cellsize.x, cy*cellsize.y)); - drawstr(s, f2, clipr, drawpt, newattr); - } - } - update <- = Continue; -} - -Scroll(topline, nlines : int) -{ - if (cellmap == nil || nlines == 0) - return; - - blankr : Rect; - scr := Rect((0,topline * cellsize.y), (ncols * cellsize.x, nrows * cellsize.y)); - - update <- = Pause; - - frames[0].clipr = scr; - frames[1].clipr = scr; - dstr := scr.subpt(Point(0, nlines * cellsize.y)); - - frames[0].draw(dstr, frames[0], nil, frames[0].clipr.min); - frames[1].draw(dstr, frames[1], nil, frames[1].clipr.min); - - if (nlines > 0) { - # scroll up - copy up from top - if (nlines > nrows - topline) - nlines = nrows - topline; - for (y := nlines + topline; y < nrows; y++) { - srccol0 := y * ncols; - dstcol0 := (y - nlines) * ncols; - cellmap[dstcol0:] = cellmap[srccol0:srccol0+ncols]; - } - for (y = nrows - nlines; y < nrows; y++) { - col0 := y * ncols; - cellmap[col0:] = blankrow; - } - blankr = Rect(Point(0, scr.max.y - (nlines * cellsize.y)), scr.max); - } else { - # scroll down - copy down from bottom - nlines = -nlines; - if (nlines > nrows - topline) - nlines = nrows - topline; - for (y := (nrows - 1) - nlines; y >= topline; y--) { - srccol0 := y * ncols; - dstcol0 := (y + nlines) * ncols; - cellmap[dstcol0:] = cellmap[srccol0:srccol0+ncols]; - } - for (y = topline; y < nlines; y++) { - col0 := y * ncols; - cellmap[col0:] = blankrow; - } - blankr = Rect(scr.min, (scr.max.x, scr.min.y + (nlines * cellsize.y))); - } - frames[0].draw(blankr, colours[0], nil, Point(0,0)); - frames[1].draw(blankr, colours[0], nil, Point(0,0)); - if (modbbox.dx() == 0) - modbbox = scr; - else - modbbox = boundingrect(modbbox, scr); - update <- = Continue; -} - -Reveal(show : int) -{ - showC = show; - if (cellmap == nil) - return; - - update <- = Pause; - for (y := 0; y < nrows; y++) { - col0 := y * ncols; - for (x := 0; x < ncols; x++) { - attr := cellmap[col0+x].attr; - if (!(attr & attrC)) - continue; - - s := ""; - s[0] = cellmap[col0 + x].ch; - (cx, cy) := cellmap[col0 + x].clipmod; - f := cellmap[col0 + x].font; - cellpos := Point(x * cellsize.x, y * cellsize.y); - clipr := Rect(cellpos, cellpos.add(Point(cellsize.x, cellsize.y))); - drawpt := cellpos.sub(Point(cx*cellsize.x, cy*cellsize.y)); - - drawstr(s, f, clipr, drawpt, attr); - } - } - update <- = Continue; -} - -# expects that pt.x already normalized -wordchar(pt : Point) : int -{ - if (pt.x < 0 || pt.x >= ncols) - return 0; - if (pt.y < 0 || pt.y >= nrows) - return 0; - - col0 := pt.y * ncols; - c := cellmap[col0 + pt.x]; - - if (c.attr & attrC && !showC) - # don't let clicking on screen 'reveal' concealed chars! - return 0; - - if (c.font == fontg1) - return 0; - - if (c.attr & attrW) { - # check for both parts of character - (modx, nil) := c.clipmod; - if (modx == 1) { - # rhs of char - check lhs is the same - if (pt.x <= 0) - return 0; - lhc := cellmap[col0 + pt.x-1]; - (lhmodx, nil) := lhc.clipmod; - if (!((lhc.attr & attrW) && (lhc.font == c.font) && (lhc.ch == c.ch) && (lhmodx == 0))) - return 0; - } else { - # lhs of char - check rhs is the same - if (pt.x >= ncols - 1) - return 0; - rhc := cellmap[col0 + pt.x + 1]; - (rhmodx, nil) := rhc.clipmod; - if (!((rhc.attr & attrW) && (rhc.font == c.font) && (rhc.ch == c.ch) && (rhmodx == 1))) - return 0; - } - } - if (c.ch >= 16r30 && c.ch <= 16r39) - # digits - return 1; - if (c.ch >= 16r41 && c.ch <= 16r5a) - # capitals - return 1; - if (c.ch >= 16r61 && c.ch <= 16r7a) - # lowercase - return 1; - if (c.ch == '*' || c.ch == '/') - return 1; - return 0; -} - -GetWord(gfxpt : Point) : string -{ - if (cellmap == nil) - return nil; - - scr := Rect((0,0), (ncols * cellsize.x, nrows * cellsize.y)); - gfxpt = gfxpt.sub(winoff); - - if (!gfxpt.in(scr)) - return nil; - - x := gfxpt.x / cellsize.x; - y := gfxpt.y / cellsize.y; - col0 := y * ncols; - - s := ""; - - # seek back - for (sx := x; sx >= 0; sx--) - if (!wordchar(Point(sx, y))) - break; - - if (sx++ == x) - return nil; - - # seek forward, constructing s - for (; sx < ncols; sx++) { - if (!wordchar(Point(sx, y))) - break; - c := cellmap[col0 + sx]; - s[len s] = c.ch; - if (c.attr & attrW) - sx++; - } - return s; -} - -Refresh() -{ - if (window == nil || modbbox.dx() == 0) - return; - - if (update != nil) - update <- = Redraw; -} - -framecolours(attr : int) : (ref Image, ref Image, ref Image, ref Image) -{ - fg : ref Image; - fgcol := attr & fgMask; - if (fgcol == fgWhite && attr & attrB) - fg = bright; - else - fg = colours[fgcol / fgBase]; - - bg : ref Image; - bgcol := attr & bgMask; - if (bgcol == bgWhite && attr & attrB) - bg = bright; - else - bg = colours[bgcol / bgBase]; - - (fg0, fg1) := (fg, fg); - (bg0, bg1) := (bg, bg); - - if (attr & attrP) - (fg0, bg0, fg1, bg1) = (bg1, fg1, bg0, fg0); - - if (attr & attrF) { - fg0 = fg; - fg1 = bg; - } - - if ((attr & attrC) && !showC) - (fg0, fg1) = (bg0, bg1); - return (fg0, bg0, fg1, bg1); -} - -kill(pid : int) -{ - prog := "/prog/" + string pid + "/ctl"; - fd := sys->open(prog, Sys->OWRITE); - if (fd != nil) { - cmd := array of byte "kill"; - sys->write(fd, cmd, len cmd); - } -} - -timer(ms : int, pc, tick : chan of int) -{ - pc <- = sys->pctl(0, nil); - for (;;) { - sys->sleep(ms); - tick <- = 1; - } -} - -# Update() commands -Redraw, Pause, Continue, CursorSet, QuitUpdate : con iota; - -Update(cmd : chan of int) -{ - flashtick := chan of int; - cursortick := chan of int; - pc := chan of int; - spawn timer(1000, pc, flashtick); - flashpid := <- pc; - spawn timer(500, pc, cursortick); - cursorpid := <- pc; - - cursor : Point; - showcursor := 0; - cursoron := 0; - quit := 0; - nultick := chan of int; - flashchan := nultick; - pcount := 1; - fgframe := 0; - - for (;!quit ;) alt { - c := <- cmd => - case c { - Redraw => - frames[0].clipr = frames[0].r; - frames[1].clipr = frames[1].r; - r := modbbox.addpt(winoff); - window.draw(r.addpt(window.r.min), frames[fgframe], nil, modbbox.min); - if (showcursor && cursoron) - drawcursor(cursor, fgframe, 1); - modbbox = Rect((0,0),(0,0)); - - Pause => - if (pcount++ == 0) - flashchan = nultick; - - Continue => - pcount--; - if (pcount == 0) - flashchan = flashtick; - - QuitUpdate => - quit++; - - CursorSet => - frames[0].clipr = frames[0].r; - frames[1].clipr = frames[1].r; - if (showcursor && cursoron) - drawcursor(cursor, fgframe, 0); - cursoron = 0; - if (curpos.x < 0 || curpos.x >= ncols || curpos.y < 0 || curpos.y >= nrows) - showcursor = 0; - else { - cursor = curpos; - showcursor = 1; - drawcursor(cursor, fgframe, 1); - cursoron = 1; - } - } - - <- flashchan => - # flip displays... - fgframe = (fgframe + 1 ) % 2; - modbbox = Rect((0,0),(0,0)); - frames[0].clipr = frames[0].r; - frames[1].clipr = frames[1].r; - window.draw(window.r.addpt(winoff), frames[fgframe], nil, Point(0,0)); - if (showcursor && cursoron) - drawcursor(cursor, fgframe, 1); - - <- cursortick => - if (showcursor) { - cursoron = !cursoron; - drawcursor(cursor, fgframe, cursoron); - } - } - kill(flashpid); - kill(cursorpid); -} - - -drawstr(s : string, f : ref Font, clipr : Rect, drawpt : Point, attr : int) -{ - (fg0, bg0, fg1, bg1) := framecolours(attr); - frames[0].clipr = clipr; - frames[1].clipr = clipr; - frames[0].draw(clipr, bg0, nil, Point(0,0)); - frames[1].draw(clipr, bg1, nil, Point(0,0)); - ulrect : Rect; - ul := (attr & attrL) && ! (attr & attrD); - - if (f != nil) { - if (ul) - ulrect = Rect((drawpt.x, drawpt.y + f.height - ulheight), (drawpt.x + clipr.dx(), drawpt.y + f.height)); - if (fg0 != bg0) { - frames[0].text(drawpt, fg0, Point(0,0), f, s); - if (ul) - frames[0].draw(ulrect, fg0, nil, Point(0,0)); - } - if (fg1 != bg1) { - frames[1].text(drawpt, fg1, Point(0,0), f, s); - if (ul) - frames[1].draw(ulrect, fg1, nil, Point(0,0)); - } - } - if (modbbox.dx() == 0) - modbbox = clipr; - else - modbbox = boundingrect(modbbox, clipr); -} - -boundingrect(r1, r2 : Rect) : Rect -{ - if (r2.min.x < r1.min.x) - r1.min.x = r2.min.x; - if (r2.min.y < r1.min.y) - r1.min.y = r2.min.y; - if (r2.max.x > r1.max.x) - r1.max.x = r2.max.x; - if (r2.max.y > r1.max.y) - r1.max.y = r2.max.y; - return r1; -} - -drawcursor(pt : Point, srcix, show : int) -{ - col0 := pt.y * ncols; - c := cellmap[col0 + pt.x]; - s := ""; - - s[0] = c.ch; - (cx, cy) := c.clipmod; - cellpos := Point(pt.x * cellsize.x, pt.y * cellsize.y); - clipr := Rect(cellpos, cellpos.add(Point(cellsize.x, cellsize.y))); - clipr = clipr.addpt(winoff); - clipr = clipr.addpt(window.r.min); - - drawpt := cellpos.sub(Point(cx*cellsize.x, cy*cellsize.y)); - drawpt = drawpt.add(winoff); - drawpt = drawpt.add(window.r.min); - - if (!show) { - # copy from appropriate frame buffer - window.draw(clipr, frames[srcix], nil, cellpos); - return; - } - - # invert colours - attr := c.attr ^ (fgMask | bgMask); - - fg, bg : ref Image; - f := c.font; - if (srcix == 0) - (fg, bg, nil, nil) = framecolours(attr); - else - (nil, nil, fg, bg) = framecolours(attr); - - prevclipr := window.clipr; - window.clipr = clipr; - - window.draw(clipr, bg, nil, Point(0,0)); - ulrect : Rect; - ul := (attr & attrL) && ! (attr & attrD); - - if (f != nil) { - if (ul) - ulrect = Rect((drawpt.x, drawpt.y + f.height - ulheight), (drawpt.x + clipr.dx(), drawpt.y + f.height)); - if (fg != bg) { - window.text(drawpt, fg, Point(0,0), f, s); - if (ul) - window.draw(ulrect, fg, nil, Point(0,0)); - } - } - window.clipr = prevclipr; -} diff --git a/appl/wm/minitel/mdisplay.m b/appl/wm/minitel/mdisplay.m deleted file mode 100644 index 24d7173f..00000000 --- a/appl/wm/minitel/mdisplay.m +++ /dev/null @@ -1,115 +0,0 @@ -# -# Minitel display handling module -# -# © 1998 Vita Nuova Limited. All rights reserved. -# - -MDisplay: module -{ - - PATH: con "/dis/wm/minitel/mdisplay.dis"; - - # Available character sets - videotex, semigraphic, french, american : con iota; - - # Fill() attributes bit mask - # - # DL CFPH WBbb bfff - # - # D = Delimiter (set "serial" attributes for rest of line) - # L = Lining (underlined text & "separated" graphics) - # C = Concealing - # F = Flashing - # P = polarity (1 = "inverse") - # H = double height - # W = double width (set H+W for double size) - # B = bright (0: fgwhite=lt.grey, 1: fgwhite=white) - # bbb = background colour - # fff = foreground colour - - fgBase : con 8r001; - bgBase : con 8r010; - attrBase : con 8r100; - - fgMask : con 8r007; - bgMask : con 8r070; - attrMask : con ~0 ^ (fgMask | bgMask); - - fgBlack, fgBlue, fgRed, fgMagenta, - fgGreen, fgCyan, fgYellow, fgWhite : con iota * fgBase; - - bgBlack, bgBlue, bgRed, bgMagenta, - bgGreen, bgCyan, bgYellow, bgWhite : con iota * bgBase; - - attrB, attrW, attrH, attrP, attrF, attrC, attrL, attrD : con attrBase << iota; - - # - # Init (ctxt) : string - # performs general module initialisation - # creates the display window of size/position r using the - # given display context. - # spawns refresh thread - # returns reason for error, or nil on success - # - # Mode(rect, width, height, ulheight, delims, fontpath) : (string, ref Draw->Image) - # set/reset display to given rectangle and character grid size - # ulheight == underline height from bottom of character cell - # if delims != 0 then "field" attrs for Put() are derived from - # preceding delimiter otherwise Put() attrs are taken as is - # - # load fonts: - # <fontpath> videotex - # <fontpath>w videotex double width - # <fontpath>h videotex double height - # <fontpath>s videotex double size - # <fontpath>g1 videotex semigraphics - # <fontpath>fr french character set - # <fontpath>usa american character set - # Note: - # charset g2 is not directly supported, instead the symbols - # of g2 that do not appear in g0 (standard videotex charset) - # are available in videotex font using unicode char codes. - # Therefore controlling s/w must map g2 codes to unicode. - # - # Cursor(pt) - # move cursor to given position - # row number (y) is 0 based - # column number (x) is 1 based - # move cursor off-screen to hide - # - # Put(str, pt, charset, attr, insert) - # render string str at position pt in the given character set - # using specified attributes. - # if insert is non-zero, all characters from given position to end - # of line are moved right by len str positions. - # - # Scroll(topline, nlines) - # move the whole displayby nlines (+ve = scroll up). - # exposed lines of display are set to spaces rendered with - # the current mode attribute flags. - # scroll region is from topline to bottom of display - # - # Reveal(reveal) - # reveal/hide all chars affected by Concealing attribute. - # - # Refresh() - # force screen update - # - # GetWord(pt) : string - # returns on-screen word at given graphics co-ords - # returns nil if blank or semigraphic charset at location - # - # Quit() - # undo Init() - - - Init : fn (ctxt : ref Draw->Context) : string; - Mode : fn (r : Draw->Rect, width, height, ulh, attr : int, fontpath : string) : (string, ref Draw->Image); - Cursor : fn (pt : Draw->Point); - Put : fn (str : string, pt : Draw->Point, chset, attr, insert : int); - Scroll : fn (topline, nlines : int); - Reveal : fn (reveal : int); - Refresh : fn (); - GetWord : fn (gfxpt : Draw->Point) : string; - Quit : fn (); -}; diff --git a/appl/wm/minitel/miniterm.b b/appl/wm/minitel/miniterm.b deleted file mode 100644 index 1c6ff759..00000000 --- a/appl/wm/minitel/miniterm.b +++ /dev/null @@ -1,1187 +0,0 @@ -# -# Copyright © 1998 Vita Nuova Limited. All rights reserved. -# - -implement Miniterm; - -include "sys.m"; - sys: Sys; - print, fprint, sprint, read: import sys; -include "draw.m"; - draw: Draw; -include "tk.m"; - tk: Tk; -include "tkclient.m"; - tkclient: Tkclient; - -include "miniterm.m"; - -Miniterm: module -{ - init: fn(ctxt: ref Draw->Context, argv: list of string); - -}; - -pgrp: int = 0; -debug: array of int = array[256] of {* => 0}; -stderr: ref Sys->FD; - -# Minitel terminal identification request - reply sequence -TERMINALID1 := array [] of { - byte SOH, - byte 'S', byte 'X', byte '1', byte 'H', byte 'N', - byte EOT -}; -TERMINALID2 := array [] of { - byte SOH, - byte 'C', byte 'g', byte '1', - byte EOT -}; - -# Minitel module identifiers -Mscreen, Mmodem, Mkeyb, Msocket, Nmodule: con iota; -Pscreen, Pmodem, Pkeyb, Psocket: con (1 << iota); -Modname := array [Nmodule] of { - Mscreen => "S", - Mmodem => "M", - Mkeyb => "K", - Msocket => "C", - * => "?", -}; - -# attributes common to all modules -Module: adt { - path: int; # bitset to connected modules - disabled: int; -}; - -# A BufChan queues events from the terminal to the modules -BufChan: adt { - path: int; # id bit - ch: chan of ref Event; # set to `in' or `dummy' channel - ev: ref Event; # next event to send - in: chan of ref Event; # real channel for Events to the device - q: array of ref Event; # subsequent events to send -}; - -# holds state information for the minitel `protocol` (chapter 6) -PState: adt { - state: int; - arg: array of int; # up to 3 arguments: X,Y,Z - nargs: int; # expected number of arguments - n: int; # progress - skip: int; # transparency; bytes to skip -}; -PSstart, PSesc, PSarg: con iota; # states - -# Terminal display modes -Videotex, Mixed, Ascii, - -# Connection methods -Direct, Network, - -# Terminal connection states -Local, Connecting, Online, - -# Special features -Echo - : con (1 << iota); - -Terminal: adt { - in: chan of ref Event; - out: array of ref BufChan; # buffered output to the minitel modules - - mode: int; # display mode - state: int; # connection state - spec: int; # special features - connect: int; # Direct, or Network - toplevel: ref Tk->Toplevel; - cmd: chan of string; # from Tk - proto: array of ref PState; # minitel protocol state - netaddr: string; # network address to dial - buttonsleft: int; # display buttons on the LHS (40 cols) - terminalid: array of byte; # ENQROM response - kbctl: chan of string; # softkeyboard control - kbmode: string; # softkeyboard mode - - init: fn(t: self ref Terminal, toplevel: ref Tk->Toplevel, connect: int); - run: fn(t: self ref Terminal, done: chan of int); - reset: fn(t: self ref Terminal); - quit: fn(t: self ref Terminal); - layout: fn(t: self ref Terminal, cols: int); - setkbmode: fn(t: self ref Terminal, tmode: int); -}; - -include "arg.m"; -include "event.m"; -include "event.b"; - -include "keyb.b"; -include "modem.b"; -include "socket.b"; -include "screen.b"; - -K: ref Keyb; -M: ref Modem; -C: ref Socket; -S: ref Screen; -T: ref Terminal; -Modules: array of ref Module; - - -init(ctxt: ref Draw->Context, argv: list of string) -{ - s: string; - netaddr: string = nil; - - sys = load Sys Sys->PATH; - tk = load Tk Tk->PATH; - tkclient = load Tkclient Tkclient->PATH; - tkclient->init(); - draw = load Draw Draw->PATH; - stderr = sys->fildes(2); - pgrp = sys->pctl(Sys->NEWPGRP|Sys->FORKNS, nil); - - arg := load Arg Arg->PATH; - arg->init(argv); - arg->setusage("miniterm [netaddr]"); - while((c := arg->opt()) != 0){ - case c { - 'D' => - s = arg->earg(); - for(i := 0; i < len s; i++){ - c = s[i]; - if(c < len debug) - debug[c] += 1; - } - * => - arg->usage(); - } - } - argv = arg->argv(); - if(len argv > 0) { - netaddr = hd argv; - argv = tl argv; - } - - if(argv != nil) - arg->usage(); - arg = nil; - - # usage: miniterm modem[!init[!number]] - # or miniterm tcp!a.b.c.d - connect: int; - initstr := dialstr := string nil; - if(netaddr == nil) - netaddr = "tcp!pdc.minitelfr.com!513"; # gateway - (nil, words) := sys->tokenize(netaddr, "!"); - if(len words == 0) { - connect = Direct; - words = "modem" :: nil; - } - if(hd words == "modem") { - connect = Direct; - words = tl words; - if(words != nil) { - initstr = hd words; - words = tl words; - if(words != nil) - dialstr = hd words; - } - if(initstr == "*") - initstr = nil; - if(dialstr == "*") - dialstr = nil; - } else { - connect = Network; - dialstr = netaddr; - } - - T = ref Terminal; - K = ref Keyb; - M = ref Modem; - C = ref Socket; - S = ref Screen; - Modules = array [Nmodule] of { - Mscreen => S.m, - Mmodem => M.m, - Mkeyb => K.m, - Msocket => C.m, - }; - - toplevel := tk->toplevel(ctxt.display, ""); - inittk(toplevel, connect); - - T.init(toplevel, connect); - K.init(toplevel); - M.init(connect, initstr, dialstr); - C.init(); - case connect { - Direct => - S.init(ctxt, Rect((0,0), (640,425)), Rect((0,0), (640,425))); - Network => - S.init(ctxt, Rect((0,0), (596,440)), Rect((0,50), (640,350))); - } - - done := chan of int; - spawn K.run(); - spawn M.run(); - spawn C.run(); - spawn S.run(); - spawn T.run(done); - <- done; - - # now tidy up - K.quit(); - M.quit(); - C.quit(); - S.quit(); - T.quit(); -} - -# the keyboard module handles keypresses and focus -BTN40x25: con "-height 24 -font {/fonts/lucidasans/unicode.6.font}"; -BTNCTL: con "-width 60 -height 20 -font {/fonts/lucidasans/unicode.7.font}"; -BTNMAIN: con "-width 80 -height 20 -font {/fonts/lucidasans/unicode.7.font}"; - -tkinitbs := array[] of { - "button .cxfin -text {Cx/Fin} -command {send keyb skey Connect}", - "button .done -text {Quitter} -command {send keyb skey Exit}", - "button .hup -text {Raccr.} -command {send term hangup}", - "button .somm -text {Somm.} -command {send keyb skey Index}", - "button .guide -text {Guide} -command {send keyb skey Guide}", - "button .annul -text {Annul.} -command {send keyb skey Cancel}", - "button .corr -text {Corr.} -command {send keyb skey Correct}", - "button .retour -text {Retour} -command {send keyb skey Previous}", - "button .suite -text {Suite} -command {send keyb skey Next}", - "button .repet -text {Répét.} -command {send keyb skey Repeat}", - "button .envoi -text {Envoi} -command {send keyb skey Send}", - "button .play -text {P} -command {send term play}", -# "button .db -text {D} -command {send term debug}" , - "button .kb -text {Clavier} -command {send term keyboard}", - "button .move -text {<-} -command {send term buttonsleft} " + BTN40x25, -}; - -tkinitdirect := array [] of { - ". configure -background black -height 480 -width 640", - - ".cxfin configure " + BTNCTL, - ".hup configure " + BTNCTL, - ".done configure " + BTNCTL, - ".somm configure " + BTNMAIN, - ".guide configure " + BTNMAIN, - ".annul configure " + BTNMAIN, - ".corr configure " + BTNMAIN, - ".retour configure " + BTNMAIN, - ".suite configure " + BTNMAIN, - ".repet configure " + BTNMAIN, - ".envoi configure " + BTNMAIN, -# ".play configure " + BTNCTL, -# ".db configure " + BTNCTL, - ".kb configure " + BTNCTL, - - "canvas .c -height 425 -width 640 -background black", - "bind .c <Configure> {send term resize}", - "bind .c <Key> {send keyb key %K}", - "bind .c <FocusIn> {send keyb focusin}", - "bind .c <FocusOut> {send keyb focusout}", - "bind .c <ButtonRelease> {focus .c; send keyb click %x %y}", - "frame .k -height 55 -width 640 -background black", - "pack propagate .k no", - "frame .klhs -background black", - "frame .krhs -background black", - "frame .krows -background black", - "frame .k1 -background black", - "frame .k2 -background black", - "pack .cxfin -in .klhs -anchor w -pady 4", - "pack .hup -in .klhs -anchor w", - "pack .somm .annul .retour .repet -in .k1 -side left -padx 2", - "pack .guide .corr .suite .envoi -in .k2 -side left -padx 2", - "pack .kb -in .krhs -anchor e -pady 4", - "pack .done -in .krhs -anchor e", - "pack .k1 -in .krows -pady 4", - "pack .k2 -in .krows", - "pack .klhs .krows .krhs -in .k -side left -expand 1 -fill x", - "pack .c .k", - "focus .c", - "update", -}; - -tkinitip := array [] of { - ". configure -background black -height 440 -width 640", - - # ip 40x25 mode support - "canvas .c40 -height 440 -width 596 -background black", - "bind .c40 <Configure> {send term resize}", - "bind .c40 <Key> {send keyb key %K}", - "bind .c40 <FocusIn> {send keyb focusin}", - "bind .c40 <FocusOut> {send keyb focusout}", - "bind .c40 <ButtonRelease> {focus .c40; send keyb click %x %y}", - "frame .k -height 427 -width 44 -background black", - "frame .gap1 -background black", - "frame .gap2 -background black", - "pack propagate .k no", - - # ip 80x25 mode support - "frame .padtop -height 50", - "canvas .c80 -height 300 -width 640 -background black", - "bind .c80 <Configure> {send term resize}", - "bind .c80 <Key> {send keyb key %K}", - "bind .c80 <FocusIn> {send keyb focusin}", - "bind .c80 <FocusOut> {send keyb focusout}", - "bind .c80 <ButtonRelease> {focus .c80; send keyb click %x %y}", - "frame .k80 -height 90 -width 640 -background black", - "pack propagate .k80 no", - "frame .klhs -background black", - "frame .krows -background black", - "frame .krow1 -background black", - "frame .krow2 -background black", - "frame .krhs -background black", - "pack .krow1 .krow2 -in .krows -pady 2", - "pack .klhs -in .k80 -side left", - "pack .krows -in .k80 -side left -expand 1", - "pack .krhs -in .k80 -side left", -}; - -tkip40x25show := array [] of { - ".cxfin configure " + BTN40x25, - ".hup configure " + BTN40x25, - ".done configure " + BTN40x25, - ".somm configure " + BTN40x25, - ".guide configure " + BTN40x25, - ".annul configure " + BTN40x25, - ".corr configure " + BTN40x25, - ".retour configure " + BTN40x25, - ".suite configure " + BTN40x25, - ".repet configure " + BTN40x25, - ".envoi configure " + BTN40x25, - ".play configure " + BTN40x25, -# ".db configure " + BTN40x25, - ".kb configure " + BTN40x25, - "pack .cxfin -in .k -side top -fill x", - "pack .gap1 -in .k -side top -expand 1", - "pack .guide .repet .somm .annul .corr .retour .suite .envoi -in .k -side top -fill x", - "pack .gap2 -in .k -side top -expand 1", - "pack .done .hup .kb .move -in .k -side bottom -pady 2 -fill x", -# "pack .db -in .k -side bottom", -}; - -tkip40x25lhs := array [] of { - ".move configure -text {->} -command {send term buttonsright}", - "pack .k .c40 -side left", - "focus .c40", - "update", -}; - -tkip40x25rhs := array [] of { - ".move configure -text {<-} -command {send term buttonsleft}", - "pack .c40 .k -side left", - "focus .c40", - "update", -}; - -tkip40x25hide := array [] of { - "pack forget .k .c40", -}; - -tkip80x25show := array [] of { - ".cxfin configure " + BTNCTL, - ".hup configure " + BTNCTL, - ".done configure " + BTNCTL, - ".somm configure " + BTNMAIN, - ".guide configure " + BTNMAIN, - ".annul configure " + BTNMAIN, - ".corr configure " + BTNMAIN, - ".retour configure " + BTNMAIN, - ".suite configure " + BTNMAIN, - ".repet configure " + BTNMAIN, - ".envoi configure " + BTNMAIN, -# ".play configure " + BTNCTL, -# ".db configure " + BTNCTL, - ".kb configure " + BTNCTL, - - "pack .cxfin .hup -in .klhs -anchor w -pady 2", - "pack .somm .annul .retour .repet -in .krow1 -side left -padx 2", - "pack .guide .corr .suite .envoi -in .krow2 -side left -padx 2", - "pack .done .kb -in .krhs -anchor e -pady 2", - "pack .padtop .c80 .k80 -side top", - "focus .c80", - "update", -}; - -tkip80x25hide := array [] of { - "pack forget .padtop .c80 .k80", -}; - -inittk(toplevel: ref Tk->Toplevel, connect: int) -{ - tkcmds(toplevel, tkinitbs); - if(connect == Direct) - tkcmds(toplevel, tkinitdirect); - else - tkcmds(toplevel, tkinitip); -} - -Terminal.layout(t: self ref Terminal, cols: int) -{ - if(t.connect == Direct) - return; - if(cols == 80) { - tkcmds(t.toplevel, tkip40x25hide); - tkcmds(t.toplevel, tkip80x25show); - } else { - tkcmds(t.toplevel, tkip80x25hide); - tkcmds(t.toplevel, tkip40x25show); - if (t.buttonsleft) - tkcmds(t.toplevel, tkip40x25lhs); - else - tkcmds(t.toplevel, tkip40x25rhs); - } -} - -Terminal.init(t: self ref Terminal, toplevel: ref Tk->Toplevel, connect: int) -{ - t.in = chan of ref Event; - t.proto = array [Nmodule] of { - Mscreen => ref PState(PSstart, array [] of {0,0,0}, 0, 0, 0), - Mmodem => ref PState(PSstart, array [] of {0,0,0}, 0, 0, 0), - Mkeyb => ref PState(PSstart, array [] of {0,0,0}, 0, 0, 0), - Msocket => ref PState(PSstart, array [] of {0,0,0}, 0, 0, 0), - }; - - t.toplevel = toplevel; - t.connect = connect; - if (t.connect == Direct) - t.spec = 0; - else - t.spec = Echo; - t.cmd = chan of string; - tk->namechan(t.toplevel, t.cmd, "term"); # Tk -> terminal - t.state = Local; - t.buttonsleft = 0; - t.kbctl = nil; - t.kbmode = "minitel"; - t.reset(); -} - -Terminal.reset(t: self ref Terminal) -{ - t.mode = Videotex; -} - -Terminal.run(t: self ref Terminal, done: chan of int) -{ - t.out = array [Nmodule] of { - Mscreen => ref BufChan(Pscreen, nil, nil, S.in, array [0] of ref Event), - Mmodem => ref BufChan(Pmodem, nil, nil, M.in, array [0] of ref Event), - Mkeyb => ref BufChan(Pkeyb, nil, nil, K.in, array [0] of ref Event), - Msocket => ref BufChan(Psocket, nil, nil, C.in, array [0] of ref Event), - }; - modcount := Nmodule; - if(debug['P']) - post(ref Event.Eproto(Pmodem, 0, Cplay, "play", 0,0,0)); -Evloop: - for(;;) { - ev: ref Event = nil; - post(nil); - alt { - # recv message from one of the modules - ev =<- t.in => - if(ev == nil) { # modules ack Equit with nil - if(--modcount == 0) - break Evloop; - continue; - } - pick e := ev { - Equit => # close modules down - post(ref Event.Equit(Pscreen|Pmodem|Pkeyb|Psocket,0)); - continue; - } - - eva := protocol(ev); - while(len eva > 0) { - post(eva[0]); - eva = eva[1:]; - } - - # send message to `plumbed' modules - t.out[Mscreen].ch <- = t.out[Mscreen].ev => - t.out[Mscreen].ev = nil; - t.out[Mmodem].ch <- = t.out[Mmodem].ev => - t.out[Mmodem].ev = nil; - t.out[Mkeyb].ch <- = t.out[Mkeyb].ev => - t.out[Mkeyb].ev = nil; - t.out[Msocket].ch <- = t.out[Msocket].ev => - t.out[Msocket].ev = nil; - - # recv message from Tk - cmd := <- t.cmd => - (n, word) := sys->tokenize(cmd, " "); - if(n >0) - case hd word { - "resize" => ; - "play" => # for testing only - post(ref Event.Eproto(Pmodem, Mmodem, Cplay, "play", 0,0,0)); - "keyboard" => - if (t.kbctl == nil) { - e: string; - (e, t.kbctl) = kb(t); - if (e != nil) - sys->print("cannot start keyboard: %s\n", e); - } else - t.kbctl <- = "click"; - "hangup" => - if(T.state == Online || T.state == Connecting) - post(ref Event.Eproto(Pmodem, 0, Cdisconnect, "",0,0,0)); - "buttonsleft" => - tkcmds(t.toplevel, tkip40x25lhs); - t.buttonsleft = 1; - if(S.image != nil) - draw->(S.image.origin)(Point(0,0), Point(44, 0)); - if (t.kbctl != nil) - t.kbctl <- = "fg"; - "buttonsright" => - tkcmds(t.toplevel, tkip40x25rhs); - t.buttonsleft = 0; - if(S.image != nil) - draw->(S.image.origin)(Point(0,0), Point(0, 0)); - if (t.kbctl != nil) - t.kbctl <- = "fg"; - "debug" => - debug['s'] ^= 1; - debug['m'] ^= 1; - } - } - - } - if (t.kbctl != nil) - t.kbctl <- = "quit"; - t.kbctl = nil; - done <-= 0; -} - -kb(t: ref Terminal): (string, chan of string) -{ - s := chan of string; - spawn dokb(t, s); - e := <- s; - if (e != nil) - return (e, nil); - return (nil, s); -} - -Terminal.setkbmode(t: self ref Terminal, tmode: int) -{ - case tmode { - Videotex => - t.kbmode = "minitel"; - Mixed or Ascii => - t.kbmode = "standard"; - } - if(t.kbctl != nil) { - t.kbctl <-= "mode"; - t.kbctl <-= "fg"; - } -} - -include "swkeyb.m"; -dokb(t: ref Terminal, c: chan of string) -{ - keyboard := load Keyboard Keyboard->PATH; - if (keyboard == nil) { - c <- = "cannot load keyboard"; - return; - } - - kbctl := chan of string; - (top, m) := tkclient->toplevel(S.ctxt, "", "Keyboard", 0); - tk->cmd(top, "pack .Wm_t -fill x"); - tk->cmd(top, "update"); - keyboard->chaninit(top, S.ctxt, ".keys", kbctl); - tk->cmd(top, "pack .keys"); - - kbctl <-= t.kbmode ; - - kbon := 1; - c <- = nil; # all ok, we are now ready to accept commands - - for (;;) alt { - mcmd := <- m => - if (mcmd == "exit") { - if (kbon) { - tk->cmd(top, ". unmap; update"); - kbon = 0; - } - } else - tkclient->wmctl(top, mcmd); - kbcmd := <- c => - case kbcmd { - "fg" => - if (kbon) - tk->cmd(top, "raise .;update"); - "click" => - if (kbon) { - tk->cmd(top, ". unmap; update"); - kbon = 0; - } else { - tk->cmd(top, ". map; raise ."); - kbon = 1; - } - "mode" => - kbctl <- = t.kbmode; - "quit" => - kbctl <- = "kill"; - top = nil; - # ensure tkclient not blocked on a send to us (probably overkill!) - alt { - <- m => ; - * => ; - } - return; - } - } -} - - -Terminal.quit(nil: self ref Terminal) -{ -} - -# a minitel module sends an event to the terminal for routing -send(e: ref Event) -{ - if(debug['e'] && e != nil) - fprint(stderr, "%s: -> %s\n", Modname[e.from], e.str()); - T.in <- = e; -} - -# post an event to one or more modules -post(e: ref Event) -{ - i,l: int; - for(i=0; i<Nmodule; i++) { - # `ev' is cleared once sent, reload it from the front of `q' - b: ref BufChan = T.out[i]; - l = len b.q; - if(b.ev == nil && l != 0) { - b.ev = b.q[0]; - na := array [l-1] of ref Event; - na[0:] = b.q[1:]; - b.q = na; - } - if (e != nil) { - if(e.path & b.path) { - if(debug['e'] > 0) { - pick de := e { - * => - fprint(stderr, "[%s<-%s] %s\n", Modname[i], Modname[e.from], e.str()); - } - } - if(b.ev == nil) # nothing queued - b.ev = e; - else { # enqueue it - l = len b.q; - na := array [l+1] of ref Event; - na[0:] = b.q[0:]; - na[l] = e; - b.q = na; - } - } - } - # set a dummy channel if nothing to send - if(b.ev == nil) - b.ch = chan of ref Event; - else - b.ch = b.in; - } -} - -# run the terminal protocol -protocol(ev: ref Event): array of ref Event -{ - # Introduced by the following sequences, the minitel protocol can be - # embedded in any normal data sequence - # ESC,0x39,X - # ESC,0x3a,X,Y - # ESC,0x3b,X,Y,Z - # ESC,0x61 - cursor position request - - ea := array [0] of ref Event; # resulting sequence of Events - changed := 0; # if set, results are found in `ea' - - pick e := ev { - Edata => - d0 := 0; # offset of start of last data sequence - p := T.proto[e.from]; - for(i:=0; i<len e.data; i++) { - ch := int e.data[i]; -# if(debug['p']) -# fprint(stderr, "protocol: [%s] %d %ux (%c)\n", Modname[e.from], p.state, ch, ch); - if(p.skip > 0) { # in transparency mode - if(ch == 0 && e.from == Mmodem) # 5.0 - continue; - p.skip--; - continue; - } - case p.state { - PSstart => - if(ch == ESC) { - p.state = PSesc; - changed = 1; - if(i > d0) - ea = eappend(ea, ref Event.Edata(e.path, e.from, e.data[d0:i])); - d0 = i+1; - } - PSesc => - p.state = PSarg; - p.n = 0; - d0 = i+1; - changed = 1; - if(ch >= 16r39 && ch <= 16r3b) #PRO1,2,3 - p.nargs = ch - 16r39 + 1; - else if(ch == 16r61) # cursor position request - p.nargs = 0; - else if(ch == ESC) { - ea = eappend(ea, ref Event.Edata(e.path, e.from, array [] of { byte ESC })); - p.state = PSesc; - } else { - # false alarm, restore as data - ea = eappend(ea, ref Event.Edata(e.path, e.from, array [] of { byte ESC, byte ch })); - p.state = PSstart; - } - PSarg => # expect `nargs' bytes - d0 = i+1; - changed =1; - if(p.n < p.nargs) - p.arg[p.n++] = ch; - if(p.n == p.nargs) { - # got complete protocol sequence - pe := proto(e.from, p); - if(pe != nil) - ea = eappend(ea, pe); - p.state = PSstart; - } - } - } - if(changed) { # some interpretation, results in `ea' - if(i > d0) - ea = eappend(ea, ref Event.Edata(e.path, e.from, e.data[d0:i])); - return ea; - } - ev = e; - return array [] of {ev}; - } - return array [] of {ev}; -} - -# append to an Event array -eappend(ea: array of ref Event, e: ref Event): array of ref Event -{ - l := len ea; - na := array [l+1] of ref Event; - na[0:] = ea[0:]; - na[l] = e; - return na; -} - -# act on a received protocol sequence -# some sequences are handled here by the terminal and result in a posted reply -# others are returned `inline' as Eproto events with the normal data stream. -proto(from: int, p: ref PState): ref Event -{ - if(debug['p']) { - fprint(stderr, "PRO%d: %ux", p.nargs, p.arg[0]); - if(p.nargs > 1) - fprint(stderr, " %ux", p.arg[1]); - if(p.nargs > 2) - fprint(stderr, " %ux", p.arg[2]); - fprint(stderr, " (%s)\n", Modname[from]); - } - case p.nargs { - 0 => # cursor position request ESC 0x61 - reply := array [] of { byte US, byte S.pos.y, byte S.pos.x }; - post(ref Event.Edata(Pmodem, from, reply)); - 1 => - case p.arg[0] { - PROTOCOLSTATUS => ; - ENQROM => # identification request - post(ref Event.Edata(Pmodem, from, T.terminalid)); - if(T.terminalid == TERMINALID1) - T.terminalid = TERMINALID2; - SETRAM1 or SETRAM2 => ; - FUNCTIONINGSTATUS => # 11.3 - PRO2(Pmodem, from, REPFUNCTIONINGSTATUS, osb()); - CONNECT => ; - DISCONNECT => - return ref Event.Eproto(Pscreen, from, Cscreenoff, "",0,0,0); - RESET => # reset the minitel terminal - all := Pscreen|Pmodem|Pkeyb|Psocket; - post(ref Event.Eproto(all, from, Creset, "",0,0,0)); # check - T.reset(); - reply := array [] of { byte SEP, byte 16r5E }; - post(ref Event.Edata(Pmodem, from, reply)); - } - 2 => - case p.arg[0] { - TO => # request for module status - PRO3(Pmodem, from, FROM, p.arg[1], psb(p.arg[1])); - NOBROADCAST => ; - BROADCAST => ; - TRANSPARENCY => # transparency mode - skip bytes - p.skip = p.arg[1]; - if(p.skip < 1 || p.skip > 127) # 5.0 - p.skip = 0; - else { - reply := array [] of { byte SEP, byte 16r57 }; - post(ref Event.Edata(Pmodem, from, reply)); - } - KEYBOARDSTATUS => - if(p.arg[1] == RxKeyb) - PRO3(Pmodem, from, REPKEYBOARDSTATUS, RxKeyb, kosb()); - START => - x := osb(); - if(p.arg[1] == PROCEDURE) - x |= 16r04; - if(p.arg[1] == SCROLLING) - x |= 16r02; - PRO2(Pmodem, from, REPFUNCTIONINGSTATUS, x); - case p.arg[1] { - PROCEDURE => # activate error correction procedure - sys->print("activate error correction\n"); - return ref Event.Eproto(Pmodem, from, Cstartecp, "",0,0,0); - SCROLLING => # set screen to scroll - return ref Event.Eproto(Pscreen, from, Cproto, "",START,SCROLLING,0); - LOWERCASE => # set keyb to invert case - return ref Event.Eproto(Pkeyb, from, Cproto, "",START,LOWERCASE,0); - } - STOP => - x := osb(); - if(p.arg[1] == SCROLLING) - x &= ~16r02; - PRO2(Pmodem, from, REPFUNCTIONINGSTATUS, osb()); - case p.arg[1] { - PROCEDURE => # deactivate error correction procedure - sys->print("deactivate error correction\n"); - return ref Event.Eproto(Pmodem, from, Cstopecp, "",0,0,0); - SCROLLING => # set screen to no scroll - return ref Event.Eproto(Pscreen, from, Cproto, "",STOP,SCROLLING,0); - LOWERCASE => # set keyb to not invert case - return ref Event.Eproto(Pkeyb, from, Cproto, "",STOP,LOWERCASE,0); - } - COPY => # copy screen to socket - # not implemented - ; - MIXED => # change video mode (12.1) - case p.arg[1] { - MIXED1 => # videotex -> mixed - reply := array [] of { byte SEP, byte 16r70 }; - return ref Event.Eproto(Pscreen, from, Cproto, "",MIXED,MIXED1,0); - MIXED2 => # mixed -> videotex - reply := array [] of { byte SEP, byte 16r71 }; - return ref Event.Eproto(Pscreen, from, Cproto, "",MIXED,MIXED2,0); - } - ASCII => # change video mode (12.2) - # TODO - ; - } - 3 => - case p.arg[0] { - OFF or ON => # link, unlink, enable, disable - modcmd(p.arg[0], p.arg[1], p.arg[2]); - PRO3(Pmodem, from, FROM, p.arg[1], psb(TxCode(p.arg[1]))); - START => - case p.arg[1] { - RxKeyb => # keyboard mode - case p.arg[2] { - ETEN => # extended keyboard - K.spec |= Extend; - C0 => # cursor control key coding from col 0 - K.spec |= C0keys; - } - PRO3(Pmodem, from, REPKEYBOARDSTATUS, RxKeyb, kosb()); - } - STOP => # keyboard mode - case p.arg[1] { - RxKeyb => # keyboard mode - case p.arg[2] { - ETEN => # extended keyboard - K.spec &= ~Extend; - C0 => # cursor control key coding from col 0 - K.spec &= ~C0keys; - } - PRO3(Pmodem, from, REPKEYBOARDSTATUS, RxKeyb, kosb()); - } - } - } - return nil; -} - -# post a PRO3 sequence to all modules on `path' -PRO3(path, from, x, y, z: int) -{ - data := array [] of { byte ESC, byte 16r3b, byte x, byte y, byte z}; - post(ref Event.Edata(path, from, data)); -} - -# post a PRO2 sequence to all modules on `path' -PRO2(path, from, x, y: int) -{ - data := array [] of { byte ESC, byte 16r3a, byte x, byte y}; - post(ref Event.Edata(path, from, data)); -} - -# post a PRO1 sequence to all modules on `path' -PRO1(path, from, x: int) -{ - data := array [] of { byte ESC, byte 16r39, byte x}; - post(ref Event.Edata(path, from, data)); -} - -# make or break links between modules, or enable and disable -modcmd(cmd, from, targ: int) -{ - from = RxTx(from); - targ = RxTx(targ); - if(from == targ) # enable or disable module - if(cmd == ON) - Modules[from].disabled = 0; - else - Modules[from].disabled = 1; - else # modify path - if(cmd == ON) - Modules[from].path |= (1<<targ); - else - Modules[from].path &= ~(1<<targ); -} - -# determine the path status byte (3.4) -# if bit 3 of `code' is set then a receive path status byte is returned -# otherwise a transmit path status byte -psb(code: int): int -{ - this := RxTx(code); - b := 16r40; # bit 6 always set - if(code == RxCode(code)) { # want a receive path status byte - mask := (1<<this); - if(Modules[Mscreen].path & mask) - b |= 16r01; - if(Modules[Mkeyb].path & mask) - b |= 16r02; - if(Modules[Mmodem].path & mask) - b |= 16r04; - if(Modules[Msocket].path & mask) - b |= 16r08; - } else { - mod := Modules[this]; - if(mod.path & Mscreen) - b |= 16r01; - if(mod.path & Mkeyb) - b |= 16r02; - if(mod.path & Mmodem) - b |= 16r04; - if(mod.path & Msocket) - b |= 16r08; - } -# if(parity(b)) -# b ^= 16r80; - return b; -} - -# convert `code' to a receive code by setting bit 3 -RxCode(code: int): int -{ - return (code | 16r08)&16rff; -} - -# covert `code' to a send code by clearing bit 3 -TxCode(code: int): int -{ - return (code & ~16r08)&16rff; -} - -# return 0 on even parity, 1 otherwise -# only the bottom 8 bits are considered -parity(b: int): int -{ - bits := 8; - p := 0; - while(bits-- > 0) { - if(b&1) - p ^= 1; - b >>= 1; - } - return p; -} - -# convert Rx or Tx code to a module code -RxTx(code: int): int -{ - rv := 0; - case code { - TxScreen or RxScreen => rv = Mscreen; - TxKeyb or RxKeyb => rv = Mkeyb; - TxModem or RxModem => rv = Mmodem; - TxSocket or RxSocket => rv = Msocket; - * => - fatal("invalid module code"); - } - return rv; -} - -# generate an operating status byte (11.2) -osb(): int -{ - b := 16r40; - if(S.cols == 80) - b |= 16r01; - if(S.spec & Scroll) - b |= 16r02; - if(M.spec & Ecp) - b |= 16r04; - if(K.spec & Invert) - b |= 16r08; -# if(parity(b)) -# b ^= 16r80; - return b; -} - -# generate a keyboard operating status byte (9.1.2) -kosb(): int -{ - b := 16r40; - if(K.spec & Extend) - b |= 16r01; - if(K.spec & C0keys) - b |= 16r04; -# if(parity(b)) -# b ^= 16r80; - return b; -} - -hex(v, n: int): string -{ - return sprint("%.*ux", n, v); -} - -tostr(ch: int): string -{ - str := ""; - str[0] = ch; - return str; -} - -toint(s: string, base: int): (int, string) -{ - if(base < 0 || base > 36) - return (0, s); - - c := 0; - for(i := 0; i < len s; i++) { - c = s[i]; - if(c != ' ' && c != '\t' && c != '\n') - break; - } - - neg := 0; - if(c == '+' || c == '-') { - if(c == '-') - neg = 1; - i++; - } - - ok := 0; - n := 0; - for(; i < len s; i++) { - c = s[i]; - v := base; - case c { - 'a' to 'z' => - v = c - 'a' + 10; - 'A' to 'Z' => - v = c - 'A' + 10; - '0' to '9' => - v = c - '0'; - } - if(v >= base) - break; - ok = 1; - n = n * base + v; - } - - if(!ok) - return (0, s); - if(neg) - n = -n; - return (n, s[i:]); -} - -tolower(s: string): string -{ - r := s; - for(i := 0; i < len r; i++) { - c := r[i]; - if(c >= int 'A' && c <= int 'Z') - r[i] = r[i] + (int 'a' - int 'A'); - } - return r; -} - -# duplicate `ch' exactly `n' times -dup(ch, n: int): string -{ - str := ""; - for(i:=0; i<n; i++) - str[i] = ch; - return str; -} - -fatal(msg: string) -{ - fprint(stderr, "fatal: %s\n", msg); - exits(msg); -} - -exits(s: string) -{ - if(s==nil); -# raise "fail: miniterm " + s; - fd := sys->open("#p/" + string pgrp + "/ctl", sys->OWRITE); - if(fd != nil) - sys->fprint(fd, "killgrp"); - exit; -} - -# Minitel byte MSB and LSB classification (p.87) -MSB(ch: int): int -{ - return (ch&16r70)>>4; -} -LSB(ch: int): int -{ - return (ch&16r0f); -} - -# Minitel character set classification (p.92) -ISC0(ch: int): int -{ - msb := (ch&16r70)>>4; - return msb == 0 || msb == 1; -} - -ISC1(ch: int): int -{ - return ch >= 16r40 && ch <= 16r5f; -} - -ISG0(ch: int): int -{ - # 0x20 (space) and 0x7f (DEL) are not in G0 - return ch > 16r20 && ch < 16r7f; -} - -tkcmds(t: ref Tk->Toplevel, cmds: array of string) -{ - n := len cmds; - for (ix := 0; ix < n; ix++) - tk->cmd(t, cmds[ix]); -} diff --git a/appl/wm/minitel/miniterm.m b/appl/wm/minitel/miniterm.m deleted file mode 100644 index e0345f81..00000000 --- a/appl/wm/minitel/miniterm.m +++ /dev/null @@ -1,120 +0,0 @@ -# -# Copyright © 1998 Vita Nuova Limited. All rights reserved. -# - -# Common control bytes -NUL: con 16r00; -SOH: con 16r01; -EOT: con 16r04; -ENQ: con 16r05; -BEL: con 16r07; -BS: con 16r08; -HT: con 16r09; -LF: con 16r0a; -VT: con 16r0b; -FF: con 16r0c; -CR: con 16r0d; -SO: con 16r0e; -SI: con 16r0f; -DLE: con 16r10; -CON: con 16r11; -XON: con 16r11; -REP: con 16r12; -SEP: con 16r13; -XOFF: con 16r13; -COFF: con 16r14; -NACK: con 16r15; -SYN: con 16r16; -CAN: con 16r18; -SS2: con 16r19; -SUB: con 16r1a; -ESC: con 16r1b; -SS3: con 16r1d; -RS: con 16r1e; -US: con 16r1f; - -SP: con 16r20; -DEL: con 16r7f; - -# Minitel Protocol - some are duplicated (chapter 6) -ASCII: con 16r31; -MIXED: con 16r32; -ETEN: con 16r41; -C0: con 16r43; -SCROLLING: con 16r43; -PROCEDURE: con 16r44; -LOWERCASE: con 16r45; -OFF: con 16r60; -ON: con 16r61; -TO: con 16r62; -FROM: con 16r63; -NOBROADCAST: con 16r64; -BROADCAST: con 16r65; -NONRETURN: con 16r64; -RETURN: con 16r65; -TRANSPARENCY: con 16r66; -DISCONNECT: con 16r67; -CONNECT: con 16r68; -START: con 16r69; -STOP: con 16r6a; -KEYBOARDSTATUS: con 16r72; -REPKEYBOARDSTATUS: con 16r73; -FUNCTIONINGSTATUS: con 16r72; -REPFUNCTIONINGSTATUS: con 16r73; -EXCHANGERATESTATUS: con 16r74; -REPEXCHANGERATESTATUS: con 16r75; -PROTOCOLSTATUS: con 16r76; -REPPROTOCOLSTATUS: con 16r77; -SETRAM1: con 16r78; -SETRAM2: con 16r79; -ENQROM: con 16r7b; -COPY: con 16r7c; -ASCII1: con 16r7d; -MIXED1: con 16r7d; -MIXED2: con 16r7e; -RESET: con 16r7f; - -# Module send and receive codes (chapter 6) -TxScreen: con 16r50; -TxKeyb: con 16r51; -TxModem: con 16r52; -TxSocket: con 16r53; -RxScreen: con 16r58; -RxKeyb: con 16r59; -RxModem: con 16r5a; -RxSocket: con 16r5b; - -# Internal Event.Eproto command constants -Cplay, # for testing -Cconnect, # e.s contains the address to dial -Cdisconnect, # -Crequestecp, # ask server to start ecp -Creset, # reset module -Cstartecp, # start error correction -Cstopecp, # stop error correction -Cproto, # minitel protocol -Ccursor, # update screen cursor -Cindicators, # update row 0 indicators - -# softmodem bug: Cscreenoff, Cscreenon -Cscreenoff, # screen: ignore data -Cscreenon, # screen: don't ignore data - -Clast - : con iota; - -# Special keys - hardware returned byte -KupPC: con 16r0203; # pc emu -KdownPC: con 16r0204; # pc emu -Kup: con 16rE012; -Kdown: con 16rE013; -Kenter: con 16r000a; -Kback: con 16r0008; -Kesc: con 16r001b; -KF1: con 16rE041; -KF2: con 16rE042; -KF3: con 16rE043; -KF4: con 16rE044; -KF13: con 16rE04D; - - diff --git a/appl/wm/minitel/mkfile b/appl/wm/minitel/mkfile deleted file mode 100644 index 16f816a6..00000000 --- a/appl/wm/minitel/mkfile +++ /dev/null @@ -1,24 +0,0 @@ -<../../../mkconfig - -TARG=\ - mdisplay.dis\ - miniterm.dis\ - swkeyb.dis\ - -MODULES=\ - mdisplay.m\ - miniterm.m\ - event.m\ - swkeyb.m\ - -SYSMODULES=\ - arg.m\ - sys.m\ - debug.m\ - draw.m\ - tk.m\ - wmlib.m\ - -DISBIN=$ROOT/dis/wm/minitel - -<$ROOT/mkfiles/mkdis diff --git a/appl/wm/minitel/modem.b b/appl/wm/minitel/modem.b deleted file mode 100644 index b7a21c1d..00000000 --- a/appl/wm/minitel/modem.b +++ /dev/null @@ -1,620 +0,0 @@ -# -# Copyright © 1998 Vita Nuova Limited. All rights reserved. -# - -#modem states for direct connection -MSstart, MSdialing, MSconnected, MSdisconnecting, - -# special features -Ecp # error correction - : con (1 << iota); - -Ecplen: con 17; # error correction block length: data[15], crc, validation (=0) - -Modem: adt { - m: ref Module; # common attributes - in: chan of ref Event; - - connect: int; # None, Direct, Network - state: int; # modem dialing state - saved: string; # response, so far (direct dial) - initstr: string; # softmodem init string (direct dial) - dialstr: string; # softmodem dial string (direct dial) - lastdialstr: string; - - spec: int; # special features - fd: ref Sys->FD; # modem data file, if != nil - cfd: ref Sys->FD; # modem ctl file, if != nil (direct dial only) - devpath: string; # path to the modem; - avail: array of byte; # already read - rd: chan of array of byte; # reader -> rd - pid: int; # reader pid if != 0 - - seq: int; # ECP block sequence number - waitsyn: int; # awaiting restart SYN SYN ... sequence - errforce: int; - addparity: int; # must add parity to outgoing data - - init: fn(m: self ref Modem, connect: int, initstr, dialstr: string); - reset: fn(m: self ref Modem); - run: fn(m: self ref Modem); - quit: fn(m: self ref Modem); - runstate: fn(m: self ref Modem, data: array of byte); - write: fn(m: self ref Modem, data: array of byte):int; # to network - reader: fn(m: self ref Modem, pidc: chan of int); -}; - -partab: array of byte; - -dump(a: array of byte, n: int): string -{ - s := sys->sprint("[%d]", n); - for(i := 0; i < n; i++) - s += sys->sprint(" %.2x", int a[i]); - return s; -} - -Modem.init(m: self ref Modem, connect: int, initstr, dialstr: string) -{ - partab = array[128] of byte; - for(c := 0; c < 128; c++) - if(parity(c)) - partab[c] = byte (c | 16r80); - else - partab[c] = byte c; - m.in = chan of ref Event; - m.connect = connect; - m.state = MSstart; - m.initstr = initstr; - m.dialstr = dialstr; - m.pid = 0; - m.spec = 0; - m.seq = 0; - m.waitsyn = 0; - m.errforce = 0; - m.addparity = 0; - m.avail = array[0] of byte; - m.rd = chan of array of byte; - m.reset(); -} - -Modem.reset(m: self ref Modem) -{ - m.m = ref Module(Pscreen, 0); -} - -Modem.run(m: self ref Modem) -{ - if(m.dialstr != nil) - send(ref Event.Eproto(Pmodem, Mmodem, Cconnect, "", 0,0,0)); -Runloop: - for(;;){ - alt { - ev := <- m.in => - pick e := ev { - Equit => - break Runloop; - Edata => - if(debug['m'] > 0) - fprint(stderr, "Modem <- %s\n", e.str()); - m.write(e.data); - if(T.state == Local || T.spec & Echo) { # loopback - if(e.from == Mkeyb) { - send(ref Event.Eproto(Pscreen, Mkeyb, Ccursor, "", 0,0,0)); - send(ref Event.Edata(Pscreen, Mkeyb, e.data)); - } - } - Eproto => - case e.cmd { - Creset => - m.reset(); - Cconnect => - if(m.pid != 0) - break; - m.addparity = 1; - T.state = Connecting; - send(ref Event.Eproto(Pscreen, Mmodem, Cindicators, "",0,0,0)); - - case m.connect { - Direct => - S.msg("Appel "+m.dialstr+" ..."); - dev := "/dev/modem"; - if(openmodem(m, dev) < 0) { - S.msg("Modem non prêt"); - T.state = Local; - send(ref Event.Eproto(Pscreen, Mmodem, Cindicators, "",0,0,0)); - break; - } - m.state = MSdialing; - m.saved = ""; - dialout(m); - T.terminalid = TERMINALID2; - Network => - S.msg("Connexion au serveur ..."); - if(debug['m'] > 0 || debug['M'] > 0) - sys->print("dial(%s)\n", m.dialstr); - (ok, cx) := sys->dial(m.dialstr, ""); - if (ok == -1){ - S.msg("Echec de la connexion"); - T.state = Local; - send(ref Event.Eproto(Pscreen, Mmodem, Cindicators, "",0,0,0)); - if(debug['m'] > 0) - sys->print("can't dial %s: %r\n", m.dialstr); - break; - } - m.fd = sys->open(cx.dir + "/data", Sys->ORDWR); - m.cfd = cx.cfd; - if(len m.dialstr >= 3 && m.dialstr[0:3] == "tcp") - m.addparity = 0; # Internet gateway apparently doesn't require parity - if(m.fd != nil) { - S.msg(nil); - m.state = MSconnected; - T.state = Online; - send(ref Event.Eproto(Pscreen, Mmodem, Cindicators, "",0,0,0)); - } - T.terminalid = TERMINALID1; - } - if(m.fd != nil) { - pidc := chan of int; - spawn m.reader(pidc); - m.pid = <-pidc; - } - Cdisconnect => - if(m.pid != 0) { - S.msg("Déconnexion ..."); - m.state = MSdisconnecting; - } - if(m.connect == Direct) - hangup(m); - else - nethangup(m); - Cplay => # for testing - case e.s { - "play" => - replay(m); - } - Crequestecp => - if(m.spec & Ecp){ # for testing: if already active, force an error - m.errforce = 1; - break; - } - m.write(array[] of {byte SEP, byte 16r4A}); -sys->print("sending request for ecp\n"); - Cstartecp => - m.spec |= Ecp; - m.seq = 0; # not in spec - m.waitsyn = 0; # not in spec - Cstopecp => - m.spec &= ~Ecp; - * => break; - } - } - b := <- m.rd => - if(debug['m'] > 0){ - fprint(stderr, "Modem -> %s\n", dump(b,len b)); - } - if(b == nil) { - m.pid = 0; - case m.state { - MSdialing => - S.msg("Echec appel"); - MSdisconnecting => - S.msg(nil); - } - m.state = MSstart; - T.state = Local; - send(ref Event.Eproto(Pscreen, Mmodem, Cscreenon, "",0,0,0)); - send(ref Event.Eproto(Pscreen, Mmodem, Cindicators, "",0,0,0)); - break; - } - m.runstate(b); - } - } - if(m.pid != 0) - kill(m.pid); - send(nil); -} - -Modem.quit(nil: self ref Modem) -{ -} - -Modem.runstate(m: self ref Modem, data: array of byte) -{ - if(debug['m']>0) - sys->print("runstate %d %s\n", m.state, dump(data, len data)); - case m.state { - MSstart => ; - MSdialing => - for(i:=0; i<len data; i++) { - ch := int data[i]; - if(ch != '\n' && ch != '\r') { - m.saved[len m.saved] = ch; - continue; - } - (code, str) := seenreply(m.saved); - case code { - Noise or Ok => ; - Success => - S.msg(nil); - m.state = MSconnected; - T.state = Online; - send(ref Event.Eproto(Pscreen, Mmodem, Cindicators, "",0,0,0)); - Failure => - hangup(m); - S.msg(str); - m.state = MSstart; - T.state = Local; - send(ref Event.Eproto(Pscreen, Mmodem, Cindicators, "",0,0,0)); - } - m.saved = ""; - } - MSconnected => - send(ref Event.Edata(m.m.path, Mmodem, data)); - MSdisconnecting => ; - } -} - -Modem.write(m: self ref Modem, data: array of byte): int -{ - if(m.fd == nil) - return -1; - if(len data == 0) - return 0; - if(m.addparity){ - # unfortunately must copy data to add parity for direct modem connection - pa := array[len data] of byte; - for(i := 0; i<len data; i++) - pa[i] = partab[int data[i] & 16r7F]; - data = pa; - } - if(debug['m']>0) - sys->print("WRITE %s\n", dump(data, len data)); - return sys->write(m.fd, data, len data); -} - -# -# minitel error correction protocol -# -# SYN, SYN, block number start of retransmission -# NUL ignored -# DLE escapes {DLE, SYN, NACK, NUL} -# NACK, block restart request -# - -crctab: array of int; -Crcpoly: con 16r9; # crc7 = x^7+x^3+1 - -# precalculate the CRC7 remainder for all bytes - -mktabs() -{ - crctab = array[256] of int; - for(c := 0; c < 256; c++){ - v := c; - crc := 0; - for(i := 0; i < 8; i++){ - crc <<= 1; # align remainder's MSB with value's - if((v^crc) & 16r80) - crc ^= Crcpoly; - v <<= 1; - } - crctab[c] = (crc<<1) & 16rFE; # pre-align the result to save <<1 later - } -} - -# return the index of the first non-NUL character (the start of a block) - -nextblock(a: array of byte, i: int, n: int): int -{ - for(; i < n; i++) - if(a[i] != byte NUL) - break; - return i; -} - -# return the data in the ecp block in a[0:Ecplen] (return nil for bad format) - -decode(a: array of byte): array of byte -{ - if(debug['M']>0) - sys->print("DECODE: %s\n", dump(a, Ecplen)); - badpar := 0; - oldcrc := int a[Ecplen-2]; - crc := 0; - op := 0; - dle := 0; - for(i:=0; i<Ecplen-2; i++){ # first byte is high-order byte of polynomial (MSB first) - c := int a[i]; - nc := c & 16r7F; # strip parity - if((c^int partab[nc]) & 16r80) - badpar++; - crc = crctab[crc ^ c]; - # collapse DLE sequences - if(!dle){ - if(nc == DLE && i+1 < Ecplen-2){ - dle = 1; - continue; - } - if(nc == NUL) - continue; # strip non-escaped NULs - } - dle = 0; - a[op++] = byte nc; - } - if(badpar){ - if(debug['E'] > 0) - sys->print("bad parity\n"); - return nil; - } - crc = (crc>>1)&16r7F; - if(int partab[crc] != oldcrc){ - if(debug['E'] > 0) - sys->print("bad crc: in %ux got %ux\n", oldcrc, int partab[crc]); - return nil; - } - b := array[op] of byte; - b[0:] = a[0:op]; - if(debug['M'] > 0) - sys->print("OUT: %s [%x :: %x]\n", dump(b,op), crc, oldcrc); - return b; -} - -Modem.reader(m: self ref Modem, pidc: chan of int) -{ - pidc <-= sys->pctl(0, nil); - if(crctab == nil) - mktabs(); - a := array[Sys->ATOMICIO] of byte; - inbuf := 0; - while(m.fd != nil) { - while((n := read(m.fd, a[inbuf:], len a-inbuf)) > 0){ - n += inbuf; - inbuf = 0; - if((m.spec & Ecp) == 0){ - b := array[n] of byte; - for(i := 0; i<n; i++) - b[i] = byte (int a[i] & 16r7F); # strip parity - m.rd <-= b; - }else{ - #sys->print("IN: %s\n", dump(a,n)); - i := 0; - if(m.waitsyn){ - sys->print("seeking SYN #%x\n", m.seq); - syn := byte (SYN | 16r80); - lim := n-3; - for(; i <= lim; i++) - if(a[i] == syn && a[i+1] == syn && (int a[i+2]&16r0F) == m.seq){ - i += 3; - m.waitsyn = 0; - sys->print("found SYN #%x@%d\n", m.seq, i-3); - break; - } - } - lim := n-Ecplen; - for(; (i = nextblock(a, i, n)) <= lim; i += Ecplen){ - b := decode(a[i:]); - if(m.errforce || b == nil){ - m.errforce = 0; - b = array[2] of byte; - b[0] = byte NACK; - b[1] = byte (m.seq | 16r40); - sys->print("NACK #%x\n", m.seq); - m.write(b); - m.waitsyn = 1; - i = n; # discard rest of block - break; - } - m.seq = (m.seq+1) & 16rF; # mod 16 counter - m.rd <-= b; - } - if(i < n){ - a[0:] = a[i:n]; - inbuf = n-i; - } - } - } - if(n <= 0) - break; - } -# m.fd = nil; - m.rd <-= nil; -} - -playfd: ref Sys->FD; -in_code, in_char: con iota; - -replay(m: ref Modem) -{ - buf := array[8192] of byte; - DMAX: con 10; - d := 0; - da := array[DMAX] of byte; - playfd = nil; - if(playfd == nil) - playfd = sys->open("minitel.txt", Sys->OREAD); - if(playfd == nil) - return; - nl := 1; - discard := 1; - state := in_code; - hs := ""; - start := 0; -mainloop: - for(;;) { - n := sys->read(playfd, buf, len buf); - if(n <= 0) - break; - for(i:=0; i<n; i++) { - ch := int buf[i]; - if(nl) - case ch { - '>' => discard = 0; - '<' => discard = 1; - if(start) - sys->sleep(1000); - '{' => start = 1; - '}' => break mainloop; - } - if(ch == '\n') - nl = 1; - else - nl = 0; - if(discard) - continue; - if(!start) - continue; - if(state == in_code && ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z'))) - hs[len hs] = ch; - else if(ch == '(') { - state = in_char; - (v, nil) := toint(hs, 16); - da[d++] = byte v; - if(d == DMAX) { - send(ref Event.Edata(m.m.path, Mmodem, da)); - d = 0; - da = array[DMAX] of byte; - sys->sleep(50); - } - hs = ""; - }else if(ch == ')') - state = in_code; - } - } - playfd = nil; - -} - -kill(pid : int) -{ - prog := "#p/" + string pid + "/ctl"; - fd := sys->open(prog, Sys->OWRITE); - if (fd != nil) { - cmd := array of byte "kill"; - sys->write(fd, cmd, len cmd); - } -} - - -# Modem stuff - - -# modem return codes -Ok, Success, Failure, Noise, Found: con iota; - -# -# modem return messages -# -Msg: adt { - text: string; - trans: string; - code: int; -}; - -msgs: array of Msg = array [] of { - ("OK", "Ok", Ok), - ("NO CARRIER", "No carrier", Failure), - ("ERROR", "Bad modem command", Failure), - ("NO DIALTONE", "No dial tone", Failure), - ("BUSY", "Busy tone", Failure), - ("NO ANSWER", "No answer", Failure), - ("CONNECT", "", Success), -}; - -msend(m: ref Modem, x: string): int -{ - a := array of byte x; - return sys->write(m.fd, a, len a); -} - -# -# apply a string of commands to modem -# -apply(m: ref Modem, s: string): int -{ - buf := ""; - for(i := 0; i < len s; i++){ - c := s[i]; - buf[len buf] = c; # assume no Unicode - if(c == '\r' || i == (len s -1)){ - if(c != '\r') - buf[len buf] = '\r'; - if(msend(m, buf) < 0) - return Failure; - buf = ""; - } - } - return Ok; -} - -openmodem(m: ref Modem, dev: string): int -{ - m.fd = sys->open(dev, Sys->ORDWR); - m.cfd = sys->open(dev+"ctl", Sys->ORDWR); - if(m.fd == nil || m.cfd == nil) - return -1; -# hangup(m); -# m.fd = sys->open(dev, Sys->ORDWR); -# m.cfd = sys->open(dev+"ctl", Sys->ORDWR); -# if(m.fd == nil || m.cfd == nil) -# return -1; - return 0; -} - -hangup(m: ref Modem) -{ - sys->sleep(1020); - msend(m, "+++"); - sys->sleep(1020); - apply(m, "ATH0"); - m.fd = nil; -# sys->write(m.cfd, array of byte "f", 1); - sys->write(m.cfd, array of byte "h", 1); - m.cfd = nil; - # HACK: shannon softmodem "off-hook" bug fix - sys->open("/dev/modem", Sys->OWRITE); -} - -nethangup(m: ref Modem) -{ - m.fd = nil; - sys->write(m.cfd, array of byte "hangup", 6); - m.cfd = nil; -} - - -# -# check `s' for a known reply or `substr' -# -seenreply(s: string): (int, string) -{ - for(k := 0; k < len msgs; k++) - if(len s >= len msgs[k].text && s[0:len msgs[k].text] == msgs[k].text) { - return (msgs[k].code, msgs[k].trans); - } - return (Noise, s); -} - -contains(s, t: string): int -{ - if(t == nil) - return 1; - if(s == nil) - return 0; - n := len t; - for(i := 0; i+n <= len s; i++) - if(s[i:i+n] == t) - return 1; - return 0; -} - -dialout(m: ref Modem) -{ - if(m.initstr != nil) - apply(m, "AT"+m.initstr); - if(m.dialstr != nil) { - apply(m, "ATD"+m.dialstr); - m.lastdialstr = m.dialstr; - m.dialstr = nil; - } -} diff --git a/appl/wm/minitel/screen.b b/appl/wm/minitel/screen.b deleted file mode 100644 index 4313d48d..00000000 --- a/appl/wm/minitel/screen.b +++ /dev/null @@ -1,1610 +0,0 @@ -# -# Occasional references are made to sections and tables in the -# France Telecom Minitel specification -# -# Copyright © 1998 Vita Nuova Limited. All rights reserved. -# - -include "mdisplay.m"; - -disp: MDisplay; - -Rect, Point : import Draw; - -# display character sets -videotex, semigraphic, french, american :import MDisplay; - -# display foreground colour attributes -fgBlack, fgBlue, fgRed, fgMagenta, -fgGreen, fgCyan, fgYellow, fgWhite :import MDisplay; - -# display background colour attributes -bgBlack, bgBlue, bgRed, bgMagenta, -bgGreen, bgCyan, bgYellow, bgWhite :import MDisplay; - -fgMask, bgMask : import MDisplay; - -# display formatting attributes -attrB, attrW, attrH, attrP, attrF, attrC, attrL, attrD :import MDisplay; - -# Initial attributes - white on black -ATTR0: con fgWhite|bgBlack&~(attrB|attrW|attrH|attrP|attrF|attrC|attrL|attrD); - -# special features -Cursor, Scroll, Insert - : con (1 << iota); - -# Screen states -Sstart, Sss2, Sesc, Srepeat, Saccent, Scsi0, Scsi1, Sus0, Sus1, Sskip, -Siso2022, Siso6429, Stransparent, Sdrcs, Sconceal, Swaitfor - : con iota; - -# Filter states -FSstart, FSesc, FSsep, FS6429, FS2022: con iota; - -Screen: adt { - m: ref Module; # common attributes - ctxt: ref Draw->Context; - in: chan of ref Event; # from the terminal - - image: ref Draw->Image; # Mdisplay image - dispr40, dispr80: Rect; # 40 and 80 column display region - oldtmode: int; # old terminal mode - rows: int; # number of screen rows (25 for minitel) - cols: int; # number of screen cols (40 or 80) - cset: int; # current display charset - - pos: Point; # current writing position (x:1, y:0) - attr: int; # display attribute set - spec: int; # special features - savepos: Point; # `pos' before moving to row zero - saveattr: int; # `attr' before moving to row zero - savech: int; # last character `Put' - delimit: int; # attr changed, make next space a delimiter - cursor: int; # update cursor soon - - state: int; # recogniser state - a0: int; # recogniser arg 0 - a1: int; # recogniser arg 1 - - fstate: int; # filter state - fsaved: array of byte; # filter `chars so far' - badp: int; # filter because of bad parameter - - ignoredata: int; # ignore data from - - init: fn(s: self ref Screen, ctxt: ref Draw->Context, r40, r80: Rect); - reset: fn(s: self ref Screen); - run: fn(s: self ref Screen); - quit: fn(s: self ref Screen); - setmode: fn(s: self ref Screen, tmode: int); - runstate: fn(s: self ref Screen, data: array of byte); - put: fn(s: self ref Screen, str: string); - msg: fn(s: self ref Screen, str: string); -}; - -Screen.init(s: self ref Screen, ctxt: ref Draw->Context, r40, r80: Rect) -{ - disp = load MDisplay MDisplay->PATH; - if(disp == nil) - fatal("can't load the display module: "+MDisplay->PATH); - - s.m = ref Module(0, 0); - s.ctxt = ctxt; - s.dispr40 = r40; - s.dispr80 = r80; - s.oldtmode = -1; - s.in = chan of ref Event; - disp->Init(s.ctxt); - s.reset(); - s.pos = Point(1, 1); - s.savech = 0; - s.cursor = 1; - s.ignoredata = 0; - s.fstate = FSstart; -} - -Screen.reset(s: self ref Screen) -{ - s.setmode(T.mode); - indicators(s); - s.state = Sstart; -} - -Screen.run(s: self ref Screen) -{ -Runloop: - for(;;) alt { - ev := <- s.in => - pick e := ev { - Equit => - break Runloop; - Eproto => - case e.cmd { - Creset => - s.reset(); - Cproto => - case e.a0 { - START => - case e.a1 { - SCROLLING => - s.spec |= Scroll; - } - STOP => - case e.a1 { - SCROLLING => - s.spec &= ~Scroll; - } - MIXED => - case e.a1 { - MIXED1 => # videotex -> mixed - if(T.mode != Mixed) - s.setmode(Mixed); - T.mode = Mixed; - MIXED2 => # mixed -> videotex - if(T.mode != Videotex) - s.setmode(Videotex); - T.mode = Videotex; - } - } - Ccursor => # update the cursor soon - s.cursor = 1; - Cindicators => - indicators(s); - Cscreenoff => - s.ignoredata = 1; - s.state = Sstart; - Cscreenon => - s.ignoredata = 0; - * => break; - } - Edata => - if(s.ignoredata) - continue; - oldpos := s.pos; - oldspec := s.spec; - da := filter(s, e.data); - while(len da > 0) { - s.runstate(da[0]); - da = da[1:]; - } - - if(s.pos.x != oldpos.x || s.pos.y != oldpos.y || (s.spec&Cursor)^(oldspec&Cursor)) - s.cursor = 1; - if(s.cursor) { - if(s.spec & Cursor) - disp->Cursor(s.pos); - else - disp->Cursor(Point(-1,-1)); - s.cursor = 0; - refresh(); - } else if(e.from == Mkeyb) - refresh(); - } - } - send(nil); -} - -# row0 indicators (1.2.2) -indicators(s: ref Screen) -{ - col: int; - ch: string; - - attr := fgWhite|bgBlack; - case T.state { - Local => - ch = "F"; - Connecting => - ch = "C"; - attr |= attrF; - Online => - ch = "C"; - } - if(s.cols == 40) { - col = 39; - attr |= attrP; - } else - col = 77; - disp->Put(ch, Point(col, 0), videotex, attr, 0); -} - -Screen.setmode(s: self ref Screen, tmode: int) -{ - dispr: Rect; - delims: int; - ulheight: int; - s.rows = 25; - s.spec = 0; - s.attr = s.saveattr = ATTR0; - s.delimit = 0; - s.pos = s.savepos = Point(-1, -1); - s.cursor = 1; - case tmode { - Videotex => - s.cset = videotex; - s.cols = 40; - dispr = s.dispr40; - delims = 1; - ulheight = 2; - s.pos = Point(1,1); - s.spec &= ~Cursor; - Mixed => -# s.cset = french; - s.cset = videotex; - s.cols = 80; - dispr = s.dispr80; - delims = 0; - ulheight = 1; - s.spec |= Scroll; - s.pos = Point(1, 1); - Ascii => - s.cset = french; - s.cols = 80; - dispr = s.dispr80; - delims = 0; - ulheight = 1; - }; - if(tmode != s.oldtmode) { - (nil, s.image) = disp->Mode(((0,0),(0,0)), 0, 0, 0, 0, nil); - T.layout(s.cols); - fontpath := sprint("/fonts/minitel/f%dx%d", s.cols, s.rows); - (nil, s.image) = disp->Mode(dispr, s.cols, s.rows, ulheight, delims, fontpath); - T.setkbmode(tmode); - } - disp->Reveal(0); # concealing enabled (1.2.2) - disp->Cursor(Point(-1,-1)); - s.oldtmode = tmode; -} - -Screen.quit(nil: self ref Screen) -{ - disp->Quit(); -} - -Screen.runstate(s: self ref Screen, data: array of byte) -{ - while(len data > 0) - case T.mode { - Videotex => - data = vstate(s, data); - Mixed => - data = mstate(s, data); - Ascii => - data = astate(s, data); - }; -} - -# process a byte from set C0 -vc0(s: ref Screen, ch: int) -{ - case ch { -# SOH => # not in spec, wait for 16r04 -# s.a0 = 16r04; -# s.state = Swaitfor; - SS2 => - s.state = Sss2; - SYN => - s.state = Sss2; # not in the spec, but acts like SS2 - ESC => - s.state = Sesc; - SO => - s.cset = semigraphic; - s.attr &= ~(attrH|attrW|attrP); # 1.2.4.2 - s.attr &= ~attrL; # 1.2.4.3 - SI => - s.cset = videotex; - s.attr &= ~attrL; # 1.2.4.3 - s.attr &= ~(attrH|attrW|attrP); # some servers seem to assume this too - SEP or SS3 => # 1.2.7 - s.state = Sskip; - BS => - if(s.pos.x == 1) { - if(s.pos.y == 0) - break; - if(s.pos.y == 1) - s.pos.y = s.rows - 1; - else - s.pos.y -= 1; - s.pos.x = s.cols; - } else - s.pos.x -= 1; - HT => - if(s.pos.x == s.cols) { - if(s.pos.y == 0) - break; - if(s.pos.y == s.rows - 1) - s.pos.y = 1; - else - s.pos.y += 1; - s.pos.x = 1; - } else - s.pos.x += 1; - LF => - if(s.pos.y == s.rows - 1) - if(s.spec&Scroll) - scroll(1, 1); - else - s.pos.y = 1; - else if(s.pos.y == 0) { # restore attributes on leaving row zero - s.pos = s.savepos; - s.attr = s.saveattr; - } else - s.pos.y += 1; - VT => - if(s.pos.y == 1) - if(s.spec&Scroll) - scroll(1, -1); - else - s.pos.y = s.rows - 1; - else if(s.pos.y == 0) - break; - else - s.pos.y -= 1; - CR => - s.pos.x = 1; - CAN => - cols := s.cols - s.pos.x + 1; - disp->Put(dup(' ', cols), Point(s.pos.x,s.pos.y), s.cset, s.attr, 0); - US => - # expect US row, col - s.state = Sus0; - FF => - s.cset = videotex; - s.attr = ATTR0; - s.pos = Point(1,1); - s.spec &= ~Cursor; - s.cursor = 1; - clear(s); - RS => - s.cset = videotex; - s.attr = ATTR0; - s.pos = Point(1,1); - s.spec &= ~Cursor; - s.cursor = 1; - CON => - s.spec |= Cursor; - s.cursor = 1; - COFF => - s.spec &= ~Cursor; - s.cursor = 1; - REP => - # repeat - s.state = Srepeat; - NUL => - # padding character - ignore, but may appear anywhere - ; - BEL => - # ah ... - ; - } -} - -# process a byte from the set c1 - introduced by the ESC character -vc1(s: ref Screen, ch: int) -{ - if(ISC0(ch)) { - s.state = Sstart; - vc0(s, ch); - return; - } - if(ch >= 16r20 && ch <= 16r2f) { - if(ch == 16r25) - s.state = Stransparent; - else if(ch == 16r23) - s.state = Sconceal; - else - s.state = Siso2022; - s.a0 = s.a1 = 0; - return; - } - - fg := bg := -1; - case ch { - 16r35 or - 16r36 or - 16r37 => - s.state = Sskip; # skip next char unless C0 - return; - - 16r5b => # CSI sequence - s.a0 = s.a1 = 0; - if(s.pos.y > 0) # 1.2.5.2 - s.state = Scsi0; - return; - - # foreground colour - 16r40 => fg = fgBlack; - 16r41 => fg = fgRed; - 16r42 => fg = fgGreen; - 16r43 => fg = fgYellow; - 16r44 => fg = fgBlue; - 16r45 => fg = fgMagenta; - 16r46 => fg = fgCyan; - 16r47 => fg = fgWhite; - - # background colour - 16r50 => bg = bgBlack; - 16r51 => bg = bgRed; - 16r52 => bg = bgGreen; - 16r53 => bg = bgYellow; - 16r54 => bg = bgBlue; - 16r55 => bg = bgMagenta; - 16r56 => bg = bgCyan; - 16r57 => bg = bgWhite; - - # flashing - 16r48 => s.attr |= attrF; - 16r49 => s.attr &= ~attrF; - - # conceal (serial attribute) - 16r58 => s.attr |= attrC; - s.delimit = 1; - 16r5f => s.attr &= ~attrC; - s.delimit = 1; - - # start lining (+separated graphics) (serial attribute) - 16r5a => s.attr |= attrL; - s.delimit = 1; - 16r59 => s.attr &= ~attrL; - s.delimit = 1; - - # reverse polarity - 16r5d => s.attr |= attrP; - 16r5c => s.attr &= ~attrP; - - # normal size - 16r4c => - s.attr &= ~(attrW|attrH); - - # double height - 16r4d => - if(s.pos.y < 2) - break; - s.attr &= ~(attrW|attrH); - s.attr |= attrH; - - # double width - 16r4e => - if(s.pos.y < 1) - break; - s.attr &= ~(attrW|attrH); - s.attr |= attrW; - - # double size - 16r4f => - if(s.pos.y < 2) - break; - s.attr |= (attrW|attrH); - } - if(fg >= 0) { - s.attr &= ~fgMask; - s.attr |= fg; - } - if(bg >= 0) { - s.attr &= ~bgMask; - s.attr |= bg; - s.delimit = 1; - } - s.state = Sstart; -} - - -# process a SS2 character -vss2(s: ref Screen, ch: int) -{ - if(ISC0(ch)) { - s.state = Sstart; - vc0(s, ch); - return; - } - case ch { - 16r41 or # grave # 5.1.2 - 16r42 or # acute - 16r43 or # circumflex - 16r48 or # umlaut - 16r4b => # cedilla - s.a0 = ch; - s.state = Saccent; - return; - 16r23 => ch = '£'; # Figure 2.8 - 16r24 => ch = '$'; - 16r26 => ch = '#'; - 16r27 => ch = '§'; - 16r2c => ch = 16rc3; # '←'; - 16r2d => ch = 16rc0; # '↑'; - 16r2e => ch = 16rc4; # '→'; - 16r2f => ch = 16rc5; # '↓'; - 16r30 => ch = '°'; - 16r31 => ch = '±'; - 16r38 => ch = '÷'; - 16r3c => ch = '¼'; - 16r3d => ch = '½'; - 16r3e => ch = '¾'; - 16r7a => ch = 'œ'; - 16r6a => ch = 'Œ'; - 16r7b => ch = 'ß'; - } - s.put(tostr(ch)); - s.savech = ch; - s.state = Sstart; -} - -# process CSI functions -vcsi(s: ref Screen, ch: int) -{ - case s.state { - Scsi0 => - case ch { - # move cursor up n rows, stop at top of screen - 'A' => - s.pos.y -= s.a0; - if(s.pos.y < 1) - s.pos.y = 1; - - # move cursor down n rows, stop at bottom of screen - 'B' => - s.pos.y += s.a0; - if(s.pos.y >= s.rows) - s.pos.y = s.rows - 1; - - # move cursor n columns right, stop at edge of screen - 'C' => - s.pos.x += s.a0; - if(s.pos.x > s.cols) - s.pos.x = s.cols; - - # move cursor n columns left, stop at edge of screen - 'D' => - s.pos.x -= s.a0; - if(s.pos.x < 1) - s.pos.x = 1; - - # direct cursor addressing - ';' => - s.state = Scsi1; - return; - - 'J' => - case s.a0 { - # clears from the cursor to the end of the screen inclusive - 0 => - rowclear(s.pos.y, s.pos.x, s.cols); - for(r:=s.pos.y+1; r<s.rows; r++) - rowclear(r, 1, s.cols); - # clears from the beginning of the screen to the cursor inclusive - 1 => - for(r:=1; r<s.pos.y; r++) - rowclear(r, 1, s.cols); - rowclear(s.pos.y, 1, s.pos.x); - # clears the entire screen - 2 => - clear(s); - } - - 'K' => - case s.a0 { - # clears from the cursor to the end of the row - 0 => rowclear(s.pos.y, s.pos.x, s.cols); - - # clears from the start of the row to the cursor - 1 => rowclear(s.pos.y, 1, s.pos.x); - - # clears the entire row in which the cursor is positioned - 2 => rowclear(s.pos.y, 1, s.cols); - } - - # deletes n characters from cursor position - 'P' => - rowclear(s.pos.y, s.pos.x, s.pos.x+s.a0-1); - - # inserts n characters from cursor position - '@' => - disp->Put(dup(' ', s.a0), Point(s.pos.x,s.pos.y), s.cset, s.attr, 1); - - # starts cursor insert mode - 'h' => - if(s.a0 == 4) - s.spec |= Insert; - - 'l' => # ends cursor insert mode - if(s.a0 == 4) - s.spec &= ~Insert; - - # deletes n rows from cursor row - 'M' => - scroll(s.pos.y, s.a0); - - # inserts n rows from cursor row - 'L' => - scroll(s.pos.y, -1*s.a0); - } - s.state = Sstart; - Scsi1 => - case ch { - # direct cursor addressing - 'H' => - if(s.a0 > 0 && s.a0 < s.rows && s.a1 > 0 && s.a1 <= s.cols) - s.pos = Point(s.a1, s.a0); - } - s.state = Sstart; - } -} - -# Screen state - Videotex mode -vstate(s: ref Screen, data: array of byte): array of byte -{ - i: int; - for(i = 0; i < len data; i++) { - ch := int data[i]; - - if(debug['s']) { - cs:=""; - if(s.cset==videotex) cs = "v"; else cs="s"; - fprint(stderr, "vstate %d, %ux (%c) %.4ux %.4ux %s (%d,%d)\n", s.state, ch, ch, s.attr, s.spec, cs, s.pos.y, s.pos.x); - } - case s.state { - Sstart => - if(ISG0(ch) || ch == SP) { - n := 0; - str := ""; - while(i < len data) { - ch = int data[i]; - if(ISG0(ch) || ch == SP) - str[n++] = int data[i++]; - else { - i--; - break; - } - } - if(n > 0) { - if(debug['s']) - fprint(stderr, "vstate puts(%s)\n", str); - s.put(str); - s.savech = str[n-1]; - } - } else if(ISC0(ch)) - vc0(s, ch); - else if(ch == DEL) { - if(s.cset == semigraphic) - ch = 16r5f; - s.put(tostr(ch)); - s.savech = ch; - } - Sss2 => - if(ch == NUL) # 1.2.6.1 - continue; - if(s.cset == semigraphic) # 1.2.3.4 - continue; - vss2(s, ch); - Sesc => - if(ch == NUL) - continue; - vc1(s, ch); - Srepeat => - # byte from `columns' 4 to 7 gives repeat count on 6 bits - # of the last `Put' character - if(ch == NUL) - continue; - if(ISC0(ch)) { - s.state = Sstart; - vc0(s, ch); - break; - } - if(ch >= 16r40 && ch <= 16r7f) - s.put(dup(s.savech, (ch-16r40))); - s.state = Sstart; - Saccent => - case s.a0 { - 16r41 => # grave - case ch { - 'a' => ch = 'à'; - 'e' => ch = 'è'; - 'u' => ch = 'ù'; - } - 16r42 => # acute - case ch { - 'e' => ch = 'é'; - } - 16r43 => # circumflex - case ch { - 'a' => ch = 'â'; - 'e' => ch = 'ê'; - 'i' => ch = 'î'; - 'o' => ch = 'ô'; - 'u' => ch = 'û'; - } - 16r48 => # umlaut - case ch { - 'a' => ch = 'ä'; - 'e' => ch = 'ë'; - 'i' => ch = 'ï'; - 'o' => ch = 'ö'; - 'u' => ch = 'ü'; - } - 16r4b => # cedilla - case ch { - 'c' => ch = 'ç'; - } - } - s.put(tostr(ch)); - s.savech = ch; - s.state = Sstart; - Scsi0 => - if(ch >= 16r30 && ch <= 16r39) { - s.a0 *= 10; - s.a0 += (ch - 16r30); - } else if((ch >= 16r20 && ch <= 16r29) || (ch >= 16r3a && ch <= 16r3f)) { # 1.2.7 - s.a0 = 0; - s.state = Siso6429; - } else - vcsi(s, ch); - Scsi1 => - if(ch >= 16r30 && ch <= 16r39) { - s.a1 *= 10; - s.a1 += (ch - 16r30); - } else - vcsi(s, ch); - Sus0 => - if(ch == 16r23) { # start DRCS definition - s.state = Sdrcs; - s.a0 = 0; - break; - } - if(ch >= 16r40 && ch < 16r80) - s.a0 = (ch - 16r40); - else if(ch >= 16r30 && ch <= 16r32) - s.a0 = (ch - 16r30); - else - s.a0 = -1; - s.state = Sus1; - Sus1 => - if(ch >= 16r40 && ch < 16r80) - s.a1 = (ch - 16r40); - else if(ch >= 16r30 && ch <= 16r39) { - s.a1 = (ch - 16r30); - s.a0 = s.a0*10 + s.a1; # shouldn't be used any more - s.a1 = 1; - } else - s.a1 = -1; - # US row, col : this is how you get to row zero - if(s.a0 >= 0 && s.a0 < s.rows && s.a1 > 0 && s.a1 <= s.cols) { - if(s.a0 == 0 && s.pos.y > 0) { - s.savepos = s.pos; - s.saveattr = s.attr; - } - s.pos = Point(s.a1, s.a0); - s.delimit = 0; # 1.2.5.3, don't reset serial attributes - s.attr = ATTR0; - s.cset = videotex; - } - s.state = Sstart; - Sskip => - # swallow the next character unless from C0 - s.state = Sstart; - if(ISC0(ch)) - vc0(s, ch); - Swaitfor => - # ignore characters until the character in a0 inclusive - if(ch == s.a0) - s.state = Sstart; - Siso2022 => - # 1.2.7 - # swallow (upto) 3 characters from column 2, - # then 1 character from columns 3 to 7 - if(ch == NUL) - continue; - if(ISC0(ch)) { - s.state = Sstart; - vc0(s, ch); - break; - } - s.a0++; - if(s.a0 <= 3) { - if(ch >= 16r20 && ch <= 16r2f) - break; - } - if (s.a0 <= 4 && ch >= 16r30 && ch <= 16r7f) { - s.state = Sstart; - break; - } - s.state = Sstart; - s.put(tostr(DEL)); - Siso6429 => - # 1.2.7 - # swallow characters from column 3, - # or column 2, then 1 from column 4 to 7 - if(ISC0(ch)) { - s.state = Sstart; - vc0(s, ch); - break; - } - if(ch >= 16r20 && ch <= 16r3f) - break; - if(ch >= 16r40 && ch <= 16r7f) { - s.state = Sstart; - break; - } - s.state = Sstart; - s.put(tostr(DEL)); - Stransparent => - # 1.2.7 - # ignore all codes until ESC, 25, 40 or ESC, 2F, 3F - # progress in s.a0 and s.a1 - match := array [] of { - array [] of { ESC, 16r25, 16r40 }, - array [] of { ESC, 16r2f, 16r3f }, - }; - if(ch == ESC) { - s.a0 = s.a1 = 1; - break; - } - if(ch == match[0][s.a0]) - s.a0++; - else - s.a0 = 0; - if(ch == match[1][s.a1]) - s.a1++; - else - s.a1 = 0; - if(s.a0 == 3 || s.a1 == 3) - s.state = Sstart; - Sdrcs => - if(s.a0 > 0) { # fixed number of bytes to skip in a0 - s.a0--; - if(s.a0 == 0) { - s.state = Sstart; - break; - } - } else if(ch == US) # US XX YY - end of DRCS - s.state = Sus0; - else if(ch == 16r20) # US 23 20 20 20 4[23] 49 - s.a0 = 4; - Sconceal => - # 1.2.4.4 - # ESC 23 20 58 - Conceal fields - # ESC 23 20 5F - Reveal fields - # ESC 23 21 XX - Filter - # progress in s.a0 - case s.a0 { - 0 => - if(ch == 16r20 || ch == 16r21) - s.a0 = ch; - 16r20 => - case ch { - 16r58 => - disp->Reveal(0); - disp->Refresh(); - 16r5f => - disp->Reveal(1); - disp->Refresh(); - } - s.state = Sstart; - 16r21 => - s.state = Sstart; - } - } - } - if (i < len data) - return data[i:]; - else - return nil; -} - -# Screen state - Mixed mode -mstate(s: ref Screen, data: array of byte): array of byte -{ - i: int; -Stateloop: - for(i = 0; i < len data; i++) { - ch := int data[i]; - - if(debug['s']) { - cs:=""; - if(s.cset==videotex) cs = "v"; else cs="s"; - fprint(stderr, "mstate %d, %ux (%c) %.4ux %.4ux %s (%d,%d)\n", s.state, ch, ch, s.attr, s.fstate, cs, s.pos.y, s.pos.x); - } - case s.state { - Sstart => - if(ISG0(ch) || ch == SP) { - n := 0; - str := ""; - while(i < len data) { - ch = int data[i]; - if(ISG0(ch) || ch == SP) - str[n++] = int data[i++]; - else { - i--; - break; - } - } - if(n > 0) { - if(debug['s']) - fprint(stderr, "mstate puts(%s)\n", str); - s.put(str); - s.savech = str[n-1]; - } - } else if(ISC0(ch)) - mc0(s, ch); - else if(ch == DEL) { - if(s.cset == semigraphic) - ch = 16r5f; - s.put(tostr(ch)); - s.savech = ch; - } - Sesc => - if(ch == NUL) - continue; - mc1(s, ch); - Scsi0 => - if(ch >= 16r30 && ch <= 16r39) { - s.a0 *= 10; - s.a0 += (ch - 16r30); - } else if(ch == '?') { - s.a0 = '?'; - } else - mcsi(s, ch); - if(T.mode != Mixed) # CSI ? { changes to Videotex mode - break Stateloop; - Scsi1 => - if(ch >= 16r30 && ch <= 16r39) { - s.a1 *= 10; - s.a1 += (ch - 16r30); - } else - mcsi(s, ch); - Sus0 => - if(ch >= 16r40 && ch < 16r80) - s.a0 = (ch - 16r40); - else if(ch >= 16r30 && ch <= 16r32) - s.a0 = (ch - 16r30); - else - s.a0 = -1; - s.state = Sus1; - Sus1 => - if(ch >= 16r40 && ch < 16r80) - s.a1 = (ch - 16r40); - else if(ch >= 16r30 && ch <= 16r39) { - s.a1 = (ch - 16r30); - s.a0 = s.a0*10 + s.a1; # shouldn't be used any more - s.a1 = 1; - } else - s.a1 = -1; - # US row, col : this is how you get to row zero - if(s.a0 >= 0 && s.a0 < s.rows && s.a1 > 0 && s.a1 <= s.cols) { - if(s.a0 == 0 && s.pos.y > 0) { - s.savepos = s.pos; - s.saveattr = s.attr; - } - s.pos = Point(s.a1, s.a0); - s.delimit = 0; # 1.2.5.3, don't reset serial attributes - s.attr = ATTR0; - s.cset = videotex; - } - s.state = Sstart; - Siso6429 => - # 1.2.7 - # swallow characters from column 3, - # or column 2, then 1 from column 4 to 7 - if(ISC0(ch)) { - s.state = Sstart; - mc0(s, ch); - break; - } - if(ch >= 16r20 && ch <= 16r3f) - break; - if(ch >= 16r40 && ch <= 16r7f) { - s.state = Sstart; - break; - } - s.state = Sstart; - s.put(tostr(DEL)); - } - } - if (i < len data) - return data[i:]; - else - return nil; - return nil; -} - -# process a byte from set C0 - Mixed mode -mc0(s: ref Screen, ch: int) -{ - case ch { - ESC => - s.state = Sesc; - SO => -# s.cset = french; - ; - SI => -# s.cset = american; - ; - BS => - if(s.pos.x > 1) - s.pos.x -= 1; - HT => - s.pos.x += 8; - if(s.pos.x > s.cols) - s.pos.x = s.cols; - LF or VT or FF => - if(s.pos.y == s.rows - 1) - if(s.spec&Scroll) - scroll(1, 1); - else - s.pos.y = 1; - else if(s.pos.y == 0) { # restore attributes on leaving row zero - if(ch == LF) { # 4.5 - s.pos = s.savepos; - s.attr = s.saveattr; - } - } else - s.pos.y += 1; - CR => - s.pos.x = 1; - CAN or SUB => # displays the error symbol - filled in rectangle - disp->Put(dup(16r5f, 1), Point(s.pos.x,s.pos.y), s.cset, s.attr, 0); - NUL => - # padding character - ignore, but may appear anywhere - ; - BEL => - # ah ... - ; - XON => # screen copying - ; - XOFF => # screen copying - ; - US => - # expect US row, col - s.state = Sus0; - } -} - -# process a byte from the set c1 - introduced by the ESC character - Mixed mode -mc1(s: ref Screen, ch: int) -{ - if(ISC0(ch)) { - s.state = Sstart; - mc0(s, ch); - return; - } - case ch { - 16r5b => # CSI sequence - s.a0 = s.a1 = 0; - if(s.pos.y > 0) # 1.2.5.2 - s.state = Scsi0; - return; - - 16r44 or # IND like LF - 16r45 => # NEL like CR LF - if(ch == 16r45) - s.pos.x = 1; - if(s.pos.y == s.rows - 1) - if(s.spec&Scroll) - scroll(1, 1); - else - s.pos.y = 1; - else if(s.pos.y == 0) { # restore attributes on leaving row zero - s.pos = s.savepos; - s.attr = s.saveattr; - } else - s.pos.y += 1; - 16r4d => # RI - if(s.pos.y == 1) - if(s.spec&Scroll) - scroll(1, -1); - else - s.pos.y = s.rows - 1; - else if(s.pos.y == 0) - break; - else - s.pos.y -= 1; - } - s.state = Sstart; -} - - -# process CSI functions - Mixed mode -mcsi(s: ref Screen, ch: int) -{ - case s.state { - Scsi0 => - case ch { - # move cursor up n rows, stop at top of screen - 'A' => - if(s.a0 == 0) - s.a0 = 1; - s.pos.y -= s.a0; - if(s.pos.y < 1) - s.pos.y = 1; - - # move cursor down n rows, stop at bottom of screen - 'B' => - if(s.a0 == 0) - s.a0 = 1; - s.pos.y += s.a0; - if(s.pos.y >= s.rows) - s.pos.y = s.rows - 1; - - # move cursor n columns right, stop at edge of screen - 'C' => - if(s.a0 == 0) - s.a0 = 1; - s.pos.x += s.a0; - if(s.pos.x > s.cols) - s.pos.x = s.cols; - - # move cursor n columns left, stop at edge of screen - 'D' => - if(s.a0 == 0) - s.a0 = 1; - s.pos.x -= s.a0; - if(s.pos.x < 1) - s.pos.x = 1; - - # second parameter - ';' => - s.state = Scsi1; - return; - - 'J' => - case s.a0 { - # clears from the cursor to the end of the screen inclusive - 0 => - rowclear(s.pos.y, s.pos.x, s.cols); - for(r:=s.pos.y+1; r<s.rows; r++) - rowclear(r, 1, s.cols); - # clears from the beginning of the screen to the cursor inclusive - 1 => - for(r:=1; r<s.pos.y; r++) - rowclear(r, 1, s.cols); - rowclear(s.pos.y, 1, s.pos.x); - # clears the entire screen - 2 => - clear(s); - } - - 'K' => - case s.a0 { - # clears from the cursor to the end of the row - 0 => rowclear(s.pos.y, s.pos.x, s.cols); - - # clears from the start of the row to the cursor - 1 => rowclear(s.pos.y, 1, s.pos.x); - - # clears the entire row in which the cursor is positioned - 2 => rowclear(s.pos.y, 1, s.cols); - } - - # inserts n characters from cursor position - '@' => - disp->Put(dup(' ', s.a0), Point(s.pos.x,s.pos.y), s.cset, s.attr, 1); - - # starts cursor insert mode - 'h' => - if(s.a0 == 4) - s.spec |= Insert; - - 'l' => # ends cursor insert mode - if(s.a0 == 4) - s.spec &= ~Insert; - - # inserts n rows from cursor row - 'L' => - scroll(s.pos.y, -1*s.a0); - s.pos.x = 1; - - # deletes n rows from cursor row - 'M' => - scroll(s.pos.y, s.a0); - s.pos.x = 1; - - # deletes n characters from cursor position - 'P' => - rowclear(s.pos.y, s.pos.x, s.pos.x+s.a0-1); - - # select Videotex mode - '{' => - if(s.a0 == '?') { - T.mode = Videotex; - s.setmode(T.mode); - } - - # display attributes - 'm' => - case s.a0 { - 0 => s.attr &= ~(attrL|attrF|attrP|attrB); - 1 => s.attr |= attrB; - 4 => s.attr |= attrL; - 5 => s.attr |= attrF; - 7 => s.attr |= attrP; - 22 => s.attr &= ~attrB; - 24 => s.attr &= ~attrL; - 25 => s.attr &= ~attrF; - 27 => s.attr &= ~attrP; - } - # direct cursor addressing - 'H' => - if(s.a0 == 0) - s.a0 = 1; - if(s.a1 == 0) - s.a1 = 1; - if(s.a0 > 0 && s.a0 < s.rows && s.a1 > 0 && s.a1 <= s.cols) - s.pos = Point(s.a1, s.a0); - } - s.state = Sstart; - Scsi1 => - case ch { - # direct cursor addressing - 'H' => - if(s.a0 == 0) - s.a0 = 1; - if(s.a1 == 0) - s.a1 = 1; - if(s.a0 > 0 && s.a0 < s.rows && s.a1 > 0 && s.a1 <= s.cols) - s.pos = Point(s.a1, s.a0); - } - s.state = Sstart; - } -} - - -# Screen state - ASCII mode -astate(nil: ref Screen, nil: array of byte): array of byte -{ - return nil; -} - -# Put a string in the current attributes to the current writing position -Screen.put(s: self ref Screen, str: string) -{ - while((l := len str) > 0) { - n := s.cols - s.pos.x + 1; # characters that will fit on this row - if(s.attr & attrW) { - if(n > 1) # fit normal width character in last column - n /= 2; - } - if(n > l) - n = l; - if(s.delimit) { # set delimiter bit on 1st space (if any) - for(i:=0; i<n; i++) - if(str[i] == ' ') - break; - if(i > 0) { - disp->Put(str[0:i], s.pos, s.cset, s.attr, s.spec&Insert); - incpos(s, i); - } - if(i < n) { - if(debug['s']) { - cs:=""; - if(s.cset==videotex) cs = "v"; else cs="s"; - fprint(stderr, "D %ux %s\n", s.attr|attrD, cs); - } - disp->Put(tostr(str[i]), s.pos, s.cset, s.attr|attrD, s.spec&Insert); - incpos(s, 1); - s.delimit = 0; - # clear serial attributes once used - # hang onto background attribute - needed for semigraphics - case s.cset { - videotex => - s.attr &= ~(attrL|attrC); - semigraphic => - s.attr &= ~(attrC); - } - } - if(i < n-1) { - disp->Put(str[i+1:n], s.pos, s.cset, s.attr, s.spec&Insert); - incpos(s, n-(i+1)); - } - } else { - disp->Put(str[0:n], s.pos, s.cset, s.attr, s.spec&Insert); - incpos(s, n); - } - if(n < len str) - str = str[n:]; - else - str = nil; - } -# if(T.state == Local || T.spec&Echo) -# refresh(); -} - -# increment the current writing position by `n' cells. -# caller must ensure that `n' characters can fit -incpos(s: ref Screen, n: int) -{ - if(s.attr & attrW) - s.pos.x += 2*n; - else - s.pos.x += n; - if(s.pos.x > s.cols) - if(s.pos.y == 0) # no wraparound from row zero - s.pos.x = s.cols; - else { - s.pos.x = 1; - if(s.pos.y == s.rows - 1 && s.spec&Scroll) { - if(s.attr & attrH) { - scroll(1, 2); - } else { - scroll(1, 1); - rowclear(s.pos.y, 1, s.cols); - } - } else { - if(s.attr & attrH) - s.pos.y += 2; - else - s.pos.y += 1; - if(s.pos.y >= s.rows) - s.pos.y -= (s.rows-1); - } - } -} - -# clear row `r' from `first' to `last' column inclusive -rowclear(r, first, last: int) -{ - # 16r5f is the semi-graphic black rectangle - disp->Put(dup(16r5f, last-first+1), Point(first,r), semigraphic, fgBlack, 0); -# disp->Put(dup(' ', last-first+1), Point(first,r), S.cset, fgBlack, 0); -} - -clear(s: ref Screen) -{ - for(r:=1; r<s.rows; r++) - rowclear(r, 1, s.cols); -} - -# called to suggest a display update -refresh() -{ - disp->Refresh(); -} - -# scroll the screen -scroll(topline, nlines: int) -{ - disp->Scroll(topline, nlines); - disp->Refresh(); -} - -# filter the specified ISO6429 and ISO2022 codes from the screen input -# TODO: filter some ISO2022 sequences -filter(s: ref Screen, data: array of byte): array of array of byte -{ - case T.mode { - Videotex => - return vfilter(s, data); - Mixed => - return mfilter(s, data); - Ascii => - return afilter(s, data); - } - return nil; -} - -# filter the specified ISO6429 and ISO2022 codes from the screen input -vfilter(s: ref Screen, data: array of byte): array of array of byte -{ - ba := array [0] of array of byte; - changed := 0; - - d0 := 0; - for(i:=0; i<len data; i++) { - ch := int data[i]; - case s.fstate { - FSstart => - if(ch == ESC) { - s.fstate = FSesc; - changed = 1; - if(i > d0) - ba = dappend(ba, data[d0:i]); - d0 = i+1; - } - FSesc => - d0 = i+1; - changed = 1; - if(ch == '[') { - s.fstate = FS6429; - s.fsaved = array [0] of byte; - s.badp = 0; -# } else if(ch == 16r20) { -# s.fstate = FS2022; -# s.fsaved = array [0] of byte; - s.badp = 0; - } else if(ch == ESC) { - ba = dappend(ba, array [] of { byte ESC }); - s.fstate = FSesc; - } else { - # false alarm - don't filter - ba = dappend(ba, array [] of { byte ESC, byte ch }); - s.fstate = FSstart; - } - FS6429 => # filter out invalid CSI sequences - d0 = i+1; - changed = 1; - if(ch >= 16r20 && ch <= 16r3f) { - if((ch < 16r30 || ch > 16r39) && ch != ';') - s.badp = 1; - a := array [len s.fsaved + 1] of byte; - a[0:] = s.fsaved[0:]; - a[len a - 1] = byte ch; - s.fsaved = a; - } else { - valid := 1; - case ch { - 'A' => ; - 'B' => ; - 'C' => ; - 'D' => ; - 'H' => ; - 'J' => ; - 'K' => ; - 'P' => ; - '@' => ; - 'h' => ; - 'l' => ; - 'M' => ; - 'L' => ; - * => - valid = 0; - } - if(s.badp) - valid = 0; - if(debug['f']) - fprint(stderr, "vfilter %d: %s%c\n", valid, string s.fsaved, ch); - if(valid) { # false alarm - don't filter - ba = dappend(ba, array [] of { byte ESC, byte '[' }); - ba = dappend(ba, s.fsaved); - ba = dappend(ba, array [] of { byte ch } ); - } - s.fstate = FSstart; - } - FS2022 => ; - } - } - if(changed) { - if(i > d0) - ba = dappend(ba, data[d0:i]); - return ba; - } - return array [] of { data }; -} - -# filter the specified ISO6429 and ISO2022 codes from the screen input - Videotex -mfilter(s: ref Screen, data: array of byte): array of array of byte -{ - ba := array [0] of array of byte; - changed := 0; - - d0 := 0; - for(i:=0; i<len data; i++) { - ch := int data[i]; - case s.fstate { - FSstart => - case ch { - ESC => - s.fstate = FSesc; - changed = 1; - if(i > d0) - ba = dappend(ba, data[d0:i]); - d0 = i+1; - SEP => - s.fstate = FSsep; - changed = 1; - if(i > d0) - ba = dappend(ba, data[d0:i]); - d0 = i+1; - } - FSesc => - d0 = i+1; - changed = 1; - if(ch == '[') { - s.fstate = FS6429; - s.fsaved = array [0] of byte; - s.badp = 0; - } else if(ch == ESC) { - ba = dappend(ba, array [] of { byte ESC }); - s.fstate = FSesc; - } else { - # false alarm - don't filter - ba = dappend(ba, array [] of { byte ESC, byte ch }); - s.fstate = FSstart; - } - FSsep => - d0 = i+1; - changed = 1; - if(ch == ESC) { - ba = dappend(ba, array [] of { byte SEP }); - s.fstate = FSesc; - } else if(ch == SEP) { - ba = dappend(ba, array [] of { byte SEP }); - s.fstate = FSsep; - } else { - if(ch >= 16r00 && ch <= 16r1f) - ba = dappend(ba, array [] of { byte SEP , byte ch }); - # consume the character - s.fstate = FSstart; - } - FS6429 => # filter out invalid CSI sequences - d0 = i+1; - changed = 1; - if(ch >= 16r20 && ch <= 16r3f) { - if((ch < 16r30 || ch > 16r39) && ch != ';' && ch != '?') - s.badp = 1; - a := array [len s.fsaved + 1] of byte; - a[0:] = s.fsaved[0:]; - a[len a - 1] = byte ch; - s.fsaved = a; - } else { - valid := 1; - case ch { - 'm' => ; - 'A' => ; - 'B' => ; - 'C' => ; - 'D' => ; - 'H' => ; - 'J' => ; - 'K' => ; - '@' => ; - 'h' => ; - 'l' => ; - 'L' => ; - 'M' => ; - 'P' => ; - '{' => # allow CSI ? { - n := len s.fsaved; - if(n == 0 || s.fsaved[n-1] != byte '?') - s.badp = 1; - * => - valid = 0; - } - if(s.badp) # only decimal params - valid = 0; - if(debug['f']) - fprint(stderr, "mfilter %d: %s%c\n", valid, string s.fsaved, ch); - if(valid) { # false alarm - don't filter - ba = dappend(ba, array [] of { byte ESC, byte '[' }); - ba = dappend(ba, s.fsaved); - ba = dappend(ba, array [] of { byte ch } ); - } - s.fstate = FSstart; - } - FS2022 => ; - } - } - if(changed) { - if(i > d0) - ba = dappend(ba, data[d0:i]); - return ba; - } - return array [] of { data }; -} - -# filter the specified ISO6429 and ISO2022 codes from the screen input - Videotex -afilter(nil: ref Screen, data: array of byte): array of array of byte -{ - return array [] of { data }; -} - -# append to an array of array of byte -dappend(ba: array of array of byte, b: array of byte): array of array of byte -{ - l := len ba; - na := array [l+1] of array of byte; - na[0:] = ba[0:]; - na[l] = b; - return na; -} - -# Put a diagnostic string to row 0 -Screen.msg(s: self ref Screen, str: string) -{ - blank := string array [s.cols -4] of {* => byte ' '}; - n := len str; - if(n > s.cols - 4) - n = s.cols - 4; - disp->Put(blank, Point(1, 0), videotex, 0, 0); - if(str != nil) - disp->Put(str[0:n], Point(1, 0), videotex, fgWhite|attrB, 0); - disp->Refresh(); -}
\ No newline at end of file diff --git a/appl/wm/minitel/socket.b b/appl/wm/minitel/socket.b deleted file mode 100644 index b3ce7fcf..00000000 --- a/appl/wm/minitel/socket.b +++ /dev/null @@ -1,49 +0,0 @@ -# -# Copyright © 1998 Vita Nuova Limited. All rights reserved. -# - -Socket: adt { - m: ref Module; # common attributes - in: chan of ref Event; - - init: fn(c: self ref Socket); - reset: fn(c: self ref Socket); - run: fn(c: self ref Socket); - quit: fn(c: self ref Socket); -}; - -Socket.init(c: self ref Socket) -{ - c.in = chan of ref Event; - c.reset(); -} - -Socket.reset(c: self ref Socket) -{ - c.m = ref Module(Pscreen, 0); -} - -Socket.run(c: self ref Socket) -{ -Runloop: - for(;;){ - ev := <- c.in; - pick e := ev { - Equit => - break Runloop; - Eproto => - case e.cmd { - Creset => - c.reset(); - * => break; - } - Edata => - } - } - send(nil); -} - -Socket.quit(c: self ref Socket) -{ - if(c==nil); -} diff --git a/appl/wm/minitel/swkeyb.b b/appl/wm/minitel/swkeyb.b deleted file mode 100644 index 50cb238f..00000000 --- a/appl/wm/minitel/swkeyb.b +++ /dev/null @@ -1,370 +0,0 @@ -### -### This data and information is not to be used as the basis of manufacture, -### or be reproduced or copied, or be distributed to another party, in whole -### or in part, without the prior written consent of Lucent Technologies. -### -### (C) Copyright 1997 Lucent Technologies -### -### Written by N. W. Knauft -### -# -# Revisions Copyright © 1998 Vita Nuova Limited. - -implement Keyboard; - -include "sys.m"; - sys: Sys; - -include "draw.m"; - draw: Draw; - -include "tk.m"; - tk: Tk; - -include "tkclient.m"; - tkclient: Tkclient; - -include "swkeyb.m"; - -#Icon path -ICPATH: con "keybd/"; - -#Font -FONT: con "/fonts/lucidasans/latin1.7.font"; -SPECFONT: con "/fonts/lucidasans/latin1.6.font"; - -# Dimension constants -KBDWIDTH: con 360; -KBDHEIGHT: con 120; -KEYSIZE: con "19"; -KEYSPACE: con 5; -KEYBORDER: con 1; -KEYGAP: con KEYSPACE - (2 * KEYBORDER); -ENDGAP: con 2 - KEYBORDER; - -# Row size constants (cumulative) -ROW1: con 14; -ROW2: con 28; -ROW3: con 41; -ROW4: con 53; -NKEYS: con 63; - -#Special key number constants -DELKEY: con 13; -TABKEY: con 14; -BACKSLASHKEY: con 27; -CAPSLOCKKEY: con 28 ; -RETURNKEY: con 40; -LSHIFTKEY: con 41; -RSHIFTKEY: con 52; -ESCKEY: con 53; -CTRLKEY: con 54; -METAKEY: con 55; -ALTKEY: con 56; -SPACEKEY: con 57; -ENTERKEY: con 58; -LEFTKEY: con 59; -RIGHTKEY: con 60; -DOWNKEY: con 61; -UPKEY: con 62; - -#Special key code constants -CAPSLOCK: con -1 ; -SHIFT: con -2; -CTRL: con -3; -ALT: con -4; -META: con -5; -MAGIC_PREFIX: con 256; -ARROW_OFFSET: con 57344; -ARROW_PREFIX: con ARROW_OFFSET + 18; - -#Special key width constants -DELSIZE: con 44; -TABSIZE: con 32; -BACKSLASHSIZE: con 31; -CAPSLOCKSIZE: con 44; -RETURNSIZE: con 43; -LSHIFTSIZE: con 56; -RSHIFTSIZE: con 55; -ESCSIZE: con 21; -CTRLSIZE: con 23; -METASIZE: con 38; -ALTSIZE: con 22; -SPACESIZE: con 100; -ENTERSIZE: con 31; - -#Arrow key code constants -UP: con ARROW_PREFIX; -DOWN: con ARROW_PREFIX + 1; -LEFT: con ARROW_PREFIX + 2; -RIGHT: con ARROW_PREFIX + 3; - -direction:= array[] of {"up", "down", "left", "right"}; -row_dimensions:= array[] of {0, ROW1, ROW2, ROW3, ROW4, NKEYS}; - -special_keys:= array[] of { - (DELKEY, DELSIZE), - (TABKEY, TABSIZE), - (BACKSLASHKEY, BACKSLASHSIZE), - (CAPSLOCKKEY, CAPSLOCKSIZE), - (RETURNKEY, RETURNSIZE), - (LSHIFTKEY, LSHIFTSIZE), - (RSHIFTKEY, RSHIFTSIZE), - (ESCKEY, ESCSIZE), - (CTRLKEY, CTRLSIZE), - (METAKEY, METASIZE), - (ALTKEY, ALTSIZE), - (SPACEKEY, SPACESIZE), - (ENTERKEY, ENTERSIZE), -}; - -keys:= array[] of { - # Unshifted - "`", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "Delete", - "Tab", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\\\\", - "CapLoc", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "\'", "Return", - "Shift", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "Shift", - "Esc", "Ctrl", " ", "Alt", " ", "Enter", "<-", "->", "v", "^", - # Shifted - "~", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "Delete", - "Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "\\{", "\\}", "|", - "CapLoc", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"", "Return", - "Shift", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "Shift", - "Esc", "Ctrl", " ", "Alt", " ", "Enter", "<-", "->", "v", "^", -}; - -keyvals:= array[] of { - # Unshifted - '`', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', - '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\\', - CAPSLOCK, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '\n', - SHIFT, 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', SHIFT, - 27, CTRL, META, ALT, 32, '\n', LEFT, RIGHT, DOWN, UP, - # Shifted - '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', - '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '|', - CAPSLOCK, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '\n', - SHIFT, 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', SHIFT, - 27, CTRL, META, ALT, 32, '\n', LEFT, RIGHT, DOWN, UP, -}; - -rowlayout := array[] of { - "frame .f1", - "frame .f2", - "frame .f3", - "frame .f4", - "frame .f5", - "frame .dummy0 -height " + string (ENDGAP), - "frame .dummy1 -height " + string KEYGAP, - "frame .dummy2 -height " + string KEYGAP, - "frame .dummy3 -height " + string KEYGAP, - "frame .dummy4 -height " + string KEYGAP, - "frame .dummy5 -height " + string (ENDGAP + 1), -}; - -# Move key flags -move_key_enabled := 0; -meta_active := 0; - -# Create keyboard widget, spawn keystroke handler -initialize(t: ref Tk->Toplevel, ctxt : ref Draw->Context, dot: string): chan of string -{ - return chaninit(t, ctxt, dot, chan of string); -} - -chaninit(t: ref Tk->Toplevel, ctxt : ref Draw->Context, dot: string, rc: chan of string): chan of string -{ - sys = load Sys Sys->PATH; - draw = load Draw Draw->PATH; - tk = load Tk Tk->PATH; - tkclient = load Tkclient Tkclient->PATH; - - tkclient->init(); - - tk->cmd(t, "frame " + dot + " -bd 2 -relief raised -width " + string KBDWIDTH - + " -height " + string KBDHEIGHT); - tkcmds(t, rowlayout); - - for(i := 0; i < NKEYS; i++) { - tk->cmd(t, "button .b" + string i + " -font " + FONT + " -width " + KEYSIZE - + " -height " + KEYSIZE + " -bd " + string KEYBORDER); - - tk->cmd(t, ".b" + string i + " configure -text {" + keys[i] + - "} -command 'send keypress " + string keyvals[i]); - } - - for(i = 0; i < len special_keys; i++) { - (keynum, keysize) := special_keys[i]; - tk->cmd(t, ".b" + string keynum + " configure -font " + SPECFONT + " -width " + string keysize); - } - - tk->cmd(t, "image create bitmap Capslock_on -file " + ICPATH + "capson.bit -maskfile " + ICPATH + "capson.bit"); - tk->cmd(t, "image create bitmap Capslock_off -file " + ICPATH + "capsoff.bit -maskfile " + ICPATH + "capsoff.bit"); - tk->cmd(t, "image create bitmap Left_arrow -file " + ICPATH + "larrow.bit -maskfile " + ICPATH + "larrow.bit"); - tk->cmd(t, "image create bitmap Right_arrow -file " + ICPATH + "rarrow.bit -maskfile " + ICPATH + "rarrow.bit"); - tk->cmd(t, "image create bitmap Down_arrow -file " + ICPATH + "darrow.bit -maskfile " + ICPATH + "darrow.bit"); - tk->cmd(t, "image create bitmap Up_arrow -file " + ICPATH + "uarrow.bit -maskfile " + ICPATH + "uarrow.bit"); - tk->cmd(t, "image create bitmap Move_on -file " + ICPATH + "moveon.bit -maskfile " + ICPATH + "moveon.bit"); - tk->cmd(t, "image create bitmap Move_off -file " + ICPATH + "moveoff.bit -maskfile " + ICPATH + "moveoff.bit"); - tk->cmd(t, "image create bitmap None -file " + ICPATH + "none.bit -maskfile " + ICPATH + "none.bit"); - tk->cmd(t, ".b" + string CAPSLOCKKEY + " configure -image Capslock_off"); - tk->cmd(t, ".b" + string LEFTKEY + " configure -image Left_arrow"); - tk->cmd(t, ".b" + string RIGHTKEY + " configure -image Right_arrow"); - tk->cmd(t, ".b" + string DOWNKEY + " configure -image Down_arrow"); - tk->cmd(t, ".b" + string UPKEY + " configure -image Up_arrow"); - - for(j:=1; j < len row_dimensions; j++) { - rowstart := row_dimensions[j-1]; - rowend := row_dimensions[j]; - for(i=rowstart; i<rowend; i++) { - if (i == rowstart) { - tk->cmd(t, "frame .f" + string j + ".dummy -width " + string ENDGAP); - tk->cmd(t, "pack .f" + string j + ".dummy -side left"); - } - tk->cmd(t, "pack .b" + string i + " -in .f" + string j + " -side left"); - if (i == rowend-1) - tk->cmd(t, "frame .f" + string j + ".dummy" + string i + " -width " + string ENDGAP); - else - tk->cmd(t, "frame .f" + string j + ".dummy" + string i + " -width " + string KEYGAP); - tk->cmd(t, "pack .f" + string j + ".dummy" + string i + " -side left"); - } - } - - tk->cmd(t, "pack .dummy0 .f1 .dummy1 .f2 .dummy2 .f3 .dummy3 .f4 .dummy4 .f5 .dummy5 -in " + dot); - tk->cmd(t,"update"); - - key := chan of string; - spawn handle_keyclicks(t, ctxt, key, rc); - return key; -} - -tkcmds(t: ref Tk->Toplevel, cmds: array of string) -{ - for(i := 0; i < len cmds; i++) - tk->cmd(t, cmds[i]); -} - -# Process key clicks and hand keycodes off to Tk -handle_keyclicks(t: ref Tk->Toplevel, ctxt : ref Draw->Context, sc, rc: chan of string) -{ - keypress := chan of string; - tk->namechan(t, keypress, "keypress"); - - minitel := 0; - caps_locked := 0; - shifted := 0; - ctrl_active := 0; - alt_active := 0; - -Work: - for(;;){ - alt { - k := <-keypress => - (n, cmdstr) := sys->tokenize(k, " \t\n"); - keycode := int hd cmdstr; - case keycode { - CAPSLOCK => - redisplay_keyboard(t, minitel, caps_locked ^= 1, caps_locked); - shifted = 0; - ctrl_active = 0; - alt_active = 0; - SHIFT => - redisplay_keyboard(t, minitel, (shifted ^= 1) ^ caps_locked, caps_locked); - CTRL => - ctrl_active ^= 1; - if (shifted) { - redisplay_keyboard(t, minitel, caps_locked, caps_locked); - shifted = 0; - } - alt_active = 0; - ALT => - alt_active ^= 1; - if (shifted) { - redisplay_keyboard(t, minitel, caps_locked, caps_locked); - shifted = 0; - } - ctrl_active = 0; - META => - if (move_key_enabled) { - if (meta_active ^= 1) - tk->cmd(t, ".b" + string METAKEY + " configure -image Move_on"); - else - tk->cmd(t, ".b" + string METAKEY + " configure -image Move_off"); - } - redisplay_keyboard(t, minitel, caps_locked, caps_locked); - shifted = 0; - ctrl_active = 0; - alt_active = 0; - * => - if (ctrl_active) { - keycode &= 16r1F; - ctrl_active = 0; - } else if (alt_active) { - keycode += MAGIC_PREFIX; - alt_active = 0; - } - if (meta_active && UP <= keycode && keycode <= RIGHT) { - spawn send_move_msg(direction[keycode - ARROW_PREFIX], sc); - } else - tk->keyboard(t, keycode); - if (shifted) { - redisplay_keyboard(t, minitel, caps_locked, caps_locked); - shifted = 0; - } - } - s := <-rc => - case s { - "kill" => - break Work; - "minitel" => - if (!minitel) { - minitel = 1; - redisplay_keyboard(t, minitel, shifted, caps_locked); - } - "standard" => - if (minitel) { - minitel = 0; - redisplay_keyboard(t, minitel, shifted, caps_locked); - } - } - } - } -} - -send_move_msg(dir: string, ch: chan of string) -{ - ch <-= dir; -} - - -# Redisplay keyboard to reflect current state (shifted or unshifted) -redisplay_keyboard(t: ref Tk->Toplevel, minitel, shifted, caps_locked: int) -{ - base: int; - - if (shifted) - base = NKEYS; - else - base = 0; - - for(i:=0; i<NKEYS; i++) { - n := base + i; - val := keyvals[n]; - key := keys[n]; - if (minitel) { - if (val >= int 'A' && val <= int 'Z') { - key = keys[n-NKEYS]; - } else if (val >= int 'a' && val <= int 'z') { - key = keys[n+NKEYS]; - } - } - - tk->cmd(t, ".b" + string i + " configure -text {" + key - + "} -command 'send keypress " + string val); - } - if (caps_locked) - tk->cmd(t, ".b" + string CAPSLOCKKEY + " configure -image Capslock_on"); - else - tk->cmd(t, ".b" + string CAPSLOCKKEY + " configure -image Capslock_off"); - tk->cmd(t, "update"); -} diff --git a/appl/wm/minitel/swkeyb.m b/appl/wm/minitel/swkeyb.m deleted file mode 100644 index 52206801..00000000 --- a/appl/wm/minitel/swkeyb.m +++ /dev/null @@ -1,21 +0,0 @@ -### -### This data and information is not to be used as the basis of manufacture, -### or be reproduced or copied, or be distributed to another party, in whole -### or in part, without the prior written consent of Lucent Technologies. -### -### (C) Copyright 1997 Lucent Technologies -### -### Written by N. W. Knauft -### - -# Revisions Copyright © 1998 Vita Nuova Limited. - -Keyboard: module -{ - PATH: con "/dis/wm/minitel/swkeyb.dis"; - - initialize: fn(t: ref Tk->Toplevel, ctxt : ref Draw->Context, - dot: string): chan of string; - chaninit: fn(t: ref Tk->Toplevel, ctxt : ref Draw->Context, - dot: string, rc: chan of string): chan of string; -}; |
