FastMouse.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
Written by: CharlieLevy
Revised by Randy Gobbel: 10-May-83 9:56:34
Revised by McGregor: June 24, 1983 4:54 pm
Last Edited by: Beach, September 1, 1983 2:08 pm
Revised by: Crow, July 8, 1986 11:48:21 am PDT for Cedar 6.1
Last Edited by: Spreitzer, November 18, 1984 4:13:53 pm PST
Doug Wyatt, April 21, 1985 3:24:27 pm PST
DIRECTORY
Commander   USING [ CommandProc, Handle, Register ],
Interminal   USING [ GetMousePosition ],
InterminalBackdoor USING [ terminal ],
IO      USING [ EndOf, GetTokenRope, RIS, SkipWhitespace, STREAM ],
PrincOpsUtils  USING [ VERSION ],
Process    USING [ SetPriority, priorityClient3, priorityRealTime ],
Rope     USING [ Equal, ROPE ],
Terminal    USING [ Position, GetMousePosition, SetMousePosition,
         WaitForBWVerticalRetrace, Virtual ],
UserProfile   USING [ ProfileChangedProc, Number, Token, CallWhenProfileChanges ],
Cursory    USING [ SetMousePositionProc, CallWhenMousePositionChanges ];
FastMouse: CEDAR MONITOR
IMPORTS Commander, Terminal, Interminal, InterminalBackdoor, IO, Process, PrincOpsUtils, UserProfile, Rope, Cursory
~ BEGIN
ROPE: TYPE = Rope.ROPE;
mouseProcess: PROCESSNIL;
vt: Terminal.Virtual ~ InterminalBackdoor.terminal;
position, prevPosition: Terminal.Position;
width: NAT;
height: NAT;
cursorInColorDisplay: BOOLFALSE;
speedUpLinear: BOOLTRUE; -- Acceleration law (linear or squared)
threshold: INTEGER ← 2; -- Speedup control parameters
thresholdxTen: INTEGER ← 20;
ampxTenX: INTEGER ← 15;
ampxTenY: INTEGER ← 15;
ratioYtoXxTen: INTEGER ← 10;
overShoot: INTEGER ← 0; -- to allow Escape velocity hack
bwOverShoot: INTEGER ← 0;
clrOverShoot: INTEGER ← 0;
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: PROC = {
IF NOT ShouldRun[] THEN RETURN;
{ENABLE UNWIND => NoteNotRunning[];
Process.SetPriority[Process.priorityRealTime]; -- this priority is above fault handlers
prevPosition ← Terminal.GetMousePosition[vt];
width ← vt.bwWidth;
height ← vt.bwHeight;
WHILE shouldRun DO
xDist, yDist, 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; overShoot ← clrOverShoot;
}
ELSE {
width ← vt.bwWidth; height ← vt.bwHeight; overShoot ← bwOverShoot;
};
LOOP;
};
};
xDelta ← position.x - prevPosition.x;
yDelta ← position.y - prevPosition.y;
xDist ← ABS[xDelta];
yDist ← ABS[yDelta];
IF xDist > threshold OR yDist > threshold THEN {
IF speedUpLinear
THEN position ← [ -- linear law speedup
MAX[ -overShoot, MIN[ width+overShoot, position.x + (xDelta*ampxTenX / 10)] ],
MAX[ 0, MIN[ height, position.y + (yDelta*ampxTenY / 10)] ]
]
ELSE position ← [ -- square law speedup
MAX[ -overShoot, MIN[ width+overShoot, position.x + (xDelta*xDist / threshold)] ],
MAX[ 0, MIN[ height,
position.y + (yDelta*yDist*ratioYtoXxTen / thresholdxTen) ] ]
];
Terminal.SetMousePosition[vt, position];
};
prevPosition ← [ MAX[ 0, MIN[ width, position.x ]], position.y ];
Cancel overshoot, preventing bounce when corrected by Interminal
ENDLOOP; 
};
NoteNotRunning[];
}; -- DoradoMouseProcess
DandelionMouseProcess: PROC = {  -- note, doesn't work on Dandelions
IF NOT ShouldRun[] THEN RETURN;
{ENABLE UNWIND => NoteNotRunning[];
Process.SetPriority[Process.priorityClient3]; -- this priority is one below fault handlers
prevPosition ← Terminal.GetMousePosition[vt];
width ← vt.bwWidth;
height ← vt.bwHeight;
WHILE shouldRun DO
xDist, yDist, 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;
xDist ← ABS[xDelta];
yDist ← ABS[yDelta];
IF xDist > thresholdX OR yDist > thresholdY THEN {
IF speedUpLinear
THEN position ← [ -- linear law speedup
MAX[ MIN[ position.x + (xDelta * ampxTenX / 10), width], 0],
MAX[ MIN[ position.y + (yDelta * ampxTenY / 10), height], 0] ]
ELSE position ← [ -- square law speedup
MAX[ MIN[ position.x + (xDelta * xDist / threshold), width], 0],
MAX[ MIN[ position.y + (yDelta * yDist / threshold), height], 0] ];
Terminal.SetMousePosition[vt, position];
};
prevPosition ← position;
ENDLOOP;
};
NoteNotRunning[];
}; -- MouseProcess
FastMouseCmd: Commander.CommandProc = {
PROC [cmd: Commander.Handle] RETURNS [result: REF ANYNIL, msg: ROPENIL]
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"}
};
ProcessProfile: UserProfile.ProfileChangedProc = TRUSTED {
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];
thresholdxTen ← threshold * 10;
ampxTenX ← UserProfile.Number["FastMouse.AmpxTen", 15];
ratioYtoXxTen ← UserProfile.Number["FastMouse.RatioYtoXxTen", 10];
ampxTenY ← ampxTenX * ratioYtoXxTen / 10;
speedUpLinear ← Rope.Equal[UserProfile.Token["FastMouse.SpeedUp", "linear"], "linear"];
bwOverShoot ← UserProfile.Number["Interminal.VBWEscape", 0] + 1;
clrOverShoot ← UserProfile.Number["Interminal.VColorEscape", 0] + 1;
IF cursorInColorDisplay THEN overShoot ← clrOverShoot ELSE overShoot ← bwOverShoot;
HandleChange[];
};
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;
};
ProcessMouseChange: Cursory.SetMousePositionProc ~ {
IF NOT running THEN RETURN;
Terminal.WaitForBWVerticalRetrace[vt];
This should wait until MouseProcess has just run since it runs at lower priority
prevPosition ← newPosition;
Terminal.SetMousePosition[vt, position];
Make sure vt.MousePosition gets set before next retrace
};
Init: PROC = {
UserProfile.CallWhenProfileChanges[ProcessProfile];
Cursory.CallWhenMousePositionChanges[ProcessMouseChange];
Commander.Register[key: "FastMouse", proc: FastMouseCmd,
doc: "Turn mouse speedup on or off"];
};
Init[];
END.