/* DClock-Handler.c ********************************************************* * * DClock-Handler.c ------ Dumb clock main handler routines, * display clock data, handle * DisplayBeep, etc. * * Author ---------------- Olaf 'Olsen' Barthel, MXM * Brabeckstrasse 35 * D-3000 Hannover 71 * * Federal Republic of Germany * * This program truly is in the PUBLIC DOMAIN. Written on a cold * and damp September evening, hoping the next morning would be * better. * * Compiled using Aztec C 5.0b, CygnusEd Professional 2 & ARexx. * ***************************************************************************/ /* Signal flag aliases. */ #define SIG_CLICK SIGBREAKF_CTRL_C #define SIG_CLOSE SIGBREAKF_CTRL_D #define SIG_TIMER SIGBREAKF_CTRL_E #define SIG_TOGGL SIGBREAKF_CTRL_F #define SIG_SHAKE SIGBREAKF_CTRL_E #define SIG_BENCH (1 << BenchSig) #define SIG_WINDO (1 << Window -> UserPort -> mp_SigBit) #define SIG_DISPL (1 << DisplaySig) #define SIG_SPEECH (1 << SpeechSig) /* Prototypes. */ struct InputEvent * EventHandler(struct InputEvent *Event); VOID Click(VOID); VOID FlushSound(VOID); UBYTE InitSound(VOID); UBYTE InitHandler(VOID); VOID FlushHandler(VOID); VOID ModifiedCloseWBench(struct Screen *); VOID AudioBeep(BYTE Volume); VOID VideoBeep(struct Screen *Screen,BYTE Perform); VOID ModifiedDisplayBeep(struct Screen *Screen); VOID PrintIt(STRPTR TimeBuff); VOID Ring(LONG Tea); VOID ShowTime(UBYTE ReallyDoIt,UBYTE Force); ULONG MaxMemSize(ULONG MemType); struct Screen * FindTheBench(VOID); VOID ShutDown(LONG HandShake); LONG RangeRand(LONG); struct Process * CreateFuncProc(char *Name,LONG Priority,APTR InitCode,ULONG StackSize); UBYTE MaxFontWidth(VOID); VOID DeleteDummyRPort(VOID); BYTE CreateDummyRPort(VOID); LONG PlayChime(VOID); VOID StopChime(VOID); LONG FindChunk(ULONG ChunkName,BPTR FileHandle); LONG LoadChimeSound(char *Name); extern VOID NewDisplayBeep(VOID); extern VOID NewCloseWBench(VOID); /* The ARexx server routines. */ STRPTR CheckDClockStatus(STRPTR); VOID RexxServer(VOID); /* Interrupt register saving functions. */ VOID int_start(VOID); VOID int_end(VOID); /* System specific function. */ LONG _main(VOID); /* The speech server. */ VOID SpeechServer(VOID); /* The magic pragmas. */ #pragma regcall(EventHandler(a0)) #pragma regcall(ModifiedDisplayBeep(a0)) /* Some global data. */ struct ArpBase *ArpBase; struct IntuitionBase *IntuitionBase; struct GfxBase *GfxBase; struct Library *DiskfontBase; struct RexxHostBase *RexxHostBase; struct Window *Window; struct Process *HandlerProcess; struct Process *RexxProcess; struct MsgPort *RexxTaskPort; struct Process *SpeechProcess; struct DSeg *DSeg; BYTE NewKick = FALSE; BYTE Printed = FALSE; LONG BenchSig = -1; LONG DisplaySig = -1; LONG SpeechSig = -1; /* Our current version tag. */ const char *VersionTag = "$VER: DClock-Handler 1.29 (15 Sep 1990)\n\r"; /* Online time. */ UBYTE OnlineHours = 0,OnlineMinutes = 0,OnlineSeconds = 0; /* The chime data. */ struct IOAudio *ChimeAudioBlock; struct MsgPort *ChimeReplyPort; UBYTE *ChimeWaveMap; /* Our dummy RastPort. */ struct BitMap *DummyMap; struct RastPort *DummyRPort; struct TextFont *DummyFont; BYTE DummyDepth; /* Static DClock window size and location. */ LONG LeftEdge = 409,TopEdge = 1,Width = 176,Height = 8; /* Audio.device control structures. */ struct MsgPort *AudioPort; struct IOAudio *Audio; /* Input.device control structures. */ struct MsgPort *InputDevPort; struct IOStdReq *InputRequestBlock; /* The interrupt handler control. */ struct Interrupt *InputHandler; /* Console.device control structures. */ struct Device *ConsoleDevice; struct IOStdReq *ConStdReq; struct InputEvent *CopyEvent; /* The library offset pointers (old ones). */ VOID *OldDisplayBeep,*OldCloseWBench; /* External data. */ extern ULONG SoundData[67]; extern ULONG RingData[1831]; extern USHORT ClockMap[456]; extern struct TextAttr DefaultFont; extern struct IntuiText TimeString; extern struct NewWindow NewWindow; extern UBYTE AnyChannel[4]; extern UBYTE SineWave[8]; extern struct Image ClockImage; extern struct Gadget ClockGadget[2]; extern struct NewWindow NewClockWindow; extern struct IntuiText ClockTxt[4]; /* EventHandler(Event) : * * This is the main interface to the handler * routine. */ struct InputEvent * EventHandler(struct InputEvent *Event) { struct InputEvent *ChainEvent; int_start(); for(ChainEvent = Event ; ChainEvent ; ChainEvent = ChainEvent -> ie_NextEvent) { /* User wants to toggle text and memory mode. */ if(ChainEvent -> ie_Class == IECLASS_RAWKEY && ChainEvent -> ie_Code == 0x5F && ChainEvent -> ie_Qualifier == RIGHT_AMIGA) { ChainEvent -> ie_Class = IECLASS_NULL; if(DSeg -> Child) Signal(DSeg -> Child,SIG_TOGGL); } /* User wants to know the time? */ if(ChainEvent -> ie_Class == IECLASS_RAWKEY && ChainEvent -> ie_Code == 0x5F && ChainEvent -> ie_Qualifier == RIGHT_ALT) { ChainEvent -> ie_Class = IECLASS_NULL; if(DSeg -> Child) Signal(DSeg -> Child,SIG_SPEECH); } /* Modify the time display size? */ if(ChainEvent -> ie_Class == IECLASS_RAWKEY && ChainEvent -> ie_Code == 0x46 && ChainEvent -> ie_Qualifier == RIGHT_AMIGA) { ChainEvent -> ie_Class = IECLASS_NULL; if(DSeg -> Child) Signal(DSeg -> Child,SIG_DISPL); } /* If we can use it, initialize the copyevent and signal * the main process to produce a click. */ if(ChainEvent -> ie_Class == IECLASS_RAWKEY && !(ChainEvent -> ie_Code & IECODE_UP_PREFIX) && DSeg -> Click) { CopyEvent -> ie_Class = ChainEvent -> ie_Class; CopyEvent -> ie_Code = ChainEvent -> ie_Code; CopyEvent -> ie_Qualifier = ChainEvent -> ie_Qualifier; /* This is safe from interrupt code, or * at least should be. */ if(DSeg -> Child) Signal(DSeg -> Child,SIG_CLICK); } } /* I had a lot of trouble getting DClock-Handler to * work with timer.device. Well, the only thing * happening in time were system crashes, so I * decided to use the timer entries of the * InputEvent structures. */ if(Event -> ie_TimeStamp . tv_secs != DSeg -> LastSecs) { DSeg -> LastSecs = Event -> ie_TimeStamp . tv_secs; if(DSeg -> Child) Signal(DSeg -> Child,SIG_TIMER); /* Each second we take a look at CIA B, port A * to find out if a carrier signal is currently * present at the serial port. Note that the port * bits are low-active. */ if(!(ciab . ciapra & CIAF_COMCD)) { /* Are we online? */ if(!DSeg -> Online) { /* Reset counters. */ OnlineHours = OnlineMinutes = OnlineSeconds = 0; DSeg -> Online = TRUE; } /* Increment time counter. */ if(++OnlineSeconds == 60) { OnlineSeconds = 0; if(++OnlineMinutes == 60) { OnlineMinutes = 0; ++OnlineHours; } } } else DSeg -> Online = FALSE; } int_end(); return(Event); } /* Click() : * * Produces the click (or what did you expect?). */ VOID Click() { char PrimaryBuffer[11]; /* Rawkey conversion buffer. */ PrimaryBuffer[0] = 0; /* Convert the input event according to the * current keymap settings. */ RawKeyConvert(CopyEvent,PrimaryBuffer,10,NULL); /* If it didn't produce a sensible result, * don't click. */ if(!PrimaryBuffer[0] || PrimaryBuffer[0] == 0x9B) return; Audio -> ioa_Volume = DSeg -> ClickVolume; /* Let it click. */ Tick: if(CheckIO(Audio)) BeginIO(Audio); } /* FlushSound() : * * Send the sound control data to NIL: */ VOID FlushSound() { if(Audio) { /* Audio device still open? */ if(Audio -> ioa_Request . io_Device) { if(!CheckIO(Audio)) WaitIO(Audio); /* Free the channel(s). */ CloseDevice(Audio); } } /* Delete the replyport. */ if(AudioPort) DeletePort(AudioPort); } /* InitSound() : * * Sets up the audio control structures. */ UBYTE InitSound() { if(Audio = (struct IOAudio *)ArpAlloc(sizeof(struct IOAudio))) { if(AudioPort = (struct MsgPort *)CreatePort(NULL,0)) { /* Open the channel. */ if(!OpenDevice(AUDIONAME,0,Audio,0)) { /* Try to allocate a vacant channel. */ Audio -> ioa_Request . io_Command = ADCMD_ALLOCATE; Audio -> ioa_Request . io_Flags = ADIOF_NOWAIT; Audio -> ioa_Request . io_Message . mn_Node . ln_Pri = 100; Audio -> ioa_Request . io_Message . mn_ReplyPort = AudioPort; Audio -> ioa_Data = AnyChannel; Audio -> ioa_Length = 4; /* Try the allocation. */ BeginIO(Audio); /* Did it return an error? */ if(!WaitIO(Audio)) { /* Prepare it for the click. */ Audio -> ioa_Request . io_Command = CMD_WRITE; Audio -> ioa_Request . io_Flags = ADIOF_PERVOL | ADIOF_NOWAIT; Audio -> ioa_Period = 180; Audio -> ioa_Volume = 0; Audio -> ioa_Length = 270; Audio -> ioa_Data = (UBYTE *)SoundData; Audio -> ioa_Cycles = 1; /* Click once. */ BeginIO(Audio); WaitIO(Audio); return(TRUE); } } } } return(FALSE); } /* InitHandler() : * * Open the console.device for keymap translation * and add the input.device handler. */ UBYTE InitHandler() { if(ConStdReq = (struct IOStdReq *)ArpAlloc(sizeof(struct IOStdReq))) { if(!OpenDevice("console.device",CONU_LIBRARY,ConStdReq,0)) { if(InputDevPort = (struct MsgPort *)CreatePort(NULL,0)) { if(InputRequestBlock = (struct IOStdReq *)CreateStdIO(InputDevPort)) { if(!OpenDevice("input.device",0,InputRequestBlock,0)) { if(InputHandler = (struct Interrupt *)ArpAlloc(sizeof(struct Interrupt))) { if(CopyEvent = (struct InputEvent *)ArpAlloc(sizeof(struct InputEvent))) { InputHandler -> is_Node . ln_Name = "DClock-Handler"; InputHandler -> is_Node . ln_Pri = 51; InputHandler -> is_Code = (VOID *)EventHandler; InputRequestBlock -> io_Command = IND_ADDHANDLER; InputRequestBlock -> io_Data = (APTR)InputHandler; DoIO(InputRequestBlock); ConsoleDevice = ConStdReq -> io_Device; return(TRUE); } } } } } } } return(FALSE); } /* FlushHandler() : * * Closes the console.device and removes the * input.device handler from the chain. */ VOID FlushHandler() { if(ConsoleDevice) CloseDevice(ConStdReq); if(InputRequestBlock) { if(InputRequestBlock -> io_Device) { InputRequestBlock -> io_Command = IND_REMHANDLER; InputRequestBlock -> io_Data = (APTR)InputHandler; DoIO(InputRequestBlock); CloseDevice(InputRequestBlock); } DeleteStdIO(InputRequestBlock); } if(InputDevPort) DeletePort(InputDevPort); } /* ModifiedCloseWBench(): * * Tells DClock to close its window before the * Workbench screen gets closed. */ VOID ModifiedCloseWBench() { if(Window) { struct Task *ThisTask = SysBase -> ThisTask; BYTE CurrentPri; /* Shut down... */ Signal(DSeg -> Child,SIG_BENCH); /* Careful - rather rude window close check, * don't waste too much time in the loop. */ CurrentPri = SetTaskPri(ThisTask,0); while(Window); SetTaskPri(ThisTask,CurrentPri); } } VOID AudioBeep(BYTE Volume) { struct IOAudio *AudioBlock; struct MsgPort *ReplyPort; /* Allocate some driver memory. */ if(AudioBlock = (struct IOAudio *)AllocMem(sizeof(struct IOAudio),MEMF_PUBLIC | MEMF_CLEAR)) { /* Time for a replyport? */ if(ReplyPort = (struct MsgPort *)CreatePort(NULL,0)) { AudioBlock -> ioa_Request . io_Message . mn_ReplyPort = ReplyPort; if(!OpenDevice(AUDIONAME,0,AudioBlock,0)) { /* Set up initial driver data. */ AudioBlock -> ioa_Request . io_Command = ADCMD_ALLOCATE; AudioBlock -> ioa_Request . io_Message . mn_Node . ln_Pri = 90; AudioBlock -> ioa_Data = &AnyChannel[0]; AudioBlock -> ioa_Length = 4; if(!DoIO(AudioBlock)) { AudioBlock -> ioa_Request . io_Command = CMD_WRITE; AudioBlock -> ioa_Request . io_Flags = ADIOF_PERVOL; AudioBlock -> ioa_Period = 223; AudioBlock -> ioa_Volume = 64 / 2 * Volume; AudioBlock -> ioa_Cycles = 150; AudioBlock -> ioa_Data = &SineWave[0]; AudioBlock -> ioa_Length = 8; /* Beeep! */ BeginIO(AudioBlock); WaitIO(AudioBlock); } CloseDevice(AudioBlock); } DeletePort(ReplyPort); } FreeMem(AudioBlock,sizeof(struct IOAudio)); } } /* VideoBeep(Screen,Perform): * * Handles the visual part of the DisplayBeep, * flashes a particular screen or restores its * original colour (well, hope so). */ VOID VideoBeep(struct Screen *Screen,BYTE Perform) { UBYTE R,G,B; /* Beep this screen? */ if(Perform) { /* Is it already beeping? */ if(!(Screen -> Flags & BEEPING)) { /* This one's beeping. */ Screen -> Flags |= BEEPING; /* Don't forget this one. */ Screen -> SaveColor0 = GetRGB4(Screen -> ViewPort . ColorMap,0); /* Reverse the colour. */ R = ((Screen -> SaveColor0 >> 8) & 0xF) ^ 0xF; G = ((Screen -> SaveColor0 >> 4) & 0xF) ^ 0xF; B = ((Screen -> SaveColor0 ) & 0xF) ^ 0xF; /* Set it. */ SetRGB4(&Screen -> ViewPort,0,R,G,B); } } else { /* Is this one beeping? */ if(Screen -> Flags & BEEPING) { /* This one isn't beeping any longer. */ Screen -> Flags &= ~BEEPING; /* Restore the saved colour. */ R = ((Screen -> SaveColor0 >> 8) & 0xF); G = ((Screen -> SaveColor0 >> 4) & 0xF); B = ((Screen -> SaveColor0 ) & 0xF); SetRGB4(&Screen -> ViewPort,0,R,G,B); } } } /* ModifiedDisplayBeep(Screen): * * Magic replacement for usual DisplayBeep() * function. */ VOID ModifiedDisplayBeep(struct Screen *Screen) { /* Flash a particular screen. */ if(Screen) { VideoBeep(Screen,TRUE); AudioBeep(DSeg -> Beep); VideoBeep(Screen,FALSE); } else { /* Flash all screens. */ ULONG IntuiLock; /* Where's the first one? Has anybody * used the LockIBase() function so * far (save me)? */ IntuiLock = LockIBase(NULL); Screen = IntuitionBase -> FirstScreen; /* Walk through the screens flashing them all. */ do VideoBeep(Screen,TRUE); while(Screen = Screen -> NextScreen); UnlockIBase(IntuiLock); /* Let it resound. */ AudioBeep(DSeg -> Beep); /* Again: where's the first screen? */ IntuiLock = LockIBase(NULL); Screen = IntuitionBase -> FirstScreen; do VideoBeep(Screen,FALSE); while(Screen = Screen -> NextScreen); UnlockIBase(IntuiLock); } } /* PrintIt(TimeBuff,CharOffset): * * Prints the formatted string into the Workbench title bar. */ VOID PrintIt(STRPTR TimeBuff) { BYTE Length = strlen((char *)TimeBuff); BYTE Offset = Width - TextLength(DummyRPort,TimeBuff,Length); SetAPen(DummyRPort,DSeg -> TextColour); SetBPen(DummyRPort,DSeg -> BackColour); SetRast(DummyRPort,1); Move(DummyRPort,(Offset >= 0 ? Offset : 0),DummyRPort -> Font -> tf_Baseline); Text(DummyRPort,TimeBuff,Length); } /* Ring(): * * This one rings the bell of the alarm clock. */ VOID Ring(LONG Tea) { struct IOAudio *AudioBlock; struct MsgPort *ReplyPort; struct Screen PublicScreen; struct Screen *FirstOne; struct Window *ClockWindow; struct IntuiMessage *Massage; ULONG Class; USHORT Code; struct Gadget *ID; ULONG IntuiLock; struct View *ViewLord; SHORT DyOffset,PlusY; LONG TimeOut; /* Remember initial first screen. */ IntuiLock = LockIBase(NULL); FirstOne = IntuitionBase -> FirstScreen; UnlockIBase(IntuiLock); /* Knockin' on heaven's door... */ OpenWorkBench(); /* Center the clock window. */ GetScreenData(&PublicScreen,sizeof(struct Screen),WBENCHSCREEN,NULL); NewClockWindow . LeftEdge = (PublicScreen . Width - NewClockWindow . Width) / 2; NewClockWindow . TopEdge = (PublicScreen . Height - NewClockWindow . Height) / 2; /* Open it and paint the background. */ if(!(ClockWindow = (struct Window *)OpenWindow(&NewClockWindow))) { DisplayBeep(NULL); goto Quit; } if(NewKick) SetAPen(ClockWindow -> RPort,2); else SetAPen(ClockWindow -> RPort,1); RectFill(ClockWindow -> RPort,2,1,ClockWindow -> Width - 3,ClockWindow -> Height - 2); RefreshGadgets(&ClockGadget[0],ClockWindow,NULL); /* Adjust the contents of the alarm time string. */ if(Tea) SPrintf(ClockTxt[3] . IText,"Alarm time » %2ld:%02ld:%02ld «",DSeg -> AlarmHour,DSeg -> AlarmMinute,DSeg -> AlarmSecond); else strcpy((char *)ClockTxt[3] . IText,"Countdown elapsed!"); PrintIText(ClockWindow -> RPort,&ClockTxt[0],0,0); /* Allocate some driver memory. */ if(AudioBlock = (struct IOAudio *)AllocMem(sizeof(struct IOAudio),MEMF_PUBLIC | MEMF_CLEAR)) { /* Time for a replyport? */ if(ReplyPort = (struct MsgPort *)CreatePort(NULL,0)) { /* Set up initial driver data. */ AudioBlock -> ioa_Data = &AnyChannel[0]; AudioBlock -> ioa_Length = 4; AudioBlock -> ioa_Request . io_Message . mn_ReplyPort = ReplyPort; AudioBlock -> ioa_Request . io_Message . mn_Node.ln_Pri = 80; /* Allocate the channels on the fly. */ if(!OpenDevice(AUDIONAME,0,AudioBlock,0)) { AudioBlock -> ioa_Request . io_Command = CMD_WRITE; AudioBlock -> ioa_Request . io_Flags = ADIOF_PERVOL; AudioBlock -> ioa_Period = 308; AudioBlock -> ioa_Volume = 64; AudioBlock -> ioa_Cycles = 1; AudioBlock -> ioa_Data = (UBYTE *)&RingData[0]; AudioBlock -> ioa_Length = 7326; IntuiLock = LockIBase(NULL); ViewLord = &IntuitionBase -> ViewLord; UnlockIBase(IntuiLock); DyOffset = ViewLord -> DyOffset; /* Ring! */ BeginIO(AudioBlock); WBenchToFront(); TimeOut = 0; /* Ring until somebody clicked our window * or a timeout occurs. */ FOREVER { /* Cycles already finished. */ if(CheckIO(AudioBlock)) BeginIO(AudioBlock); Class = Code = NULL; if(Massage = GetMsg(ClockWindow -> UserPort)) { Class = Massage -> Class; Code = Massage -> Code; ID = (struct Gadget *)Massage -> IAddress; ReplyMsg(Massage); if((Class == GADGETUP && !ID -> GadgetID) || Class == VANILLAKEY) break; } PlusY = 1 - RangeRand(3); /* Make the view vibrate. */ if(DyOffset + PlusY >= 0) ViewLord -> DyOffset = DyOffset + PlusY; RethinkDisplay(); /* Wait a tick. */ Delay(1); /* Now for the timeout... */ if((TimeOut++) >= (TICKS_PER_SECOND * 30)) break; } ViewLord -> DyOffset = DyOffset; RethinkDisplay(); /* Still ringing? */ if(!CheckIO(AudioBlock)) WaitIO(AudioBlock); /* Tick! */ CloseDevice(AudioBlock); } DeletePort(ReplyPort); } FreeMem(AudioBlock,sizeof(struct IOAudio)); } /* Bring original first screen to the front again. */ Quit: if(FirstOne != ClockWindow -> WScreen) ScreenToBack(ClockWindow -> WScreen); if(ClockWindow) CloseWindow(ClockWindow); } /* ShowTime(ReallyDoIt,Force): * * Yes, it's Showtime! This one compiles the date/timestring * and prints it. */ VOID ShowTime(UBYTE ReallyDoIt,UBYTE Force) { UBYTE TempBuff[30]; static char *Months[12] = { "Jan","Feb","Mar", "Apr","May","Jun", "Jul","Aug","Sep", "Oct","Nov","Dec" }; static char *Days[7] = { "Sun", /* Note: these have to appear right in this order. */ "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static char *LongDays[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; static char DateBuff[15],TimeBuff[10]; static UBYTE LastState = 2; static SHORT MonthVectors[14] = { -1, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364 }; LONG JulianDate, Day0, Day1, Day2, Day3; LONG Year, Month, Day; struct DateStamp Date; DateStamp(&Date); JulianDate = Date . ds_Days + DDELTA; Year = (JulianDate / 146097) * 400; Day0 = Day1 = JulianDate %= 146097; Year += (JulianDate / 36524) * 100; Day2 = Day1 %= 36524; Year += (Day2 / 1461) * 4; Day3 = Day1 %= 1461; Year += Day3 / 365; Month = 1 + (Day1 %= 365); Day = Month % 30; Month /= 30; if((Day3 >= 59 && Day0 < 59) || (Day3 < 59 && (Day2 >= 59 || Day0 < 59))) Day1++; if(Day1 > MonthVectors[1 + Month]) Month++; Day = Day1 - MonthVectors[Month]; DSeg -> CurrentTime . Year = Year; DSeg -> CurrentTime . Month = Month; DSeg -> CurrentTime . Day = Day; DSeg -> CurrentTime . Weekday = Date . ds_Days % DAYS_PER_WEEK; DSeg -> CurrentTime . Hour = Date . ds_Minute / MINS_PER_HOUR; DSeg -> CurrentTime . Minute = Date . ds_Minute % MINS_PER_HOUR; DSeg -> CurrentTime . Second = Date . ds_Tick / TICS_PER_SEC; /* Did we need a change? */ if(LastState != DSeg -> Seconds) { LastState = DSeg -> Seconds; Force = TRUE; } if(ReallyDoIt) { if(DSeg -> Seconds) { SPrintf(TempBuff,"%s %02ld-%s-%02ld %02ld:%02ld:%02ld", Days[DSeg -> CurrentTime . Weekday], DSeg -> CurrentTime . Day, Months[DSeg -> CurrentTime . Month - 1], DSeg -> CurrentTime . Year % 100, DSeg -> CurrentTime . Hour, DSeg -> CurrentTime . Minute, DSeg -> CurrentTime . Second); PrintIt(TempBuff); } else { if(!(Date . ds_Tick / TICKS_PER_SECOND) || Force) { SPrintf(TempBuff,"%s %02ld-%s-%02ld %02ld:%02ld", Days[DSeg -> CurrentTime . Weekday], DSeg -> CurrentTime . Day, Months[DSeg -> CurrentTime . Month - 1], DSeg -> CurrentTime . Year % 100, DSeg -> CurrentTime . Hour, DSeg -> CurrentTime . Minute); SetRast(DummyRPort,1); PrintIt(TempBuff); } } } if((!DSeg -> CurrentTime . Second || !Printed) && DSeg -> SetEnv) { Printed = TRUE; SPrintf(DateBuff,"%02ld-%s-%02ld",Day,Months[DSeg -> CurrentTime . Month - 1],DSeg -> CurrentTime . Year % 100); SPrintf(TimeBuff,"%02ld:%02ld",DSeg -> CurrentTime . Hour,DSeg -> CurrentTime . Minute); Setenv("DAY",LongDays[DSeg -> CurrentTime . Weekday]); Setenv("DATE",DateBuff); Setenv("TIME",TimeBuff); } } /* MaxMemSize(MemType): * * Returns the length of memory block of a special * kind. Borrowed from Louis A. Mamakos' GfxMem 0.4. */ ULONG MaxMemSize(ULONG MemType) { ULONG BlockSize = 0; struct MemHeader *MemHeader; Forbid(); /* Walk through the memory lists adding the * amount of memory bound to them. */ for(MemHeader = (struct MemHeader *)SysBase -> MemList . lh_Head ; MemHeader -> mh_Node . ln_Succ ; MemHeader = (struct MemHeader *)MemHeader -> mh_Node . ln_Succ) { if(MemHeader -> mh_Attributes & MemType) BlockSize += ((ULONG)MemHeader -> mh_Upper - (ULONG)MemHeader -> mh_Lower); } Permit(); return(BlockSize); } /* FindTheBench(): * * Tries to locate the Workbench screen in the linked list * of system screens. This is rather a rude method and should * be exercised only while Intuition is locked. * * This could be lot easier if using LockPubScreen(). */ struct Screen * FindTheBench() { struct Screen *WBench; ULONG IntuiLock = LockIBase(NULL); /* Start with the first one. */ WBench = IntuitionBase -> FirstScreen; /* Scan the list... */ do { /* The type we want? */ if((WBench -> Flags & SCREENTYPE) == WBENCHSCREEN) { UnlockIBase(IntuiLock); return(WBench); } } while(WBench = WBench -> NextScreen); /* Failed! */ UnlockIBase(IntuiLock); return(NULL); } /* PlayChime(): * * Plays the hour chime. */ LONG PlayChime() { LONG Rate = 447,Length = 8,Cycles = 150,Volume = 64 / 2; if(ChimeReplyPort) return(FALSE); ObtainSemaphore(DSeg -> SoundSemaphore); if(DSeg -> SoundData && DSeg -> SoundLength) { ChimeWaveMap = (UBYTE *)DSeg -> SoundData; Length = DSeg -> SoundLength; Volume = DSeg -> SoundVolume; Rate = DSeg -> SoundRate; Cycles = 1; } else ChimeWaveMap = &SineWave[0]; /* Allocate some driver memory. */ if(!(ChimeAudioBlock = (struct IOAudio *)AllocMem(sizeof(struct IOAudio),MEMF_PUBLIC | MEMF_CLEAR))) { ReleaseSemaphore(DSeg -> SoundSemaphore); return(FALSE); } /* Time for a replyport? */ if(!(ChimeReplyPort = (struct MsgPort *)CreatePort(NULL,0))) { FreeMem(ChimeAudioBlock,sizeof(struct IOAudio)); ChimeAudioBlock = NULL; ReleaseSemaphore(DSeg -> SoundSemaphore); return(FALSE); } /* Set up initial driver data. */ ChimeAudioBlock -> ioa_Data = &AnyChannel[0]; ChimeAudioBlock -> ioa_Length = 4; ChimeAudioBlock -> ioa_Request . io_Message . mn_ReplyPort = ChimeReplyPort; ChimeAudioBlock -> ioa_Request . io_Message . mn_Node . ln_Pri = 90; /* Allocate the channels on the fly. */ if(OpenDevice(AUDIONAME,0,ChimeAudioBlock,0)) { FreeMem(ChimeAudioBlock,sizeof(struct IOAudio)); DeletePort(ChimeReplyPort); ChimeAudioBlock = NULL; ChimeReplyPort = NULL; ReleaseSemaphore(DSeg -> SoundSemaphore); return(FALSE); } ChimeAudioBlock -> ioa_Request . io_Command = CMD_WRITE; ChimeAudioBlock -> ioa_Request . io_Flags = ADIOF_PERVOL; ChimeAudioBlock -> ioa_Cycles = Cycles; ChimeAudioBlock -> ioa_Data = ChimeWaveMap; ChimeAudioBlock -> ioa_Length = Length; ChimeAudioBlock -> ioa_Period = Rate; ChimeAudioBlock -> ioa_Volume = Volume; BeginIO(ChimeAudioBlock); } /* StopChime(): * * Stops the hour chime. */ VOID StopChime() { if(ChimeAudioBlock) { if(ChimeAudioBlock -> ioa_Request . io_Device) { if(!CheckIO(ChimeAudioBlock)) WaitIO(ChimeAudioBlock); CloseDevice(ChimeAudioBlock); } if(!DSeg -> SoundData || !DSeg -> SoundLength) { if(ChimeWaveMap != &SineWave[0]) FreeMem(ChimeWaveMap,8); } FreeMem(ChimeAudioBlock,sizeof(struct IOAudio)); DeletePort(ChimeReplyPort); ChimeAudioBlock = NULL; ChimeReplyPort = NULL; ReleaseSemaphore(DSeg -> SoundSemaphore); } } /* FindChunk(ChunkName,FileHandle): * * Tries to locate an iff-chunk inside a file. */ LONG FindChunk(ULONG ChunkName,BPTR FileHandle) { LONG OldPosition; ULONG FormType = 0; /* The format of a typical IFF-chunk. */ struct { ULONG IFF_Type; ULONG IFF_Length; } Chunk; /* Remember initial file position. */ OldPosition = Seek(FileHandle,0,OFFSET_CURRENT); /* Try to find it. */ FOREVER { /* Read the first bytes. */ if(Read(FileHandle,&Chunk,sizeof(Chunk)) != sizeof(Chunk)) { Seek(FileHandle,OldPosition,OFFSET_BEGINNING); return(FALSE); } /* Is it a FORM-chunk? */ if(OldPosition == 0 && FormType == 0 && Chunk . IFF_Type == 'FORM') { Read(FileHandle,&FormType,sizeof(LONG)); /* Check the form type. */ if(FormType == ChunkName) return(TRUE); continue; } /* Is it the chunk type we want? */ if(Chunk . IFF_Type == ChunkName) return(TRUE); /* Skip chunk. */ Seek(FileHandle,Chunk . IFF_Length,OFFSET_CURRENT); } } /* LoadChimeSound(Name): * * Loads an IFF-8SVX-soundfile to be used as the hour * chime sound. */ LONG LoadChimeSound(char *Name) { BPTR SoundHandle; APTR SoundData; LONG SoundRate,SoundLength,SoundVolume; /* The format of the VoiceHeader-chunk. */ struct { ULONG oneShotHiSamples, repeatHiSamples, samplesPerHiCycle; UWORD samplesPerSec; UBYTE ctOctave, sCompression; LONG volume; } VoiceHeader; /* No name? Reset to defaults. */ if(!RexxStrCmp(Name,"OFF")) { ObtainSemaphore(DSeg -> SoundSemaphore); /* Free the memory. */ if(DSeg -> SoundLength && DSeg -> SoundData) FreeMem(DSeg -> SoundData,DSeg -> SoundLength); /* Mark sound slot as vacant. */ DSeg -> SoundLength = 0; DSeg -> SoundData = NULL; ReleaseSemaphore(DSeg -> SoundSemaphore); return(TRUE); } /* Open the file for reading. */ if(!(SoundHandle = Open(Name,MODE_OLDFILE))) return(FALSE); /* Is it a sound file? */ if(!FindChunk('8SVX',SoundHandle)) { Close(SoundHandle); return(FALSE); } /* Look for the VoiceHeader. */ if(!FindChunk('VHDR',SoundHandle)) { Close(SoundHandle); return(FALSE); } /* Read the header. */ if(Read(SoundHandle,&VoiceHeader,sizeof(VoiceHeader)) != sizeof(VoiceHeader)) { Close(SoundHandle); return(FALSE); } /* Fill in the more important information. */ SoundLength = VoiceHeader . oneShotHiSamples + VoiceHeader . repeatHiSamples; SoundRate = ((GfxBase -> DisplayFlags & PAL) ? 3546895 : 3579545) / VoiceHeader . samplesPerSec; SoundVolume = (VoiceHeader . volume > 64 ? 64 : VoiceHeader . volume); /* Proceed with the body chunk. */ if(!FindChunk('BODY',SoundHandle)) { Close(SoundHandle); return(FALSE); } /* Allocate space for the sound data. */ if(!(SoundData = (APTR)AllocMem(SoundLength,MEMF_PUBLIC | MEMF_CHIP))) { Close(SoundHandle); return(FALSE); } /* Read the sound data. */ if(Read(SoundHandle,SoundData,SoundLength) != SoundLength) { FreeMem(SoundData,SoundLength); Close(SoundHandle); return(FALSE); } /* Close the file. */ Close(SoundHandle); /* Lock access to sound data. */ ObtainSemaphore(DSeg -> SoundSemaphore); /* Free last sound. */ if(DSeg -> SoundLength && DSeg -> SoundData) FreeMem(DSeg -> SoundData,DSeg -> SoundLength); /* Fill in the data. */ DSeg -> SoundData = SoundData; DSeg -> SoundLength = SoundLength; DSeg -> SoundRate = SoundRate; DSeg -> SoundVolume = SoundVolume; /* Unlock it again. */ ReleaseSemaphore(DSeg -> SoundSemaphore); return(TRUE); } /* CheckDClockStatus(Arg): * * Checks DClock option flags and returns them. */ STRPTR CheckDClockStatus(STRPTR Arg) { /* Static result string. */ STATIC UBYTE Response[200]; /* Every option to be checked. */ STATIC STRPTR Options[17] = { "BEEP", "CLICK", "CLICKVOLUME", "PRIORITY", "TEXTCOLOUR", "BACKCOLOUR", "ALARM", "ALARMTIME", "SETENV", "VERSION", "COUNTDOWN", "HOUR", "SECONDS", "SOUND", "PAGE", "SPEECH", "LINE" }; /* A temporary string. */ UBYTE TempString[20]; LONG i,TheOption = -1; /* Clear the string. */ Response[0] = 0; /* Check which option matches the argument. * If none matches we'll produce a string * filled with all options. */ for(i = 0 ; i < 16 ; i++) { if(!RexxStrCmp(Arg,Options[i])) { TheOption = i; break; } } /* Is it BEEP? */ if(TheOption == 0 || TheOption == -1) { if(TheOption == -1) { strcat(Response,Options[0]); strcat(Response," "); } strcat(Response,(DSeg -> Beep ? "ON" : "OFF")); } /* Is it CLICK? */ if(TheOption == 1 || TheOption == -1) { if(TheOption == -1) { strcat(Response," "); strcat(Response,Options[1]); strcat(Response," "); } strcat(Response,(DSeg -> Click ? "ON" : "OFF")); } /* Is it CLICKVOLUME? */ if(TheOption == 2 || TheOption == -1) { if(TheOption == -1) { strcat(Response," "); strcat(Response,Options[2]); strcat(Response," "); } BuildValueString(DSeg -> ClickVolume,TempString); strcat(Response,TempString); } /* Is it PRIORITY? */ if(TheOption == 3 || TheOption == -1) { if(TheOption == -1) { strcat(Response," "); strcat(Response,Options[3]); strcat(Response," "); } BuildValueString(DSeg -> Priority,TempString); strcat(Response,TempString); } /* Is it TEXTCOLOUR? */ if(TheOption == 4 || TheOption == -1) { if(TheOption == -1) { strcat(Response," "); strcat(Response,Options[4]); strcat(Response," "); } BuildValueString(DSeg -> TextColour,TempString); strcat(Response,TempString); } /* Is it BACKCOLOUR? */ if(TheOption == 5 || TheOption == -1) { if(TheOption == -1) { strcat(Response," "); strcat(Response,Options[5]); strcat(Response," "); } BuildValueString(DSeg -> Priority,TempString); strcat(Response,TempString); } /* Is it ALARM? */ if(TheOption == 6 || TheOption == -1) { if(TheOption == -1) { strcat(Response," "); strcat(Response,Options[6]); strcat(Response," "); } strcat(Response,(DSeg -> Alarm ? "ON" : "OFF")); } /* Is it ALARMTIME? */ if(TheOption == 7 || TheOption == -1) { if(TheOption == -1) { strcat(Response," "); strcat(Response,Options[7]); strcat(Response," "); } SPrintf(TempString,"%02ld:%02ld:%02ld",DSeg -> AlarmHour,DSeg -> AlarmMinute,DSeg -> AlarmSecond); strcat(Response,TempString); } /* Is it SETENV? */ if(TheOption == 8 || TheOption == -1) { if(TheOption == -1) { strcat(Response," "); strcat(Response,Options[8]); strcat(Response," "); } strcat(Response,(DSeg -> SetEnv ? "ON" : "OFF")); } /* Is it VERSION? */ if(TheOption == 9 || TheOption == -1) { if(TheOption == -1) { strcat(Response," "); strcat(Response,Options[9]); strcat(Response," "); } BuildValueString(DSeg -> Revision,TempString); strcat(Response,TempString); } /* Is it COUNTDOWN? */ if(TheOption == 10 || TheOption == -1) { if(TheOption == -1) { strcat(Response," "); strcat(Response,Options[10]); strcat(Response," "); } BuildValueString(DSeg -> Countdown,TempString); strcat(Response,TempString); } /* Is it HOUR? */ if(TheOption == 11 || TheOption == -1) { if(TheOption == -1) { strcat(Response," "); strcat(Response,Options[11]); strcat(Response," "); } strcat(Response,(DSeg -> Hour ? "ON" : "OFF")); } /* Is it SECONDS? */ if(TheOption == 12 || TheOption == -1) { if(TheOption == -1) { strcat(Response," "); strcat(Response,Options[12]); strcat(Response," "); } strcat(Response,(DSeg -> Seconds ? "ON" : "OFF")); } /* Is it SOUND? */ if(TheOption == 13 || TheOption == -1) { if(TheOption == -1) { strcat(Response," "); strcat(Response,Options[13]); strcat(Response," "); } strcat(Response,((DSeg -> SoundData && DSeg -> SoundLength) ? "ON" : "OFF")); } /* Is it PAGE? */ if(TheOption == 14 || TheOption == -1) { if(TheOption == -1) { strcat(Response," "); strcat(Response,Options[14]); strcat(Response," "); } BuildValueString(DSeg -> Page,TempString); strcat(Response,TempString); } /* Is it SPEECH? */ if(TheOption == 15 || TheOption == -1) { if(TheOption == -1) { strcat(Response," "); strcat(Response,Options[15]); strcat(Response," "); } strcat(Response,(DSeg -> Speech ? "ON" : "OFF")); } /* Is it LINE? */ if(TheOption == 16 || TheOption == -1) { if(TheOption == -1) { strcat(Response," "); strcat(Response,Options[16]); strcat(Response," "); } strcat(Response,(DSeg -> Online ? "ON" : "OFF")); } /* Return the result of our 'questionnaire'. */ return(Response); } /* RexxServer(): * * Rexx server subtask (process), handles the rexx commands * asynchronously. */ VOID RexxServer() { struct RexxMessage *RexxMsg; /* Rexx signal message. */ ULONG SignalSet; /* Incoming signals. */ STRPTR StringResult; /* Result string (error message). */ LONG NumResult; /* Return code. */ LONG ArgCount; /* String counter. */ UBYTE Arg1[20],Arg2[20]; /* Both arguments. */ /* Get the base register (hey Manx, where's * the __saveds equivalent?). */ geta4(); /* Create the communication port. */ if(!(RexxTaskPort = (struct MsgPort *)CreatePort(NULL,0))) goto Quit; /* Synchronization. */ Signal(HandlerProcess,SIG_SHAKE); /* Ad infinitum. */ FOREVER { /* Wait for a signal. */ SignalSet = Wait(SIG_CLOSE | (1 << RexxTaskPort -> mp_SigBit)); /* A rexx message? */ if(SignalSet & (1 << RexxTaskPort -> mp_SigBit)) { /* Intercept all messages. */ while(RexxMsg = GetMsg(RexxTaskPort)) { ArgCount = 0; /* Get the command string. */ StringResult = GetRexxCommand(RexxMsg); /* Get command and argument. */ GetToken(StringResult,&ArgCount,Arg1,20); GetToken(StringResult,&ArgCount,Arg2,20); /* Zero default values. */ StringResult = NULL; NumResult = 0; /* Adjust Click volume? */ if(!RexxStrCmp(Arg1,"CLICKVOLUME")) { if(GetStringValue(Arg2) > 64 || GetStringValue(Arg2) < 0) NumResult = 10; else DSeg -> ClickVolume = GetStringValue(Arg2); } /* Adjust task priority? */ if(!RexxStrCmp(Arg1,"PRIORITY")) { if(GetStringValue(Arg2) > 127 || GetStringValue(Arg2) < -128) NumResult = 10; else { DSeg -> Priority = GetStringValue(Arg2); SetTaskPri(DSeg -> Child,DSeg -> Priority); } } /* Turn DisplayBeep() off? */ if(!RexxStrCmp(Arg1,"BEEP")) { if(!RexxStrCmp(Arg2,"OFF")) DSeg -> Beep = FALSE; else { if(!RexxStrCmp(Arg2,"ON")) DSeg -> Beep = TRUE; else NumResult = 10; } } /* Turn keyboard click off? */ if(!RexxStrCmp(Arg1,"CLICK")) { if(!RexxStrCmp(Arg2,"OFF")) DSeg -> Click = FALSE; else { if(!RexxStrCmp(Arg2,"ON")) DSeg -> Click = TRUE; else NumResult = 10; } } /* Set text colour? */ if(!RexxStrCmp(Arg1,"TEXTCOLOUR")) DSeg -> TextColour = GetStringValue(Arg2); /* Set background colour? */ if(!RexxStrCmp(Arg1,"BACKCOLOUR")) DSeg -> BackColour = GetStringValue(Arg2); /* Turn alarm on? */ if(!RexxStrCmp(Arg1,"ALARM")) { if(!RexxStrCmp(Arg2,"OFF")) DSeg -> Alarm = FALSE; else { if(!RexxStrCmp(Arg2,"ON")) DSeg -> Alarm = TRUE; else NumResult = 10; } } /* Set alarm time? */ if(!RexxStrCmp(Arg1,"ALARMTIME")) { char TimeBuff[3]; LONG TheTime; TimeBuff[2] = 0; TimeBuff[0] = Arg2[0]; TimeBuff[1] = Arg2[1]; TheTime = GetStringValue(TimeBuff); if(TheTime >= 0 && TheTime <= 23) DSeg -> AlarmHour = TheTime; else NumResult = 10; TimeBuff[0] = Arg2[3]; TimeBuff[1] = Arg2[4]; TheTime = GetStringValue(TimeBuff); if(TheTime >= 0 && TheTime <= 59) DSeg -> AlarmMinute = TheTime; else NumResult = 10; TimeBuff[0] = Arg2[6]; TimeBuff[1] = Arg2[7]; TheTime = GetStringValue(TimeBuff); if(TheTime >= 0 && TheTime <= 59) DSeg -> AlarmSecond = TheTime; else NumResult = 10; } /* Set environment variables? */ if(!RexxStrCmp(Arg1,"SETENV")) { if(!RexxStrCmp(Arg2,"OFF")) DSeg -> SetEnv = FALSE; else { if(!RexxStrCmp(Arg2,"ON")) DSeg -> SetEnv = TRUE; else NumResult = 10; } } /* Set the countdown tea timer? */ if(!RexxStrCmp(Arg1,"COUNTDOWN")) { if(GetStringValue(Arg2) > 0) DSeg -> Countdown = GetStringValue(Arg2); else NumResult = 10; } /* Refresh the display? */ if(!RexxStrCmp(Arg1,"REFRESH")) { if(CloseWorkBench()) OpenWorkBench(); } /* Turn hour alarm on? */ if(!RexxStrCmp(Arg1,"HOUR")) { if(!RexxStrCmp(Arg2,"OFF")) DSeg -> Hour = FALSE; else { if(!RexxStrCmp(Arg2,"ON")) DSeg -> Hour = TRUE; else NumResult = 10; } } /* Turn keyboard click off? */ if(!RexxStrCmp(Arg1,"SECONDS")) { if(!RexxStrCmp(Arg2,"OFF")) DSeg -> Seconds = FALSE; else { if(!RexxStrCmp(Arg2,"ON")) DSeg -> Seconds = TRUE; else NumResult = 10; } } /* Load a sound file? */ if(!RexxStrCmp(Arg1,"SOUND")) { if(!LoadChimeSound(Arg2)) NumResult = 10; } /* Get a DClock status. */ if(!RexxStrCmp(Arg1,"STATUS")) StringResult = CheckDClockStatus(Arg2); /* Show a special page? */ if(!RexxStrCmp(Arg1,"PAGE")) { if(GetStringValue(Arg2) > 4 || GetStringValue(Arg2) < 0 || (GetStringValue(Arg2) == 4 && DSeg -> Countdown < 1)) NumResult = 10; else DSeg -> Page = GetStringValue(Arg2); } /* Tell the current time? */ if(!RexxStrCmp(Arg1,"TELLTIME")) { if(!SpeechProcess) { Forbid(); if(SpeechProcess = CreateFuncProc("DClock-Speech",5,SpeechServer,4000)) Wait(SIG_SHAKE); Permit(); } if(SpeechProcess) Signal(SpeechProcess,SIG_CLICK); } /* Read the clock chip? */ if(!RexxStrCmp(Arg1,"READTIME")) { if(!ReadClock()) NumResult = 10; } /* Reply the rexx command. */ ReplyRexxCommand(RexxMsg,NumResult,0,StringResult); } } /* Remove the server task. */ if(SignalSet & SIG_CLOSE) break; } /* Delete the port. */ Quit: if(RexxTaskPort) DeletePort(RexxTaskPort); /* Hey, I'm done! */ Signal(HandlerProcess,SIG_SHAKE); } /* SpeechServer(): * * Asks the narrator device to tell the time. */ VOID SpeechServer() { ULONG SignalSet; /* Signal mask. */ struct MsgPort *NarratorPort; /* Narrator reply port. */ struct narrator_rb *NarratorRequest; /* Narrator device link. */ char WorkString[40]; /* Phoneme string. */ /* Numbers above twenty. */ static char *AboveTwenty[4] = { "TWEH4NTIY4", "THER4TIY4", "FOH4RTIY4", "FIH4FTIY4" }; /* Numbers from zero to nineteen. */ static char *SingleNumbers[20] = { "ZIY4ROW", "WAH4N", "TUW4", "THRIY4", "FOH4R", "FAY4V", "SIH4KKSZ", "SEH4VVEHN", "EY4T", "NAY4N", "TEH4N", "IY4LAEEHFAEEHN", "TWEH4LF", "THER4TIY4N", "FOH4RTIY4N", "FIH4FTIY4N", "SIH4KKSZTIY4N", "SEH4VVEHNTIY4N", "EY3TIY4N", "NAY4NTIY4N" }; /* The two halves of the day. */ static char *TimeOfDay[2] = { " EY5EH3M.", " BPIY5EH3M." }; /* Get the data base register. */ geta4(); /* Allocate speech driver data. */ if(NarratorPort = CreatePort(NULL,0)) { if(NarratorRequest = (struct narrator_rb *)CreateExtIO(NarratorPort,sizeof(struct narrator_rb))) { /* Say which channels we need. */ NarratorRequest -> ch_masks = &AnyChannel[0]; NarratorRequest -> nm_masks = 4; /* This is a write request. */ NarratorRequest -> message . io_Command = CMD_WRITE; NarratorRequest -> message . io_Data = (APTR)WorkString; /* Try to open the speech driver. */ if(!OpenDevice("narrator.device",0,NarratorRequest,0)) { /* Handshake with handler process. */ Signal(HandlerProcess,SIG_SHAKE); if(RexxProcess) Signal(RexxProcess,SIG_SHAKE); FOREVER { /* Wait for signal. */ SignalSet = Wait(SIG_CLOSE | SIG_CLICK); /* Are we to shut down? */ if(SignalSet & SIG_CLOSE) break; /* Prevent time data from being changed. */ Forbid(); strcpy(WorkString,"IH4THZ "); /* Is the hour below twelve? */ if(DSeg -> CurrentTime . Hour <= 12) strcat(WorkString,SingleNumbers[DSeg -> CurrentTime . Hour]); /* Is it above twelve? */ if(DSeg -> CurrentTime . Hour > 12) strcat(WorkString,SingleNumbers[DSeg -> CurrentTime . Hour - 12]); strcat(WorkString," EH4KLAA4K "); /* Are the minutes above zero and below twenty? */ if(DSeg -> CurrentTime . Minute < 20 && DSeg -> CurrentTime . Minute > 0) strcat(WorkString,SingleNumbers[DSeg -> CurrentTime . Minute]); /* Are the minutes above twenty? */ if(DSeg -> CurrentTime . Minute >= 20) { /* Append the tenths first. */ strcat(WorkString,AboveTwenty[DSeg -> CurrentTime . Minute / 10 - 2]); /* Add the minutes if any. */ if(DSeg -> CurrentTime . Minute % 10) strcat(WorkString,SingleNumbers[DSeg -> CurrentTime . Minute % 10]); } /* Add a.m./p.m. */ if(DSeg -> CurrentTime . Hour > 12) strcat(WorkString,TimeOfDay[1]); else { if(DSeg -> CurrentTime . Hour > 0 && DSeg -> CurrentTime . Hour < 12) strcat(WorkString,TimeOfDay[0]); } Permit(); /* Remember the length (-1 doesn't work here for some reason). */ NarratorRequest -> message . io_Length = strlen(WorkString); /* Say it and wait for it to terminate. */ SendIO(NarratorRequest); WaitIO(NarratorRequest); GetMsg(NarratorPort); } /* Deallocate our resources and exit. */ while(!CheckIO(NarratorRequest)) { AbortIO(NarratorRequest); WaitIO(NarratorRequest); GetMsg(NarratorPort); } CloseDevice(NarratorRequest); } DeleteExtIO(NarratorRequest); } DeletePort(NarratorPort); } /* Lock & quit. */ Forbid(); /* SpeechProcess = NULL;*/ Signal(HandlerProcess,SIG_SHAKE); /* The following piece of code is a kludge as any * can be. The speech server process is created * by CreateFuncProc() and runs fine but ends up * stumbling across illegal code on exit. My solution * to this problem is to save a pointer to the * current process code, clear out the pointer * for the rest of the handler and to jump into the * RemTask code. */ #asm xref _SpeechProcess xref _LVORemTask moveq #0,d0 move.l _SpeechProcess,a1 move.l d0,_SpeechProcess move.l (4).w,a6 jmp _LVORemTask(a6) #endasm } /* CreateFuncProc(): * * Similar to CreateTask this routine will start a * 'C' function as a process. The technique used * by this nifty little piece of code was originally * conceived by Leo Schwab. */ struct Process * CreateFuncProc(char *Name,LONG Priority,APTR InitCode,ULONG StackSize) { struct Process *ChildProc = NULL; struct { BPTR NextSeg; /* Pointer to next segment. */ WORD FirstCode; /* First instruction (JMP). */ APTR RealCode; /* Address of function. */ } *FakeSeg; /* Allocate the segment. */ if(FakeSeg = (struct FakeSeg *)AllocMem(sizeof(*FakeSeg),MEMF_PUBLIC | MEMF_CLEAR)) { struct MsgPort *ChildPort; /* Fill in the data. */ FakeSeg -> FirstCode = 0x4EF9; /* JMP EA */ FakeSeg -> RealCode = InitCode; /* EA */ /* Create the process. */ if(ChildPort = (struct MsgPort *)CreateProc(Name,Priority,MKBADDR(FakeSeg),StackSize)) ChildProc = (struct Process *)ChildPort -> mp_SigTask; Delay(TICKS_PER_SECOND); /* Free the segment. */ FreeMem(FakeSeg,sizeof(*FakeSeg)); } /* Return the pointer to the process. */ return(ChildProc); } /* CreateDummyRPort(): * * Creates a hidden RastPort for time/graphics * rendering. */ BYTE CreateDummyRPort() { struct TextAttr FontRequest; SHORT i; /* Allocate the BitMap. */ if(!(DummyMap = (struct DummyMap *)AllocMem(sizeof(struct BitMap),MEMF_PUBLIC | MEMF_CLEAR))) return(FALSE); /* How many bitplanes are there in the display? */ DummyDepth = Window -> RPort -> BitMap -> Depth; /* Initialize the bitmap pointers. */ InitBitMap(DummyMap,DummyDepth,Width,Height); /* Allocate the memory. */ for(i = 0 ; i < DummyDepth ; i++) { if(!(DummyMap -> Planes[i] = AllocMem(Byte(Width) * Height,MEMF_CHIP))) return(FALSE); } /* Mangle the RastPort. */ if(!(DummyRPort = (struct RastPort *)AllocMem(sizeof(struct RastPort),MEMF_PUBLIC | MEMF_CLEAR))) return(FALSE); /* Initialize the RastPort. */ InitRastPort(DummyRPort); /* Link it to the BitMap. */ DummyRPort -> BitMap = DummyMap; /* Clear the drawing area. */ SetRast(DummyRPort,1); /* Copy the font attributes. */ FontRequest . ta_Name = (STRPTR)Window -> IFont -> tf_Message . mn_Node . ln_Name; FontRequest . ta_YSize = Window -> IFont -> tf_YSize; FontRequest . ta_Style = Window -> IFont -> tf_Style; FontRequest . ta_Flags = Window -> IFont -> tf_Flags; /* Can we open the font (is it already in the system * list)? */ if(!(DummyFont = (struct TextFont *)OpenFont(&FontRequest))) { /* It is probably a diskfont. */ if(DiskfontBase = (struct Library *)OpenLibrary("diskfont.library",0)) { if(!(DummyFont = (struct TextFont *)OpenDiskFont(&FontRequest))) return(FALSE); else CloseLibrary(DiskfontBase); } } /* Attach the font to the RastPort. */ SetFont(DummyRPort,DummyFont); /* And set the drawing mode. */ SetDrMd(DummyRPort,JAM2); return(TRUE); } /* DeleteDummyRPort(): * * Removes the hidden RastPort from memory and get rid * of the font attached to it. */ VOID DeleteDummyRPort() { SHORT i; if(DummyMap) { for(i = 0 ; i < DummyDepth ; i++) if(DummyMap -> Planes[i]) FreeMem(DummyMap -> Planes[i],Byte(Width) * Height); FreeMem(DummyMap,sizeof(struct BitMap)); DummyMap = NULL; } if(DummyRPort) { FreeMem(DummyRPort,sizeof(struct RastPort)); DummyRPort = NULL; } if(DummyFont) { CloseFont(DummyFont); DummyFont = NULL; } } /* MaxFontWidth(): * * Calculate the maximum width of the display by looking for * the widest letter. Note that currently only fixed width * fonts are allowed to be used as the system font. */ UBYTE MaxFontWidth() { BYTE FontWidth = 0,TempWidth; char Shuttle[2]; SHORT i; /* We cannot use a single character to check the width * of a letter, so we'll fake a string. */ Shuttle[1] = 0; /* Check all letters. */ for(i = 0 ; i < 256 ; i++) { Shuttle[0] = i; /* Is it wider than the last letter? */ if((TempWidth = TextLength(Window -> RPort,Shuttle,1)) > FontWidth) FontWidth = TempWidth; } return(FontWidth); } /* ShutDown(HandShake): * * Closes DClock and waits for removal. */ VOID ShutDown(LONG HandShake) { /* Restore system functions. */ if(OldDisplayBeep) SetFunction((struct Library *)IntuitionBase,-0x60,OldDisplayBeep); if(OldCloseWBench) SetFunction((struct Library *)IntuitionBase,-0x4E,OldCloseWBench); FlushHandler(); FlushSound(); if(Window) { SetRast(DummyRPort,1); ClipBlit(DummyRPort,0,0,Window -> WScreen -> BarLayer -> rp,LeftEdge,1,Width,Height,0xC0); CloseWindow(Window); } DeleteDummyRPort(); /* Return the special signals. */ if(BenchSig != -1) FreeSignal(BenchSig); if(DisplaySig != 1) FreeSignal(DisplaySig); if(SpeechSig != 1) FreeSignal(SpeechSig); /* Free the Rexx host port. */ if(DSeg -> RexxHost) DeleteRexxHost(DSeg -> RexxHost); if(RexxProcess) { Signal(RexxProcess,SIG_CLOSE); Wait(SIG_SHAKE); } if(SpeechProcess) { Signal(SpeechProcess,SIG_CLOSE); Wait(SIG_SHAKE); } if(ArpBase) CloseLibrary(ArpBase); /* Sneak out before someone can * UnLoadSeg() us. */ Forbid(); /* Goodbye father. */ if(DSeg -> Father) Signal(DSeg -> Father,HandShake); } /* _main(): * * Modified Aztec C startup-routine, no CLI parsing, * no Workbench parsing, absolutely nothing. */ LONG _main() { volatile ULONG MemSize,MaxSize; ULONG SignalSet; UBYTE Force,Blink = FALSE,i,EmptyWidth,FullWidth; LONG RexxMask = 0; struct Screen *Workbench; HandlerProcess = (struct Process *)SysBase -> ThisTask; /* If somebody called us from CLI... */ if(HandlerProcess -> pr_CLI) return(-1); /* Is the DSeg structure anywhere? */ DSeg = (struct DSeg *)FindPort(PORTNAME); /* Sneak out if handler was accidentally started from * Workbench. */ if(!DSeg || !DSeg -> Father) { struct WBStartup *WBenchMsg; WaitPort(&HandlerProcess -> pr_MsgPort); WBenchMsg = (struct WBStartup *)GetMsg(&HandlerProcess -> pr_MsgPort); if(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0)) { DisplayBeep(NULL); CloseLibrary(IntuitionBase); } Forbid(); ReplyMsg(WBenchMsg); return; } /* Check if it's below our current revision * number (probably doesn't support some * structure tags). */ if(DSeg -> Revision < REVISION) { Forbid(); Signal(DSeg -> Father,DSeg -> RingBack); return; } if(!(ArpBase = (struct ArpBase *)OpenLibrary(ArpName,ArpVersion))) { Forbid(); Signal(DSeg -> Father,DSeg -> RingBack); return; } IntuitionBase = ArpBase -> IntuiBase; GfxBase = ArpBase -> GfxBase; /* Open RexxHostLibrary if possible. If the open * fails, it doesn't matter. */ RexxHostBase = ArpOpenLibrary("rexxhost.library",34); /* Try to find the Workbench screen. */ if(!(Workbench = (struct Screen *)FindTheBench())) { ShutDown(DSeg -> RingBack); return; } /* Open the backdrop window. */ if(!(Window = (struct Window *)OpenWindow(&NewWindow))) { ShutDown(DSeg -> RingBack); return; } /* Initialize the click sound data. */ if(!InitSound()) { ShutDown(DSeg -> RingBack); return; } /* Install the handler code. */ if(!InitHandler()) { ShutDown(DSeg -> RingBack); return; } if((BenchSig = AllocSignal(-1)) == -1) { ShutDown(DSeg -> RingBack); return; } if((DisplaySig = AllocSignal(-1)) == -1) { ShutDown(DSeg -> RingBack); return; } if((SpeechSig = AllocSignal(-1)) == -1) { ShutDown(DSeg -> RingBack); return; } /* Adjust the window left offset. */ Width = MaxFontWidth() * 22; Height = Window -> IFont -> tf_YSize; LeftEdge = Workbench -> Width - 55 - Width; EmptyWidth = TextLength(Window -> RPort,"E",1); FullWidth = TextLength(Window -> RPort,"F",1); /* Create the dummy RastPort. */ if(!CreateDummyRPort()) { ShutDown(DSeg -> RingBack); return; } /* Check if we are running unter Kickstart 2.x. */ if(SysBase -> LibNode . lib_Version >= 36) { USHORT TempCol; NewKick = TRUE; for(i = 0 ; i < 228 ; i++) { TempCol = ClockMap[i]; ClockMap[i] = ClockMap[i + 228]; ClockMap[i + 228] = TempCol; } for(i = 0 ; i < 3 ; i++) ClockTxt[i] . FrontPen = 1; LeftEdge = Workbench -> Width - 25 - Width; } /* Create the rexx server task. */ if(RexxHostBase) { if(!(RexxProcess = (struct Process *)CreateFuncProc("DClock-Rexx",5,RexxServer,4000))) { Shut: ShutDown(DSeg -> RingBack); return; } /* Wait for handshake. */ Wait(SIG_SHAKE); /* No port? Fail fast! */ if(!RexxTaskPort) goto Shut; } /* Fill in the window and bring it to the front. */ ShowTime(FALSE,TRUE); /* Patch system function. */ OldDisplayBeep = SetFunction((struct Library *)IntuitionBase,-0x60,NewDisplayBeep); OldCloseWBench = SetFunction((struct Library *)IntuitionBase,-0x4E,NewCloseWBench); /* If RexxHostBase is around, add the rexx port. */ if(RexxHostBase) { if(DSeg -> RexxHost = CreateRexxHost("DCLOCK")) RexxMask = (1 << DSeg -> RexxHost -> rh_Port . mp_SigBit); } /* Initialize the signal-semaphore. */ InitSemaphore(DSeg -> SoundSemaphore); /* Now we are truly running. */ DSeg -> Child = SysBase -> ThisTask; /* How much memory is there around in this * Amiga? */ MaxSize = MaxMemSize(MEMF_CHIP) + MaxMemSize(MEMF_FAST); /* Tell father to finish. */ Signal(DSeg -> Father,DSeg -> RingBack); DSeg -> Father = NULL; /* Go into infinite loop waiting for signals. */ FOREVER { SignalSet = Wait(SIG_TIMER | SIG_CLOSE | SIG_CLICK | SIG_TOGGL | SIG_BENCH | SIG_WINDO | SIG_DISPL | SIG_SPEECH | RexxMask); /* Was it a Rexx call? */ if((SignalSet & RexxMask) && RexxMask) { struct RexxMessage *RexxMsg; /* Capture the messages... */ while(RexxMsg = GetMsg((struct MsgPort *)DSeg -> RexxHost)) { /* Send them to the rexx server task. */ if(GetRexxCommand(RexxMsg)) PutMsg(RexxTaskPort,RexxMsg); else FreeRexxCommand(RexxMsg); } } /* Change the display mode? */ if(SignalSet & SIG_TOGGL) { DSeg -> Page++; SetRast(DummyRPort,1); if(DSeg -> Page == 5) DSeg -> Page = 0; if(DSeg -> Countdown < 1 && DSeg -> Page == 4) DSeg -> Page = 0; Force = TRUE; } /* Are we to click? */ if((SignalSet & SIG_CLICK) && DSeg -> Click) Click(); /* Are we to shut down? */ if(SignalSet & SIG_CLOSE) { if(ChimeAudioBlock) StopChime(); ShutDown(SIG_CLOSE); return; } if(SignalSet & SIG_BENCH) { /* Close the window... */ if(Window) { CloseWindow(Window); Window = NULL; DeleteDummyRPort(); Delay(25); FOREVER { /* Wait for wakeup call... */ if((SetSignal(NULL,NULL) & SIG_CLOSE) == SIG_CLOSE) { SetSignal(NULL,SIG_CLOSE); SignalSet = SIG_CLOSE; break; } if(Workbench = (struct Screen *)FindTheBench()) { SignalSet = NULL; break; } Delay(25); } } /* Finish? */ if(SignalSet & SIG_CLOSE) { if(ChimeAudioBlock) StopChime(); ShutDown(SIG_CLOSE); return; } /* Open the window. */ if(!(Window = (struct Window *)OpenWindow(&NewWindow))) { BeatIt: if(ChimeAudioBlock) StopChime(); ShutDown(SIG_CLOSE); return; } Width = MaxFontWidth() * 22; Height = Window -> IFont -> tf_YSize; /* Re-adjust the Window offset. */ if(NewKick) LeftEdge = Workbench -> Width - 25 - Width; else LeftEdge = Workbench -> Width - 55 - Width; EmptyWidth = TextLength(Window -> RPort,"E",1); FullWidth = TextLength(Window -> RPort,"F",1); /* Redraw the time if necessary. */ Printed = FALSE; if(!CreateDummyRPort()) goto BeatIt; } /* A window refresh call came in. */ if(SignalSet & SIG_WINDO) { struct IntuiMessage *IMsg; /* Reply the Message (don't need it). */ if(IMsg = GetMsg(Window -> UserPort)) ReplyMsg(IMsg); } /* Give it a brief beep to indicate the hour? */ if(DSeg -> Hour && !DSeg -> CurrentTime . Minute && !DSeg -> CurrentTime . Second) PlayChime(); /* Check if chime has ended. */ if(ChimeAudioBlock) { if(CheckIO(ChimeAudioBlock)) StopChime(); } /* Ready to ring the bell? */ if(DSeg -> CurrentTime . Hour == DSeg -> AlarmHour && DSeg -> CurrentTime . Minute == DSeg -> AlarmMinute && DSeg -> CurrentTime . Second == DSeg -> AlarmSecond && DSeg -> Alarm) Ring(TRUE); /* If needed, decrement the tea timer. */ if(DSeg -> Countdown > 0) { if(!(--DSeg -> Countdown)) Ring(FALSE); } /* Check the display signal before we * redraw it. */ if(SignalSet & SIG_DISPL) { DSeg -> Seconds ^= TRUE; Force = TRUE; } /* We are to tell the time. */ if(SignalSet & SIG_SPEECH) { /* Remove speech process if speech * has been turned off after it has * been run once. */ if(!DSeg -> Speech) { if(SpeechProcess) { Signal(SpeechProcess,SIG_CLOSE); Wait(SIG_SHAKE); } } else { /* Try to create the speech process. */ if(!SpeechProcess) { Forbid(); if(SpeechProcess = CreateFuncProc("DClock-Speech",5,SpeechServer,4000)) Wait(SIG_SHAKE); Permit(); } /* Tell the time. */ if(SpeechProcess) Signal(SpeechProcess,SIG_CLICK); } } /* Show time and date. */ if(DSeg -> Page == 0) ShowTime(TRUE,Force); else ShowTime(FALSE,Force); Force = FALSE; /* Show memory display. */ if(DSeg -> Page == 1) { LONG BarLength; /* For single bitplane Workbench * screen -> use fill pattern. */ static USHORT Checkers[8]= { 0xAAAA,0x5555, 0xAAAA,0x5555, 0xAAAA,0x5555, 0xAAAA,0x5555 }; /* How much memory is still available? */ MemSize = AvailMem(MEMF_CHIP) + AvailMem(MEMF_FAST); /* How LONG will the bar be? */ BarLength = ((Width - (EmptyWidth + FullWidth)) * MemSize) / MaxSize; if(NewKick) { SetAPen(DummyRPort,2); SetBPen(DummyRPort,3); } else { SetAPen(DummyRPort,1); SetBPen(DummyRPort,2); } Move(DummyRPort,0,Window -> IFont -> tf_Baseline); Text(DummyRPort,"E",1); Move(DummyRPort,Width - FullWidth,Window -> IFont -> tf_Baseline); Text(DummyRPort,"F",1); /* Draw the full part. */ if(Window -> RPort -> BitMap -> Depth < 2) SetAfPt(DummyRPort,&Checkers[0],1); if(NewKick) SetAPen(DummyRPort,2); else SetAPen(DummyRPort,3); RectFill(DummyRPort,EmptyWidth,0,Width - FullWidth - BarLength,Height - 1); if(Window -> RPort -> BitMap -> Depth < 2) SetAfPt(DummyRPort,NULL,0); /* Add the empty part. */ if(NewKick) SetAPen(DummyRPort,3); else SetAPen(DummyRPort,2); RectFill(DummyRPort,Width - FullWidth - BarLength,0,Width - (FullWidth + 1),Height - 1); } /* Numeric memory display. */ if(DSeg -> Page == 2) { UBYTE TempBuff[30]; SPrintf(TempBuff,"C: %07ld F: %07ld",AvailMem(MEMF_CHIP),AvailMem(MEMF_FAST)); PrintIt(TempBuff); } /* Show online timer? */ if(DSeg -> Page == 3) { UBYTE TempBuff[50]; SPrintf(TempBuff,"OFFLINE %02ld:%02ld:%02ld",OnlineHours,OnlineMinutes,OnlineSeconds); if(DSeg -> Online) { if(Blink ^= TRUE) { TempBuff[0] = ' '; TempBuff[1] = 'O'; TempBuff[2] = 'N'; } else { TempBuff[0] = ' '; TempBuff[1] = ' '; TempBuff[2] = ' '; } } PrintIt(TempBuff); } /* Show the countdown. */ if(DSeg -> Page == 4) { UBYTE TempBuff[50]; if(DSeg -> Countdown > 0) { SPrintf(TempBuff,"Countdown %ld ",-DSeg -> Countdown); TempBuff[22] = 0; PrintIt(TempBuff); } else DSeg -> Page = 0; } /* Transfer the image portion. */ ClipBlit(DummyRPort,0,0,Window -> WScreen -> BarLayer -> rp,LeftEdge,1,Width,Height,0xC0); } }