DIRECTORY Atom, CedarProcess, IO, MessageWindow, Process, Rope, SlackProcess, TIPUser, Terminal, Interminal, ViewerClasses, ViewerOps; SlackProcessImpl: CEDAR MONITOR LOCKS handle USING handle: SlackHandle IMPORTS Atom, CedarProcess, IO, MessageWindow, Process, Terminal, Interminal, ViewerOps EXPORTS SlackProcess = BEGIN Point: TYPE = SlackProcess.Point; -- RECORD [x, y: REAL] SlackHandle: TYPE = REF SlackData; SlackData: PUBLIC TYPE = SlackProcess.SlackData; -- this is the monitored record type EventProc: TYPE = SlackProcess.EventProc; MouseEventProc: TYPE = SlackProcess.MouseEventProc; LoggingProc: TYPE = SlackProcess.LoggingProc; AbortProc: TYPE = SlackProcess.AbortProc; AbortData: TYPE = SlackProcess.AbortData; Log: TYPE = SlackProcess.Log; LogData: TYPE = SlackProcess.LogData; Queue: TYPE = SlackProcess.Queue; QueueData: TYPE = SlackProcess.QueueData; ClientDatas: TYPE = SlackProcess.ClientDatas; ClientDatasData: TYPE = SlackProcess.ClientDatasData; Actions: TYPE = SlackProcess.Actions; ActionsData: TYPE = SlackProcess.ActionsData; Points: TYPE = SlackProcess.Points; PointsData: TYPE = SlackProcess.PointsData; MouseProcs: TYPE = SlackProcess.MouseProcs; MouseProcsData: TYPE = SlackProcess.MouseProcsData; EventProcs: TYPE = SlackProcess.EventProcs; EventProcsData: TYPE = SlackProcess.EventProcsData; ReceivedData: TYPE = SlackProcess.ReceivedData; BashedData: TYPE = SlackProcess.BashedData; EventsData: TYPE = SlackProcess.EventsData; Viewer: TYPE = ViewerClasses.Viewer; Create: PUBLIC PROC [queueSize: NAT _ 50, logSize: NAT _ 50, loggingProc: LoggingProc _ NIL, abortProc: AbortProc _ NIL, abortData: REF ANY _ NIL, abortViewer: ViewerClasses.Viewer _ NIL] RETURNS[handle: SlackHandle] = { handle _ NEW[SlackData _ [slackProcess: NIL, queue: NEW[QueueData _ [] ], log: NEW[LogData _ [] ], abort: NEW[AbortData _ [] ] ] ]; handle.queue.clientDatas _ NEW[ClientDatasData[queueSize]]; handle.queue.actions _ NEW[ActionsData[queueSize]]; handle.queue.points _ NEW[PointsData[queueSize]]; handle.queue.mouseProcs _ NEW[MouseProcsData[queueSize]]; handle.queue.eventProcs _ NEW[EventProcsData[queueSize]]; handle.queue.size _ queueSize; handle.log.received _ NEW[ReceivedData[logSize]]; handle.log.bashed _ NEW[BashedData[logSize]]; handle.log.events _ NEW[EventsData[logSize]]; handle.log.points _ NEW[PointsData[logSize]]; handle.log.size _ logSize; handle.log.logger _ loggingProc; handle.abort^ _ [enabled: FALSE, viewer: abortViewer, proc: abortProc, data: abortData]; }; FlushQueue: PUBLIC ENTRY PROC [handle: SlackHandle] = { FlushQueueInternal[handle]; }; FlushQueueInternal: INTERNAL PROC [handle: SlackHandle] = { queueSize: NAT _ handle.queue.size; FOR i: NAT IN [0..queueSize-1] DO handle.queue.clientDatas[i] _ NIL; handle.queue.actions[i] _ NIL; handle.queue.points[i] _ [0,0]; handle.queue.mouseProcs[i] _ NIL; handle.queue.eventProcs[i] _ NIL; ENDLOOP; handle.queue.head _ 0; handle.queue.tail _ 0; BROADCAST handle.queue.notFull; }; Restart: PUBLIC ENTRY PROC [handle: SlackHandle] = TRUSTED { Process.Detach[handle.slackProcess _ FORK Actor[handle] ]; }; QueueInputAction: PUBLIC ENTRY PROC [handle: SlackHandle, callBack: MouseEventProc, inputAction: LIST OF REF ANY, point: Point, clientData: REF ANY] = { InternalQueueInputAction[handle, callBack, inputAction, point, clientData]; }; InternalQueueInputAction: INTERNAL PROC [handle: SlackHandle, callBack: MouseEventProc, inputAction: LIST OF REF ANY, point: Point, clientData: REF ANY] = TRUSTED { queue: Queue _ handle.queue; qIndex: NAT _ 0; WHILE (queue.tail+1) MOD queue.size = queue.head DO WAIT queue.notFull ENDLOOP; qIndex _ queue.tail; queue.clientDatas[qIndex] _ clientData; queue.actions[qIndex] _ inputAction; queue.points[qIndex] _ point; queue.mouseProcs[qIndex] _ callBack; queue.eventProcs[qIndex] _ NIL; queue.tail _ (qIndex + 1) MOD queue.size; LogReceived[log: handle.log, event: inputAction.first, point: point, bash: FALSE]; IF handle.slackProcess = NIL THEN Process.Detach[handle.slackProcess _ FORK Actor[handle]]; }; IsBashable: INTERNAL PROC [atom: ATOM, bashable: LIST OF ATOM] RETURNS [BOOL] = { FOR list: LIST OF ATOM _ bashable, list.rest UNTIL list = NIL DO IF atom = list.first THEN RETURN[TRUE]; REPEAT FINISHED => RETURN[FALSE]; ENDLOOP; }; QueueOrBashInputAction: PUBLIC ENTRY PROC [handle: SlackHandle, callBack: MouseEventProc, inputAction: LIST OF REF ANY, bashable: LIST OF ATOM, point: Point, clientData: REF ANY] = TRUSTED { queue: Queue _ handle.queue; prevAction: ATOM _ PeekActionTail[queue]; qIndex: NAT _ (queue.tail - 1 + queue.size) MOD queue.size; IF IsBashable[prevAction, bashable] THEN { -- bash queue.clientDatas[qIndex] _ clientData; queue.actions[qIndex] _ inputAction; queue.points[qIndex] _ point; queue.mouseProcs[qIndex] _ callBack; queue.eventProcs[qIndex] _ NIL; LogReceived[log: handle.log, event: inputAction.first, point: point, bash: TRUE]; IF handle.slackProcess = NIL THEN Process.Detach[handle.slackProcess _ FORK Actor[handle]]; } ELSE InternalQueueInputAction[handle, callBack, inputAction, point, clientData]; -- queue. }; QueueInputActionNoPoint: PUBLIC ENTRY PROC [handle: SlackHandle, callBack: EventProc, inputAction: LIST OF REF ANY, clientData: REF ANY] = TRUSTED { InternalQueueInputActionNoPoint[handle, callBack, inputAction, clientData]; }; InternalQueueInputActionNoPoint: INTERNAL PROC [handle: SlackHandle, callBack: EventProc, inputAction: LIST OF REF ANY, clientData: REF ANY] = TRUSTED { queue: Queue _ handle.queue; qIndex: NAT _ 0; WHILE (queue.tail+1) MOD queue.size = queue.head DO WAIT queue.notFull ENDLOOP; qIndex _ queue.tail; queue.clientDatas[qIndex] _ clientData; queue.actions[qIndex] _ inputAction; queue.points[qIndex] _ [-9999.0, -9999.0]; queue.mouseProcs[qIndex] _ NIL; queue.eventProcs[qIndex] _ callBack; queue.tail _ (qIndex + 1) MOD queue.size; LogReceived[log: handle.log, event: inputAction.first, point: [-9999.0, -9999.0], bash: FALSE]; IF handle.slackProcess = NIL THEN Process.Detach[handle.slackProcess _ FORK Actor[handle]]; }; QueueOrBashInputActionNoPoint: PUBLIC ENTRY PROC [handle: SlackHandle, callBack: EventProc, inputAction: LIST OF REF ANY, bashable: LIST OF ATOM, clientData: REF ANY] = TRUSTED { queue: Queue _ handle.queue; prevAction: ATOM _ PeekActionTail[queue]; qIndex: NAT _ (queue.tail - 1 + queue.size) MOD queue.size; IF IsBashable[prevAction, bashable] THEN { -- bash queue.clientDatas[qIndex] _ clientData; queue.actions[qIndex] _ inputAction; queue.points[qIndex] _ [-9999.0, -9999.0]; queue.mouseProcs[qIndex] _ NIL; queue.eventProcs[qIndex] _ callBack; LogReceived[log: handle.log, event: inputAction.first, point: [-9999.0, -9999.0], bash: TRUE]; IF handle.slackProcess = NIL THEN Process.Detach[handle.slackProcess _ FORK Actor[handle]]; } ELSE InternalQueueInputActionNoPoint[handle, callBack, inputAction, clientData]; -- queue }; EmptyQ: INTERNAL PROC [queue: Queue] RETURNS [BOOL] = { RETURN[queue.tail = queue.head]; }; PeekActionTail: PRIVATE INTERNAL PROC [queue: Queue] RETURNS [whatToDo: ATOM] = { IF queue.head = queue.tail THEN RETURN[NIL]; whatToDo _ NARROW[queue.actions[(queue.tail - 1 + queue.size) MOD queue.size].first, ATOM]; }; Actor: PROC [handle: SlackHandle] = { -- runs in autonomous process(es) to service queue(s) IF handle.abort.enabled THEN [] _ CedarProcess.Fork[Killer, handle, [excited, TRUE]]; DoActorsJob[handle ! UNWIND => NilIt[handle]]; NilIt[handle]; }; NilIt: PRIVATE ENTRY PROC [handle: SlackHandle] = TRUSTED {handle.slackProcess _ NIL}; Killer: PROC [data: REF] RETURNS [results: REF _ NIL] = { handle: SlackHandle _ NARROW[data]; UNTIL handle.slackProcess = NIL DO [] _ MaybeKill[handle]; Process.Pause[ticks: Process.SecondsToTicks[1]]; ENDLOOP; }; MaybeKill: PRIVATE ENTRY PROC [handle: SlackHandle] RETURNS [killed: BOOL _ FALSE]= { ClientWantsKill: PROC RETURNS [killIt: BOOL _ FALSE] = { mouse: Interminal.MousePosition; tsc: TIPUser.TIPScreenCoords _ NEW[TIPUser.TIPScreenCoordsRec]; vt: Terminal.Virtual _ Terminal.Current[]; keyBits: Terminal.KeyBits _ Terminal.GetKeys[vt: vt]; killBits: BOOL _ (keyBits[LeftShift]=down OR keyBits[RightShift]=down) AND keyBits[Spare3]=down; IF killBits THEN { IF handle.abort.viewer=NIL THEN RETURN[TRUE]; -- viewer does not matter mouse _ Interminal.GetMousePosition[]; tsc^ _ [ mouseX: mouse.mouseX, mouseY: (IF mouse.color THEN vt.colorHeight ELSE vt.bwHeight) - mouse.mouseY, color: mouse.color ]; RETURN[ViewerOps.MouseInViewer[tsc: tsc].viewer=handle.abort.viewer]; }; }; IF handle.slackProcess#NIL AND ClientWantsKill[] THEN TRUSTED { Process.Abort[handle.slackProcess]; -- kill the actor slack process handle.slackProcess _ NIL; FlushQueueInternal[handle]; -- you have to do this HERE holding the handle lock! IF handle.abort.proc#NIL THEN handle.abort.proc[handle.abort.data]; -- notify the user that abort happened. Hold the lock. RETURN[TRUE]; }; }; DoActorsJob: PROC [handle: SlackHandle] = { -- autonomous process(es) to service queue(s) inputAction: LIST OF REF ANY; point: Point; clientData: REF ANY; mouseEventProc: MouseEventProc; eventProc: EventProc; [mouseEventProc, eventProc, inputAction, point, clientData] _ NextAction[handle]; WHILE inputAction # NIL DO IF mouseEventProc = NIL THEN eventProc[inputAction, clientData] ELSE mouseEventProc[inputAction, point, clientData]; [mouseEventProc, eventProc, inputAction, point, clientData] _ NextAction[handle]; ENDLOOP; }; NextAction: ENTRY PROC [handle: SlackHandle] RETURNS [mouseEventProc: MouseEventProc, eventProc: EventProc, inputAction: LIST OF REF ANY, point: Point, clientData: REF ANY] = { queue: Queue _ handle.queue; IF EmptyQ[queue] THEN { handle.slackProcess _ NIL; -- no mouse action. Let process die. Caller will terminate. inputAction _ NIL; } ELSE { [mouseEventProc, eventProc, inputAction, point, clientData] _ DeQueueAction[handle]; BROADCAST queue.notFull; }; }; DeQueueAction: INTERNAL PROC [handle: SlackHandle] RETURNS [mouseEventProc: MouseEventProc, eventProc: EventProc, inputAction: LIST OF REF ANY, point: Point, clientData: REF ANY] = { queue: Queue _ handle.queue; where: NAT _ queue.head; IF queue.tail = queue.head THEN ERROR; mouseEventProc _ queue.mouseProcs[where]; eventProc _ queue.eventProcs[where]; inputAction _ queue.actions[where]; point _ queue.points[where]; clientData _ queue.clientDatas[where]; queue.head _ (queue.head + 1) MOD queue.size; LogActed[handle.log, inputAction, point, mouseEventProc#NIL, clientData]; }; LogRawMouse: PUBLIC ENTRY PROC [handle: SlackHandle, point: Point] = { log: Log _ handle.log; where: NAT _ log.head; log.events[where] _ $RawMouse; log.points[where] _ point; log.head _ (log.head + 1) MOD log.size; }; LogReceived: INTERNAL PROC [log: Log, event: REF ANY, point: Point, bash: BOOL] = { where: NAT _ log.head; log.received[where] _ TRUE; log.bashed[where] _ bash; log.events[where] _ event; log.points[where] _ point; log.head _ (log.head + 1) MOD log.size; }; LogActed: INTERNAL PROC [log: Log, action: LIST OF REF ANY, point: Point, mouseEvent: BOOL, clientData: REF ANY] = { where: NAT _ log.head; log.received[where] _ FALSE; log.bashed[where] _ FALSE; log.events[where] _ action.first; log.points[where] _ point; log.head _ (log.head + 1) MOD log.size; IF log.loggerEnabled THEN log.logger[point, action, mouseEvent, clientData]; }; debugLog: BOOL _ FALSE; OutputLog: PUBLIC ENTRY PROC [handle: SlackHandle, stream: IO.STREAM] = { log: Log _ handle.log; i: NAT _ (log.head - 1 + log.size) MOD log.size; -- back up pointer one slot IF debugLog THEN stream.PutF["\nLog Head = %g", IO.int[log.head] ]; stream.PutF["\nMost Recent Event: "]; WHILE i#log.head DO IF log.events[i] = $RawMouse THEN stream.PutF["\nRAW MOUSE [%g, %g]", [real[log.points[i].x]], [real[log.points[i].y]] ] ELSE IF log.bashed[i] THEN IF ISTYPE[log.events[i], ATOM] THEN stream.PutF["\nBashed %g at [%g, %g]", [rope[Atom.GetPName[NARROW[log.events[i]]]]], [real[log.points[i].x]], [real[log.points[i].y]] ] ELSE stream.PutF["\nBashed %g at [%g, %g]", [character[NARROW[log.events[i], REF CHAR]^]], [real[log.points[i].x]], [real[log.points[i].y]] ] ELSE IF log.received[i] THEN IF ISTYPE[log.events[i], ATOM] THEN stream.PutF["\nReceived %g at [%g, %g]", [rope[Atom.GetPName[NARROW[log.events[i]]]]], [real[log.points[i].x]], [real[log.points[i].y]] ] ELSE stream.PutF["\nReceived %g at [%g, %g]", [character[NARROW[log.events[i], REF CHAR]^]], [real[log.points[i].x]], [real[log.points[i].y]] ] ELSE IF ISTYPE[log.events[i], ATOM] THEN stream.PutF["\nActed on %g at [%g, %g]", [rope[Atom.GetPName[NARROW[log.events[i]]]]], [real[log.points[i].x]], [real[log.points[i].y]] ] ELSE stream.PutF["\nActed on %g at [%g, %g]", [character[NARROW[log.events[i], REF CHAR]^]], [real[log.points[i].x]], [real[log.points[i].y]] ]; i _ (i - 1 + log.size) MOD log.size; ENDLOOP; stream.PutF["\nLeast Recent Event.\n"]; }; EnableSessionLogging: PUBLIC ENTRY PROC [handle: SlackHandle] = { IF handle.log.loggerEnabled THEN MessageWindow.Append["Warning: EnableSessionLogging called while already Enabled", TRUE] ELSE handle.log.loggerEnabled _ TRUE; }; DisableSessionLogging: PUBLIC ENTRY PROC [handle: SlackHandle] = { IF NOT handle.log.loggerEnabled THEN MessageWindow.Append["Warning: DisableSessionLogging called while already Disabled", TRUE] ELSE handle.log.loggerEnabled _ FALSE; }; RegisterLogger: PUBLIC ENTRY PROC [handle: SlackHandle, loggingProc: LoggingProc] = { IF handle.log.logger#NIL THEN MessageWindow.Append["Warning: RegisterLogger called while already Registered", TRUE] ELSE handle.log.logger _ loggingProc; }; EnableAborts: PUBLIC ENTRY PROC [handle: SlackHandle] = { IF handle.abort.enabled THEN MessageWindow.Append["Warning: EnableAborts called while already Enabled", TRUE] ELSE handle.abort.enabled _ TRUE; }; DisableAborts: PUBLIC ENTRY PROC [handle: SlackHandle] = { IF NOT handle.abort.enabled THEN MessageWindow.Append["Warning: DisableAborts called while already Disabled", TRUE] ELSE handle.abort.enabled _ FALSE; }; RegisterAbortProc: PUBLIC ENTRY PROC [handle: SlackHandle, abortProc: AbortProc _ NIL, abortData: REF ANY _ NIL, abortViewer: ViewerClasses.Viewer _ NIL] = { IF handle.abort.proc#NIL THEN MessageWindow.Append["Warning: RegisterAbortProc called while already Registered", TRUE] ELSE { handle.abort.proc _ abortProc; handle.abort.data _ abortData; handle.abort.viewer _ abortViewer; }; }; END. SlackProcessImpl.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Last edited by Pier on October 17, 1986 6:04:57 pm PDT SlackProcess implements a general queue which can be used to "take up the slack" between user generated input events and system generated responses to these events. Multiple slack processes and their associated queues may be used by many clients; the slack process issues a handle containing MONITORED data for use by the client. Actions may be queued by calls to QueueInputAction or QueueInputActionNoPoint. If the client wishes, calls to QueueOrBashInputAction, QueueOrBashInputActionNoPoint will replace the prior action on the queue with the new action IF the prior action is of the same type as the new action. The type of an action is denoted by an ATOM as the first element of the input action list. Each SlackProcess provides a logging feature for clients. A client can register a Logging procedure and enable/disable Session logging. With logging enabled, the registered Logging procedure will be called every time an action is dequeued. The client may then save a log or script of the session in progress for later playback to a future SlackProcess. Removes all unprocessed actions from the slack queue. CAUTION: If the caller is counting on a certain set of actions to be done atomically, this call could destroy its invariants. Sometimes the Painter process is aborted because of a bug in one of the dispatched procedures. This procedure allows the user to get things going again. Add an action to the slack process queue. Add an action to the tail of the slack process queue. This action will definitely be performed, in its turn. Process.Detach is an unsafe operation. Add an action to the slack process queue. If the previous action is of the same sort, then overwrite it instead of adding a new one (useful for dragging). Add an action to the slack processes queue. This action will definitely be performed, in its turn. Add an action to the tail of the slack process queue. This action will definitely be performed, in its turn. Add an action to the slack processes queue. If the previous action is of the same sort, then overwrite it instead of adding a new one (useful for testing Gravity). May FORK a process that looks to see if SHIFT-SWAT is down and aborts Actor if it is. IF MaybeKill[] THEN handle.abort.proc[handle.abortData]; -- WHERE TO DO THIS ?? SlackProcess keeps a ring buffer of recent input actions (called the log) for debugging purposes. Output the "LogSize-1" most recent events to the stream. The most recent event is output first. SlackProcess supports reporting of all actions dequeued from the slack queue, which can be recorded and played back by the client to recreate the session. The client must provide a logging procedure to be called whenever an action is dequeued for the application. The registered LoggingProc will be called at every dequeue of an event. The registered abortProc will be called (if aborts are enabled) with abortData as argument when the slack process is aborted. Κ‘˜code™Kšœ Οmœ1™žœžœžœžœžœžœ˜˜K™)KšœK˜KK˜K˜—š œžœžœ>žœžœžœžœžœžœžœ˜€K™”K˜Kšœžœ˜Kš žœžœžœžœžœ˜OKšœ˜Kšœ'˜'Kšœ$˜$Kšœ˜Kšœ$˜$Kšœžœ˜Kšœžœ ˜)KšœKžœ˜RKšžœžœžœ&žœ˜[K˜K˜—š  œžœžœžœ žœžœžœžœžœ˜Qš žœžœžœžœžœžœž˜@Kšžœžœžœžœ˜'—šž˜Kšžœžœžœ˜—Kšžœ˜K˜K˜—š œžœžœžœ>žœžœžœžœ žœžœžœžœžœžœ˜ΎK™›K˜Kšœ žœ˜)Kšœžœ!žœ ˜;šžœ"žœ‘˜2Kšœ'˜'Kšœ$˜$Kšœ˜Kšœ$˜$Kšœžœ˜KšœKžœ˜QKšžœžœžœ&žœ˜[K˜—KšžœM‘ ˜ZK˜K˜—šŸœžœžœžœ9žœžœžœžœžœžœžœ˜”K™cKšΟbœ,˜KK˜K˜—šŸœžœžœ9žœžœžœžœžœžœžœ˜˜K™mK˜Kšœžœ˜Kš žœžœžœžœžœ˜OKšœ˜Kšœ'˜'Kšœ$˜$Kšœ*˜*Kšœžœ˜Kšœ$˜$Kšœžœ ˜)KšœXžœ˜_Kšžœžœžœ&žœ˜[K˜K˜—šŸœžœžœžœ9žœžœžœžœ žœžœžœžœžœžœ˜²K™€K˜Kšœ žœ˜)Kšœžœ!žœ ˜;šžœ"žœ‘˜2Kšœ'˜'Kšœ$˜$Kšœ*˜*Kšœžœ˜Kšœ$˜$KšœXžœ˜^Kšžœžœžœ&žœ˜[K˜—Kšž’ œ-‘˜YK˜—š  œžœžœžœžœ˜7Kšžœ˜ K˜K™—š  œžœžœžœžœ žœ˜QKšžœžœžœžœ˜,Kšœ žœ-žœžœ˜[K˜K˜—š œžœ‘5˜[K™UKšžœžœ2žœ˜UKšœžœ˜.Kšœ˜Kšœ˜K˜—š  œΠbk ’£œžœžœ˜VK˜—š  œžœžœžœ žœžœ˜9Kšœžœ˜#šžœžœž˜"Kšžœ žœ&‘™OKš œ ˜K˜0Kšžœ˜—Kšœ˜K˜—š   œ£ ’£œžœ žœžœ˜Uš  œžœžœ žœžœ˜8K˜ Kšœžœ˜?K•StartOfExpansion[]˜*K˜5Kšœ žœžœžœ˜`šžœ žœ˜Kš žœžœžœžœžœ‘˜GK˜&šœ˜K˜Kšœ žœ žœžœ˜MK˜Kšœ˜—K–[ViewerClasses.Viewer]šžœ?˜EKšœ˜—K˜K˜—š žœžœžœžœžœ˜?Kšœ$‘˜CKšœžœ˜Kš’œ ‘4˜PKšžœžœžœ'‘6˜zKšžœžœ˜ Kšœ˜—Kšœ˜K˜—š  œžœ‘-˜YKš œ žœžœžœžœ˜Kšœ ˜ Kšœ žœžœ˜Kšœ˜Kšœ˜KšœQ˜Qšžœžœž˜Kšžœžœžœ#˜?Kšžœ0˜4KšœQ˜QKšžœ˜—Kšœ˜K˜—š  œžœžœžœEžœžœžœžœžœžœ˜°K˜šžœžœ˜Kšœžœ‘<˜WKšœžœ˜K˜—šžœ˜KšœT˜TKšž œ˜K˜—Kšœ˜K˜—š  œžœžœžœEžœžœžœžœžœžœ˜ΆKšœ˜Kšœžœ˜Kšžœžœžœ˜&Kšœ)˜)Kšœ$˜$Kšœ#˜#Kšœ˜Kšœ&˜&Kšœžœ ˜-Kšœ8žœ˜IK˜—˜K™a—š  œžœžœžœ(˜FK˜Kšœžœ ˜Kšœ˜Kšœ˜Kšœžœ ˜'K˜K˜—š   œžœžœžœžœžœ˜SKšœžœ ˜Kšœžœ˜Kšœ˜Kšœ˜Kšœ˜Kšœžœ ˜'K˜K˜—š œžœžœžœžœžœžœžœžœžœ˜tKšœžœ ˜Kšœžœ˜Kšœžœ˜Kšœ!˜!Kšœ˜Kšœžœ ˜'Kšžœžœ3˜LK˜K˜—Kšœ žœžœ˜š   œžœžœžœžœžœ˜IK˜Kšœžœžœ ‘˜LKšœ_™_Kšžœ žœ žœ˜CKšœ%˜%šžœ ž˜šžœž˜!šœ#˜#Kšœ˜Kšœ˜——šžœžœž˜šžœžœžœž˜#šœ&˜&Kšœžœ˜-Kšœ˜Kšœ˜——šžœ'˜+Kšœ žœžœžœ˜.Kšœ˜Kšœ˜——šžœžœž˜šžœžœžœž˜#šœ(˜(Kšœžœ˜-Kšœ˜Kšœ˜——šžœ)˜-Kšœ žœžœžœ˜.Kšœ˜Kšœ˜——šž˜šžœžœžœž˜#šœ(˜(Kšœžœ˜-Kšœ˜Kšœ˜——šžœ)˜-Kšœ žœžœžœ˜.Kšœ˜Kšœ˜——Kšœžœ ˜$Kšžœ˜—Kšœ'˜'K˜—˜K™ΡK™—š œž œžœ˜AKšžœžœTžœ˜yKšžœžœ˜%K˜K˜—š œž œžœ˜BKšžœžœžœVžœ˜Kšžœžœ˜&K˜K˜—š œž œžœ4˜UKšžœžœžœQžœ˜sKšžœ!˜%K˜—K˜š  œžœ˜9KšžœžœLžœ˜mKšžœžœ˜!Kšœ˜K˜—š  œžœ˜:KšžœžœžœNžœ˜sKšžœžœ˜"Kšœ˜K˜—š œžœ.žœ žœžœžœ&žœ˜Kšœ}™}KšžœžœžœTžœ˜všžœ˜Kšœ˜Kšœ˜Kšœ"˜"K˜—K˜—K˜Kšžœ˜—…—8LU