summaryrefslogtreecommitdiff
path: root/appl/wm/minitel/mdisplay.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/wm/minitel/mdisplay.b')
-rw-r--r--appl/wm/minitel/mdisplay.b799
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;
+}