-- Kaleidoscope.mesa
-- Last Edited 
--  Gobbel	28-Feb-82 16:39:08
--  Yamamoto	19-Sep-83 10:08:58

-- Copyright (C) 1984 by Xerox Corporation. All rights reserved.

DIRECTORY
  Display USING [Bitmap, Black, replaceFlags],
  Environment USING [bitsPerWord, wordsPerPage],
  Exec USING [AddCommand, ExecProc],
  Heap USING [Create],
  Inline USING [BITAND, BITSHIFT, BITXOR, LongCOPY],
  Mopcodes USING [zLI4, zSHIFT, zWLFS],
  MSegment USING [Address, Create, Handle],
  Process USING [Abort, Detach, DisableTimeout, EnableAborts, 
    priorityBackground, priorityNormal, SecondsToTicks, SetPriority, Yield],
  Profile USING [initialToolStateDefault],
  ToolWindow USING [Activate, AdjustProcType, Box, Create, 
    CreateSubwindow, Deactivate, Show, TransitionProcType],
  UserInput USING [AttentionProcType, ClearInputFocusOnMatch, 
    SetAttention, SetInputFocus],
  UserTerminal USING [Background, hasBorder, screenHeight, 
    screenWidth, SetBackground, SetBorder],
  Window USING [Box, GetBox, GetParent, Handle];


