-- ALEMouse.mesa -- Edited by Sweet, January 26, 1981 1:30 PM DIRECTORY ALEOps, AltoDisplay USING [ Cursor, CursorBits, CursorHandle, CursorXY, MouseXY], Ascii, FrameDefs USING [MakeCodeResident, SelfDestruct, UnlockCode], FrameOps USING [MyGlobalFrame], ImageDefs, Inline USING [BITAND, BITNOT, BITOR, BITSHIFT, LowHalf], KeyDefs USING [KeyBits, Keys], ProcessDefs, StreamDefs USING [CursorTrack], Storage, String, Window; ALEMouse: MONITOR IMPORTS ALEOps, FrameDefs, FrameOps, ImageDefs, Inline, ProcessDefs, Storage, StreamDefs, String, Window EXPORTS ALEOps = BEGIN OPEN ALEOps; wakeup: CONDITION; halt: BOOLEAN; posChanging: BOOLEAN; lookForPosChange: CONDITION; aWhile: CONDITION; keyboardFree: CONDITION; commandUsingKeys: BOOLEAN ← FALSE; WaitToLook: ENTRY PROC = BEGIN WHILE ~posChanging DO WAIT lookForPosChange ENDLOOP; END; GiveUpKeys: ENTRY PROC = BEGIN commandUsingKeys ← TRUE; WHILE commandUsingKeys DO WAIT keyboardFree ENDLOOP; END; GiveBackKeys: PUBLIC ENTRY PROC = BEGIN commandUsingKeys ← FALSE; NOTIFY keyboardFree; END; Confirm: PUBLIC PROC RETURNS [BOOLEAN] = BEGIN OutString["[]"L]; DO SELECT ReadChar[] FROM 'y, 'Y, Ascii.CR => {OutString[" YES"]; RETURN[TRUE]}; 'n, 'N, Ascii.DEL, Ascii.ControlH => {OutString[" NO"]; RETURN[FALSE]}; ENDCASE; ENDLOOP; END; WaitAwhile: ENTRY PROC = {WAIT aWhile}; NoticeChange: PROC [validate: BOOLEAN ← FALSE] = BEGIN -- reads monitor data (mumblePos) without the lock -- but value is only a hint, so probably ok to do it anyChange: BOOLEAN ← FALSE; IF originPos # showingOrigin THEN BEGIN anyChange ← TRUE; Window.InvalidateBox[feedbackWindow, originValueBox]; END; IF sourcePos # showingSource THEN BEGIN anyChange ← TRUE; Window.InvalidateBox[feedbackWindow, sourceValueBox]; END; IF destPos # showingDest THEN BEGIN anyChange ← TRUE; Window.InvalidateBox[feedbackWindow, destValueBox]; END; IF validate OR anyChange THEN Window.ValidateTree[feedbackWindow]; END; FeedbackNoticer: PROC = BEGIN posChanging ← FALSE; ProcessDefs.SetTimeout[@aWhile, ProcessDefs.MsecToTicks[300]]; DO WaitToLook[]; IF halt THEN RETURN; NoticeChange[]; WaitAwhile[]; ENDLOOP; END; mouse: POINTER TO Coordinate ← AltoDisplay.MouseXY; cursor: POINTER TO Coordinate ← AltoDisplay.CursorXY; keys: POINTER TO KeyDefs.KeyBits ← KeyDefs.Keys; BufferSize: CARDINAL = 32; buffer: POINTER TO ARRAY OF Operation; head, tail: CARDINAL ← 0; NonFull, NonEmpty: CONDITION; AddCmd: PUBLIC ENTRY PROC [cmd: Operation] = BEGIN Enqueue[cmd]; END; Enqueue: INTERNAL PROC [cmd: Operation] = BEGIN WHILE (tail+1) MOD BufferSize = head DO WAIT NonFull ENDLOOP; buffer[tail] ← cmd; tail ← (tail + 1) MOD BufferSize; NOTIFY NonEmpty; END; GetCmd: PUBLIC ENTRY PROC RETURNS [cmd: Operation] = BEGIN WHILE tail = head DO WAIT NonEmpty ENDLOOP; cmd ← buffer[head]; head ← (head + 1) MOD BufferSize; NOTIFY NonFull; END; GetOriginPos: PUBLIC ENTRY PROCEDURE [toPrint: BOOLEAN ← FALSE] RETURNS [APosition] = {IF toPrint THEN showingOrigin ← originPos; RETURN[originPos]}; ASetOriginPos: PUBLIC ENTRY PROCEDURE [pos: APosition] = BEGIN aSource: APosition = Absolute[sourcePos]; aDest: APosition = Absolute[destPos]; rNew: RPosition = Relative[pos]; IF state.displayTicks THEN DisplayBoxTicks[FALSE]; originPos ← pos; sourcePos ← Relative[aSource]; destPos ← Relative[aDest]; DrawOrigin[PicturePlace[pos]]; IF state.displayTicks THEN DisplayBoxTicks[TRUE]; END; GetSourcePos: PUBLIC ENTRY PROCEDURE [toPrint: BOOLEAN ← FALSE] RETURNS [RPosition] = {IF toPrint THEN showingSource ← sourcePos; RETURN[sourcePos]}; ASetSourcePos: PUBLIC ENTRY PROCEDURE [pos: APosition] = BEGIN sourcePos ← Relative[pos]; DrawSource[PicturePlace[pos]]; END; GetDestPos: PUBLIC ENTRY PROCEDURE [toPrint: BOOLEAN ← FALSE] RETURNS [RPosition] = {IF toPrint THEN showingDest ← destPos; RETURN[destPos]}; ASetDestPos: PUBLIC ENTRY PROCEDURE [pos: APosition] = BEGIN destPos ← Relative[pos]; DrawDest[PicturePlace[pos]]; END; originPos, showingOrigin: APosition ← [0,0]; sourcePos, destPos, showingSource, showingDest: RPosition ← [0,0]; screenCorner: Coordinate; screenBottom: Coordinate; cornerPos: PUBLIC APosition ← [0,0]; UnitsPerDot: ARRAY [-3..4] OF ADistance = [128, 64, 32, 16, 8, 4, 2, 1]; GrainMask: ARRAY [0..4] OF CARDINAL = [177760B, 177770B, 177774B, 177776B, 177777B]; inch: INTEGER = 16; InchMask: CARDINAL = GrainMask[0]; cursorPicture: AltoDisplay.CursorHandle = AltoDisplay.Cursor; Cursors: PUBLIC ARRAY CursorShape OF AltoDisplay.CursorBits ← [ [0, 0, 0, 0, 0, 0, 0, 0, 360B, 340B, 340B, 260B, 30B, 14B, 6, 3], [0, 0, 0, 0, 4000B, 76000B, 43000B, 57400B, 43600B, 73400B, 43000B, 76000B, 4000B, 0, 0, 0], [0, 0, 0, 0, 10B, 37B, 63B, 165B, 365B, 165B, 63B, 37B, 10B, 0, 0, 0], [0, 0, 200B, 200B, 1240B, 700B, 4210B, 2020B, 37076B, 2020B, 4210B, 700B, 1240B, 200B, 200B, 0], [0, 0, 0, 200B, 700B, 700B, 200B, 6230B, 17574B, 6230B, 200B, 700B, 700B, 200B, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 377B, 200B, 200B, 200B, 200B, 200B, 200B, 200B], [0, 200B, 200B, 200B, 200B, 200B, 200B, 200B, 77600B, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1740B, 3260B, 6230B, 4210B, 7770B, 4210B, 6230B, 3260B, 1740B, 0, 0, 0]]; ChangeCursor: PROC [shape: CursorShape] = BEGIN cursorPicture↑ ← Cursors[shape]; END; AScreen: PUBLIC PROC [aPos: APosition] RETURNS [Coordinate] = BEGIN RETURN [Screen[Relative[aPos]]]; END; Screen: PUBLIC PROC [rPos: RPosition] RETURNS [Coordinate] = BEGIN RETURN [[ x: screenCorner.x + DotsForADistance[rPos.x + originPos.x - cornerPos.x], y: screenCorner.y + DotsForADistance[rPos.y + originPos.y - cornerPos.y]]]; END; Relative: PUBLIC PROC [aPos: APosition] RETURNS [RPosition] = {RETURN [[x: aPos.x - originPos.x, y: aPos.y - originPos.y]]}; Absolute: PUBLIC PROC [rPos: RPosition] RETURNS [APosition] = {RETURN [[x: rPos.x + originPos.x, y: rPos.y + originPos.y]]}; APos: PROC [pos: Coordinate] RETURNS [APosition] = BEGIN pos ← HotSpot[pos]; RETURN [[ x: cornerPos.x + ADistanceForDots[(pos.x - screenCorner.x)], y: cornerPos.y + ADistanceForDots[(pos.y - screenCorner.y)]]]; END; APosForPlace: PUBLIC PROC [place: Window.Place] RETURNS [APosition] = BEGIN RETURN [[x: ADistanceForDots[place.x], y: ADistanceForDots[place.y]]]; END; ADistanceForDots: PUBLIC PROC [dots: INTEGER, mag: [-3..4] ← state.magnify] RETURNS [ADistance] = BEGIN RETURN [LONG[dots] * UnitsPerDot[mag]]; END; DotsForADistance: PUBLIC PROC [dist: ADistance, mag: [-3..4] ← state.magnify] RETURNS [INTEGER] = BEGIN RETURN [Short[dist / UnitsPerDot[mag]]]; END; Short: PUBLIC PROC [d: ADistance] RETURNS [INTEGER] = BEGIN SELECT d FROM < FIRST[INTEGER] => RETURN[FIRST[INTEGER]]; > LAST[INTEGER] => RETURN[LAST[INTEGER]]; ENDCASE => RETURN [Inline.LowHalf[d]]; END; PicturePlace: PUBLIC PROC [aPos: APosition] RETURNS [Window.Place] = BEGIN RETURN [ [x: DotsForADistance[aPos.x], y: DotsForADistance[aPos.y]]]; END; Mask: PROC [d: ADistance, mask: UNSPECIFIED] RETURNS [ADistance] = BEGIN rec: RECORD [SELECT OVERLAID * FROM ad => [dist: ADistance], pair => [low, high: INTEGER], ENDCASE]; rec.dist ← d; rec.low ← Inline.BITAND[rec.low, mask]; RETURN[rec.dist]; END; NearestFract: PROC [pos: Coordinate] RETURNS [RPosition] = BEGIN RETURN [[ x: Mask [ cornerPos.x - originPos.x + ADistanceForDots[pos.x - screenCorner.x], GrainMask[state.grain]], y: Mask [ cornerPos.y - originPos.y + ADistanceForDots[pos.y - screenCorner.y], GrainMask[state.grain]]]]; END; NearestInch: PROC [pos: Coordinate] RETURNS [RPosition] = BEGIN RETURN [[ x: Mask [ cornerPos.x - originPos.x + ADistanceForDots[pos.x - screenCorner.x], InchMask], y: Mask [ cornerPos.y - originPos.y + ADistanceForDots[pos.y - screenCorner.y], InchMask]]]; END; RoundToInch: PROC [rPos: RPosition] RETURNS [RPosition] = BEGIN RETURN [ [x: Mask[rPos.x, InchMask], y: Mask[rPos.y, InchMask]]]; END; ARoundToInch: PUBLIC PROC [aPos: APosition] RETURNS [APosition] = LOOPHOLE[RoundToInch]; HotSpot: PROC [pos: Coordinate] RETURNS [Coordinate] = BEGIN RETURN [[x: pos.x + 8, y: pos.y + 8]]; END; ColdCorner: PROC [pos: Coordinate] RETURNS [Coordinate] = BEGIN RETURN [[x: pos.x - 8, y: pos.y - 8]]; END; Clip: PROC [pos: Coordinate] RETURNS [Coordinate] = BEGIN RETURN [ [ x: MAX[MIN[MaxX, pos.x], 0], y: MAX[MIN[MaxY, pos.y], 0]]]; END; DrawSource: INTERNAL PROC [place: Window.Place] = INLINE {Enqueue[[drawSource[place]]]}; UndrawSource: INTERNAL PROC = INLINE {Enqueue[[undrawSource[]]]}; DrawDest: INTERNAL PROC [place: Window.Place] = INLINE {Enqueue[[drawDest[place]]]}; UndrawDest: INTERNAL PROC = INLINE {Enqueue[[undrawDest[]]]}; DrawOrigin: INTERNAL PROC [place: Window.Place] = INLINE {Enqueue[[drawOrigin[place]]]}; BoxSelection: INTERNAL PROC [pos1, pos2: APosition] = INLINE {Enqueue[[boxSelection[pos1, pos2]]]}; Copy: INTERNAL PROC [delta: APosition] = INLINE {Enqueue[[copy[delta]]]}; ZoomOut: INTERNAL PROC = INLINE {Enqueue[[zoomOut[]]]}; SetOrigin: INTERNAL PROC [pos: APosition] = INLINE {Enqueue[[setOrigin[pos]]]}; ZoomIn: INTERNAL PROC [p1: APosition, p2: APosition] = INLINE {Enqueue[[zoomIn[p1, p2]]]}; Move: INTERNAL PROC [delta: APosition] = INLINE {Enqueue[[move[delta]]]}; Undelete: INTERNAL PROC = {Enqueue[[undelete[]]]}; DrawRect: INTERNAL PROC [from, to: APosition] = INLINE {Enqueue[[drawRect[from, to]]]}; Delete: INTERNAL PROC = INLINE {Enqueue[[delete[]]]}; Draw: INTERNAL PROC [from, to: APosition] = INLINE {Enqueue[[draw[from, to]]]}; oldP: PaddleRec; TrackMouse: PUBLIC ENTRY PROC = BEGIN OPEN AltoDisplay; mouseNow, cursorNow, homePos, setCursor, selStart: Coordinate; currentRPos, homeRPos: RPosition; state: MouseState; track: TrackMode; selection: SelectionMode; screenCorner ← [ BitmapBox.place.x, BitmapBox.place.y + FrameBox.place.y]; screenBottom ← [ screenCorner.x + BitmapBox.dims.w, screenCorner.y + BitmapBox.dims.h]; oldP ← [FALSE, FALSE, FALSE, FALSE, FALSE]; state ← clear; track ← fast; ChangeCursor[point]; DO WAIT wakeup[ ! ABORTED => CONTINUE]; mouseNow ← mouse↑; cursorNow ← cursor↑; IF halt THEN EXIT; BEGIN -- to set up checkPaddles label SELECT state FROM clear => SELECT TRUE FROM keys.Red = down => BEGIN SELECT TRUE FROM keys.Keyset2 = down => BEGIN Enqueue[[undrawUpper[]]]; state ← upper; ChangeCursor[upper]; track ← fast; END; keys.Keyset1 = down => BEGIN track ← slow; homePos ← cursorNow; UndrawSource[]; ChangeCursor[source]; state ← red; END; keys.Keyset5 = down => BEGIN homeRPos ← NearestFract[HotSpot[cursorNow]]; track ← fine; homePos ← cursorNow; posChanging ← TRUE; NOTIFY lookForPosChange; UndrawSource[]; ChangeCursor[source]; state ← red; END; ENDCASE => BEGIN homeRPos ← NearestInch[HotSpot[cursorNow]]; track ← inch; homePos ← cursorNow; posChanging ← TRUE; NOTIFY lookForPosChange; UndrawSource[]; ChangeCursor[source]; state ← red; END; END; -- of clear to red transition keys.Yellow = down => BEGIN SELECT TRUE FROM keys.Keyset2 = down => BEGIN Enqueue[[undrawLower[]]]; state ← lower; ChangeCursor[lower]; track ← fast; END; keys.Keyset1 = down => BEGIN track ← slow; homePos ← cursorNow; UndrawDest[]; ChangeCursor[dest]; state ← yellow; END; keys.Keyset5 = down => BEGIN currentRPos ← homeRPos ← NearestFract[HotSpot[cursorNow]]; track ← fine; homePos ← mouseNow ← cursorNow ← ColdCorner[Screen[homeRPos]]; posChanging ← TRUE; NOTIFY lookForPosChange; UndrawDest[]; ChangeCursor[dest]; state ← yellow; END; ENDCASE => BEGIN currentRPos ← homeRPos ← NearestInch[HotSpot[cursorNow]]; track ← inch; homePos ← mouseNow ← cursorNow ← ColdCorner[Screen[homeRPos]]; posChanging ← TRUE; NOTIFY lookForPosChange; UndrawDest[]; ChangeCursor[dest]; state ← yellow; END; END; -- of clear to yellow transition keys.Blue = down => BEGIN ChangeCursor[select]; state ← blue; SELECT TRUE FROM keys.Keyset1 = down AND keys.Keyset2 = down => BEGIN selStart ← cursorNow; selection ← box; track ← fast; END; keys.Keyset1 = down => BEGIN selection ← add; track ← slow; homePos ← cursorNow; END; keys.Keyset2 = down => BEGIN selection ← sub; track ← slow; homePos ← cursorNow; END; ENDCASE => BEGIN selection ← new; track ← slow; homePos ← cursorNow; END; END; -- of clear to blue transition ENDCASE => GO TO checkPaddles; red => SELECT TRUE FROM keys.Red = down => SELECT track FROM fine => IF keys.Keyset5 = up THEN BEGIN track ← inch; homeRPos ← RoundToInch[currentRPos]; cursorNow ← homePos ← ColdCorner[Screen[currentRPos]]; mouse↑ ← cursor↑ ← mouseNow ← cursorNow; END; inch => IF keys.Keyset5 = down THEN BEGIN track ← fine; homeRPos ← currentRPos; homePos ← mouseNow ← mouse↑ ← cursorNow; END; ENDCASE; ENDCASE => BEGIN -- red came up state ← clear; mouseNow ← cursorNow; ChangeCursor[point]; SELECT track FROM inch, fine => BEGIN sourcePos ← currentRPos; posChanging ← FALSE; END; slow => BEGIN Enqueue[[sourceToClose[APos[cursorNow]]]]; END; ENDCASE => ERROR; DrawSource[PicturePlace[Absolute[sourcePos]]]; track ← fast; END; yellow => SELECT TRUE FROM keys.Yellow = down => SELECT track FROM fine => IF keys.Keyset5 = up THEN BEGIN track ← inch; homeRPos ← RoundToInch[currentRPos]; cursorNow ← homePos ← ColdCorner[Screen[currentRPos]]; mouse↑ ← cursor↑ ← mouseNow ← cursorNow; END; inch => IF keys.Keyset5 = down THEN BEGIN track ← fine; homeRPos ← currentRPos; homePos ← mouseNow ← mouse↑ ← cursorNow; END; ENDCASE; ENDCASE => BEGIN -- yellow came up state ← clear; mouseNow ← cursorNow; ChangeCursor[point]; SELECT track FROM inch, fine => BEGIN destPos ← currentRPos; posChanging ← FALSE; END; slow => BEGIN Enqueue[[destToClose[APos[cursorNow]]]]; END; ENDCASE => ERROR; DrawDest[PicturePlace[Absolute[destPos]]]; track ← fast; END; blue => IF keys.Blue = up THEN BEGIN aPos: APosition = APos[cursorNow]; state ← clear; track ← fast; mouseNow ← cursorNow; ChangeCursor[point]; SELECT selection FROM new => Enqueue[ [newSelection[aPos]]]; add => Enqueue[ [addSelection[aPos]]]; sub => Enqueue[ [subSelection[aPos]]]; box => BoxSelection[APos[selStart], aPos]; ENDCASE; END ELSE SELECT TRUE FROM keys.Keyset1 = down AND selection = new => selection ← add; keys.Keyset2 = down AND selection = new => selection ← sub; ENDCASE; upper => IF keys.Red = up THEN BEGIN aPos: APosition = APos[cursorNow]; Enqueue[ [drawUpper[PicturePlace[aPos]]]]; ChangeCursor[point]; state ← clear; track ← fast; END; lower => IF keys.Yellow = up THEN BEGIN aPos: APosition = APos[cursorNow]; Enqueue[ [drawLower[PicturePlace[aPos]]]]; ChangeCursor[point]; state ← clear; track ← fast; END; ENDCASE; EXITS checkPaddles => BEGIN -- no buttons down, now or last time IF keys.Keyset1 = down THEN oldP.alpha ← TRUE; IF keys.Keyset2 = down THEN oldP.beta ← TRUE; IF keys.Keyset3 = down THEN oldP.move ← TRUE; IF keys.Keyset4 = down THEN oldP.draw ← TRUE; IF keys.Keyset3 = up AND keys.Keyset4 = up THEN BEGIN SELECT TRUE FROM oldP.move AND oldP.draw => IF ~(oldP.alpha OR oldP.beta) THEN Copy[[ x: destPos.x - sourcePos.x, y: destPos.y - sourcePos.y]]; oldP.move => SELECT TRUE FROM oldP.alpha AND oldP.beta => ZoomOut[]; oldP.alpha => SetOrigin[Absolute[sourcePos]]; oldP.beta => SELECT TRUE FROM ~upperWindow.notInTree AND ~lowerWindow.notInTree => ZoomIn[ APosForPlace[upperWindow.box.place], APosForPlace[lowerWindow.box.place]]; ~upperWindow.notInTree => Enqueue[ [slide[APosForPlace[upperWindow.box.place]]]]; ENDCASE; ENDCASE => Move[[ x: destPos.x - sourcePos.x, y: destPos.y - sourcePos.y]]; oldP.draw => SELECT TRUE FROM oldP.alpha AND oldP.beta => Undelete[]; oldP.alpha => DrawRect[ from: Absolute[sourcePos], to: Absolute[destPos]]; oldP.beta => Delete[]; ENDCASE => Draw[ from: Absolute[sourcePos], to: Absolute[destPos]]; ENDCASE; oldP.alpha ← keys.Keyset1 = down; oldP.beta ← keys.Keyset2 = down; oldP.move ← FALSE; oldP.draw ← FALSE; END; END; END; SELECT track FROM fast => cursor↑ ← mouse↑ ← Clip[mouseNow]; slow => BEGIN setCursor.x ← homePos.x + (mouseNow.x-homePos.x)/4; setCursor.y ← homePos.y + (mouseNow.y-homePos.y)/4; cursor↑ ← Clip[setCursor]; END; inch => BEGIN currentRPos.x ← homeRPos.x + inch * ((mouseNow.x-homePos.x)/4); currentRPos.y ← homeRPos.y + inch * ((mouseNow.y-homePos.y)/4); cursor↑ ← Clip[ColdCorner[Screen[currentRPos]]]; SELECT state FROM red => sourcePos ← currentRPos; yellow => destPos ← currentRPos; ENDCASE; END; fine => BEGIN currentRPos.x ← homeRPos.x + UnitsPerDot[ALEOps.state.grain] * ((mouseNow.x-homePos.x)/8); currentRPos.y ← homeRPos.y + UnitsPerDot[ALEOps.state.grain] * ((mouseNow.y-homePos.y)/8); cursor↑ ← Clip[ColdCorner[Screen[currentRPos]]]; SELECT state FROM red => sourcePos ← currentRPos; yellow => destPos ← currentRPos; ENDCASE; END; ENDCASE; ENDLOOP; END; pressFile: STRING ← [40]; dataFile: STRING ← [40]; -- currently used control keys -- BDFGIJKLMOPQRSTWX KeyWatcher: PROC = BEGIN OPEN Ascii; c: CHARACTER; DO ENABLE Rubout => {ClearText[]; LOOP}; c ← ReadChar[]; ClearText[]; SELECT c FROM ControlT => BEGIN AddCmd[[showTicks[]]]; END; ControlP => BEGIN OutString["Press to file: "L]; ReadString[pressFile]; AddCmd[[pressOut[pressFile]]]; GiveUpKeys[]; END; ControlI => BEGIN OutString["Input from file: "L]; ReadString[dataFile]; AddCmd[[readIn[dataFile]]]; END; ControlJ => BEGIN OutString["Jam output to file: "L]; ReadString[dataFile]; AddCmd[[jamOut[dataFile]]]; END; ControlO => BEGIN OutString["Output to file: "L]; ReadString[dataFile]; AddCmd[[writeOut[dataFile]]]; END; ControlG => BEGIN Grains: ARRAY CHARACTER ['0..'4] OF STRING = [ "1"""L, "1/2"""L, "1/4"""L, "1/8"""L, "1/16"""L]; OutString["Grain = "L]; c ← ReadChar[]; IF c IN ['0..'4] THEN {OutString[Grains[c]]; IF Confirm[] THEN state.grain ← c - '0}; ClearText[]; END; ControlD => BEGIN newTexture: LineTexture; Textures: ARRAY LineTexture OF STRING = [ "2/1"L, "4/1"L, "6/1"L, "solid"L]; OutString["Dashes = "L]; DO c ← ReadChar[]; SELECT c FROM '2 => newTexture ← d2; '4 => newTexture ← d4; '6 => newTexture ← d6; 's, 'S => newTexture ← solid; Ascii.DEL => SIGNAL Rubout; ENDCASE => {OutString["?"L]; LOOP}; EXIT; ENDLOOP; OutString[Textures[newTexture]]; IF Confirm[] THEN state.currentTexture ← newTexture; ClearText[]; END; ControlF => BEGIN newFont: FontSize; OutString["Font size = "L]; DO SELECT (c ← ReadChar[]) FROM 's, 'S => {OutString["small"L]; newFont ← small; EXIT}; 'l, 'L => {OutString["large"L]; newFont ← large; EXIT}; Ascii.DEL => GO TO done; ENDCASE => OutChar['?]; ENDLOOP; IF Confirm[] THEN state.currentFont ← newFont; GO TO done; EXITS done => ClearText[]; END; ControlL => BEGIN newMode: LabelMode ← state.currentLabelMode; newVis: BOOLEAN ← state.showingLabels; OutString["Label Mode = "L]; DO SELECT (c ← ReadChar[]) FROM 'p, 'P => {OutString["portrait"L]; newMode ← portrait; EXIT}; 'l, 'L => {OutString["landscape"L]; newMode ← landscape; EXIT}; 'i, 'I => {OutString["invisible"L]; newVis ← FALSE; EXIT}; 'v, 'V => {OutString["visible"L]; newVis ← TRUE; EXIT}; Ascii.DEL => GO TO done; ENDCASE => OutChar['?]; ENDLOOP; IF Confirm[] THEN {state.currentLabelMode ← newMode; state.showingLabels ← newVis}; GO TO done; EXITS done => ClearText[]; END; ControlM => BEGIN nS: STRING ← [2]; n: INTEGER; Mags: ARRAY [-3..4] OF STRING = [ "8"L, "4"L, "2"L, "1"L, "2"L, "4"L, "8"L, "16"L]; OutString["Minimum magnify = "L]; ReadString[nS]; n ← String.StringToDecimal[nS ! String.InvalidNumber => GO TO clear]; IF n IN [-3..4] THEN BEGIN OutString[" ("L]; OutString[Mags[n]]; OutString[IF n < 0 THEN " inches per dot)"L ELSE " dots per inch)"L]; IF Confirm[] THEN state.minMagnify ← n; END; GO TO clear; EXITS clear => ClearText[]; END; ControlQ => BEGIN OutString["Quit"L]; IF pictureChanged THEN OutString[" - picture changed"L]; IF Confirm[] THEN ImageDefs.StopMesa[] ELSE ClearText[]; END; ControlK => BEGIN OutString["Kill Picture"L]; IF pictureChanged THEN OutString[" - picture changed"L]; IF Confirm[] THEN AddCmd[[reset[]]] ELSE ClearText[]; END; ControlR => BEGIN OutString["Redraw selections using current defaults"L]; IF Confirm[] THEN AddCmd[[redrawSelections[]]] ELSE ClearText[]; END; ControlX => BEGIN OutString["Move and Rotate 90 deg."L]; IF Confirm[] THEN AddCmd[ [xlateAndRotate[Absolute[sourcePos], Absolute[destPos]]]] ELSE ClearText[]; END; ControlW => BEGIN OutString["Line width = "L]; c ← ReadChar[]; IF c IN ['1..'4] THEN {OutChar[c]; IF Confirm[] THEN state.currentWidth ← c-'0}; ClearText[]; END; ControlS => BEGIN nS: STRING ← [2]; n: INTEGER; OutString["Scale = "L]; ReadString[nS]; n ← String.StringToDecimal[nS ! String.InvalidNumber => GO TO clear]; IF n IN [1..16] THEN {OutString["/16 in/foot"L]; IF Confirm[] THEN state.sixteenthsPerFoot ← n}; GO TO clear; EXITS clear => ClearText[]; END; ControlB => BEGIN OutString["Blowup (to shrink line widths): "L]; c ← ReadChar[]; IF c IN ['1..'4] THEN {OutChar[c]; IF Confirm[] THEN state.blowup ← c - '0}; ClearText[]; END; ControlZ => BEGIN feet: BOOLEAN ← FALSE; OutString["Dimension selected line"L]; IF Confirm[] THEN {OutString["feet?"L]; feet ← Confirm[]}; ClearText[]; AddCmd[ [dimensionSelection[Absolute[GetSourcePos[]], feet]]]; END; DEL => NULL; LF => AddCmd[[repaint[]]]; <= SP => NULL; ENDCASE => BEGIN OutString["Insert label"L]; AddCmd[ [collectLabel[c, Absolute[GetSourcePos[]]]]]; GiveUpKeys[]; END; ENDLOOP; END; handler: PROCESS; feedback, readkeys: PROCESS; StartMouseHandler: PUBLIC PROCEDURE = BEGIN OPEN ProcessDefs; MousePriority: Priority = 6; MouseLevel: InterruptLevel = 11; MouseBit: WORD = Inline.BITSHIFT[1, MouseLevel]; save: Priority = GetPriority[]; StreamDefs.CursorTrack[FALSE]; FrameDefs.MakeCodeResident[FrameOps.MyGlobalFrame[]]; SetPriority[MousePriority]; halt ← FALSE; handler ← FORK TrackMouse; SetPriority[save]; CV[MouseLevel] ← @wakeup; DIW↑ ← Inline.BITOR[DIW↑, MouseBit]; feedback ← FORK FeedbackNoticer; readkeys ← FORK KeyWatcher; END; StopMouseHandler: PUBLIC PROCEDURE = BEGIN OPEN ProcessDefs; MouseLevel: InterruptLevel = 7; MouseBit: WORD = Inline.BITSHIFT[1, MouseLevel]; halt ← TRUE; AwakenFeedback[]; JOIN feedback; JOIN handler; CV[MouseLevel] ← NIL; DIW↑ ← Inline.BITAND[DIW↑, Inline.BITNOT[MouseBit]]; FrameDefs.UnlockCode[FrameOps.MyGlobalFrame[]]; END; AwakenFeedback: ENTRY PROC = BEGIN NOTIFY lookForPosChange; ProcessDefs.Yield[]; END; DestroyMouseHandler: PUBLIC PROCEDURE = BEGIN IF ~halt THEN StopMouseHandler[]; FrameDefs.SelfDestruct[]; END; buffer ← Storage.Node[BufferSize * SIZE[Operation]]; END.