-- FastMouse.mesa

-- Revised by McGregor: June 24, 1983 4:54 pm
-- Revised by Randy Gobbel: 10-May-83 9:56:34
-- Written by: CharlieLevy
Last Edited by: Beach, September 1, 1983 2:08 pm
Last Edited by: Spreitzer, November 18, 1984 4:13:53 pm PST
Revised by: Crow, February 15, 1984 2:32:48 pm PST for Cedar 5.1
DIRECTORY
Commander USING [Handle, Register],
Terminal USING [Current, Position, GetMousePosition, SetMousePosition, WaitForBWVerticalRetrace, Virtual],
Interminal USING [GetMousePosition],
IOUSING [EndOf, GetTokenRope, RIS, SkipWhitespace, STREAM],
Process  USING [SetPriority, priorityClient3],
PrincOpsUtils USING [VERSION],
UserProfile USING [ProfileChangedProc, Number, Token, CallWhenProfileChanges],
Rope USING [Equal, ROPE];
FastMouse: CEDAR MONITOR
IMPORTS Commander, Terminal, Interminal, IO, Process, PrincOpsUtils, UserProfile, Rope
~ BEGIN
ROPE: TYPE = Rope.ROPE;
mouseProcess: PROCESSNIL;
vt: Terminal.Virtual;
position, prevPosition: Terminal.Position;
width: NAT;
height: NAT;
cursorInColorDisplay: BOOLEANFALSE;
speedUpLinear: BOOLEANTRUE;  -- Acceleration law (linear or squared)
threshold: INTEGER ← 2;
ampxTen: INTEGER ← 15;
enabled: BOOLTRUE;
shouldRun: BOOLTRUE;
running: BOOLFALSE;
ShouldRun: ENTRY PROC RETURNS [run: BOOL] = {
IF NOT (run ← shouldRun) THEN RETURN;
IF running THEN RETURN [FALSE];
running ← TRUE;
};
NoteNotRunning: ENTRY PROC = {running ← FALSE};
DoradoMouseProcess: PROCEDURE = BEGIN
IF NOT ShouldRun[] THEN RETURN;
{ENABLE UNWIND => NoteNotRunning[];
Process.SetPriority[Process.priorityClient3];  -- this priority is one below fault handlers
vt ← Terminal.Current[];
prevPosition ← Terminal.GetMousePosition[vt];
width ← vt.bwWidth;
height ← vt.bwHeight;
WHILE shouldRun DO
amp, xDelta, yDelta: INTEGER;
Terminal.WaitForBWVerticalRetrace[vt];
position ← Terminal.GetMousePosition[vt];
IF prevPosition = position THEN LOOP;
TRUSTED {
IF cursorInColorDisplay # Interminal.GetMousePosition[].color THEN {
prevPosition ← position;
cursorInColorDisplay ← Interminal.GetMousePosition[].color;
IF cursorInColorDisplay
THEN { width ← vt.colorWidth; height ← vt.colorHeight; }
ELSE { width ← vt.bwWidth; height ← vt.bwHeight; };
LOOP;
};
};
xDelta ← position.x - prevPosition.x;
yDelta ← position.y - prevPosition.y;
amp ← MAX[ ABS[xDelta], ABS[yDelta] ];
IF amp > threshold THEN {
IF speedUpLinear
THEN position ← [             -- linear law speedup
MAX[ MIN[ position.x + (xDelta * ampxTen / 10), width], 0],
MAX[ MIN[ position.y + (yDelta * ampxTen / 10), height], 0] ]
ELSE position ← [             -- square law speedup
MAX[ MIN[ position.x + (xDelta * amp / threshold), width], 0],
MAX[ MIN[ position.y + (yDelta * amp / threshold), height], 0] ];
Terminal.SetMousePosition[vt, position];
};
prevPosition ← position;
ENDLOOP;
};
NoteNotRunning[];
END; -- DoradoMouseProcess
DandelionMouseProcess: PROCEDURE = BEGIN
IF NOT ShouldRun[] THEN RETURN;
{ENABLE UNWIND => NoteNotRunning[];
Process.SetPriority[Process.priorityClient3];  -- this priority is one below fault handlers
vt ← Terminal.Current[];
prevPosition ← Terminal.GetMousePosition[vt];
width ← vt.bwWidth;
height ← vt.bwHeight;
WHILE shouldRun DO
amp, xDelta, yDelta: INTEGER;
Terminal.WaitForBWVerticalRetrace[vt];
position ← Terminal.GetMousePosition[vt];
IF prevPosition = position THEN LOOP;
xDelta ← position.x - prevPosition.x;
yDelta ← position.y - prevPosition.y;
amp ← MAX[ ABS[xDelta], ABS[yDelta] ];
IF amp > threshold THEN {
IF speedUpLinear
THEN position ← [             -- linear law speedup
MAX[ MIN[ position.x + (xDelta + xDelta/2), width], 0],
MAX[ MIN[ position.y + (yDelta + yDelta/2), height], 0] ]
ELSE position ← [             -- square law speedup
MAX[ MIN[ position.x + (xDelta * amp / threshold), width], 0],
MAX[ MIN[ position.y + (yDelta * amp / threshold), height], 0] ];
Terminal.SetMousePosition[vt, position];
};
prevPosition ← position;
ENDLOOP;
};
NoteNotRunning[];
END; -- MouseProcess
FastMouseCmd: PROC [cmd: Commander.Handle] RETURNS [result: REF ANYNIL, msg: ROPENIL] -- Commander.CommandProc-- =
BEGIN
in: IO.STREAMIO.RIS[cmd.commandLine];
ok, on: BOOLFALSE;
[] ← in.SkipWhitespace[];
IF NOT in.EndOf[] THEN {
arg: ROPE ← in.GetTokenRope[].token;
[] ← in.SkipWhitespace[];
IF NOT in.EndOf[] THEN NULL
ELSE IF arg.Equal["on", FALSE] THEN {ok ← TRUE; on ← TRUE}
ELSE IF arg.Equal["off", FALSE] THEN {ok ← TRUE; on ← FALSE};
};
IF ok
THEN {enabled ← on; HandleChange[]}
ELSE {result ← $Failure; msg ← "Usage: FastMouse on|off"}
END;
ProcessProfile: UserProfile.ProfileChangedProc = TRUSTED BEGIN
enableToken: ROPE ← UserProfile.Token["FastMouse.Enabled", "UNSPECIFIED"];
IF enableToken.Equal["TRUE"] THEN enabled ← TRUE ELSE
IF enableToken.Equal["FALSE"] THEN enabled ← FALSE;
threshold ← UserProfile.Number["FastMouse.Threshold", 2];
ampxTen ← UserProfile.Number["FastMouse.AmpxTen", 15];
speedUpLinear ← Rope.Equal[UserProfile.Token["FastMouse.SpeedUp", "linear"], "linear"];
HandleChange[];
END;
HandleChange: ENTRY PROC = {
shouldRun ← enabled;
IF shouldRun AND NOT running THEN SELECT PrincOpsUtils.VERSION[].machineType FROM
dorado => mouseProcess ← FORK DoradoMouseProcess[];
dandelion => mouseProcess ← FORK DandelionMouseProcess[];
dolphin => mouseProcess ← FORK DoradoMouseProcess[];
ENDCASE;
};
Init: PROCEDURE =
BEGIN
UserProfile.CallWhenProfileChanges[ProcessProfile];
Commander.Register[key: "FastMouse", proc: FastMouseCmd, doc: "Turn mouse speedup on or off"];
END;
Init[];
END.