TerminalSenderImpl.mesa
Copyright Ó 1990, 1992 by Xerox Corporation. All rights reserved.
Mike Spreitzer January 7, 1987 7:34:38 pm PST
Last tweaked by Mike Spreitzer on July 6, 1992 2:34 pm PDT
Willie-s, April 23, 1992 10:53 am PDT
DIRECTORY Atom, BackStop, Basics, BasicTime, CardToCardTab, CedarProcess, Commander, CommanderOps, IO, KeyMapping, KeyMappingTypes, KeySymsKB, KeySymsSun, KeyTypes, Process, Real, RelativeTimes, RemoteEventTime, RemoteImagerDataTypes, Rope, SimpleFeedback, TerminalSender, TerminalSenderPrivate, TIPKeyboards, UserInput, UserInputDiscrimination, UserInputGetActions, UserInputOps, UserInputTypes;
TerminalSenderImpl: CEDAR MONITOR
LOCKS sr USING sr: Sender
IMPORTS Atom, BackStop, Basics, BasicTime, CardToCardTab, CedarProcess, Commander, CommanderOps, IO, KeyMapping, Process, Real, RemoteEventTime, Rope, SimpleFeedback, TIPKeyboards, UserInputDiscrimination, UserInputGetActions, UserInputOps
EXPORTS TerminalSender
=
BEGIN OPEN RET:RemoteEventTime, TerminalSender;
WaitMode: TYPE ~ UserInputTypes.WaitMode;
PrincOpsKeyName: TYPE ~ RemoteImagerDataTypes.PrincOpsKeyName;
Sender: TYPE ~ REF SenderPrivate;
SenderPrivate: PUBLIC TYPE ~ TerminalSenderPrivate.SenderPrivate;
ActionKind: TYPE = MACHINE DEPENDENT { --copied from DCedar's ClassIncreek
deltaEventTime, eventTime,
deltaMouse, mousePosition, penPosition,
keyDown, keyUp, keyStillDown, allUp,
timedOut -- never stored --
};
allUp is generated when full keyboard state is entered; action readers should
interpret allUp[] as a request to clear the current state in preparation for starting over.
shortWM: WaitMode ¬ dontWait;
timeout: Milliseconds ¬ 10;
closeAbortly: BOOL ¬ TRUE;
debug: BOOL ¬ TRUE;
StartSending: PUBLIC PROC
[et1: EventTime,
xlateKC: KeyCodeTranslation,
hostDescr: ROPE,
sourceData: REF ANY,
GetAction: GetActionProc,
initialState: ActionBodyList,
consumer: IO.STREAM,
Finish: PROC [sender: Sender, sourceData: REF ANY, consumer, pushStream: IO.STREAM] ¬ NIL,
Push: PROC [IO.STREAM] ¬ NIL,
pushStream: IO.STREAM ¬ NIL,
Other: PROC [Sender]]
RETURNS [Sender] = {
sr: Sender ~ NEW [SenderPrivate ¬ [et1: et1, xlateKC: xlateKC, sourceData: sourceData, hostDescr: hostDescr, GetAction: GetAction, consumer: consumer, pushStream: pushStream, Finish: Finish, Push: Push, Other: Other]];
TRUSTED {Process.Detach[sr.sender ¬ FORK SendProcess[sr, initialState]]};
RETURN [sr]};
SendProcess: PROC [sr: Sender, initialState: ActionBodyList] ~ {
OPEN sr;
et0: EventTime ~ et1.Sub[RET.SmallConsCC[0, 1]];
eg0: BasicTime.ExtendedGMT ~ RET.ToEGMT[et0];
SendAction: ENTRY PROC [sr: Sender, ab: ActionBody] = {
ENABLE UNWIND => NULL;
dms: Milliseconds ¬ ab.deltaTime;
SELECT TRUE FROM
ab.kind = $TimeOut => ERROR --shouldn't happen--;
ab.kind = $EventTime, dms > CARD16.LAST => {
SendHeader[eventTime, 0];
SendEventTime[et0.Add[RET.SmallConsCC[0, ab.eventTime]]];
IF ab.kind = $EventTime THEN RETURN ELSE dms ¬ 0};
dms > BYTE.LAST => {
SendHeader[deltaEventTime, 0];
SendCard16[dms];
dms ¬ 0};
ENDCASE => NULL;
SELECT ab.kind FROM
$TimeIsPassing => {SendHeader[deltaEventTime, 0]; SendCard16[dms]};
$End => going ¬ FALSE;
$AllUp, $KeyStillDown => NULL;
$Key => {dkc: KeyCode ~ xlateKC[ab.keyCode];
IF dkc <= PrincOpsKeyName.LAST.ORD
THEN {SendHeader[IF ab.down THEN keyDown ELSE keyUp, dms]; SendByte[dkc]}};
$IntegerPosition => {SendHeader[mousePosition, dms]; SendMousePosition[[mouseX: ab.x, mouseY: ab.y, color: DisplayToColor[ab.display]]]};
$Position => {SendHeader[mousePosition, dms]; SendMousePosition[[mouseX: Real.Round[ab.rx], mouseY: Real.Round[ab.ry], color: DisplayToColor[ab.display]]]};
ENDCASE => NULL;
RETURN};
SendChar: PROC [c: CHAR] = INLINE {consumer.PutChar[c]};
SendByte: PROC [byte: BYTE] = INLINE {SendChar[VAL[byte]]};
SendHeader: PROC [kind: ActionKind, dms: BYTE]
~ {SendByte[kind.ORD]; SendByte[dms]};
SendMousePosition: PROC [mp: RemoteImagerDataTypes.MousePosition] = INLINE {
SendInteger[mp.mouseX];
SendInteger[mp.mouseY+16383 - (IF mp.color THEN 32768 ELSE 0)]
};
SendEventTime: PROC [eventTime: EventTime] = {
SendCard[eventTime.hi];
SendCard16[eventTime.lo];
};
SendCard: PROC [card: CARD] = INLINE {
SendCard16[Basics.HighHalf[card]];
SendCard16[Basics.LowHalf[card]];
};
SendCard16: PROC [cardinal: CARD16] = INLINE {
SendByte[Basics.HighByte[cardinal]];
SendByte[Basics.LowByte[cardinal]];
};
SendInteger: PROC [i: INTEGER] = {SendCard16[IF i<0 THEN i+10000H ELSE i]};
NextAction: GetActionProc ~ {
IF initialState#NIL THEN {
head: ActionBodyList ~ initialState;
initialState ¬ initialState.rest;
RETURN [head.first]};
RETURN GetAction[sr.sourceData, waitMode, waitInterval, acceptance]};
FinishProcess: PROC ~ {
IF debug THEN SimpleFeedback.PutFL[$TerminalSender, oneLiner, $Debug, "%g Finishing Viewers Terminal sender to %g.", LIST[[time[BasicTime.Now[]]], [rope[hostDescr]]] ];
IF Finish#NIL THEN Finish[sr, sourceData, consumer, pushStream];
};
TsToEgmt: PROC [ts: RelativeTimes.TimeStamp] RETURNS [BasicTime.ExtendedGMT] ~ {
ds: NAT ¬ ts/1D3;
us: NAT ¬ (ts - ds*1D3)*1D3 + eg0.usecs;
IF us >= 1D6 THEN {us ¬ us - 1D6; ds ¬ ds + 1};
RETURN [[eg0.gmt.Update[ds], us]]};
EgmtToTs: PROC [eg: BasicTime.ExtendedGMT] RETURNS [RelativeTimes.TimeStamp] ~ {
ds: CARD ¬ BasicTime.Period[from: eg0.gmt, to: eg.gmt];
dus: INT ¬ INT[eg.usecs] - INT[eg0.usecs];
IF dus < 0 THEN {ds ¬ ds - 1; dus ¬ dus + 1D6};
RETURN [[ds*1000 + CARD[dus+500]/1D3]]};
wm: WaitMode ¬ shortWM;
ab: ActionBody ¬ [$TimeOut, [0], 0];
firstDownPullTime, pushTime, doneTime: BasicTime.ExtendedGMT ¬ eg0;
firstDownTS, firstDownPullTS, pushEventTS, pushTS, doneTS: RelativeTimes.TimeStamp ¬ [0];
noDown: BOOL ¬ TRUE;
nDown: NAT ¬ 0;
{ENABLE UNWIND => FinishProcess[];
IF debug THEN SimpleFeedback.PutFL[$TerminalSender, oneLiner, $Debug, "%g Starting Viewers Terminal sender to %g.", LIST[[time[BasicTime.Now[]]], [rope[hostDescr]]] ];
IF verboseDebugDelay THEN SimpleFeedback.PutFL[$TerminalSender, oneLiner, $Debug, "eg0 = [gmt: %g, usecs: %g].", LIST[[time[eg0.gmt]], [cardinal[eg0.usecs]]] ];
{ENABLE IO.Error => IF (ec = StreamClosed OR ec = Failure) THEN CONTINUE;
WHILE going DO
IF initialState=NIL THEN Other[sr];
IF NOT going THEN EXIT;
ab ¬ NextAction[NIL, wm, timeout, all];
IF debugDelay AND ab.kind=$Key AND ab.down THEN {
IF noDown THEN {
firstDownTS ¬ ab.eventTime;
firstDownPullTime ¬ BasicTime.ExtendedNow[];
firstDownPullTS ¬ EgmtToTs[firstDownPullTime];
noDown ¬ FALSE;
nDown ¬ 1}
ELSE nDown ¬ nDown.SUCC};
IF NOT going THEN EXIT;
CedarProcess.CheckAbort[];
IF ab.kind = $TimeOut THEN {
IF debugDelay AND NOT noDown THEN {
pushEventTS ¬ ab.eventTime;
pushTS ¬ EgmtToTs[pushTime ¬ BasicTime.ExtendedNow[]]};
IF Push # NIL THEN Push[pushStream];
IF debugDelay AND NOT noDown THEN {
doneTS ¬ EgmtToTs[doneTime ¬ BasicTime.ExtendedNow[]];
SimpleFeedback.PutFL[$TerminalSender, oneLiner, $Debug, "Delay to pull=%g, t.o.Inp=%g, t.o.Real=%g, done=%gms; %g dn.", LIST[[cardinal[firstDownPullTS-firstDownTS]], [cardinal[pushEventTS-firstDownTS]], [cardinal[pushTS-firstDownTS]], [cardinal[doneTS-firstDownTS]], [cardinal[nDown]]] ];
IF verboseDebugDelay THEN SimpleFeedback.PutFL[$TerminalSender, oneLiner, $Debug, "First down input TS = %g, real EGmt = [%g, %g], pull TS = %g.", LIST[[cardinal[firstDownTS]], [time[firstDownPullTime.gmt]], [cardinal[firstDownPullTime.usecs]], [cardinal[firstDownPullTS]]] ];
noDown ¬ TRUE};
wm ¬ forever;
LOOP}
ELSE wm ¬ shortWM;
SendAction[sr, ab];
IF ab.kind = $End THEN going ¬ FALSE;
ENDLOOP;
}};
FinishProcess[];
RETURN};
debugDelay, verboseDebugDelay: BOOL ¬ FALSE;
FromUihToDcedar: PUBLIC PROC [uih: UserInput.Handle]
RETURNS [kct: KeyCodeTranslation] ~ {
best: CardToCardTab.Ref ~ CardToCardTab.Create[];
second: CardToCardTab.Ref ~ CardToCardTab.Create[];
mapping: KeyMappingTypes.Mapping ¬ UserInputOps.GetMapping[uih];
Update: PROC [dSofar: KeyCode, tab: CardToCardTab.Ref, ks: CARD] RETURNS [KeyCode] ~ {
found: BOOL;
dNew: CARD;
[found, dNew] ¬ tab.Fetch[ks];
IF found THEN RETURN [dNew];
RETURN [dSofar]};
kct ¬ NEW [KeyCodeTranslationRep ¬ ALL[255]];
FOR dkn: PrincOpsKeyName IN PrincOpsKeyName DO
ks1, ks2: CARD;
success: BOOL;
[ks1, ks2, success] ¬ TIPKeyboards.KeySymsFromKeyCode[VAL[dkn.ORD]];
IF success THEN {
[] ¬ best.Store[ks1, dkn.ORD];
[] ¬ second.Store[ks2, dkn.ORD];
};
IF dkn.ORD = noteKC THEN noted ¬ noted+1;
ENDLOOP;
[] ¬ best.Store[KeySymsKB.A10, PrincOpsKeyName[Arrow].ORD]; --??
[] ¬ best.Store[KeySymsKB.R10, PrincOpsKeyName[Arrow].ORD];
[] ¬ best.Store[KeySymsKB.KeypadSix, PrincOpsKeyName[STUFF].ORD];
[] ¬ best.Store[KeySymsKB.RightMeta, PrincOpsKeyName[EXPAND].ORD];
[] ¬ best.Store[KeySymsKB.R15, PrincOpsKeyName[USERABORT].ORD];
[] ¬ best.Store[KeySymsKB.RightAlt, PrincOpsKeyName[USERABORT].ORD];
[] ¬ best.Store[KeySymsKB.LeftAlt, PrincOpsKeyName[A11].ORD]; --just put it somewhere; hopefully A11 isn't used for anything interesting
[] ¬ best.Store[KeySymsSun.Paste, PrincOpsKeyName[A8].ORD]; --TIPKeyboards already says PASTE is used for LineFeed!
[] ¬ best.Store[66202, PrincOpsKeyName[COPY].ORD];
[] ¬ best.Store[66203, PrincOpsKeyName[A8].ORD];
FOR kc: KeyTypes.KeyCode IN KeyTypes.KeyCode DO
n: NAT ~ KeyMapping.CountKeySyms[mapping, kc];
hisBest, hisSecond: KeyCode ¬ 255;
FOR i: NAT IN [0..n) DO
ks: CARD ~ KeyMapping.GetKeySym[mapping, kc, i];
hisBest ¬ Update[hisBest, best, ks];
hisSecond ¬ Update[hisSecond, second, ks];
IF kc.ORD = noteKC THEN noted ¬ noted+1;
ENDLOOP;
IF kc.ORD = noteKC THEN noted ¬ noted+1;
IF hisBest#255 THEN kct[kc] ¬ hisBest
ELSE
kct[kc] ¬ hisSecond;
ENDLOOP;
RETURN};
noteKC: CARD ¬ CARD.LAST;
noted: CARD ¬ 0;
lastTimeStamp: RelativeTimes.TimeStamp ~ [LAST[CARD32]];
StateOfIncreek: PUBLIC PROC [creek: UserInput.Handle] RETURNS [ActionBodyList, RelativeTimes.TimeStamp] ~ {
ab: ActionBody ~ UserInputGetActions.GetInputActionBody[creek];
IF ab.kind=$End OR ab.kind=$TimeOut OR ab.eventTime=lastTimeStamp THEN ERROR;
RETURN [
LIST[
[$EventTime, ab.eventTime, 0]
],
ab.eventTime
]};
UserInputGetAction: PUBLIC PROC [sourceData: REF ANY, waitMode: WaitMode, waitInterval: Milliseconds, acceptance: UserInputTypes.Acceptance] RETURNS [ActionBody] --GetActionProc-- ~ TRUSTED {
creek: UserInput.Handle ~ UserInputDiscrimination.NarrowHandle[sourceData];
RETURN UserInputGetActions.GetInputActionBody[creek, waitMode, waitInterval, acceptance]};
StopSending: PUBLIC ENTRY PROC [sr: Sender] ~ {
ENABLE UNWIND => NULL;
sr.going ¬ FALSE;
RETURN};
WithSender: PUBLIC ENTRY PROC [sr: Sender, With: PROC [IO.STREAM]] ~ {
ENABLE UNWIND => NULL;
With[sr.consumer];
RETURN};
SendTimeReply: PUBLIC PROC [sr: Sender, org, mid: EventTime, descToo: BOOL, desc: EventDesc] ~ {
OPEN sr;
SendEventTime: PROC [eventTime: EventTime] = INLINE {
SendCard[eventTime.hi];
SendCard16[eventTime.lo];
};
SendCard: PROC [card: CARD] = INLINE {
SendCard16[Basics.HighHalf[card]];
SendCard16[Basics.LowHalf[card]];
};
SendCard16: PROC [cardinal: CARDINAL] = INLINE {
SendByte[Basics.HighByte[cardinal]];
SendByte[Basics.LowByte[cardinal]];
};
SendByte: PROC [byte: BYTE] = INLINE {SendChar[VAL[byte]]};
SendChar: PROC [c: CHAR] = INLINE {consumer.PutChar[c]};
dcopy: EventDesc ¬ desc;
SendEventTime[org];
SendEventTime[mid];
IF descToo THEN TRUSTED {consumer.UnsafePutBlock[[base: LOOPHOLE[@dcopy], count: BYTES[EventDesc]]]};
RETURN};
SendCutBuffer: PUBLIC PROC [sr: Sender, buffer: ATOM, key: CARD, data: ROPE] ~ {
OPEN sr;
SendRope: PROC [r: ROPE] ~ {
SendCard[r.Length];
consumer.PutRope[r];
RETURN};
SendCard: PROC [card: CARD] = INLINE {
SendCard16[Basics.HighHalf[card]];
SendCard16[Basics.LowHalf[card]];
};
SendCard16: PROC [cardinal: CARDINAL] = INLINE {
SendByte[Basics.HighByte[cardinal]];
SendByte[Basics.LowByte[cardinal]];
};
SendByte: PROC [byte: BYTE] = INLINE {SendChar[VAL[byte]]};
SendChar: PROC [c: CHAR] = INLINE {consumer.PutChar[c]};
SendRope[Atom.GetPName[buffer]];
SendCard[key];
SendRope[data];
RETURN};
Close: PUBLIC ENTRY PROC [sr: Sender] ~ {
ENABLE UNWIND => NULL;
sr.consumer.Close[abort: closeAbortly !IO.Error => CONTINUE];
RETURN};
PushProcess: PROC [sr: Sender] = {
Process.SetPriority[Process.priorityForeground];
WHILE sr.going DO
EnterAndPush: PROC = {
PushWithLock: ENTRY PROC [sr: Sender] = {
ENABLE UNWIND => NULL;
sr.Push[sr.pushStream];
RETURN};
PushWithLock[sr];
RETURN};
Process.Pause[pushPeriod];
IF pushMsg # NIL THEN lastPushMsg ¬ pushMsg;
IF sr.Push # NIL THEN pushMsg ¬ BackStop.Call[EnterAndPush];
ENDLOOP;
};
pushMsg, lastPushMsg: Rope.ROPE ¬ NIL;
pushPeriod: Process.Ticks ¬ Process.MsecToTicks[333];
DisplayToColor: PROC [ra: REF ANY] RETURNS [BOOL] ~ {
SELECT ra FROM
$Main, $Display0, NIL => RETURN [FALSE];
ENDCASE => RETURN [TRUE]};
optionDesc: ROPE ¬ "((+|-)(debug|debugDelay|verboseDebugDelay))* --- show/change flg(s)";
optionUsage: ROPE ¬ Rope.Concat["TerminalSenderOption ", optionDesc];
OptionCmd: PROC [cmd: Commander.Handle] RETURNS [result: REF ANY ¬ NIL, msg: ROPE ¬ NIL] ~ {
argv: CommanderOps.ArgumentVector ~ CommanderOps.Parse[cmd];
Set: PROC [name: ROPE, sense: BOOL] RETURNS [BOOL] ~ {
SELECT TRUE FROM
name.Equal["debug", FALSE] => debug ¬ sense;
name.Equal["debugDelay", FALSE] => debugDelay ¬ sense;
name.Equal["verboseDebugDelay", FALSE] => verboseDebugDelay ¬ sense;
ENDCASE => RETURN [TRUE];
RETURN [FALSE]};
i: NAT ¬ 1;
IF argv.argc<1 THEN RETURN [$Null, optionUsage];
WHILE i < argv.argc DO
SELECT TRUE FROM
argv[i].Length = 0 => RETURN [$Failure, optionUsage];
argv[i].Fetch[0] = '+ => IF Set[argv[i].Substr[1], TRUE] THEN RETURN [$Failure, optionUsage];
argv[i].Fetch[0] = '- => IF Set[argv[i].Substr[1], FALSE] THEN RETURN [$Failure, optionUsage];
ENDCASE => RETURN [$Failure, optionUsage];
i ¬ i.SUCC;
ENDLOOP;
cmd.out.PutFL["TerminalSender options are: debug=%g, debugDelay=%g, verboseDebugDelay=%g.\n", LIST[ [boolean[debug]], [boolean[debugDelay]], [boolean[verboseDebugDelay]] ]];
RETURN};
Commander.Register["TerminalSenderOption", OptionCmd, optionDesc];
END.