diff options
Diffstat (limited to 'appl/wm/minitel/mdisplay.b')
| -rw-r--r-- | appl/wm/minitel/mdisplay.b | 799 |
1 files changed, 799 insertions, 0 deletions
diff --git a/appl/wm/minitel/mdisplay.b b/appl/wm/minitel/mdisplay.b new file mode 100644 index 00000000..b3c629f9 --- /dev/null +++ b/appl/wm/minitel/mdisplay.b @@ -0,0 +1,799 @@ +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; +} |
