/* MouseClock.c *************************************************************
*
*	MouseClock ----	Ein Uhren-Utility, das ausnahmsweise nicht
*			irgendwo auf einem obskuren Bildschirm
*			(meinetwegen auch Workbench-Bildschirm)
*			existiert, sondern sich in der Nähe des
*			Mauszeigers aufhält. Die Position und
*			der Bildschirm, auf dem sich die Anzeige
*			von MouseClock aufhält, ist unabhängig vom
*			Workbench-Bildschirm. Ähnlich wie beim
*			Mauszeiger liegen die Sprites, die Mouseclock
*			zur Anzeige verwendet, immer auf dem vordersten
*			Bildschirm.
*
*			Besonders stolz (soviel Eitelkeit darf ich
*			mir hoffentlich gönnen) bin ich auf den Interrupt-
*			code, der die Anzeige bewegt. Es hat auf
*			Anhieb geklappt und ist noch nicht ein einziges
*			mal abgeschmiert.
*
*	Autor ---------	Olaf Barthel, ED Electronic Design Hannover
*			Brabeckstraße 35
*			D-3000 Hannover 71
*
*			Bundesrepublik Deutschland
*
*			(C) Copyright 1989 by Olaf Barthel &
*			    ED Electronic Design Hannover
*
****************************************************************************/

#include <intuition/intuitionbase.h>
#include <libraries/dosextens.h>
#include <graphics/gfxbase.h>
#include <exec/interrupts.h>
#include <graphics/sprite.h>
#include <devices/timer.h>
#include <exec/memory.h>

	/* Vorwärtsdeklarationen, um Compiler-Warnungen zu
	 * zu vermeiden.
	 */

extern struct Library	*OpenLibrary();
extern struct Window	*OpenWindow();
extern struct ViewPort	*ViewPortAddress();
extern struct Message	*GetMsg();
extern struct MsgPort	*CreatePort();
extern struct MsgPort	*FindPort();
extern void		*AllocMem();
extern PLANEPTR		AllocRaster();
extern long		AvailMem();

	/* Globale Variablen, die zur Handhabung des Displays
	 * und des Fensters dienen.
	 */

struct IntuitionBase	*IntuitionBase;
struct GfxBase		*GfxBase;
struct Window		*Window;
struct IntuiMessage	*Massage;
struct MsgPort		*MouseClockPort;

	/* Wir orientieren uns zwar nicht am vordersten ViewPort,
	 * wenn wir die Sprites setzen, passen aber die Farben
	 * der Sprites an, wenn ein Bildschirm weniger als 32
	 * Farben hat.
	 */

struct ViewPort *VPort;

	/* Die beiden Sprites der Anzeige. */

struct SimpleSprite Position1,Position2;

	/* Mit diesen Variablen wird später Schrift in die Sprites
	 * gedruckt.
	 */

struct BitMap	PointerBMap;
struct RastPort	PointerRPort;

	/* Aktuelle Position des Mauszeigers, soll die Interruptroutine
	 * den Mauszeiger bearbeiten?
	 */

WORD CurX,CurY,DoPosition = FALSE;

	/* Datenspeicher für die Spritedaten. */

UWORD Pointer1Data[(2 + 72) * 2],Pointer2Data[(2 + 72) * 2];

	/* Zeichenfarben für Schrift und Hintergrund. */

UBYTE Render[3],Shadow[3];

	/* Mit den folgenden Daten werden Fonts zum Zeichnen der
	 * Kurzinfo definiert.
	 */

struct TextAttr AboutFont[3] =
{
	{(UBYTE *)"topaz.font",8,FS_NORMAL ,FPF_ROMFONT},
	{(UBYTE *)"topaz.font",8,FSF_BOLD  ,FPF_ROMFONT},
	{(UBYTE *)"topaz.font",8,FSF_ITALIC,FPF_ROMFONT}
};

	/* Dies ist die "Kurzinfo", die gezeigt wird, wenn unser
	 * Fenster aktiviert worden ist. Der Titlebar des
	 * Workbench-Bildschirms wird ein wenig verschönert.
	 */

struct IntuiText AboutTxt[4] =
{
	{3,1,JAM1,  0,1,(struct TextAttr *)&AboutFont[1],(UBYTE *)"MouseClock",			(struct IntuiText *)&AboutTxt[ 1]},
	{2,1,JAM1, 88,1,(struct TextAttr *)&AboutFont[2],(UBYTE *)"v1.2",			(struct IntuiText *)&AboutTxt[ 2]},
	{2,1,JAM1,128,1,(struct TextAttr *)&AboutFont[0],(UBYTE *)"© 1989 by",			(struct IntuiText *)&AboutTxt[ 3]},
	{0,1,JAM1,232,1,(struct TextAttr *)&AboutFont[2],(UBYTE *)"Electronic Design Hannover",	(struct IntuiText *)NULL}
};

	/* Bitmapdaten um das ED-Symbol über dem Schließgadget
	 * zeichnen zu können.
	 */

