[comp.windows.news] ClockRoot.ps - a big psuedo-3D clock root animation.

patl@bodacia.Eng.Sun.COM (Pat Lashley [MtV NeWStech Eng.]) (04/11/91)

Here is a cute NeWS hack - it draws a large psuedo-3D clock on the
framebuffer.  It differs from most NeWS clocks in that it determines
the time from currenttime so there is no tendency to drift under
heavy system loads.

This is for OpenWindows Version 2.  There are also some minor
dependancies on TNT 2.0 (getting the 3D colors, and automatic
invocation from the Damage Service.)

To use, add the following fragment to your .startup.ps:

	UserProfile begin
	    /UsePaintRoot?	true	def
	end

And the following to .user.ps.  Note that the code below assumes that
you have put the main clockroot code in ps/clockroot.ps.  The only
place that this filename is used is in the next-to-last line below.

    % For the hack to make a file either directly executable from the
    % shell or runable under psh...
    %
    /;exit	/pop cvx    	    	    	?def
    

    % Replace the proc in the root damage interests.
    % The standard one executes PaintRoot.  The new one will send
    % /HandleDamage to the framebuffer if it is an instance.
    %
    % The RootDamage interest isn't created until after .user.ps is
    % executed, so we will fork off a process to wait for it...
    {
	{
	    /RootDamageInterests where { pop exit } if
	    5 60 div sleep
	} loop
	
	RootDamageInterests /Name get begin
	    /Damaged {
		gsave   	    %indent%
		    GcDict 1 index /Canvas get
		    get setstate

		    dup /Canvas get isinstance? {
			/HandleDamage 1 index /Canvas get send
		    } {
			newprocessgroup
			damagepath clipcanvas PaintRoot
			newpath clipcanvas
			pop				% pop the event
		    } ifelse
		grestore	    %exdent%
	    } def
	end

	(ps/clockroot.ps) run
    } fork pop


