<> <> <> <> <> DIRECTORY PrincOps, PrincOpsUtils, Buttons, ViewerOps, CKViewerPanel, CKViewerButtons, MessageWindow, Basics, Real, Process, Terminal, InterminalBackdoor; CKViewerOpsImpl: CEDAR PROGRAM IMPORTS PrincOpsUtils, Buttons, Terminal, InterminalBackdoor, Basics, Real, Process, MessageWindow, CKViewerButtons, ViewerOps EXPORTS CKViewerButtons, CKViewerPanel = BEGIN OPEN MW: MessageWindow; CKViewer: TYPE = CKViewerButtons.CKViewer; CKViewerState: TYPE = CKViewerButtons.CKViewerState; terminal: Terminal.Virtual ~ InterminalBackdoor.terminal; sym: BOOLEAN _ FALSE; bbspace: PrincOps.BBTableSpace; bb: PrincOps.BBptr; grayword: CARDINAL _ 0; bitmapB: Terminal.FrameBuffer _ NIL; lbpp,lbppB: CARDINAL _ 0; -- log (base 2) bits per pixel bpp,bppB: CARDINAL _ 0; -- bits per pixel full: BOOLEAN _ FALSE; ashow,bshow: BOOLEAN _ TRUE; splat: BOOLEAN _ TRUE; cornerSize: CARDINAL _ 4; cornerColor: CARDINAL _ 8; Triple: TYPE = RECORD[r,g,b: [0..256)]; testmap: ARRAY[0..8] OF Triple _ [ [255,255,255], -- white [255,255, 0], -- yellow [ 0,255,255], -- cyan [ 0,255, 0], -- green [255, 0,255], -- magenta [255, 0, 0], -- red [ 0, 0,255], -- blue [127,127,127], -- gray (background) [ 0, 0, 0] -- black ]; black: [0..8] = 8; --testmap[8] is black gray: [0..8] = 7; --testmap[8] is gray white: [0..8] = 0; --testmap[0] is white Diag: PROC [] = { <> Bham: PROC [fx,fy,lx,ly: INTEGER, color: [0..8]] = { <> yt: REAL; xx, xi: INTEGER; yy, yi: INTEGER; dx: INTEGER _ lx-fx; dy: INTEGER _ ly-fy; IF dy>dx OR dx<=0 OR dy<=0 THEN ERROR; FOR xi IN [0..dx) DO yt _ (Real.Float[dy]/Real.Float[dx])*Real.Float[xi]; yi _ Real.FixI[yt+0.5]; xx_fx+xi; yy_fy+yi; TestRect[xx,yy,1,1,color]; TestRect[iWidth-xx,yy,1,1,color]; TestRect[xx,iHeight-yy,1,1,color]; TestRect[iWidth-xx,iHeight-yy,1,1,color]; ENDLOOP; }; iWidth: INTEGER _ terminal.colorWidth; iHeight: INTEGER _ terminal.colorHeight; deltaX: INTEGER _ iWidth/40; deltaY: INTEGER _ (deltaX*iHeight)/iWidth; xi: INTEGER _ 0; yi: INTEGER _ 0; IF full THEN Positive[] ELSE { FOR i: CARDINAL IN[0..9) DO Terminal.SetColor[vt: terminal, aChannelValue: i, red: testmap[i].r, green: testmap[i].g, blue: testmap[i].b]; ENDLOOP; SetShowButtons[TRUE,FALSE,FALSE]; }; TestRect[0,0,terminal.colorWidth,terminal.colorHeight,black]; --black background FOR n: INTEGER _ 0, n+1 UNTIL xi >=iWidth DO Bham[fx: xi, fy: 0, lx: iWidth-1, ly: iHeight-n*deltaY, color: white]; xi_xi+deltaX; ENDLOOP; }; ShowTPat: PROC = { x: CARDINAL = 60; h: CARDINAL = 60; w: CARDINAL = 50; gap: CARDINAL = 15; IF full THEN Positive[] ELSE { FOR i: CARDINAL IN[0..9) DO Terminal.SetColor[vt: terminal, aChannelValue: i, red: testmap[i].r, green: testmap[i].g, blue: testmap[i].b]; ENDLOOP; SetShowButtons[TRUE,FALSE,FALSE]; }; <> TestRect[0,0,terminal.colorWidth,terminal.colorHeight,gray]; <> { s: CARDINAL = cornerSize; c: CARDINAL = cornerColor; -- color TestRect[0,0,s,s,c]; TestRect[terminal.colorWidth-s,0,s,s,c]; TestRect[0,terminal.colorHeight-s,s,s,c]; TestRect[terminal.colorWidth-s,terminal.colorHeight-s,s,s,c]; }; <> FOR i: CARDINAL IN[0..8) DO r,g,b: [0..256); r _ (i MOD 2)*255; g _ ((i/2) MOD 2)*255; b _ ((i/4) MOD 2)*255; TestRect[4,i*h+10,x,h-20,i+1]; ENDLOOP; <> FOR i: CARDINAL IN[0..8) DO r,g,b: [0..256); r _ (i MOD 2)*255; g _ ((i/2) MOD 2)*255; b _ ((i/4) MOD 2)*255; TestRect[x+i*w+gap,0,w-gap,8*h,i+1]; ENDLOOP; }; TestRect: PROC[x,y,w,h: CARDINAL, i: [0..8]] = { IF NOT (x> bb.dst _ [word: frameA.base + LONG[y]*frameA.wordsPerLine + x, bit: 0]; bb.dstBpl _ 16*frameA.wordsPerLine; bb.src _ [word: @rgword, bit: 0]; bb.width _ 16*w; bb.height _ h; bb.flags.dstFunc _ null; PrincOpsUtils.BITBLT[bb]; <> bb.dst _ [word: frameB.base + LONG[y]*frameB.wordsPerLine + x/2, bit: 8*(x MOD 2)]; bb.dstBpl _ 16*frameB.wordsPerLine; bb.src _ [word: @bbword, bit: bb.dst.bit]; bb.width _ 8*w; bb.height _ h; bb.flags.dstFunc _ null; PrincOpsUtils.BITBLT[bb]; }; ARect: PROC[x,y,w,h: CARDINAL, i: [0..256), fn: PrincOps.DstFunc _ null] = TRUSTED { iword: CARDINAL _ MakeGray[i]; xbit: CARDINAL _ Basics.BITSHIFT[x,lbpp]; frameA: Terminal.FrameBuffer ~ terminal.GetColorFrameBufferA[]; bb.dst _ [word: frameA.base + LONG[y]*frameA.wordsPerLine + xbit/16, bit: xbit MOD 16]; bb.dstBpl _ 16*frameA.wordsPerLine; bb.src _ [word: @iword, bit: bb.dst.bit]; bb.width _ Basics.BITSHIFT[w,lbpp]; bb.height _ h; bb.flags.dstFunc _ fn; PrincOpsUtils.BITBLT[bb]; }; index: [0..256) _ 0; Rectangle: PROC[lx,ty,rx,by: CARDINAL, fn: PrincOps.DstFunc _ null, clear: BOOLEAN _ FALSE] = { IF full THEN { r,g,b: [0..256); IF clear THEN r_g_b_0 ELSE {r _ MyRandom[256]; g _ MyRandom[256]; b _ MyRandom[256];}; FullRect[lx,ty,rx-lx,by-ty,r,g,b]; } ELSE { index _ IF clear THEN 0 ELSE (index + 1) MOD 256; ARect[lx,ty,rx-lx,by-ty,index,fn]; IF clear AND bitmapB#NIL THEN BRectangle[lx,ty,rx,by,null,TRUE]; }; }; BRectangle: PROC[lx,ty,rx,by: CARDINAL, fn: PrincOps.DstFunc _ xor, clear: BOOLEAN _ FALSE] = TRUSTED { xbit: CARDINAL _ Basics.BITSHIFT[lx,lbppB]; black: CARDINAL _ IF clear THEN 0 ELSE 177777B; frameB: Terminal.FrameBuffer ~ terminal.GetColorFrameBufferB[]; bb.dst _ [word: frameB.base + LONG[ty]*frameB.wordsPerLine + xbit/16, bit: xbit MOD 16]; bb.dstBpl _ 16*frameB.wordsPerLine; bb.src _ [word: @black, bit: 0]; bb.width _ Basics.BITSHIFT[(rx-lx),lbppB]; bb.height _ by-ty; bb.flags.dstFunc _ fn; PrincOpsUtils.BITBLT[bb]; }; RandomSplat: PROCEDURE [ckViewer: CKViewer] = { screenX: CARDINAL = terminal.colorWidth; screenY: CARDINAL = terminal.colorHeight; XorRatio: CARDINAL = 3; ty, ty2, lx, w, h: CARDINAL; fn: PrincOps.DstFunc _ (IF MyRandom[XorRatio]=0 THEN xor ELSE null); SetGray[MyRandom[256]]; IF sym THEN { ty _ ty2 _ MyRandom[screenY/2]; lx _ MyRandom[screenX/2]; w _ MyRandom[screenX/2-lx]; h _ MyRandom[screenY/2-ty]; Rectangle[lx, ty, lx+w, ty+h, fn]; ty _ screenY/2+(screenY/2-ty)-h; Rectangle[lx, ty, lx+w, ty+h, fn]; lx _ screenX/2+(screenX/2-lx)-w; Rectangle[lx, ty, lx+w, ty+h, fn]; Rectangle[lx, ty2, lx+w, ty2+h, fn]; } ELSE { ty _ MyRandom[screenY]; lx _ MyRandom[screenX]; w _ MyRandom[screenX-lx]; h _ MyRandom[screenY-ty]; Rectangle[lx, ty, lx+w, ty+h, fn]; }; IF bitmapB#NIL THEN { ty _ MyRandom[screenY]; lx _ MyRandom[screenX]; w _ MyRandom[screenX-lx]; h _ MyRandom[screenY-ty]; BRectangle[lx, ty, lx+w, ty+h]; }; IF ckViewer.state.slow THEN Wait[200]; -- slow mode }; ShowConverge: PROCEDURE = { hThick: CARDINAL _ terminal.colorHeight/300; hStep: CARDINAL _ terminal.colorHeight/30; vThick: CARDINAL _ terminal.colorHeight/300; vStep: CARDINAL _ terminal.colorHeight/30; IF full THEN Positive[] ELSE { FOR i: CARDINAL IN[0..9) DO Terminal.SetColor[vt: terminal, aChannelValue: i, red: testmap[i].r, green: testmap[i].g, blue: testmap[i].b]; ENDLOOP; SetShowButtons[TRUE,FALSE,FALSE]; }; TestRect[0,0,terminal.colorWidth,terminal.colorHeight,black]; --black background FOR xx: CARDINAL _ 0, xx+hStep UNTIL xx > terminal.colorWidth DO TestRect[xx, 0, vThick, terminal.colorHeight, white]; ENDLOOP; FOR v: CARDINAL _ 0, v+vStep UNTIL v > terminal.colorHeight DO TestRect[0, v, terminal.colorWidth, hThick ,white]; ENDLOOP; }; ShowBig: PROCEDURE [big: BOOLEAN, color: {red, green, blue}] = { r,g,b: CARDINAL; rectheight: CARDINAL _ terminal.colorHeight/16; --sixteen rectangles cInc: CARDINAL _ IF big THEN 20B ELSE 1; maxIntensity: CARDINAL _ IF full THEN 400B ELSE (Basics.BITSHIFT[1,bpp]); groupSize: CARDINAL _ MAX[16/maxIntensity,1]; groupIndex: CARDINAL _ 16/groupSize; IF NOT full THEN SetShowButtons[TRUE,FALSE,FALSE] ELSE SetShowButtons[TRUE,TRUE,TRUE]; FOR gI: CARDINAL IN [0..groupIndex) DO inten: CARDINAL _ gI*cInc; SELECT color FROM red => {r_inten; b_g_0}; green => {g_inten; b_r_0}; blue => {b_inten; r_g_0}; ENDCASE => ERROR; IF full THEN { Terminal.SetRedMap[terminal, gI, r]; Terminal.SetGreenMap[terminal, gI, g]; Terminal.SetBlueMap[terminal, gI, b]; } ELSE Terminal.SetColor[vt: terminal, aChannelValue: gI, red: r, green: g, blue: b]; FOR x: CARDINAL IN [0..groupSize) DO nextRect: CARDINAL _ gI*groupSize + x; Rect[0,nextRect*rectheight,terminal.colorWidth,rectheight, gI]; ENDLOOP; ENDLOOP; }; ShowBigRed: PROCEDURE [big: BOOLEAN] = { ShowBig[big, red]; }; ShowBigGreen: PROCEDURE [big: BOOLEAN] = { ShowBig[big, green]; }; ShowBigBlue: PROCEDURE [big: BOOLEAN] = { ShowBig[big, blue]; }; ShowCBars: PROCEDURE = { graywidth: CARDINAL _ terminal.colorWidth/128; --this must integer divide evenly grayheight: CARDINAL _ terminal.colorHeight/2; --this must integer divide evenly cbarwidth: CARDINAL _ (terminal.colorWidth)/9; --this may truncate cbarheight: CARDINAL _ terminal.colorHeight/2; --this must integer divide evenly IF full THEN HalfPositive[] ELSE { --set up gray map k: CARDINAL _ (Basics.BITSHIFT[1,bpp])/2; -- half the number of pixel values FOR n: CARDINAL IN [0..k) DO Terminal.SetColor[vt: terminal, aChannelValue: n+200B, red: n*2, green: n*2, blue: n*2]; --use top half of colormap. Only makes sense for 8 BPP ENDLOOP; }; FOR i: CARDINAL IN[0..9) DO --set up color bars IF full THEN {Terminal.SetRedMap[terminal, i,testmap[i].r]; Terminal.SetGreenMap[terminal, i,testmap[i].g]; Terminal.SetBlueMap[terminal, i,testmap[i].b];} ELSE Terminal.SetColor[vt: terminal, aChannelValue: i, red: testmap[i].r, green: testmap[i].g, blue: testmap[i].b] ENDLOOP; FOR x: CARDINAL IN [0..128) DO --gray wedge Rect[x*graywidth, 0, graywidth, grayheight, x+128]; ENDLOOP; FOR x: CARDINAL IN [0..8] DO --colorbars Rect[x*cbarwidth, cbarheight, cbarwidth, cbarheight, x]; ENDLOOP; SetShowButtons[TRUE,full,full]; }; NextRoll: PROCEDURE [] = { OPEN Terminal; r,g,b,r0,g0,b0: [0..256); IF full THEN { r0_GetRedMap[terminal, 0]; g0_GetGreenMap[terminal, 0]; b0_GetBlueMap[terminal, 0]; FOR i: CARDINAL IN[0..255) DO r _ GetRedMap[terminal, i+1]; g _ GetGreenMap[terminal, i+1]; b _ GetBlueMap[terminal, i+1]; SetRedMap[terminal, i,r]; SetGreenMap[terminal, i,g]; SetBlueMap[terminal, i,b]; ENDLOOP; SetRedMap[terminal, 255,r0]; SetGreenMap[terminal, 255,g0]; SetBlueMap[terminal, 255,b0]; } ELSE { [r0,g0,b0] _ Terminal.GetColor[vt: terminal, aChannelValue: 0]; FOR i: CARDINAL IN[0..255) DO [r,g,b] _ Terminal.GetColor[vt: terminal, aChannelValue: i+1]; Terminal.SetColor[terminal,i,0,r,g,b]; ENDLOOP; Terminal.SetColor[terminal,255,0,r0,g0,b0]; }; Wait[100]; }; NextScramble: PROCEDURE [] = { SetUpColors[]; Wait[200]; }; background: INTEGER _ 0; -- initially black ClearScreen: PROCEDURE = { IF NOT full THEN { Terminal.SetColor[terminal, 0, 0, background, background, background]; SetGray[0] } ELSE {Terminal.SetGreenMap[terminal, 0,0]; Terminal.SetBlueMap[terminal, 0,0]; Terminal.SetRedMap[terminal, 0,0];}; Rectangle[lx: 0, ty: 0, rx: terminal.colorWidth, by: terminal.colorHeight, fn: null, clear: TRUE]; }; Wait: PROCEDURE[millisecs: CARDINAL] = INLINE { Process.Pause[Process.MsecToTicks[millisecs]] }; <> defaultSeed: CARDINAL = 27183; numCalls: INTEGER = 3; <> a: ARRAY [0..55] OF CARDINAL; <> <> p: INTEGER [0..55]; <> <> InitRandom: PUBLIC PROC[seed: INTEGER] RETURNS[INTEGER] = { <> <> <0, seed is scaled if necessary and then used; if seed<0, a seed>> <> <<(after scaling) is the integer value returned.>> minSeed: CARDINAL = LAST[CARDINAL]/10; g, gPrev, gSave: CARDINAL; IF seed<=0 THEN seed _ defaultSeed; <> WHILE seed> <> <<(-1)**(i-1) * (F(i) - seed*F(i-1)) MOD maxRand, 0> <<(where F(i) denotes the i-th Fibonacci number) throughout the rest of a. Then>> <> <> a[55] _ gPrev _ seed; g _ 1; FOR i: INTEGER IN [1..54] DO p _ (21*i) MOD 55; a[p] _ gSave _ g; g _ gPrev-g; gPrev _ gSave; ENDLOOP; THROUGH [1..numCalls) DO RandomGen[]; ENDLOOP; <> p _ 1; RETURN[seed] };--InitRandom Random: PUBLIC PROC RETURNS[CARDINAL] = INLINE { p _ p-1; IF p=0 THEN { RandomGen[]; p _ 55 }; RETURN[a[p]] };--Random RandomGen: PROC = INLINE { <> <> <> FOR i: INTEGER IN [1..24] DO a[i] _ a[i] - a[i+31]; ENDLOOP; FOR i: INTEGER IN [25..55] DO a[i] _ a[i] - a[i-24]; ENDLOOP; };--RandomGen Choose: PUBLIC PROC[min, max: CARDINAL] RETURNS[CARDINAL--[min..max]--] = { intervalLen: CARDINAL; IF min > max THEN ERROR; intervalLen _ max - min + 1; --is 0 when min=0, max=LAST[CARDINAL] IF intervalLen = 0 THEN RETURN[Random[]]; DO <> <<"odd interval" at the high end of this range (there is no odd interval if intervalLen>> <> <> r, rem: CARDINAL; <> p _ p-1; IF p=0 THEN { RandomGen[]; p _ 55 }; r _ a[p]; rem _ r MOD intervalLen; IF (r - rem) > LOOPHOLE[-LOOPHOLE[intervalLen,INTEGER],CARDINAL] THEN LOOP; RETURN[min + rem]; ENDLOOP; };--Choose SetLogBitsPerPixel: PROC[n: [0..4)] RETURNS[BOOLEAN] = { b: CARDINAL _ Basics.BITSHIFT[1,n]; mode: Terminal.ColorMode _ [FALSE,b,1]; IF NOT Terminal.LegalColorMode[terminal, mode] THEN { mode.bitsPerPixelChannelB _ 0; IF NOT Terminal.LegalColorMode[terminal, mode] THEN RETURN[FALSE]; }; []_Terminal.SetColorMode[terminal, mode]; Terminal.TurnOnColorDisplay[terminal]; lbpp _ n; bpp _ b; full _ FALSE; lbppB _ 0; bppB _ 1; SetShowButtons[TRUE, TRUE, FALSE]; bitmapB _ terminal.GetColorFrameBufferB[]; ClearScreen[]; SetUpColors[]; RETURN[TRUE]; }; Set24BitsPerPixel: PROC = { mode: Terminal.ColorMode _ [TRUE,0,0]; IF NOT Terminal.LegalColorMode[terminal, mode] THEN RETURN; Process.SetPriority[Process.priorityNormal]; []_Terminal.SetColorMode[terminal, mode]; Terminal.TurnOnColorDisplay[terminal]; lbpp _ 0; bpp _ 0; full _ TRUE; SetShowButtons[TRUE, TRUE, FALSE]; bitmapB _ NIL; ClearScreen[]; SetUpColors[]; Process.SetPriority[Process.priorityBackground]; }; Go: PUBLIC PROC [ckViewer: CKViewer] = { [] _ InitRandom[0]; public _ ckViewer; <> <> SELECT TRUE FROM SetLogBitsPerPixel[3] => NULL; SetLogBitsPerPixel[2] => NULL; ENDCASE => { MW.Append[message: "NO COLOR DISPLAY !!", clearFirst: TRUE]; MW.Blink[]; RETURN; }; Process.SetPriority[Process.priorityBackground]; DO IF ckViewer.state.quit THEN { ClearScreen; ViewerOps.DestroyViewer[ckViewer.container]; []_Terminal.SetColorBitmapState[vt: terminal, newState: none, newMode: Terminal.GetColorMode[terminal], newVisibility: none]; EXIT; }; IF ckViewer.state.testingPanel THEN LOOP; --turned off by FinalizeTest IF ckViewer.state.bpp=1 AND bpp#1 THEN { [] _ SetLogBitsPerPixel[0]; SetSplat[] }; IF ckViewer.state.bpp=2 AND bpp#2 THEN { [] _ SetLogBitsPerPixel[1]; SetSplat[] }; IF ckViewer.state.bpp=4 AND bpp#4 THEN { [] _ SetLogBitsPerPixel[2]; SetSplat[] }; IF ckViewer.state.bpp=8 AND bpp#8 THEN { [] _ SetLogBitsPerPixel[3]; SetSplat[] }; IF ckViewer.state.bpp=24 AND NOT full THEN { Set24BitsPerPixel[]; SetSplat[] }; IF ckViewer.state.aToggle THEN { SetShowButtons[NOT ashow, bshow,FALSE]; ckViewer.state.aToggle _ FALSE}; IF ckViewer.state.bToggle THEN { SetShowButtons[ashow, NOT bshow,FALSE]; ckViewer.state.bToggle _ FALSE}; IF ckViewer.state.random THEN { sym _ FALSE; SetSplat[]; ClearScreen[]; ckViewer.state.random _ FALSE; }; IF ckViewer.state.symmetric THEN { sym _ TRUE; SetSplat[]; ClearScreen[]; ckViewer.state.symmetric _ FALSE}; IF NOT ckViewer.state.freeze THEN { ckViewer.state.roll _ ckViewer.state.scramble _ FALSE; --only TRUE when frozen SELECT TRUE FROM ckViewer.state.testPattern => { splat _ FALSE; ShowTPat[]; ckViewer.state.testPattern _ FALSE; }; ckViewer.state.cbars => {ShowCBars[]; splat _ ckViewer.state.cbars _ FALSE}; ckViewer.state.converge => {ShowConverge[]; splat _ ckViewer.state.converge _ FALSE}; ckViewer.state.diag => {Diag[]; splat _ ckViewer.state.diag _ FALSE}; ckViewer.state.bigRed => {ShowBigRed[big: TRUE]; splat _ ckViewer.state.bigRed _ FALSE}; ckViewer.state.bigGreen => {ShowBigGreen[big: TRUE]; splat _ ckViewer.state.bigGreen _ FALSE}; ckViewer.state.bigBlue => {ShowBigBlue[big: TRUE]; splat _ ckViewer.state.bigBlue _ FALSE}; ckViewer.state.smallRed => {ShowBigRed[big: FALSE]; splat _ ckViewer.state.smallRed _ FALSE}; ckViewer.state.smallGreen => {ShowBigGreen[big: FALSE]; splat _ ckViewer.state.smallGreen _ FALSE}; ckViewer.state.smallBlue => {ShowBigBlue[big: FALSE]; splat _ ckViewer.state.smallBlue _ FALSE}; ENDCASE => IF splat THEN RandomSplat[ckViewer]; } ELSE { SELECT TRUE FROM ckViewer.state.scramble => NextScramble[]; ckViewer.state.roll => NextRoll[]; ENDCASE => NULL; }; ENDLOOP; }; SetSplat: PROCEDURE [] = { splat _ TRUE; SetShowButtons[ashow,bshow,FALSE]; CKViewerButtons.SetStaticButtons[public, IF sym THEN public.buttons.symmetricButton ELSE public.buttons.randomButton] }; SetShowButtons: PROCEDURE [a, b, c: BOOL] = { vis: Terminal.ChannelsVisible; ashow_a; bshow_b; vis _ SELECT TRUE FROM a AND b => all, a AND NOT b => aOnly, NOT a AND b => bOnly, NOT a AND NOT b => none, ENDCASE => ERROR; Terminal.SetVisibility[vt: terminal, visibility: vis]; Buttons.SetDisplayStyle[public.buttons.aChanButtton, IF ashow THEN $WhiteOnBlack ELSE $BlackOnWhite]; Buttons.SetDisplayStyle[public.buttons.bChanButton, IF bshow THEN $WhiteOnBlack ELSE $BlackOnWhite]; }; SetUpPanelTest: PUBLIC PROCEDURE [ckViewer: CKViewer] = { ckViewer.state.testingPanel _ TRUE; Wait[2000]; --stupid synchronizer so main loop in Go will quit messing with screen ClearScreen []; --FILL FIFOS WITH Zeroes Terminal.SetVisibility[vt: terminal, visibility: all]; --so when ChannelOn glitches nothing bad will happen Wait[36]; --be sure both a and b actually show SetShowButtons[ashow,bshow,TRUE]; --restore ashow/bshow }; public: CKViewer _ NIL; TRUSTED { bb _ InitBB[@bbspace] }; END.