USHORT ClipMap[40] =
{
	0xFFFF,0xF000,0xFFFF,0xF000,0xF001,0xF000,0xF7FF,0xF000,
	0xFFFF,0xD000,0xF03F,0xD000,0xF7FF,0x9000,0xFFFF,0x3000,
	0xF000,0x7000,0xFFFF,0xF000,

	0x0000,0x0000,0x1FFF,0x0000,0x3FFF,0x8000,0x3800,0xC000,
	0x1F80,0xE000,0x3FC0,0xE000,0x3801,0xE000,0x1FFF,0xC000,
	0x0FFF,0x8000,0x0000,0x0000
};

	/* Zum Einzeichnen des ED-Symbols notwendige Definition. */

struct Image ClipImage =
{
	0,0,
	20,10,2,
	(USHORT *)ClipMap,
	0x03,0x00,
	(struct Image *)NULL
};

	/* Dieses Fenster wird geöffnet, um u.A. festellen zu
	 * können, ob der Anwender das Programm beenden möchte,
	 * oder sich die Farbe des Mauszeigers geändert hat.
	 */

struct NewWindow NewWindow =
{
	561,0,
	26,10,
	1,1,
	CLOSEWINDOW | NEWPREFS | REFRESHWINDOW | ACTIVEWINDOW,
	RMBTRAP | WINDOWCLOSE | SIMPLE_REFRESH,
	(struct Gadget *)NULL,
	(struct Image *)NULL,
	(STRPTR)NULL,
	(struct Screen *)NULL,
	(struct BitMap *)NULL,
	0,0,
	0,0,
	WBENCHSCREEN
};

	/* Falls sich DMouse irgendwo im Speicher versteckt, borgen
	 * wir den DMouse Mousetimeout, um die Sprites rechtzeitig
	 * verschwinden zu lassen.
	 */

struct DMSFragment
{
	struct MsgPort	Port;	/* Globaler Messageport. */
	short		Version;/* Aktuelle Version. */
	short		Acc;	/* Beschleunigung. */
	short		AThresh;/* Beschleunigungsschwelle. */
	long		STo;	/* Screentimeout. */
	long		MTo;	/* Mousetimeout. */
};

	/* Diese Variablen sind notwendig, wenn man sich vom
	 * CLI abkoppeln möchte, von dem man gestartet wird.
	 */

long  _stack		= 4000;
long  _priority		= 1;
long  _BackGroundIO	= NULL;
char *_procname		= "MouseClock v1.2";

	/* Hilfsstrukturen zur Kontrolle des timer.device. */

struct timerequest	*tr_TimerRequest= NULL;
struct MsgPort		*tr_TimerPort	= NULL;

	/* Mit dieser Struktur wird die Routine zum Positionieren
	 * der Anzeige in den Vertical Blanking Interrupt gehängt.
	 */

struct Interrupt VBlankServer;

	/* Da wir diese Symbole noch brauchen, um in einigen
	 * Assemblerroutinen herumzukleistern, aber die Assembler-
	 * routinen, die gleich folgen werden, diese Symbole für
	 * den Compiler nicht sichtbar erzeugen, schustern wir
	 * ein wenig herum.
	 */

extern void NuCloseScreen();
extern void HandleVBlank();

	/* Hier wird später einmal ein Zeiger auf eine System-
	 * routine erscheinen, OpenScreen() um genau zu sein.
	 * Dieser Wert wird von SetFunction() zurückgeliefert,
	 * wenn wir in den Libraries herumstochern.
	 */

long OldCloseScreen;

#asm
_HandleVBlank:	MOVEM.L	A2-A6/D2-D7,-(A7)	; Ein paar Register retten

		JSR	_geta4#			; Adressregister geraderücken
		JSR	_MyHandleVBlank		; C-Routine anspringen

		CLR.L	D0			; Da wir im Interrupt sind,
						; muß D0 gelöscht werden.

		MOVEM.L	(A7)+,A2-A6/D2-D7	; Die Register restaurieren

		RTS				; ...und weiter.

_NuCloseScreen:	MOVEM.L	D2/D3/A4/A6,-(SP)	; Ein paar Register retten

		MOVE.L	A0,-(SP)		; Screen-Zeiger als Argument
						; übernehmen

		JSR	_geta4#			; Adressregister geraderücken
		JSR	_ModifiedCloseScreen	; C-Routine anspringen

		ADDQ.L	#4,SP			; Stack korrigieren
		MOVE.L  D0,A0			; Screen weiterleiten

		MOVE.L  _OldCloseScreen,A1	; Alte Routine merken

		MOVEM.L	(SP)+,D2/D3/A4/A6	; Register restaurieren

		JMP	(A1)			; Alte Routine anspringen