Things you might want to change:

	The time zone adjustment is currently hard-coded (I couldn't
	find any convienient portable way to obtain it from the system
	in NeWS.)  Look for the definition of /TimeZone around line
	77.  The first number is the offset from GMT in hours (i.e. `-8'
	US PST); the second number is a constant to convert hours to
	seconds.

	The psuedo-3D effect looks better with wider lines, but performance
	is severely impacted.  Look for the definition of /3DlineWidth at
	about line 230.  There is a commented-out version which calculates
	the linewidth as a function of the clock radius.

Notes:
	This may not paint until the first tic interval has elapsed.

	When playing with various alternate root images, I often find
	it useful to have the following entry under Utilities in my
	.openwin-menu file
		"Paint Root"	POSTSCRIPT PaintRoot



Enjoy,
-Pat

------------------------- ps/clockroot.ps ------------------------------
(echo '/;exit /pop cvx def' | exec psh - $0) ;exit

% ClockRoot - Display psuedo-3D clock as XNeWS root image.
% 
% Copyright (C) 1991 PM Lashley
%   This program is free software; you can redistribute it and/or modify
%   it under the terms of the GNU General Public License as published by
%   the Free Software Foundation; either version 1, or (at your option)
%   any later version.
% 
%   This program is distributed in the hope that it will be useful,
%   but WITHOUT ANY WARRANTY; without even the implied warranty of
%   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%   GNU General Public License for more details.
% 
%   You should have received a copy of the GNU General Public License
%   along with this program; if not, write to the Free Software
%   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
%
%   If you have any other GNU/FSF software, you probably already
%   have a copy of the licence.  If not, it is also available
%   electronically from various sources including anonymous ftp
%   from prep.ai.mit.edu, uunet.uu.net, et. al. and anonymous
%   uucp from osu-cis.
%
%   If you need to contact me personally, I can be reached via e-mail
%   to `plashley@Sun.COM'.

%---------------------------------------------------------------------------
% This program implements a clock drawn as a large psuedo-3D indented pie
% (for the hours) surrounded by an indented `ditch' (for the minutes).
% It is updated every minute.  In the morning, the ditch is indented
% clockwise from 12 to the current minute.  In the afternoon, the
% ditch is indented clockwise from the current minute to 12.
%
% Unlike other PostScript clocks, this one determines the time via
% the currenttime operator at every update so it won't drift if
% clock tic events are temporarily blocked.
%
% This version uses two features of TNT 2.0 or later:
%      1)  The OpenLook 3D color variables in ClassFramebuffer
%	   (or ClassWindow).
%      2)  It spawns an instance of ClassEventMgr to handle the
%	   clock tic events.
%

% The colors aren't defined in ClassFramebuffer by default,
% so let's grab the ones from ClassWindow.
% 
/colors ClassWindow send /setcolors ClassFramebuffer send


%   Put all of our definitions in one dictionary to prevent clashes.
%
/ClockDict 40 dict def
ClockDict begin
    % The default is to put the clock on only the primary root canvas.
    % You will want to change this if you are running multi-headed or
    % if you  you prefer the clock to be on one of the non-default
    % planegroup in a multi-planegroup display.
    %
    /ClockCanvases  	[ rootcanvases 0 get ]	    def
    

    % Minutes difference between GMT and local timezone.
    %
    % There doesn't seem to be any good, portable, way to obtain the timezone
    % delta from NeWS.  (What we really need is a localtime operator.)
    %
    % This number will have to be changed by hand when changing to/from
    % Daylight Savings Time (or equivalent.)
    %
    /TimeZone   -8 3600 mul	    def	    	% US/PST


    % Note: This is only calculated -once- when the file is loaded...
    %
    /TimeDelta  	    %indent%
	(%socketc37) (r) file 4 string readstring pop	%  (sec-since-epoc)
	0 getcard32 	    	    	    	    	%  sec-since-epoc
	
	currenttimems 1000 idiv				%  sec sec-since-start
	
	% We can't just subtract because the times are 32bit
	% unsigned numbers.  Since we are only displaying to
	% minutes, the loss of second accuracy shouldn't hurt...
	-1 bitshift 16#7fffffff and 43200 mod exch
	-1 bitshift 16#7fffffff and 43200 mod exch
	sub
	1 bitshift
	TimeZone add
	86400 mod
    def		     %exdent%
    
    %   degrees  =>  -
    /InsidePath {
	dup 0 eq {
	    pop
	} {
	    0 0 moveto
	    0 0 rr 0 5 -1 roll arcn			    %  x y r ang1 deg    
	    0 0 lineto
	    closepath
	} ifelse
    } def
    
    %   degrees  bool  =>  -
    /OutsidePath {
	dup 0 eq {
	    pop
	} {
	    r 0 moveto    	    	    	    	    %  deg bool
	    {
		0 0 R 0  4 index  arcn 	    	    	    %  deg
		0 0 r  4 -1 roll  0 arc
	    } {
		0 0 R 0  4 index  arc 	    	    	    %  deg
		0 0 r  4 -1 roll  0 arcn
	    } ifelse
	    closepath
	} ifelse
    } def
    

    /ClockFont {
	/NewCenturySchlbk-Bold findfont
	%   /Charter-Roman findfont
	%   /Bembo-Bold findfont
	
	r rr sub .7 mul scalefont
	false printermatchfont
	
	//ClockDict /ClockFont 2 index put
    } def
    
    
    %   topleftcolor  bottomrightcolor  radius  degrees  =>  -
    /Clockwise3DArc {
	4 dict begin
	    /deg exch def
	    /rad exch def
	    /br  exch def
	    /tl  exch def
	    
	    br setcolor
	    0 0 rad 0 deg -45 max arcn stroke
	    
	    deg -45 lt {
		tl setcolor
		0 0 rad -45 deg -225 max arcn stroke
	    } if
	    
	    deg -225 lt {
		br setcolor
		0 0 rad -225 deg arcn stroke
	    } if
	end
    } def
    
    %   topleftcolor  bottomrightcolor  radius  degrees  =>  -
    /Widdershins3DArc {
	4 dict begin
	    /deg exch def
	    /rad exch def
	    /br  exch def
	    /tl  exch def
	    
	    tl setcolor
	    0 0 rad 0 deg -225 min arc stroke
	    
	    deg -225 gt {
		br setcolor
		0 0 rad -225  deg -45 min  arc stroke
	    } if
	    
	    deg -45 gt {
		tl setcolor
		0 0 rad -45 deg arc stroke
	    } if
	end
    } def
    

    %   string  =>  -
    /CenterShow {
	dup stringbbox xyadd -2 div exch -2 div exch
	rmoveto show
    } def
    

    /ClockTic createevent dup begin
	/Name	    /UpdateClock 	def
	/Canvas	    globalroot		def
    end def

    
    /SendTrigger {
	//ClockDict begin
	    ClockTic /IsQueued get not {
		ClockTic /TimeStamp	    %indent%
		    currenttime 1 add
		put 	    	    %exdent%

		ClockTic	sendevent
	    } if
	end
    } def


    /ConvertTime {
	% First get the current time-since-server-startup,
	% and convert it to seconds.
	currenttimems 1000 idiv 		    %  sec
	
	/R /size self send min .49 mul def	    %  X Y W' H'
	/r R .75 mul def
	/rr r .8 mul def
	/r2 r rr add 2 div def

%	/3DlineWidth r 200 div round dup 1 le { pop 0 } if def
	/3DlineWidth 0 def
	
	% Now add the delta and constrain it to be within 24 hours.
	TimeDelta add
	dup 0 lt { 86400 add } if
	86400 mod			    %  sec-since-midnight
	60 idiv				    %  min-since-midnight
	dup 60 mod -6 mul /MM exch def
	2 idiv neg /HH exch def
	
	/AfterNoon HH -360 le def
	AfterNoon {
	    /HH HH 360 add def
	} if
    } def


    /SetTransform {
	/bbox self send 2 div exch 2 div exch  	    %  X Y W' H'
	4 2 roll xysub 	    	    	    	    %  x y
	translate 90 rotate 	    	    	    %  -
    } def

    
    /PaintHands {
	3DlineWidth setlinewidth
	% 2.5 setlinewidth
	
	/bbox self send rectpath
	R 0 moveto
	% 0 0 R 1 add 0 360 arc closepath
	HH InsidePath
	MM AfterNoon not OutsidePath
	/BG2 self send setcolor eofill
	
	/BG self send setcolor
	HH InsidePath fill
	MM AfterNoon not OutsidePath fill
	
	MM 0 ne {
	    % Vertical at 12:00
	    AfterNoon { /BG3 } { /BG0 } ifelse self send setcolor
	    R 0 moveto r 0 lineto stroke
	    
	    AfterNoon {
		/BG3 self send /BG0 self send r MM
		Widdershins3DArc
	    } {
		/BG0 self send /BG3 self send r MM
		Clockwise3DArc
	    } ifelse
	    
	    MM -135 le MM -315 ge and AfterNoon eq
	    { /BG3 } { /BG0 } ifelse self send setcolor
	    0 0 r MM dup arcn    0 0 R MM dup arcn
	    closepath stroke
	    
	    R 0 moveto
	    AfterNoon {
		/BG0 self send /BG3 self send R MM
		Widdershins3DArc
	    } {
		/BG3 self send /BG0 self send R MM
		Clockwise3DArc
	    } ifelse
	} if
	
	HH 0 ne {
	    /BG0 self send setcolor
	    0 0 moveto
	    
	    /BG3 self send /BG0 self send rr HH Clockwise3DArc
	    
	    HH -135 le HH -315 ge and
	    { /BG0 } { /BG3 } ifelse self send setcolor
	    0 0 moveto    0 0 rr HH dup arcn
	    closepath stroke
	} if
    } def

end 	    	    %-%	    ClockDict
    
    
/RootFixer /new ClassEventMgr send def
RootFixer /ProcessName (Root Clock) put   	   	    % -

{
    newprocessgroup
    /ClearContext currentprocess send
    currentprocess /ErrorDetailLevel 2 put

    countdictstack array dictstack
    //ClockDict arraycontains? not {
	//ClockDict begin
	%exdent%
    } if
    
    ClockCanvases {
	dup /RootFixer currentprocess harden put
	/Paint {
	    % We may be executed in other processes, so the clock dict
	    % must be opened and closed within the /Paint function.
	    %
	    //ClockDict begin
		ConvertTime
		SetTransform
		
		newpath
		/bbox self send rectpath
		r  3DlineWidth sub 0 moveto 0 0 r  3DlineWidth sub 0 360 arc
		rr 3DlineWidth add 0 moveto 0 0 rr 3DlineWidth add 0 360 arcn
		eoclip newpath
		
		PaintHands
	    end
	} /installmethod 3 index send
	
	%   This is just like /HandleDamage, except that it doesn't
	%   clip to the damage path first.
	%
	%   -  =>  -
	/fix {
	    gsave
	    self setcanvas
	    ForkFix? {
		FixProcess /State get /zombie ne {
		    newpath clipcanvas
		    FixProcess killprocess
		} if
		/FixProcess {
		    /FixAll self send
		    newpath clipcanvas
		    /FixProcess nullprocess def
		} fork def
	    } {
		/FixAll self send
		newpath clipcanvas
	    } ifelse
	    grestore
	} /installmethod 3 index send
	
	
	/FixAll {
	    % We may be executed in other processes, so the clock dict
	    % must be opened and closed within the /Paint function.
	    %
	    //ClockDict begin
		SetTransform
		PaintHands
		
		% Now for some numbers and tic marks...
		
		%	/FG self send setcolor
		%	DecorPath fill
		
		ClockFont setfont
		
		/FG self send setcolor
		-90 rotate
		
		0  r2 	    moveto  	(12) CenterShow
		r2  0	    moveto 	(3)  CenterShow
		0  0 r2 sub moveto	(6)  CenterShow
		0 r2 sub  0 moveto 	(9)  CenterShow
		
		4 {
		    -30 rotate	0 r2 moveto 	(.) CenterShow
		    -30 rotate	0 r2 moveto 	(.) CenterShow
		    -30 rotate
		} repeat
	    end
	} /installmethod 4 -1 roll send
    } forall
    
    
    globalroot null
    1 dict dup begin
	/UpdateClock	{   	    % event
	    pop
	    ClockCanvases  { /paint exch send } forall
	    SendTrigger
	} def
    end /new ClassInterest send     	 	    % interest
    /addclient currentprocess  send		    % emgr
    
    
    ClockCanvases {
	{ ConvertTime fix } exch send
    } forall
    SendTrigger
    
    % Tell the server not to deliver framebuffer damage events
    % to the X11 window manager.
    % 
    false setxrootpattern
} /callmanager RootFixer send

% -------------------------- end ps/clockroot.ps ----------------------------
--
X-Face: #FowkUVVz[9{ux;7z%!?7>\5DCdVqaja5uk!4Z~)5*f@-"n&||t35?wVN+UloPr-Q;iR\;t
 snA%,sJ:+$a[eV(aKz4\=`MIH#{`/#HW>TT6Hx=Xp06oj>ta|]bFa'1BiI5Wj_y7n,l)tFuEd(oE`V
 3w'0..-`[}nX:VVJ&@Br$cCu|/iqA4VC}/APx:gge9-fj(@V*~W[L@KP@^AcXvel])1%zy[&c}t"\z
 :X,J8<1D%I;J>tY6EZ7lx,8R&JhgPyZ4Zz[3J`#N@zc&d<"V+&O*;gRd^)xC`34h8[!Vb+