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+