#endasm

	/* ModifiedCloseScreen(Screen) :
	 *
	 *	Ein Patch für die CloseScreen() Routine, damit
	 *	sich die Interrupt-Routine nicht an Bildschirme
	 *	heranmacht, die gerade geschlossen werden.
	 */

struct Screen *
ModifiedCloseScreen(Screen)
struct Screen *Screen;
{
		/* Bildschirm nach hinten legen. */

	ScreenToBack(Screen);

		/* Zweimal auf den Durchlauf des Vertical Blanking
		 * warten, damit der Handler auch wirklich mitkriegt,
		 * daß er einen Bildschirm nicht manipulieren kann,
		 * den es in Zehntelsekunden nicht mehr geben wird.
		 * Gut, das ist die kompliziertere Methode, aber
		 * wenigstens geht sie nicht immer in die Hose,
		 * was ohne diesen Patch sehr häufig passiert.
		 */

	WaitTOF();
	WaitTOF();

		/* Man kann ja nicht immer alles auf dem Stack
		 * herumliegen lassen, also liefern wir den Zeiger
		 * zurück, damit die alte CloseScreen() Routine
		 * nicht im Regen steht.
		 */

	return(Screen);
}

	/* MyHandleVBlank() :
	 *
	 *	Dies ist eine echte Interrupt-Routine in C,
	 *	ja das gibt es wirklich und es funktioniert
	 *	sogar. In dieser Routine werden die Sprites
	 *	bewegt und die Farben gesetzt. Auf diese
	 *	Weise ist es wesentlich schneller als eine
	 *	multitaskingfähige Unterroutine, die z.B.
	 *	auf das timer.device wartet.
	 */

void
MyHandleVBlank()
{
		/* WER konnte es ahnen, daß Intuition diese
		 * Daten auf einen Bildschirm bezieht, der
		 * 640 Pixel in X-Richtung und 512 in Y-Richtung
		 * besitzt? Es hat mich eine Menge Zeit gekostet,
		 * herauszufinden, weshalb sich die Anzeige
		 * schneller bewegte als der Mauszeiger selbst,
		 * bis ich herausbekam, daß diese Daten
		 * nicht für Sprites gedacht sind, die sich nur
		 * in Lo-res Schritten bewegen können.
		 */

	CurX = IntuitionBase -> MouseX >> 1;
	CurY = IntuitionBase -> MouseY >> 1;

		/* Falls nötig, werden die Farben der Sprites
		 * an den aktuellen Bildschirm angepaßt.
		 */

	VPort = &IntuitionBase -> FirstScreen -> ViewPort;

		/* Na woher wissen wir denn, wie tief der Bildschirm
		 * ist? Na, wozu hängt eine RasInfo am ViewPort?
		 */

	if(VPort -> RasInfo -> BitMap -> Depth < 5)
	{
		SetRGB4(VPort,22,Shadow[0],Shadow[1],Shadow[2]);
		SetRGB4(VPort,21,Render[0],Render[1],Render[2]);
	}

	if(DoPosition)
	{
		/* Sprites an die Position rechts neben dem
		 * Mauszeiger rücken.
		 */

		MoveSprite(NULL,&Position1,CurX + 8,CurY);
		MoveSprite(NULL,&Position2,CurX + 24,CurY);
	}
}

	/* AddVBlankServer() :
	 *
	 *	Dies initialisiert eine Struktur, mit deren Hilfe
	 *	ein Interruptserver in den Vertical Blanking
	 *	Interrupt eingeklinkt wird.
	 */

void
AddVBlankServer()
{
	VBlankServer . is_Data		= (APTR)NULL;
	VBlankServer . is_Code		= HandleVBlank;
	VBlankServer . is_Node . ln_Succ= NULL;
	VBlankServer . is_Node . ln_Pred= NULL;
	VBlankServer . is_Node . ln_Type= NT_INTERRUPT;
	VBlankServer . is_Node . ln_Pri	= 0;
	VBlankServer . is_Node . ln_Name= "MouseClock";

		/* Herzlichen Dank an Dan Silva, er benutzt eine
		 * ähnliche Methode, um DPaint Colourcycling
		 * beizubringen. Ich hab's einfach einmal
		 * ausprobiert und es lief sofort!
		 */

	AddIntServer(5,&VBlankServer);
}

	/* RemVBlankServer() :
	 *
	 *	Wirft den Interruptserver wieder aus der Liste
	 *	der Interrupts.
	 */

void
RemVBlankServer()
{
		/* Hoffentlich baut Exec keinen Mist und
		 * schickt den Server wirklich in die Wüste.
		 */

	RemIntServer(5,&VBlankServer);
}

	/* OpenTimerDevice() :
	 *
	 *	Öffnet das timer.device für unsere Zwecke.
	 */

