<<>> <> <> <> <> DIRECTORY KeySymsSun, KeySymsOSF, KeySymsHP, Process, Rope, Xl, XlAscii, XlAsciiInput, XlCutBuffers; XlAsciiInputImpl: CEDAR MONITOR IMPORTS Process, Rope, XlAscii, Xl, XlCutBuffers EXPORTS XlAsciiInput = BEGIN OPEN Xl, XlAsciiInput; events: EventFilter = CreateEventFilter[destroyNotify, keyPress]; check: CONDITION; BroadCast: ENTRY PROC[] = { ENABLE UNWIND => NULL; BROADCAST check; }; WindowDestroyed: PUBLIC ERROR = CODE; bufferSize: INT ~ 120; Buffer: TYPE = RECORD [ in, out: INT ¬ 0, data: PACKED ARRAY [0..bufferSize) OF CHAR, next: REF Buffer ¬ NIL ]; HandleRec: TYPE = MONITORED RECORD [ connection: Xl.Connection ¬ NIL, window: Window ¬ nullWindow, buffer: REF Buffer ¬ NIL, lastBuffer: REF Buffer ¬ NIL, alive: BOOL ¬ TRUE ]; cnt: INT ¬ 0; unused: REF Buffer ¬ NIL; GetBuffer: INTERNAL PROC [] RETURNS [buff: REF Buffer] = { IF unused=NIL THEN buff ¬ NEW[Buffer] ELSE { buff ¬ unused; unused ¬ buff.next; buff.in ¬ buff.out ¬ 0; buff.next ¬ NIL; cnt ¬ cnt-1; }; }; DisposeBuffer: INTERNAL PROC [buff: REF Buffer] = { IF buff#NIL AND cnt<4 THEN {cnt ¬ cnt+1; buff.next ¬ unused; unused ¬ buff; }; }; EnableAsciiInput: PUBLIC PROC [c: Xl.Connection, w: Xl.Window] RETURNS [Handle] = { h: REF HandleRec ¬ NEW[HandleRec ¬ [connection: c, window: w]]; match: Match ¬ NEW[MatchRep ¬ [proc: EventProc, handles: events, tq: CreateTQ[], data: h]]; Xl.AddDispatch[c, w, match, [keyPress: TRUE, keyRelease: TRUE, structureNotify: TRUE]]; RETURN [h]; }; listWithPaste: LIST OF Xl.KeySym = LIST[KeySymsSun.Paste, KeySymsOSF.Paste, KeySymsHP.Paste]; EventProc: EventProcType = { ENABLE Xl.XError => GOTO oops; h: REF HandleRec ~ NARROW[clientData]; WITH event SELECT FROM keyPress: KeyPressEvent => { char: CHAR; keysym: Xl.KeySym; matched: Xl.KeySym; isModifier: BOOL; [char: char, keysym: keysym, matched: matched, isModifier: isModifier] ¬ XlAscii.Convert[event.connection, keyPress.keyCode, keyPress.state, listWithPaste]; IF isModifier THEN RETURN; IF matched = KeySymsSun.Paste OR matched = KeySymsOSF.Paste OR matched = KeySymsHP.Paste THEN { r: ROPE ¬ XlCutBuffers.Get[event.connection]; TypeIn[h, r]; RETURN }; IF char#0c THEN QueueChar[h, char]; }; destroyNotify: DestroyNotifyEvent => { IF destroyNotify.window=h.window THEN { h.alive ¬ FALSE; BroadCast[]; } }; ENDCASE => {}; EXITS oops => {}; }; TypeIn: PUBLIC ENTRY PROC [handle: Handle, chars: REF] = { ENABLE UNWIND => NULL; WITH handle SELECT FROM h: REF HandleRec => { WITH chars SELECT FROM ch: REF CHAR => InternalQueueChar[h, ch­]; r: Rope.ROPE => { EachChar: INTERNAL Rope.ActionType = {InternalQueueChar[h, c]}; [] ¬ Rope.Map[base: r, action: EachChar]; }; ENDCASE => {}; }; ENDCASE => {}; }; QueueChar: ENTRY PROC [h: REF HandleRec, ch: CHAR] = { ENABLE UNWIND => NULL; InternalQueueChar[h, ch] }; InternalQueueChar: INTERNAL PROC [h: REF HandleRec, ch: CHAR] = { IF ORD[ch]=0 THEN {BROADCAST check; RETURN}; IF h.buffer=NIL THEN { buff: REF Buffer ¬ GetBuffer[]; h.lastBuffer ¬ h.buffer ¬ buff; }; IF h.lastBuffer.in>=bufferSize THEN { h.lastBuffer.next ¬ GetBuffer[]; h.lastBuffer ¬ h.lastBuffer.next; }; h.lastBuffer.data[h.lastBuffer.in] ¬ ch; h.lastBuffer.in ¬ h.lastBuffer.in+1; BROADCAST check; }; CharAvailable: PUBLIC PROC [handle: Handle, wait: BOOL ¬ FALSE] RETURNS [n: INT ¬ 0] = { EntryCharAvailable: ENTRY PROC [] = { ENABLE { UNWIND => NULL; ABORTED => GOTO oops; }; IF wait THEN { WHILE h.buffer=NIL OR h.buffer.out>=h.buffer.in DO WAIT check; IF ~h.alive OR ~Xl.Alive[h.connection] THEN RETURN WITH ERROR WindowDestroyed; Process.CheckForAbort[]; ENDLOOP; n ¬ ABS[h.buffer.in-h.buffer.out]; } ELSE { IF h.buffer=NIL OR h.buffer.out>=h.buffer.in THEN n ¬ 0 ELSE n ¬ ABS[h.buffer.in-h.buffer.out]; }; EXITS oops => RETURN WITH ERROR ABORTED; }; h: REF HandleRec ¬ NARROW[handle]; IF ~h.alive OR ~Xl.Alive[h.connection] THEN ERROR WindowDestroyed; IF h.buffer=NIL AND ~wait THEN RETURN [0]; EntryCharAvailable[] }; GetChar: PUBLIC PROC [handle: Handle, wait: BOOL ¬ TRUE] RETURNS [ch: CHAR¬0c] = { EntryGetChar: ENTRY PROC [] = { ENABLE { UNWIND => NULL; ABORTED => GOTO oops; }; IF wait THEN { WHILE h.buffer=NIL OR h.buffer.out>=h.buffer.in DO WAIT check; IF ~h.alive OR ~Xl.Alive[h.connection] THEN RETURN WITH ERROR WindowDestroyed; Process.CheckForAbort[]; ENDLOOP } ELSE { IF h.buffer=NIL OR h.buffer.out>=h.buffer.in THEN RETURN; }; ch ¬ h.buffer.data[h.buffer.out]; h.buffer.out ¬ h.buffer.out+1; IF h.buffer.out>=bufferSize THEN { buf: REF Buffer ¬ h.buffer; h.buffer ¬ h.buffer.next; IF h.buffer=NIL THEN h.lastBuffer ¬ NIL; DisposeBuffer[buf]; }; EXITS oops => RETURN WITH ERROR ABORTED; }; h: REF HandleRec ¬ NARROW[handle]; IF ~h.alive OR ~Xl.Alive[h.connection] THEN ERROR WindowDestroyed; EntryGetChar[]; }; ResetBuffer: PUBLIC ENTRY PROC [handle: Handle] = { ENABLE UNWIND => NULL; WITH handle SELECT FROM h: REF HandleRec => { DisposeBuffer[h.buffer]; h.buffer ¬ h.lastBuffer ¬ NIL; }; ENDCASE => {}; }; InitCond: ENTRY PROC [] = TRUSTED { ENABLE UNWIND => NULL; Process.InitializeCondition[@check, Process.MsecToTicks[10000]]; }; InitCond[]; END.