Kaleidoscope: MONITOR
  IMPORTS 
    Display, Exec, Heap, Inline, Process, Profile, MSegment, 
    ToolWindow, UserInput, UserTerminal, Window =
  BEGIN
  
  Bit: TYPE = MACHINE DEPENDENT {on(0), off(1)};
  BitDesc: TYPE = MACHINE DEPENDENT RECORD
	  [offset(0: 0..7): [0..377B] ← 0,
	  pos(0: 8..11): [0..17B] ← 0,
	  size(0: 12..15): [0..17B] ← 1];
  
  z: UNCOUNTED ZONE ← Heap.Create[2];
  Generator: TYPE = RECORD[a, b, c: INTEGER, periodCount: CARDINAL];
  
  
  
  period: CARDINAL;
  persistence: CARDINAL;
  
  windowSide: CARDINAL ← 256;
  screenWidthWords: CARDINAL = EVEN[(windowSide+15)/16];
  screenAreaWords: CARDINAL = screenWidthWords*windowSide;
  screenAreaPages: CARDINAL =
    (screenAreaWords+Environment.wordsPerPage-1)/Environment.wordsPerPage;
  spotShift: INTEGER = -(Environment.bitsPerWord - Log2[windowSide/2]);
  
  boxEdge: INTEGER = windowSide + 2;
  toolBox: Window.Box ← [[50, 50], [boxEdge, boxEdge]];
  bigBox:  Window.Box ← [[0, 0], [4*windowSide+2, 3*windowSide+2]];
  
  xStateB: Generator ← [1, -1849,3,];
  xStateE: Generator ← TRASH;
  yStateB: Generator ← [1, -1809,3,];
  yStateE: Generator ← TRASH;
  pBitmap: LONG POINTER TO UNSPECIFIED ← NIL;
  bitmapSeg: MSegment.Handle ← NIL;
  
  toolWindow, subWindow: Window.Handle ← NIL;
  wait: CONDITION ← [timeout: Process.SecondsToTicks[1]];
  finished: CONDITION;
  bouncer: PROCESS;
  running: BOOLEAN ← FALSE;
  stop: BOOLEAN ← FALSE;
    
  MakeDesc: PROCEDURE [UNSPECIFIED] RETURNS [BitDesc] =
    MACHINE CODE {Mopcodes.zLI4; Mopcodes.zSHIFT};
  
  Log2: PROCEDURE [n: CARDINAL] RETURNS [log: INTEGER] =
    BEGIN
    log ← SELECT n FROM
      >= 100000B => 15,
      >= 40000B =>  14,
      >= 20000B =>  13,
      >= 10000B =>  12,
      >= 4000B =>   11,
      >= 2000B =>   10,
      >= 1000B =>   9,
      >= 400B =>    8,
      >= 200B =>    7,
      >= 100B =>    6,
      >= 40B =>     5,
      >= 20B =>     4,
      >= 10B =>     3,
      >= 4 =>       2,
      >= 2 =>       1,
      ENDCASE =>    0;
    END;
  
  Set: PROCEDURE [value: Bit, word: LONG POINTER, desc: BitDesc] =
	  MACHINE CODE {Mopcodes.zWLFS};
  
  EVEN: PROCEDURE [v: UNSPECIFIED] RETURNS [UNSPECIFIED] =
	  INLINE {RETURN[v+Inline.BITAND[v, 1]]};
  
  Advance: ENTRY PROCEDURE [state: LONG POINTER TO Generator] =
    BEGIN OPEN state;
    a ← Inline.BITXOR[(a+b), b];
    periodCount ← periodCount - 1;
    IF periodCount = 0 THEN {b ← Inline.BITXOR[(b+c), c]; periodCount ← period};
    END;
  
	  
  
  Spots: PROCEDURE [x, y: INTEGER] =
    BEGIN	-- Draw 8 spots with kaleidoscopic symmetry
    x0: CARDINAL = Inline.BITSHIFT[x, spotShift];
    y0: CARDINAL = Inline.BITSHIFT[y, spotShift];
    IF x0 < y0 THEN -- Discard points in other triangle
      BEGIN
      x1: CARDINAL = (windowSide-1)-x0;
      y1: CARDINAL = (windowSide-1)-y0;
      Set[on, pBitmap+x0*screenWidthWords, MakeDesc[y0]];
      Set[on, pBitmap+y0*screenWidthWords, MakeDesc[x0]];
      Set[on, pBitmap+x1*screenWidthWords, MakeDesc[y0]];
      Set[on, pBitmap+y0*screenWidthWords, MakeDesc[x1]];
      Set[on, pBitmap+x1*screenWidthWords, MakeDesc[y1]];
      Set[on, pBitmap+y1*screenWidthWords, MakeDesc[x1]];
      Set[on, pBitmap+x0*screenWidthWords, MakeDesc[y1]];
      Set[on, pBitmap+y1*screenWidthWords, MakeDesc[x0]];
      END;
    END;
  
  Erases: PROCEDURE [x, y: INTEGER] =
    BEGIN	-- Same as Spots except this erases.
    x0: CARDINAL = Inline.BITSHIFT[x, spotShift];
    y0: CARDINAL = Inline.BITSHIFT[y, spotShift];
    IF x0 < y0 THEN
      BEGIN
      x1: CARDINAL = (windowSide-1)-x0;
      y1: CARDINAL = (windowSide-1)-y0;
      Set[off, pBitmap+x0*screenWidthWords, MakeDesc[y0]];
      Set[off, pBitmap+y0*screenWidthWords, MakeDesc[x0]];
      Set[off, pBitmap+x1*screenWidthWords, MakeDesc[y0]];
      Set[off, pBitmap+y0*screenWidthWords, MakeDesc[x1]];
      Set[off, pBitmap+x1*screenWidthWords, MakeDesc[y1]];
      Set[off, pBitmap+y1*screenWidthWords, MakeDesc[x1]];
      Set[off, pBitmap+x0*screenWidthWords, MakeDesc[y1]];
      Set[off, pBitmap+y1*screenWidthWords, MakeDesc[x0]];
      END;
    END;
  
  PaintKals:  ENTRY PROCEDURE =  {
    FOR i: CARDINAL IN [0..4) DO 
      FOR j: CARDINAL IN [0..3) DO 
	Display.Bitmap[
	  window: subWindow, 
	  box: [[i*windowSide, j*windowSide], [windowSide, windowSide]],
	  address: [pBitmap,0,0], bitmapBitWidth: windowSide, 
	  flags: Display.replaceFlags];
	ENDLOOP;
     ENDLOOP;
    };
  
  NotifyQuit: ENTRY PROC = {NOTIFY finished};
  
  Run: PROCEDURE =
    BEGIN
    ENABLE UNWIND => NULL;
    oldBackground: UserTerminal.Background ← UserTerminal.SetBackground[white];
    IF UserTerminal.hasBorder THEN UserTerminal.SetBorder[377B, 377B];
    Process.SetPriority[Process.priorityBackground];
    running ← TRUE;
    xStateB.periodCount ← period;
    yStateB.periodCount ← period;
    xStateE ← xStateB;
    yStateE ← yStateB;
    FOR i: CARDINAL IN[1..persistence] DO -- Run the b(egin) generators ahead
      [] ← Advance[@xStateB];
      [] ← Advance[@yStateB];
      [] ← Spots[xStateB.a, yStateB.a];
      ENDLOOP;
    DO -- Main loop
      ENABLE ABORTED => EXIT;
      THROUGH [0..16) DO
	Advance[@xStateE]; Advance[@yStateE];	-- Advance and erase 8 spots
	Erases[xStateE.a, yStateE.a];
	Advance[@xStateB]; Advance[@yStateB];	-- Advance and put 8 spots
	Spots[xStateB.a, yStateB.a];
	ENDLOOP;
      PaintKals[];
      Process.Yield[];
      IF stop THEN {NotifyQuit[]; EXIT};
      ENDLOOP;
    running ← FALSE;
    [] ← UserTerminal.SetBackground[oldBackground];
    IF UserTerminal.hasBorder THEN UserTerminal.SetBorder[210B, 42B];
    END;
  
  MyDisplay: PROC[Window.Handle] = {
    Display.Black[subWindow, [[0,0], subWindow.GetBox[].dims]];
    PaintKals[];
    };
  
  AttentionProc: UserInput.AttentionProcType = {
    Process.Detach[FORK StopRunning[toolWindow]]};
  
  StopRunning: ENTRY PROCEDURE [w: Window.Handle] = {
    Process.SetPriority[Process.priorityNormal];
    stop ← TRUE;
    WAIT finished;
    [] ← ToolWindow.Deactivate[w]};
  
  StartRunning: ENTRY PROC [window: Window.Handle] = {
    IF running THEN RETURN;
    IF subWindow = NIL THEN subWindow ← ToolWindow.CreateSubwindow[
      parent: window, display: MyDisplay, 
      box: [[0,0], toolWindow.GetBox[].dims]];
    UserInput.SetInputFocus[subWindow, NIL, FALSE];
    UserInput.SetAttention[subWindow.GetParent[], AttentionProc];
    UserInput.SetAttention[subWindow, AttentionProc];
    pBitmap↑ ← 177777B;
    Inline.LongCOPY[
      from: pBitmap, to: pBitmap+1, 
      nwords: screenAreaPages*256 - 1];
    stop ← FALSE;
    bouncer ← FORK Run;
    running ← TRUE};
  
  WaitTilFinished: ENTRY PROC = {WAIT finished};
  
  TransitionProc: ToolWindow.TransitionProcType = {
    ENABLE UNWIND => NULL;
    SELECT new FROM
      inactive, tiny => {
	IF old = active THEN {
	  UserInput.ClearInputFocusOnMatch[subWindow];
	  IF ~stop THEN {stop ← TRUE; WaitTilFinished[]};
	  Process.Abort[bouncer];
	  JOIN bouncer};
	IF new = inactive THEN subWindow ← NIL};
      active => IF old # active THEN StartRunning[window];
      ENDCASE};
  
  KalCommand: Exec.ExecProc = {ToolWindow.Activate[toolWindow]};
  
      
  AdjustProc: ENTRY ToolWindow.AdjustProcType = {};
  
  Init: PROCEDURE =
    BEGIN
    i,j: CARDINAL ← 0;
    period ← 10000;
    persistence ← 5000;
    bitmapSeg ← MSegment.Create[file: NIL, release: [],
      fileBase: 0, pages: screenAreaPages];
    pBitmap ← MSegment.Address[bitmapSeg];
    toolWindow ← ToolWindow.Create[
      name: "Kaleidoscope"L, adjust: AdjustProc, transition: TransitionProc,
      box: [place: [0,0], 
      dims: [w: UserTerminal.screenWidth, h: UserTerminal.screenHeight]],
      initialState: Profile.initialToolStateDefault, named: FALSE];
    IF Profile.initialToolStateDefault # inactive THEN { 
      TransitionProc[
	window: toolWindow, old: inactive, new: Profile.initialToolStateDefault];
      ToolWindow.Show[toolWindow]};
    Exec.AddCommand["Kaleidoscope.~"L, KalCommand];
    Process.EnableAborts[@wait];
    Process.DisableTimeout[@finished];
    END;
  
  Init[];

END...