BOOL
OpenTimerDevice()
{
	if(!(tr_TimerPort = (struct MsgPort *)CreatePort(NULL,0)))
		return(FALSE);

	if(!(tr_TimerRequest = (struct timerequest *)AllocMem(sizeof(struct timerequest),MEMF_PUBLIC | MEMF_CLEAR)))
	{
		DeletePort(tr_TimerPort);

		return(FALSE);
	}

		/* Präzisionszeitgeber aktivieren. */

	if(OpenDevice(TIMERNAME,UNIT_VBLANK,tr_TimerRequest,0))
	{
		FreeMem(tr_TimerRequest,(long)sizeof(struct timerequest));
		DeletePort(tr_TimerPort);

		return(FALSE);
	}

	tr_TimerRequest -> tr_node . io_Message . mn_ReplyPort	= tr_TimerPort;
	tr_TimerRequest -> tr_node . io_Command			= TR_ADDREQUEST;
	tr_TimerRequest -> tr_node . io_Flags			= 0;
	tr_TimerRequest -> tr_node . io_Error			= 0;

	return(TRUE);
}

	/* CloseTimerDevice() :
	 *
	 *	Gibt den angeforderten Speicher frei und schließt das
	 *	timer.device.
	 */

void
CloseTimerDevice()
{
	if(tr_TimerRequest)
	{
		CloseDevice(tr_TimerRequest);
		FreeMem(tr_TimerRequest,sizeof(struct timerequest));
	}

	if(tr_TimerPort)
		DeletePort(tr_TimerPort);
}

	/* WaitTime(Seconds,Micros) :
	 *
	 *	Wartet eine Zeitperiode.
	 */

void
WaitTime(Seconds,Micros)
register ULONG Seconds,Micros;
{
	tr_TimerRequest -> tr_time . tv_secs	= Seconds;
	tr_TimerRequest -> tr_time . tv_micro	= Micros;

	DoIO(tr_TimerRequest);
}

	/* OpenPixel6Font() :
	 *
	 *	Aktiviert einen speziellen, 4 Pixel breiten und
	 *	6 Zeilen hohen Zeichensatz, mit dem später in die
	 *	Sprites gezeichnet wird.
	 */

struct TextFont *
OpenPixel6Font()
{
	static struct TextFont Pixel6Font;

	static long Pixel6Dump[138] =
	{
		0x04AA2240,0x00002E2E,0xEAEEEEE0,0x0204E4C4,
		0xCEE6AE2A,0x8AA4C4C6,0xEAAAAAE6,0x864004A2,
		0x242A4000,0x2A222A88,0x2AA444E2,0x2AAAA888,
		0xA42A8ECA,0xAAA84AAA,0xAA2482A0,0x04044424,
		0xE0E04A2E,0xEEEE2EE0,0x08016EC8,0xAEEAE42C,
		0x8EEACAC4,0x4AAE4444,0x42000008,0x042A4400,
		0x8A28222A,0x2A2444E2,0x0AAAA88A,0xA4AA8A6A,
		0x8AA24AAE,0xA4842200,0x040A0240,0x04048E2E,
		0xE2EE2EE0,0x42044AC4,0xCE86AE4A,0xEAA487AC,
		0x464AA4E6,0x26000000,0x00000800,0x00000000,
		0x00008000,0x00000000,0x00000000,0x00000000,
		0x0000000F,0x00000004,0x00040004,0x00080004,
		0x00000004,0x00000004,0x000C0004,0x00000004,
		0x00100004,0x00140004,0x00180004,0x001C0004,
		0x00200004,0x00240004,0x00280004,0x002C0004,
		0x00300004,0x00340004,0x00380004,0x003C0004,
		0x00400004,0x00440004,0x00480004,0x004C0004,
		0x00500004,0x00540004,0x00580004,0x005C0004,
		0x00600004,0x00640004,0x00680004,0x006C0004,
		0x00700004,0x00000004,0x00740004,0x00780004,
		0x007C0004,0x00800004,0x00840004,0x00880004,
		0x008C0004,0x00900004,0x00940004,0x00980004,
		0x009C0004,0x00A00004,0x00A40004,0x00A80004,
		0x00AC0004,0x00B00004,0x00B40004,0x00B80004,
		0x00BC0004,0x00C00004,0x00C40004,0x00C80004,
		0x00CC0004,0x00D00004,0x00D40004,0x00D80004,
		0x00DC0004,0x00E00004,0x00E40004,0x00E80004,
		0x00EC0004,0x00540004,0x005400C3,0x66E00000,
		0x04002C30,0x78303035,0x43303030,0x342C0A09,
		0x09307830,0x30363030,0x3030342C,0x30783030,
		0x36343030,0x30342C30,0x78303036,0x38303030,
		0x342C3078,0x30303643,0x30303034,0x2C0A0909,
		0x30783030,0x37303030,0x30342C30,0x78303030,
		0x30303030,0x342C3078,0x30303734,0x30303034,
		0x2C307830,0x30373830
	};

	Pixel6Font . tf_Message . mn_Node . ln_Name = "Pixel.font";
	Pixel6Font . tf_Message . mn_Node . ln_Type = NT_FONT;
	Pixel6Font . tf_Message . mn_Node . ln_Pri  = 0;

	Pixel6Font . tf_YSize     = 6;
	Pixel6Font . tf_Style     = 0;
	Pixel6Font . tf_Flags     = 66;
	Pixel6Font . tf_XSize     = 4;
	Pixel6Font . tf_Baseline  = 4;
	Pixel6Font . tf_BoldSmear = 1;
	Pixel6Font . tf_Accessors = 0;
	Pixel6Font . tf_LoChar    = 32;
	Pixel6Font . tf_HiChar    = 95;
	Pixel6Font . tf_CharData  = (APTR)&Pixel6Dump[0];
	Pixel6Font . tf_Modulo    = 30;
	Pixel6Font . tf_CharLoc   = (APTR)((char *)&Pixel6Dump[0] + (0xC4EE3A - 0xC4ED86));
	Pixel6Font . tf_CharSpace = 0;
	Pixel6Font . tf_CharKern  = 0;

	return(&Pixel6Font);
}

	/* CloseAll(ReturnCode) :
	 *
	 *	Gibt allen angeforderten Speicher frei, schließt
	 *	Libraries, Devices, Fenster, etc. und verläßt das
	 *	Programm.
	 */

