<<>> <> DIRECTORY Draw2d, Imager, ViewerClasses; PreemptablePaintProcs: CEDAR DEFINITIONS = BEGIN <<*** Imager Contexts *** A locked context is used to guard imager operations from context changes in the viewers world. >> LockedContext: TYPE = REF LockedContextRec; LockedContextRec: TYPE = MONITORED RECORD[ stateHasChanged: CONDITION, suspend: BOOL ¬ FALSE, abort: BOOL ¬ FALSE, context: Imager.Context, -- cannot be used directly, only through DoUnderLock below. zip: Draw2d.Zip, -- extra accelerator (logically part of context) pauseWhileIdle: PauseProc, viewer: ViewerClasses.Viewer ]; NilLockedContext: PROC [] RETURNS [LockedContext]; DoUnderLock: PROC [lc: LockedContext, Action: LockedContextActionProc]; <> LockedContextActionProc: TYPE = PROC [context: Imager.Context, zip: Draw2d.Zip]; AbortContext: PROC [lc: LockedContext]; <> <<*** Paint Procs *** The client writes a UnlockedPaintProc that is identical to an ordinary paint proc except that it paints through a locked context using DoUnderLock. The procedure below is used (inside a traditional Paint Proc) to invoke the UnlockedPaintProc. The first four parameters are passed directly through from the traditional Paint Proc, the next parameter, UnlockedPaint, is the new paint proc using a locked context, and the last two parameters determine an on-off cycle that will be described shortly. Painting, of course, does not really happen without locks. The illusion is created by forking the UnlockedPaint proc, delaying for the "on" portion of the cycle (so that PaintWithoutLocks holds the viewers locks while UnlockedPaint is painting), suspending the locked context, forking a processes that will paint the viewer again after the "off" portion of the duty cycle, and then returning (releasing the viewers locks for the "off" portion of the cycle). The whatChanged field is used to rendezvous with the suspended UnlockedPaint proc. Because it frequently releases the locks PaintWithoutLocks is easy to preempt. >> PaintWithoutLocks: PROC [ self: ViewerClasses.Viewer, context: Imager.Context, whatChanged: REF, clear: BOOL, UnlockedPaint: UnlockedPaintProc, PauseWhilePaint: PauseProc ¬ DefaultPauseWhilePaint, PauseWhileIdle: PauseProc ¬ DefaultPauseWhileIdle ] RETURNS [lc: LockedContext]; UnlockedPaintProc: TYPE = PROC [self: ViewerClasses.Viewer, lc: LockedContext, whatChanged: REF, clear: BOOL]; PauseProc: TYPE = PROC[]; DefaultPauseWhilePaint: PauseProc; <<~ {Process.PauseMsec[2000]}; --2 seconds of paint>> DefaultPauseWhileIdle: PauseProc; <<~ {Process.PauseMsec[100]}; --1/10 second idle>> <<*** For Advanced Use ***>> SuspendContext: PROC [lc: LockedContext]; RestartContext: PROC [lc: LockedContext]; END.