DIRECTORY Basics USING [LongDiv, LongMult], Booting USING [CheckpointProc, RollbackProc, RegisterProcs], ClassIncreek USING [Action, ActionBody], ClassInscript USING [Inscript, SetWritePage, WriteEntry], Interminal USING [CursorArray, DownUp, HasPenType, KeyName, KeyState, MousePosition, Side], InterminalBackdoor USING [], Intime USING [AdjustEventTime, DeltaDeltaTime, DeltaTime, EventTime, EventTimeDifference, maxDeltaDeltaTime, maxDeltaTime, msPerDeltaTick, MsTicks, MsTicksToDeltaTime, ReadEventTime, SubtractMsTicksFromEventTime], Loader USING [MakeProcedureResident, MakeGlobalFrameResident], Process USING [Priority, GetPriority, SetPriority], Terminal USING [Create, GetBWCursorPattern, GetBWCursorPosition, GetColorCursorPattern, GetKeys, GetMousePosition, Position, RegisterNotifier, SetBWCursorPattern, SetBWCursorPosition, SetColorCursorPattern, SetColorCursorPosition, SetColorCursorState, SetMousePosition, SwapAction, SwapNotifier, Virtual, WaitForBWVerticalRetrace], TerminalFace USING [refreshRate], UserProfile USING [CallWhenProfileChanges, Number, ProfileChangedProc]; InterminalImpl: MONITOR IMPORTS Basics, Booting, ClassInscript, Intime, Loader, Process, Terminal, TerminalFace, UserProfile EXPORTS Interminal, InterminalBackdoor SHARES Interminal = BEGIN OPEN Interminal, ClassIncreek, ClassInscript, Intime; inscript: ClassInscript.Inscript _ NIL; terminal: PUBLIC Terminal.Virtual _ Terminal.Create[]; wordlength: CARDINAL = 16; Internal: ERROR[code: INTEGER] = CODE; -- "can't happen errors" mousePosition: Terminal.Position _ terminal.GetMousePosition[]; keyState: KeyState _ [bits[ALL[up]]]; -- _currentKeys^, see init -- eventTime: EventTime; -- _ReadEventTime[], initialized below lastMousePosition: Terminal.Position; lastKeyState: KeyState _ [bits[ALL[up]]]; -- _keyState, to start, see init lastEventTime: EventTime; doTrack: BOOL _ TRUE; hotX: INTEGER _ 0; hotY: INTEGER _ 0; vBWEscape: INTEGER _ 10; -- escape velocity from BW display vColorEscape: INTEGER _ 10; -- escape velocity from color display allZeros: CursorArray _ ALL[0]; grainDots: INTEGER; defaultGrainDots: INTEGER_5; grainMsTicks: Intime.MsTicks; defaultGrainTicks: Intime.MsTicks_200; -- 5 actions per second grainTicks: INTEGER; grainTicksLeft: INTEGER; rR: INTEGER ~ TerminalFace.refreshRate*2; grainMsPerTick: Intime.MsTicks _ (1000+rR-1)/rR; -- System pulse duration in Ms. SetCursorOffset: PUBLIC SAFE PROC[deltaX, deltaY: INTEGER, enableTracking: BOOL _ TRUE] = CHECKED { hotX _ deltaX; hotY _ deltaY; doTrack _ enableTracking; }; GetCursorOffset: PUBLIC SAFE PROC RETURNS[deltaX, deltaY: INTEGER, trackingEnabled: BOOL] = CHECKED { RETURN[deltaX: hotX, deltaY: hotY, trackingEnabled: doTrack]; }; SetCursorPosition: PUBLIC SAFE PROC[posX, posY: INTEGER, enableTracking: BOOL _ TRUE] = CHECKED { doTrack _ enableTracking; terminal.SetBWCursorPosition[[x: posX, y: posY]]; }; GetCursorPosition: PUBLIC SAFE PROC RETURNS[deltaX, deltaY: INTEGER, trackingEnabled: BOOL] = CHECKED { pos: Terminal.Position = terminal.GetBWCursorPosition[]; RETURN[deltaX: pos.x, deltaY: pos.y, trackingEnabled: doTrack]; }; hasColor: BOOL _ FALSE; DisplayRec: TYPE = RECORD[xMin,xMax,yMin,yMax: INTEGER, color: BOOL]; left,right,display: POINTER TO DisplayRec; leftRep: DisplayRec; rightRep: DisplayRec; ActionRecorder: PROC = { DO WaitForTick[]; -- run once per tick IF hasColor THEN HardCase[] ELSE { mousePosition _ terminal.GetMousePosition[]; mousePosition.x _ MAX[MIN[display.xMax, mousePosition.x], display.xMin]; mousePosition.y _ MAX[MIN[display.yMax, mousePosition.y], display.yMin]; terminal.SetMousePosition[mousePosition]; IF doTrack THEN terminal.SetBWCursorPosition[[x: mousePosition.x+hotX, y: mousePosition.y+hotY]]; }; keyState _ [bits[terminal.GetKeys[]]]; IF keyState # lastKeyState THEN { eventTime _ ReadEventTime[]; [] _ EnterKeyState[]; } ELSE DoMousePosition[]; ENDLOOP; }; HardCase: PROC = INLINE { pattern: CursorArray; updateCursorBits: BOOL _ FALSE; mouse: Terminal.Position _ terminal.GetMousePosition[]; toH,fromH: INTEGER; hideCursor: PROC = INLINE { IF display.color THEN { pattern _ terminal.GetColorCursorPattern[]; [] _ terminal.SetColorCursorPosition[[-100,-100]]; } ELSE { pattern _ terminal.GetBWCursorPattern[]; terminal.SetBWCursorPattern[allZeros]; terminal.SetBWCursorPosition[[-100,-100]] --hides bw cursor }; }; setCursorBits: PROC = INLINE { IF display.color THEN terminal.SetColorCursorPattern[pattern] ELSE terminal.SetBWCursorPattern[pattern]; }; mouse.y _ MAX[MIN[mouse.y,display.yMax], display.yMin]; SELECT TRUE FROM mouse.x>display.xMax => { -- the mouse is moving right IF display=left AND (mouse.x-display.xMax>(IF display.color THEN vColorEscape ELSE vBWEscape)) THEN { -- move to right display hideCursor[]; fromH _ display.yMax; display _ right; toH _ display.yMax; mousePosition.x _ display.xMin; terminal.SetMousePosition[mousePosition]; mouse.y _ Basics.LongDiv[Basics.LongMult[mouse.y,toH],fromH]; setCursorBits[]} ELSE mousePosition.x _ display.xMax }; mouse.x { -- the mouse is moving left IF display=right AND (display.xMin-mouse.x>(IF display.color THEN vColorEscape ELSE vBWEscape)) THEN { -- move to left display hideCursor[]; fromH _ display.yMax; display _ left; toH _ display.yMax; mousePosition.x _ display.xMax; terminal.SetMousePosition[mousePosition]; mouse.y _ Basics.LongDiv[Basics.LongMult[mouse.y,toH],fromH]; setCursorBits[]} ELSE mousePosition.x _ display.xMin }; ENDCASE => mousePosition.x _ mouse.x; mousePosition.y _ mouse.y; terminal.SetMousePosition[mousePosition]; IF doTrack THEN { cursorPosition: Terminal.Position ~ [x: mousePosition.x+hotX, y: mousePosition.y+hotY]; IF ~display.color THEN terminal.SetBWCursorPosition[cursorPosition] ELSE terminal.SetColorCursorPosition[cursorPosition]; }; }; DoMousePosition: PROC = INLINE { IF (grainTicksLeft_grainTicksLeft-1) > 0 THEN RETURN; grainTicksLeft_grainTicks; IF mousePosition#lastMousePosition THEN { eventTime _ ReadEventTime[]; []_EnterMousePosition[FALSE]; }; }; mouseAB: mousePosition ActionBody _ [contents: mousePosition[mousePosition: [mousePosition.x, FALSE, mousePosition.y]]]; maxDeltaMouse: INTEGER = LAST[[-8..8)]; EnterMousePosition: PROC[enterFullPos: BOOL] RETURNS [enteredFull: BOOL_FALSE] = { dX: INTEGER = mousePosition.x-lastMousePosition.x; dY: INTEGER = mousePosition.y-lastMousePosition.y; mouseA: Action; IF NOT enterFullPos AND (ABS[dX]<=maxDeltaMouse AND ABS[dY]<=maxDeltaMouse) THEN { dMouseB: deltaMouse ActionBody _ [contents: deltaMouse[value: [dX, -dY]]]; mouseA_@dMouseB; } ELSE { y: INTEGER _ IF display.color THEN terminal.colorHeight-mousePosition.y-1 ELSE terminal.bwHeight-mousePosition.y-1; mouseAB.mousePosition_[mousePosition.x, display.color, y]; mouseA_@mouseAB; }; IF EnterAction[mouseA].enteredFull THEN RETURN[TRUE]; lastMousePosition _ mousePosition; }; KMethod: TYPE = {action, state}; chordWaits: NAT _ 0; MouseClickIndex: NAT = 0; EnterKeyState: PROC [method: KMethod _ action] RETURNS [enteredFull: BOOL_FALSE] = { Kn: PROC [v: CARDINAL] RETURNS [KeyName] = INLINE {RETURN[LOOPHOLE[v]]}; Kv: PROC [n: KeyName] RETURNS [CARDINAL] = INLINE {RETURN[LOOPHOLE[n]]}; IF mousePosition#lastMousePosition AND EnterMousePosition[FALSE].enteredFull THEN RETURN [TRUE]; DO scanNeeded: BOOL _ FALSE; tempState: KeyState _ keyState; tempTime: EventTime; FOR i: CARDINAL IN [0..SIZE[KeyState]) DO IF keyState.words[i] # lastKeyState.words[i] THEN IF chordWaits # 0 AND i = MouseClickIndex THEN { newRed: DownUp _ keyState.bits[Red]; newBlue: DownUp _ keyState.bits[Blue]; oldRed: DownUp _ lastKeyState.bits[Red]; -- Left oldBlue: DownUp _ lastKeyState.bits[Blue]; -- Right oldYellow: DownUp _ lastKeyState.bits[Yellow]; -- Center noChange: BOOL _ FALSE; noEvent: BOOL _ FALSE; THROUGH [0..chordWaits) DO SELECT TRUE FROM newRed = newBlue => { IF keyState.bits[Yellow] = up THEN { tempState.bits[Yellow] _ keyState.bits[Yellow] _ newRed; tempState.bits[Red] _ keyState.bits[Red] _ up; tempState.bits[Blue] _ keyState.bits[Blue] _ up; }; EXIT; }; newRed # oldRed, newBlue # oldBlue => { WaitForTick[]; tempState _ [bits[terminal.GetKeys[]]]; FOR kw: CARDINAL IN [0..SIZE[KeyState]) DO IF tempState.words[i] # keyState.words[i] THEN { scanNeeded _ TRUE; EXIT}; ENDLOOP; IF scanNeeded THEN { tempTime _ ReadEventTime[]; newRed _ tempState.bits[Red]; newBlue _ tempState.bits[Blue]; IF newRed # newBlue THEN EXIT; IF newRed = oldYellow THEN EXIT; }; }; ENDCASE => { noChange _ TRUE; EXIT; }; ENDLOOP; IF keyState.words[MouseClickIndex] = lastKeyState.words[MouseClickIndex] THEN { noEvent _ TRUE; LOOP; }; }; FOR j: KeyName IN [Kn[i*wordlength]..Kn[(i + 1)*wordlength]) DO x: DownUp = keyState.bits[j]; IF x # lastKeyState.bits[j] THEN { aUpDown: keyUp ActionBody _ SELECT TRUE FROM x = up => [contents: keyUp[value: j]], method = action => [contents: keyDown[value: j]], ENDCASE => [contents: keyStillDown[value: j]]; IF EnterAction[@aUpDown].enteredFull THEN RETURN[TRUE]; }; ENDLOOP; ENDLOOP; lastKeyState _ keyState; IF NOT scanNeeded THEN EXIT; keyState _ tempState; eventTime _ tempTime; ENDLOOP; }; EnterFullKeyState: PROC RETURNS [enteredFull: BOOL] = { aAllUp: allUp ActionBody _ [contents: allUp[]]; IF EnterKeyState[].enteredFull THEN RETURN[TRUE]; -- enter any new actions -- lastKeyState _ [bits[ALL[up]]]; IF EnterAction[@aAllUp].enteredFull THEN RETURN[TRUE];--reader should clear state next time RETURN[EnterKeyState[state].enteredFull]; -- enter current state of all down keys -- }; EnterTime: PROC RETURNS [dTime: DeltaDeltaTime, enteredFull: BOOL] = INLINE { a: deltaEventTime ActionBody; difference: MsTicks _ EventTimeDifference[@eventTime, @lastEventTime]; deltaTime: CARDINAL; -- s/b DeltaTime, but can be out of range [deltaTime, difference] _ MsTicksToDeltaTime[difference]; IF deltaTime > maxDeltaTime THEN RETURN[0, EnterFullTime[].enteredFull]; SubtractMsTicksFromEventTime[@eventTime, difference]; IF deltaTime <= maxDeltaDeltaTime THEN RETURN[deltaTime, FALSE]; a _ [contents: deltaEventTime[value: deltaTime]]; RETURN[0, EnterAction[@a].enteredFull]; }; EnterFullTime: PROC RETURNS [enteredFull: BOOL] = { a: eventTime ActionBody _ [contents: eventTime[eventTime: eventTime]]; RETURN[EnterAction[@a].enteredFull]; }; EnterFullState: PROC = INLINE { IF ~EnterFullTime[].enteredFull AND ~ EnterFullKeyState[].enteredFull THEN []_EnterMousePosition[TRUE]; }; InsertAction: PUBLIC SAFE PROC [action: ClassIncreek.ActionBody] = TRUSTED { [] _ EnterAction[@action]; }; EnterAction: PROC [action: Action] RETURNS [enteredFull: BOOL _ FALSE] = { length: CARDINAL = SELECT action.kind FROM deltaEventTime => SIZE[deltaEventTime ActionBody], keyDown => SIZE[keyDown ActionBody], keyStillDown => SIZE[keyStillDown ActionBody], keyUp => SIZE[keyUp ActionBody], allUp => SIZE[allUp ActionBody], eventTime => SIZE[eventTime ActionBody], deltaMouse => SIZE[deltaMouse ActionBody], mousePosition => SIZE[mousePosition ActionBody], penPosition => SIZE[penPosition ActionBody], timedOut => SIZE[timedOut ActionBody], ENDCASE => ERROR Internal[1]; action.deltaDeltaTime _ 0; WITH action SELECT FROM eventTime, deltaEventTime => NULL; ENDCASE => { [action.deltaDeltaTime, enteredFull] _ EnterTime[]; IF enteredFull THEN RETURN[TRUE]; --full action record was made }; IF ~WriteEntry[inscript, DESCRIPTOR[action, length]] THEN { IF ~SetWritePage[inscript] THEN RETURN[FALSE]; EnterFullState[]; RETURN[TRUE]; }; lastEventTime _ eventTime; -- time only changes when something's been entered }; SetMousePosition: PUBLIC SAFE PROC [pos: MousePosition] = CHECKED { terminal.SetMousePosition[[x: pos.mouseX, y: pos.mouseY]]; }; GetMousePosition: PUBLIC SAFE PROC RETURNS [pos: MousePosition] = TRUSTED { p: Terminal.Position = terminal.GetMousePosition[]; pos _ [p.x, display.color, p.y]; }; DefaultMouseGrain: PUBLIC SAFE PROC RETURNS [ticks: Intime.MsTicks, dots: INTEGER] = CHECKED { RETURN[defaultGrainTicks, defaultGrainDots]; }; SetMouseGrain: PUBLIC SAFE PROC [ticks: Intime.MsTicks, dots: INTEGER] = CHECKED { grainDots_MIN[defaultGrainDots, dots]; grainMsTicks_MIN[defaultGrainTicks, ticks]; grainTicks_(grainMsTicks+grainMsPerTick-1)/grainMsPerTick; grainTicksLeft_0; }; SetCursorPattern: PUBLIC SAFE PROC [cursorPattern: CursorArray] = TRUSTED { IF display.color THEN terminal.SetColorCursorPattern[cursorPattern] ELSE terminal.SetBWCursorPattern[cursorPattern]; }; GetCursorPattern: PUBLIC SAFE PROC RETURNS [cursorPattern: CursorArray] = TRUSTED { IF display.color THEN RETURN[terminal.GetColorCursorPattern[]] ELSE RETURN[terminal.GetBWCursorPattern[]]; }; TurnOnColorCursor: PUBLIC SAFE PROC [side: Side] = TRUSTED { color: POINTER TO DisplayRec; IF hasColor THEN TurnOffColorCursor[]; IF NOT terminal.hasColorDisplay THEN RETURN; SELECT side FROM left => {display _ right; color _ left}; right => {display _ left; color _ right}; ENDCASE => ERROR; color^ _ [xMin: 0, xMax: terminal.colorWidth-1, yMin: 0, yMax: terminal.colorHeight-1, color: TRUE]; hasColor _ TRUE; terminal.SetColorCursorPosition[[-100, -100]]; [] _ terminal.SetColorCursorState[$visible]; }; TurnOffColorCursor: PUBLIC SAFE PROC = TRUSTED { IF hasColor THEN { IF display.color THEN { display _ (IF display=left THEN right ELSE left); [] _ terminal.SetColorCursorState[$invisible]; terminal.SetBWCursorPattern[terminal.GetColorCursorPattern[]]; }; hasColor _ FALSE; IF display=left THEN right^ _ left^ ELSE left^ _ right^; }; }; HasPen: PUBLIC SAFE PROC [display: HasPenType] = CHECKED { }; WaitForTick: PROC = INLINE { WaitForEnabled[]; terminal.WaitForBWVerticalRetrace[]; }; WaitForEnabled: ENTRY PROC = INLINE { UNTIL terminalHandlerEnabled DO WAIT enableTerminalHandler; ENDLOOP; }; terminalProcess: PROCESS; StartActionRecorder: PUBLIC SAFE PROC [scr: ClassInscript.Inscript] = TRUSTED { KeyboardPriority: Process.Priority = 6; save: Process.Priority = Process.GetPriority[]; inscript _ scr; right _ @rightRep; left _ @leftRep; right^ _ left ^ _ [xMin: 0, xMax: terminal.bwWidth-1, yMin: 0, yMax: terminal.bwHeight-1, color: FALSE]; display _ right; --most bw displays are to the right of the color monitor Process.SetPriority[KeyboardPriority]; terminalProcess _ FORK ActionRecorder[]; Process.SetPriority[save]; Terminal.RegisterNotifier[terminal, InscriptController]; Booting.RegisterProcs[c: InscriptCPProc, r: InscriptRBProc]; SetMouseGrain[defaultGrainTicks, defaultGrainDots]; EnableTerminalHandler[]; -- InscriptController[enable]; We're it. }; InscriptController: Terminal.SwapNotifier = TRUSTED { SELECT action FROM coming => { IF terminalHandlerCurrent = 0 THEN EnableTerminalHandler[]; terminalHandlerCurrent _ terminalHandlerCurrent + 1; }; going => { terminalHandlerCurrent _ terminalHandlerCurrent - 1; IF terminalHandlerCurrent = 0 THEN DisableTerminalHandler[]; }; here, gone => NULL; ENDCASE=>ERROR; }; InscriptCPProc: Booting.CheckpointProc = TRUSTED { DisableTerminalHandler[]; }; InscriptRBProc: Booting.RollbackProc = TRUSTED { EnableTerminalHandler[]; }; terminalHandlerCurrent: INTEGER _ 0; terminalHandlerEnabled: BOOL _ FALSE; enableTerminalHandler: CONDITION; EnableTerminalHandler: ENTRY PROC = { ENABLE UNWIND => NULL; Intime.AdjustEventTime[TRUE]; eventTime _ lastEventTime _ ReadEventTime[]; keyState _ [bits[terminal.GetKeys[]]]; lastKeyState _ keyState; EnterFullState[]; terminalHandlerEnabled_TRUE; NOTIFY enableTerminalHandler; }; DisableTerminalHandler: ENTRY PROC = {terminalHandlerEnabled _ FALSE}; SetColorDisplaySide: PUBLIC SAFE PROC[side: Side] = CHECKED { IF GetColorDisplaySide[]#side THEN { temp: POINTER TO DisplayRec ~ right; right _ left; left _ temp; }; }; GetColorDisplaySide: PUBLIC SAFE PROC RETURNS[Side] = TRUSTED { RETURN[IF left.color THEN left ELSE right]; }; ProcessProfile: UserProfile.ProfileChangedProc = CHECKED { cw: INT _ UserProfile.Number["Interminal.chordWaits", 0]; SELECT cw FROM > 1000 => cw _ 1000; < 0 => cw _ 0; ENDCASE; chordWaits _ (cw+grainMsPerTick-1)/grainMsPerTick; vBWEscape _ UserProfile.Number["Interminal.VBWEscape", 0]; vColorEscape _ UserProfile.Number["Interminal.VColorEscape", 0]; }; Loader.MakeProcedureResident[--e.g.--ActionRecorder]; Loader.MakeGlobalFrameResident[--e.g.--ActionRecorder]; UserProfile.CallWhenProfileChanges[ProcessProfile]; END. :InterminalImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Stone, June 23, 1982 2:21 pm color cursor and pen Swinehart, April 29, 1982 5:16 pm, register with multiplexor, etc. Doug Wyatt, April 24, 1985 10:11:59 pm PST Russ Atkinson (RRA) January 9, 1986 0:27:37 am PST Interminal.MousePosition now has a boolean on it to declare which display the point is on to keep things consistent with the hardware world, we do all our internal calculations with InternalMP, then construct a MousePosition when recording the Action Values at time t, maintained correct by ActionRecorder main loop Values at time t-1, also maintained by main loop allUp: PUBLIC KeyState _ [words[words: [177777B, 177777B, 177777B, 177777B, 177777B]]]; Mouse grain threshhold stuff This procedure runs as an independent process. It constitutes the low-level action recording mechanisms for user input actions. Runs as detached process, records terminal actions. moves the mouse between displays check if mouse is moving out of current display an integer divide, but we get our precision where we need it an integer divide, but we get our precision where we need it check threshholds in this procedure we convert the mousePosition, which corresponds to the cursor's coordinates to a value which is relative to the lower left corner of the display flip the origin The number of screen refreshes to wait before accepting Red or Blue transtitions as given. This determines how close together Red&Blue have to be up/down to synthesize Yellow up/down. The default is to cater to 3-button users. Values in the range of 3 to 5 are recommended. recording kbd action or recording current downness Hi-res mouse recording We are trying to synthesize Yellow from Red and Blue We loop until Red and Blue are both down or both up => Yellow down or up (and we clear the observed state of Red and Blue) A key state change occurs => use Red & Blue as they are (and we get to go through this whole thing again) we exhaust the chordWaits => use Red & Blue as they are Chord Yellow up or down. keyState.bits[Yellow] can be set to down either from the real keyboard or from chording. This is a conveience for testing, since it is unlikely that anyone with a working 3-button mouse normally wants to use chording instead of the Yellow (middle) button. It is a transition of Red or Blue, not Yellow yet. If the states of Red & Blue differ, then we want to record both actions. A fast red or blue down and up should not be ignored. So we only want to loop if this will actually change Yellow. No chording involved Not really a transition For debugging Unconditionally enter the current time, preferably as an increment to the previous entry. Assume that an absolute time value is entered occasionally. adjust time for roundoff in delta Unconditionally enter the current absolute time. -- a procedure to fake actions may insert single action, or may record the full state of the input devices. enteredFull tells which. Would be nice if something in the language would handle this. It's not only convenience and efficiency; the code should not have to enumerate the tag type here. page full, start a new one with complete information action lost because inscript could not be expanded used to change hardware's idea of where the mouse is; use with great care!! set current display to bw make both recs bw this declares on which display the pen is located <> initialize the display records to reflect the standard display [vt: Virtual, action: SwapAction, clientData: REF ANY] START HERE Κπ˜codešœ™Kšœ Οmœ1™Kšœžœ&˜3Kšœ žœ½˜ΛKšœ žœ˜!Kšœ žœ6˜G——K˜šΠblœž˜Kšžœ]˜dKšžœ˜&Kšžœ ˜Kšœžœžœ1˜=K˜Kšœ#žœ˜'Kšœ žœ&˜6K˜—Kšœϊ™ϊK˜Kšœ žœ˜K˜Kšœ žœžœžœΟc˜?K™Kšœ@™@K˜?Kšœžœ ˜CKšœ &˜K™—Kšœ žœžœ˜Kšœžœ˜Kšœžœ˜K˜Kšœ žœ "˜;Kšœžœ %˜AK˜šœžœ˜K˜—šœ™šœ žœ˜Kšœžœ˜—˜Kšœ' ˜>—šœ žœ˜šœžœ˜Kšœžœ˜)—Kšœ1 ˜P—K˜—šΟnœžœžœžœžœžœžœžœ˜eK˜7K˜K˜—š‘œžœžœžœžœžœžœžœ˜fKšžœ7˜=Kšœ˜K˜—š‘œžœžœžœ žœžœžœžœ˜cK˜Kšœ1˜1K˜K˜—š‘œžœžœžœžœžœžœžœ˜hKšœ8˜8Kšžœ9˜?Kšœ˜K˜—Kšœ žœžœ˜Kš œ žœžœžœ žœ˜EKšœžœžœ ˜*K˜K˜˜Kšœ™K˜—š‘œžœ˜Kšœ3™3šž˜Kšœ ˜#K˜šžœ ˜ Kšžœ ˜šžœ˜K˜,Kšœžœžœ/˜HKšœžœžœ/˜HK˜)šžœ ž˜K˜Q—K˜——K˜&šžœ˜šžœ˜Kšœ˜Kšœ˜Kšœ˜—Kšžœ˜—Kšžœ˜—šœ˜K˜——š‘œžœžœ˜Kšœ ™ Kšœ/™/K˜Kšœžœžœ˜K˜7Kšœ žœ˜šœ žœžœ˜šžœ˜šžœ˜Kšœ+˜+Kšœ2˜2K˜—šžœ˜K˜(K˜&Kšœ* ˜;Kšœ˜—K˜——K˜šœžœžœ˜Kšžœžœ(˜=Kšžœ&˜*K˜K˜—Kšœ žœžœ&˜7šžœžœž˜šœ ˜6š žœžœžœžœžœ žœ ˜~K˜ K˜K˜K˜K˜K˜)———šœ<™K˜Kšžœ#˜'šœ ˜5š žœžœžœžœžœ žœ ˜~K˜ K˜K˜K˜K˜K˜)———šœ<™K˜9Kšžœžœžœ!˜HK˜5Kšœ!™!Kšžœ žœžœ žœ˜@K˜1Kšžœ!˜'Kšœ˜K˜—Kšœ3™3K˜š‘ œžœžœžœ˜3K˜FKšžœ˜$Kšœ˜K˜—š‘œžœžœ˜šžœž˜#Kšœ"žœžœ˜C—šœ˜K˜——š ‘ œžœžœžœ%žœ˜LKšœ™K˜K˜K˜—š ‘ œžœžœžœžœ˜JKšœL™LKšœ™šœžœžœ ž˜*Kšœžœ˜2Kšœ žœ˜$Kšœžœ˜.Kšœ žœ˜ Kšœ žœ˜ Kšœ žœ˜(Kšœžœ˜*Kšœžœ˜0Kšœžœ˜,Kšœ žœ˜&Kšžœžœ ˜Kšœ‘™‘—K˜šžœžœž˜Kšœžœ˜"šžœ˜ K˜3Kš žœ žœžœžœ ˜@K˜——šžœž œžœ˜;Kšœ4™4šžœžœžœžœ˜.Kšœ3™3—K˜Kšžœžœ˜—Kšœ 2˜MKšœ˜K˜—š ‘œžœžœžœžœ˜CKšœK™KK˜:K˜K˜—š ‘œžœžœžœžœžœ˜KK˜3K˜ K˜K˜—š‘œžœžœžœžœžœžœ˜^Kšžœ&˜,Kšœ˜K˜—š ‘ œžœžœžœžœžœ˜RKšœ žœ˜&Kšœ žœ˜+K˜:K˜K˜K˜—š ‘œžœžœžœ žœ˜Kšžœ˜Kšžœ.˜2Kšžœ,˜0—K˜K˜—š ‘œžœžœžœžœ žœ˜Sšžœ˜Kšžœžœ"˜-Kšžœžœ ˜+—K˜K˜—š ‘œžœžœžœžœ˜˜>Kšœ˜—Kšœ žœ˜Kšžœžœžœ˜8Kšœ™Kšœ˜—Kšœ˜—K˜Kšœ1™1Kš ‘œžœžœžœžœ˜=K˜š‘ œžœžœ˜K˜K˜$K˜K˜—š‘œžœžœžœ˜%šžœž˜Kšžœ˜Kšžœ˜—šœ˜K˜——Kšœžœ˜K˜š ‘œžœžœžœ!žœ˜OKšœI™IKšœ'˜'Kšœ/˜/Kšœ>™>K˜K˜#˜5Kšœ+žœ˜2—Kšœ 8˜IKšœ&˜&Kšœžœ˜(Kšœ˜K˜8K˜