void
CloseAll(ReturnCode)
long ReturnCode;
{
	register short i;

	DoPosition = FALSE;

		/* Die beiden Sprites brauchen wir nicht mehr. Deshalb
		 * ab in die Wüste damit.
		 */

	FreeSprite(2);
	FreeSprite(3);

	if(Window)
	{
		while(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort))
			ReplyMsg(Massage);

		CloseWindow(Window);
	}

		/* Bitmapdaten freigeben. */

	for(i = 0 ; i < 2 ; i++)
		if(PointerBMap . Planes[i])
			FreeRaster(PointerBMap . Planes[i],16,72);

	if(IntuitionBase)
	{
			/* Intuition wieder Kontrolle über die
			 * eigenen Routinen geben.
			 */

		Forbid();
		SetFunction(IntuitionBase,-0x42,OldCloseScreen);
		Permit();

		CloseLibrary(IntuitionBase);
	}

	if(GfxBase)
		CloseLibrary(GfxBase);

	CloseTimerDevice();

		/* Den MessagePort, mit dem wir erreichbar waren,
		 * schließen.
		 */

	if(MouseClockPort)
		DeletePort(MouseClockPort);

	RemVBlankServer();

	exit(ReturnCode);
}

	/* OpenAll() :
	 *
	 *	Öffnet alles, was geöffnet werden muß und
	 *	bereitet Speicher vor, den wir später brauchen
	 *	werden.
	 */

void
OpenAll()
{
	register short i;

	if(!(MouseClockPort = (struct MsgPort *)CreatePort("MouseClockPort",0)))
		CloseAll(20);

	if(!OpenTimerDevice())
		CloseAll(20);

	if(!(IntuitionBase = (VOID *)OpenLibrary("intuition.library",LIBRARY_VERSION)))
		CloseAll(20);

		/* Jetzt hängen wir unsere eigene CloseScreen()
		 * Routine ins System.
		 */

	Forbid();
	OldCloseScreen = SetFunction(IntuitionBase,-0x42,NuCloseScreen);
	Permit();

	if(!(GfxBase = (VOID *)OpenLibrary("graphics.library",LIBRARY_VERSION)))
		CloseAll(20);

		/* Fenster öffnen und richtig plazieren. */

	if(!(Window = (struct Window *)OpenWindow(&NewWindow)))
		CloseAll(20);

	WindowToBack(Window);

		/* Kleine Information, falls das Fenster gerade aktiv ist. */

	SetWindowTitles(Window,-1,NULL);

		/* ED-Symbol einzeichnen. */

	DrawImage(Window -> RPort,&ClipImage,4,0);

		/* Sprites initialisieren. */

	Position1 . height	= 72;
	Position1 . x		= 0;
	Position1 . y		= 0;

	Position2 . height	= 72;
	Position2 . x		= 0;
	Position2 . y		= 0;

	if(GetSprite(&Position1,2) < 0)
		CloseAll(20);

	if(GetSprite(&Position2,3) < 0)
		CloseAll(20);

		/* Mini-Zeichenbereich zum Vorbereiten der Sprites
		 * initialisieren.
		 */

	InitBitMap(&PointerBMap,2,16,72);
	InitRastPort(&PointerRPort);

	PointerRPort . BitMap = &PointerBMap;

	for(i = 0 ; i < 2 ; i++)
		if(!(PointerBMap . Planes[i] = (PLANEPTR)AllocRaster(16,72)))
			CloseAll(20);

		/* Zeichensatz setzen und Zeichenfarbe einstellen. */

	SetFont(&PointerRPort,OpenPixel6Font());

	SetAPen(&PointerRPort,1);
	SetBPen(&PointerRPort,0);
	SetDrMd(&PointerRPort,JAM1);

	DoPosition = TRUE;

		/* Jetzt wird die Spritekontrolle aktiviert. */

	AddVBlankServer();
}

	/* WhichDate() :
	 *
	 *	Trägt das aktuelle Datum in eine Zeichenkette ein,
	 *	die später von uns genutzt werden wird.
	 */

char *
WhichDate()
{
	static char Buff[10];
	struct DateStamp Date;
	long n,Month,Day,Year;

		/* Tolle Technik nicht wahr? Stammt von ...äh,
		 * Rob Peck... oder, hmm... Tom Rokicki?
		 */

	DateStamp(&Date);

	n = Date . ds_Days - 2251;
	Year = (4 * n + 3) / 1461;
	n -= 1461 * Year / 4;
	Year += 1984;
	Month = (5 * n + 2) / 153;
	Day = n - (153 * Month + 2) / 5 + 1;
	Month += 3;

	if(Month > 12)
	{
		Year++;
		Month -= 12;
	}

	Year -= 1900;

		/* Europäisches Format: Tag. Monat. Jahr */

	sprintf(Buff,"%02d-%02d-%02d",Day,Month,Year);

	return(Buff);
}

	/* WhichTime() :
	 *
	 *	Trägt die aktuelle Zeit in eine Zeichenkette ein, die
	 *	wir später verwenden werden.
	 */

char *
WhichTime()
{
	static char Buff[10];

	struct DateStamp TimeStamp;
	long Hours,Minutes,Seconds;

	DateStamp(&TimeStamp);

	Hours	= TimeStamp . ds_Minute / 60;
	Minutes	= TimeStamp . ds_Minute % 60;
	Seconds	= TimeStamp . ds_Tick / TICKS_PER_SECOND;

		/* 24 Stunden-Format. */

	sprintf(Buff,"%2d:%02d:%02d",Hours,Minutes,Seconds);

	return(Buff);
}

	/* WhichMem(Type) :
	 *
	 *	Trägt den freien Speicher eines Typs in eine
	 *	Zeichenkette ein, die auch in die Sprites
	 *	einzeichnet werden wird.
	 */

char *
WhichMem(Type)
long Type;
{
	static char Buff[10];

	long BlockSize;

	BlockSize = AvailMem(Type);

	sprintf(Buff,"%8d",BlockSize);

	return(Buff);
}	

	/* Diese beiden Zeilen schließen zwei Routinen aus,
	 * die wir nicht benötigen; es spart zudem ein wenig
	 * Speicher.
	 */

void _cli_parse(){}
void _wb_parse(){}

	/* MapToSprite(Sprite,BMap) :
	 *
	 *	Wandelt den Inhalt einer Bitmap in ein Array
	 *	aus UWORDs um, das dem typischen Format eines
	 *	Hardwaresprites entspricht.
	 */

void
MapToSprite(Sprite,BMap)
UWORD Sprite[];
struct BitMap *BMap;
{
	register short i,j = 0;
	UWORD *Plane[2];

		/* Ein Hardwaresprite hat ein recht "drolliges"
		 * Format, das sich sonst in keiner anderen
		 * Form auf dem Amiga findet:
		 *
		 *	2 UWORDs für Position und Kontrolldaten
		 *	n UWORDs für die eigentlich Spritedaten,
		 *	  wobei sich die Daten für beide Planes
		 *	  abwechseln.
		 *	2 UWORDs für dubiose Zwecke.
		 *
		 * Die Daten für das Sprite folgen nicht wie auf
		 * dem Amiga üblich blockweise, sondern Zeile
		 * für Zeile gemischt.
		 */

	Plane[0] = (UWORD *)BMap -> Planes[0];
	Plane[1] = (UWORD *)BMap -> Planes[1];

		/* Spritedaten löschen. */

	for(i = 0 ; i < 72 * 2 ; i++)
		Sprite[2 + i] = 0;

		/* Bitplanes in Sprite-Format wandeln. */

	for(i = 0 ; i < 72 ; i++)
		for(j = 0 ; j < 2 ; j++)
			Sprite[2 + (2 * i) + j] = Plane[j][i];
}

	/* DoPtrTxt(Y,Txt) :
	 *
	 *	Druckt einen Text in die Bitmap, die hinterher
	 *	in ein Sprite umgewandelt wird. Um den Kontrast
	 *	zu steigern, wird noch ein Schatten hinzugefügt.
	 */

void
DoPtrTxt(Y,Txt)
LONG Y;
char *Txt;
{
		/* Schatten einzeichnen. */

	Move(&PointerRPort,1,Y + 1);
	SetAPen(&PointerRPort,2);
	Text(&PointerRPort,Txt,4);

		/* Schrift einzeichnen. */

	Move(&PointerRPort,0,Y);
	SetAPen(&PointerRPort,1);
	Text(&PointerRPort,Txt,4);
}

	/* AdjustColours() :
	 *
	 *	Die Farben der Anzeige von MouseClock sind von den
	 *	Farben des Mauszeigers abhängig. In dieser Funktion
	 *	werden sie aus den Preferences herauskopiert.
	 */

void
AdjustColours()
{
	struct Preferences *Prefs;
	register short i;

		/* Puffer anlegen. */

	if(Prefs = (struct Preferences *)AllocMem(sizeof(struct Preferences),MEMF_PUBLIC))
	{
			/* Preferences kopieren. */

		GetPrefs(Prefs,sizeof(struct Preferences));

			/* Schattenfarbe in Komponenten aufsplitten. */

		Shadow[0] = ((Prefs -> color18 >> 8) & 0xF);
		Shadow[1] = ((Prefs -> color18 >> 4) & 0xF);
		Shadow[2] = ((Prefs -> color18     ) & 0xF);

			/* Zeichenfarbe in Komponenten aufsplitten. */

		Render[0] = ((Prefs -> color19 >> 8) & 0xF);
		Render[1] = ((Prefs -> color19 >> 4) & 0xF);
		Render[2] = ((Prefs -> color19     ) & 0xF);

			/* Puffer wieder freigeben. */

		FreeMem(Prefs,sizeof(struct Preferences));
	}
}

	/* main() :
	 *
	 *	Endlich! Nach soviel Vorbereitung nun schließlich
	 *	das Hauptprogramm.
	 */

void
main()
{
		/* Fall MouseClock ein zweites Mal gestartet
		 * wird, teilt es dem schon laufenden Task mit, daß
		 * er sich entfernen soll. Diese Nachricht wird mit
		 * den folgenden Strukturen verschickt.
		 */

	struct MsgPort *ReplyPort;
	struct Message WakeUp,*Terminate;

	struct DMSFragment *DMS = (struct DMSFragment *)FindPort("DMouse");
	LONG MouseTimeout = 4;

		/* Zeichenpuffer, um die gewünschten Daten auf zwei
		 * Sprites verteilen zu können.
		 */

	char DateBuff1[5],DateBuff2[5];
	char TimeBuff1[5],TimeBuff2[5];
	char FastBuff1[5],FastBuff2[5];
	char ChipBuff1[5],ChipBuff2[5];
	char *ptr;

		/* Letzte Position des Mauszeigers. */

	LONG LastX = 0,LastY = 0;

		/* Wie oft lief die Prüfschleife schon durch, ohne
		 * daß sich der Mauszeiger bewegte und wie
		 * oft lief sie schon durch, ohne daß der Inhalt der
		 * Sprites verändert werden mußte?
		 */

	LONG MoveCount = 0,i;

		/* Eingabeart, schon wieder so eine Erfindung von =RJ=,
		 * was meint ihr, welchen Ärger ich mit ProSuite
		 * hatte! Ich habe fast alles noch einmal schreiben
		 * müssen, da die Hälfte der Includes zu nichts zu
		 * gebrauchen war!
		 */

	ULONG Class;

		/* Falls schon eine MouseClock läuft, wird ihr
		 * eine Nachricht zugesandt, die sie dazu veranlassen
		 * soll, sich zu entfernen.
		 */

	if(MouseClockPort = (struct MsgPort *)FindPort("MouseClockPort",0))
	{
			/* Die Rückantwort soll hier eingehen. */

		if(!(ReplyPort = (struct MsgPort *)CreatePort(NULL,0)))
			exit(20);

			/* Nachricht initialisieren. */

		WakeUp . mn_Node . ln_Type	= NT_MESSAGE;
		WakeUp . mn_Length		= sizeof(struct Message);
		WakeUp . mn_ReplyPort		= ReplyPort;

			/* An den MessagePort schicken und
			 * auf Bestätigung warten.
			 */

		PutMsg(MouseClockPort,&WakeUp);
		Wait(1 << ReplyPort -> mp_SigBit);

			/* Rückantwortsport löschen. */

		DeletePort(ReplyPort);

		exit(0);
	}

		/* Alles öffnen und Farben der Sprites anpassen. */

	OpenAll();
	AdjustColours();

	FOREVER
	{
			/* Sollen wir uns schon entfernen? */

		if(Terminate = (struct Message *)GetMsg(MouseClockPort))
		{
			ReplyMsg(Terminate);
			CloseAll(0);
		}

		if(DMS)
			if((MouseTimeout = DMS -> MTo - 1) < 0)
				MouseTimeout = 0;

		Class = NULL;

			/* Ist irgendetwas am Fenster passiert? */

		if(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort))
		{
			Class = Massage -> Class;

			ReplyMsg(Massage);
		}

			/* Schließgadget gewählt? */

		if(Class == CLOSEWINDOW)
			CloseAll(0);

			/* Die Farben des Mauszeigers könnten sich
			 * geändert haben.
			 */

		if(Class == NEWPREFS)
			AdjustColours();

			/* Das ED-Symbol muß nachgezeichnet werden. */

		if(Class == REFRESHWINDOW)
		{
			BeginRefresh(Window);
			DrawImage(Window -> RPort,&ClipImage,4,0);
			EndRefresh(Window,TRUE);
		}

			/* Wir wurden aktiviert und melden uns
			 * auch gleich.
			 */

		if(Class == ACTIVEWINDOW)
		{
			PrintIText(Window -> WScreen -> BarLayer -> rp,&AboutTxt[0],4,0);
			DrawImage(Window -> WScreen -> BarLayer -> rp,&ClipImage,210,0);
		}

			/* Wenn sich der Mauszeiger seit fünf
			 * Sekunden nicht bewegt hat, wird die
			 * Anzeige von MouseClock ausgeschaltet.
			 * Bewegt sich der Mauszeiger anschließend
			 * jedoch wieder, werden die Sprites
			 * angeschaltet.
			 */

		if(CurX != LastX || CurY != LastY)
		{
				/* Sprites wieder anschalten. */

			if(MoveCount > MouseTimeout)
			{
				GetSprite(&Position1,2);
				GetSprite(&Position2,3);

				DoPosition = TRUE;
			}

			MoveCount = 0;
		}
		else
		{
			if((++MoveCount) >= MouseTimeout)
			{
					/* Sprites ausschalten. */

				if(MoveCount == MouseTimeout)
				{
					DoPosition = FALSE;

					WaitTOF();
					WaitTOF();

					FreeSprite(2);
					FreeSprite(3);
				}

				continue;
			}
		}

			/* Nur um sicherzugehen, daß wir es überhaupt
			 * bemerken, wenn sich der Mauszeiger nicht
			 * bewegt hat.
			 */

		LastX = CurX;
		LastY = CurY;

			/* Datum abfragen und aufspalten. */

		ptr = WhichDate();

		for(i = 0 ; i < 4 ; i++)
		{
			DateBuff1[i] = ptr[i];
			DateBuff2[i] = ptr[i + 4];
		}

		DateBuff1[4] = DateBuff2[4] = 0;

			/* Zeit abfragen und aufspalten. */

		ptr = WhichTime();

		for(i = 0 ; i < 4 ; i++)
		{
			TimeBuff1[i] = ptr[i];
			TimeBuff2[i] = ptr[i + 4];
		}

		TimeBuff1[4] = TimeBuff2[4] = 0;

			/* Chip-Ram abfragen und aufspalten. */

		ptr = WhichMem(MEMF_CHIP);

		for(i = 0 ; i < 4 ; i++)
		{
			ChipBuff1[i] = ptr[i];
			ChipBuff2[i] = ptr[i + 4];
		}

		ChipBuff1[4] = ChipBuff2[4] = 0;

			/* Fast-Ram abfragen und aufspalten. */

		ptr = WhichMem(MEMF_FAST);

		for(i = 0 ; i < 4 ; i++)
		{
			FastBuff1[i] = ptr[i];
			FastBuff2[i] = ptr[i + 4];
		}

		FastBuff1[4] = FastBuff2[4] = 0;

			/* Bitmap löschen und linkes
			 * Sprite erzeugen.
			 */

		SetRast(&PointerRPort,0);

		DoPtrTxt(10,TimeBuff2);
		DoPtrTxt(22,DateBuff2);
		DoPtrTxt(34,ChipBuff2);
		DoPtrTxt(46,FastBuff2);

		MapToSprite(Pointer2Data,&PointerBMap);

			/* Bitmap löschen und rechtes
			 * Sprite erzeugen.
			 */

		SetRast(&PointerRPort,0);

		DoPtrTxt(4,"TIME");
		DoPtrTxt(10,TimeBuff1);

		DoPtrTxt(16,"DATE");
		DoPtrTxt(22,DateBuff1);

		DoPtrTxt(28,"CHIP");
		DoPtrTxt(34,ChipBuff1);

		DoPtrTxt(40,"FAST");
		DoPtrTxt(46,FastBuff1);

		MapToSprite(Pointer1Data,&PointerBMap);

			/* Neue Sprites anzeigen. */

		ChangeSprite(NULL,&Position1,Pointer1Data);
		ChangeSprite(NULL,&Position2,Pointer2Data);

			/* Eine Zeitperiode warten. */

		WaitTime(1,0);
	}
}
