/******************************************************************************\ * KeyBang * * ------- * * by Mike Stark * * Revisions: * * 1.0 30 Mar 92 Added patterns, removed floating point math, added intro * * window, improved input handler and added command options. * * 0.9 23 Feb 92 Added the input handler so that keybanging would get the key * * events before intuition. * * 0.8 Made PAL/NTSC independent. * * 0.7 Added the ability to play random sounds. * * 0.? Original version (Shapes in response to intuition events) * * Credits: * * Michael Balzer (balzer@heike.informatik.uni-dortmund.de), the author of * * "Bomber" for a C example of how to use the audio device. * * Carolyn Scheppner of CATS for "OneKey" which is a concise example of how * * to install and use an input handler. * \******************************************************************************/ #include "KeyBang.h" #include "table.h" char *version = "\0$VER: KeyBang 1.0 by Mike Stark (March 30, 1992)"; #define VECTORS 30 #define CLOCK 3579545 #define FORM 0x464f524d #define BODY 0x424f4459 #define VHDR 0x56484452 #define EIGHT_SVX 0x38535658 #define SOUNDDIRNAME "Sounds:" extern void BabyInputHandler(); SHORT ColorTable[16] = { 0x0000, 0x0eca, 0x0c00, 0x0f60, 0x0090, 0x03f1, 0x000f, 0x02cd, 0x0f0c, 0x0a0f, 0x0950, 0x0fe0, 0x0fff, 0x0ccc, 0x0888, 0x0444 }; #define PATTERNS 6 USHORT PatternData[] = { /* Solid */ 0xffff, /* Brick */ 0x0101, 0x0101, 0x0101, 0xff01, 0x0101, 0x0101, 0x0101, 0x01ff, /* Grid */ 0xffff, 0x0303, 0x0303, 0x0303, /* Diagonal lines */ 0xf0f0, 0x3c3c, 0x0f0f, 0xc3c3, /* Other diagonal lines */ 0x0f0f, 0x3c3c, 0xf0f0, 0xc3c3, /* Other brick pattern */ 0x0300, 0x0300, 0x0300, 0xffff, 0x0003, 0x0003, 0x0003, 0xffff }; struct PatternInfo { int Power; USHORT *Data; } Patterns[PATTERNS] = { {0, &PatternData[0]}, {3, &PatternData[1]}, {2, &PatternData[9]}, {2, &PatternData[13]}, {2, &PatternData[17]}, {3, &PatternData[21]} }; #define MESSAGELINES 8 char *Message[] = { "KeyBang 1.0", "by Mike Stark", "March 30, 1992", "", "--> Alt-Alt-F5 exits <--", "", "This program is shareware.", "Please read KeyBang.doc." }; char *ScreenTitle = "KeyBang"; struct IntuitionBase *IntuitionBase = NULL; struct GfxBase *GfxBase = NULL; struct Screen *BabyScreen = NULL; struct Window *BabyWindow = NULL, *IntroWindow = NULL; struct Remember *MemList = NULL; BYTE *BabyTmpRaster = NULL; APTR OldWindow; struct IOAudio *AudioMsg = NULL; struct MsgPort *IOReplyPort = NULL; struct MsgPort *KeyPort = NULL; struct IOStdReq *InputRequest = NULL; struct Interrupt HandlerInfo; struct SoundInfo **Sound; int Depth = 4; int Sounds = 0; int HandlerData[2]; int LeftSoundPlaying = FALSE, RightSoundPlaying = FALSE; int WinX = 640, WinY = 200, XDPM, YDPM; int Unit[2]; BOOL Audio = FALSE, OneShape = FALSE; main(argc, argv) int argc; char **argv; { struct MyInputEvent *Message; struct IntuiMessage *IMessage; struct IOAudio *SoundMsg; int Type, XSize, YSize, X, Y, Signal, Sample, SampleUnit; BOOL Done, DoAction, Mouse; int Count = 0, Seed, Pattern, Colors; GetOptions(argc, argv); Initialize(); Colors = 1 << Depth; Signal = 1 << KeyPort->mp_SigBit; Signal |= 1 << IOReplyPort->mp_SigBit; Signal |= 1 << BabyWindow->UserPort->mp_SigBit; Done = FALSE; while (!Done) { DoAction = FALSE; Wait(Signal); if (Audio) while (SoundMsg = (struct IOAudio *)GetMsg(IOReplyPort)) { SampleUnit = (int)SoundMsg->ioa_Request.io_Unit; if (SampleUnit & 9) RightSoundPlaying = FALSE; if (SampleUnit & 6) LeftSoundPlaying = FALSE; FreeMem(SoundMsg, sizeof(struct IOAudio)); } while (Message = (struct MyInputEvent *)GetMsg(KeyPort)) { if (Message->IE.ie_Class == IECLASS_RAWKEY) { if (!((Message->IE.ie_Code & IECODE_UP_PREFIX) || (Message->IE.ie_Qualifier & IEQUALIFIER_REPEAT))) { DoAction = TRUE; Mouse = FALSE; } if (Message->IE.ie_Code == 0x45) { Erase(BabyWindow->RPort); } if ((Message->IE.ie_Code == 0x54) && ((Message->IE.ie_Qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT)) == (IEQUALIFIER_LALT | IEQUALIFIER_RALT))) { DoAction = FALSE; Done = TRUE; } } FreeMem(Message, sizeof(struct MyInputEvent)); } while (IMessage = (struct IntuiMessage *)GetMsg(BabyWindow->UserPort)) { if (!(IMessage->Code & IECODE_UP_PREFIX)) { Mouse = TRUE; X = IMessage->MouseX; Y = IMessage->MouseY; YSize = Random(0) / 505 + 10; XSize = 2 * YSize * XDPM / YDPM; if (X < XSize) X = XSize; if (Y < YSize) Y = YSize; if (X > WinX - XSize) X = WinX - XSize; if (Y > WinY - YSize) Y = WinY - YSize; DoAction = TRUE; } ReplyMsg(IMessage); } if (DoAction) { if (IntroWindow) { CloseWindow(IntroWindow); IntroWindow= NULL; (void)Random(Seed); SetDrMd(BabyWindow->RPort, JAM2); } if (OneShape) Erase(BabyWindow->RPort); Sample = Random(0) * Sounds / 32768; if (Audio) MakeSound(Sample); if (!Mouse) { YSize = Random(0) / 505 + 10; XSize = 2 * YSize * XDPM / YDPM; Y = Random(0) * (WinY - 2 * YSize) / 32768 + YSize; X = Random(0) * (WinX - 2 * XSize) / 32768 + XSize; } Pattern = Random(0) * PATTERNS / 32768; SetAfPt(BabyWindow->RPort, Patterns[Pattern].Data, Patterns[Pattern].Power); SetOPen(BabyWindow->RPort, Random(0) * (Colors - 1) / 32768 + 1); SetAPen(BabyWindow->RPort, Random(0) * (Colors - 1) / 32768 + 1); SetBPen(BabyWindow->RPort, Random(0) * (Colors - 1) / 32768 + 1); Type = Random(0) * 5 / 32768; switch (Type) { case 0: Circle(BabyWindow->RPort, YSize, X, Y); break; default: Polygon(BabyWindow->RPort, Type + 2, YSize, X, Y); break; } } } CleanUp(FALSE); } Erase(RPort) struct RastPort *RPort; /********************\ * Erase * * ----- * * Clear the KeyBang window. I know that there are more efficient ways of * doing this but this isn't too bad and that's how I coded it. \********************/ { Move(RPort, 0, 0); SetDrMd(RPort, JAM1); SetAfPt(RPort, Patterns[0].Data, Patterns[0].Power); SetOPen(RPort, 0); SetAPen(RPort, 0); ClearScreen(RPort); SetDrMd(RPort, JAM2); } Polygon(RPort, Vertices, Size, X, Y) struct RastPort *RPort; int Size, X, Y; /********************\ * Polygon * * ------- * * Draw a polygon with "Vertices" vertices and "Size" pixels high centered * at "X", "Y". Polygons are corrected for the aspect ratio of the screen. The * AreaInfo and TmpRas structures of the window into which the polygon is to * be drawn must be properly initialized. See Initialize. \********************/ { int Phase, XPos, YPos, i; Phase = Random(0) * SINETABENTRIES / 32768; XPos = Size * SineTab[Phase] / 128; YPos = Size * SineTab[(Phase + SINETABENTRIES / 4) % SINETABENTRIES] / 128; AreaMove(RPort, X + 2 * XPos * XDPM / YDPM, Y + YPos); for (i = 1; i < Vertices; i++) { XPos = Size * SineTab[(i * SINETABENTRIES / Vertices + Phase) % SINETABENTRIES] / 128; YPos = Size * SineTab[(i * SINETABENTRIES / Vertices + Phase + SINETABENTRIES / 4) % SINETABENTRIES] / 128; AreaDraw(RPort, X + 2 * XDPM * XPos / YDPM, Y + YPos); } AreaEnd(RPort); } int Circle(RPort, Size, X, Y) struct RastPort *RPort; int Size, X, Y; /********************\ * Circle * * ------ * * Draw a circle "Size" pixels high centered at "X", "Y". Circles are * corrected for the aspect ratio of the screen. The AreaInfo and TmpRas * structures of the window into which the circle is to be drawn must be * properly initialized. See Initialize. \********************/ { AreaEllipse(RPort, X, Y, 2 * Size * XDPM / YDPM, Size); AreaEnd(RPort); } Initialize() /********************\ * Initialize * * ---------- * * Allocates all of the resources that are used by the program. If some * of the resources are not available, this routine does not return. \********************/ { struct NewWindow NewWindow; struct NewScreen NewScreen; SHORT *VectorTable; static UBYTE AudioChannels[] = {3, 5, 10, 12}; extern struct Library *SysBase; int i, Width, Length; struct Process *ThisProcess; /* Open libraries */ IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 29); if (IntuitionBase == NULL) CleanUp(FALSE); GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 29); if (GfxBase == NULL) CleanUp(FALSE); /* Get display size and aspect ratio from GfxBase */ XDPM = GfxBase->NormalDPMX; YDPM = GfxBase->NormalDPMY; WinX = GfxBase->NormalDisplayColumns; WinY = GfxBase->NormalDisplayRows; /* Open the screen */ NewScreen.LeftEdge = 0; NewScreen.TopEdge = 0; NewScreen.Width = WinX; NewScreen.Height = WinY; NewScreen.Depth = Depth; NewScreen.DetailPen = 1; NewScreen.BlockPen = 1; NewScreen.ViewModes = HIRES; NewScreen.Type = CUSTOMSCREEN; NewScreen.Font = NULL; NewScreen.DefaultTitle = (UBYTE *)ScreenTitle; NewScreen.Gadgets = NULL; NewScreen.CustomBitMap = NULL; if ((BabyScreen = (struct Screen *)OpenScreen(&NewScreen)) == NULL) CleanUp(FALSE); /* Open the window */ NewWindow.LeftEdge = 0; NewWindow.TopEdge = 0; NewWindow.Width = WinX; NewWindow.Height = WinY; NewWindow.DetailPen = 1; NewWindow.BlockPen = 1; NewWindow.IDCMPFlags = MOUSEBUTTONS; NewWindow.Flags = SIMPLE_REFRESH | BORDERLESS | ACTIVATE | BACKDROP | RMBTRAP; NewWindow.FirstGadget = NULL; NewWindow.CheckMark = NULL; NewWindow.Title = NULL; NewWindow.Screen = BabyScreen; NewWindow.BitMap = NULL; NewWindow.MinWidth = WinX; NewWindow.MinHeight = WinY; NewWindow.MaxWidth = WinX; NewWindow.MaxHeight = WinY; NewWindow.Type = CUSTOMSCREEN; LoadRGB4(&BabyScreen->ViewPort, &ColorTable[0], 1 << Depth); if ((BabyWindow = (struct Window *)OpenWindow(&NewWindow)) == NULL) CleanUp(FALSE); /* Make system requexters come up on this screen */ ThisProcess = (struct Process *)FindTask(0); OldWindow = ThisProcess->pr_WindowPtr; ThisProcess->pr_WindowPtr = (APTR)BabyWindow; /* Hide the screen title bar */ ShowTitle(BabyScreen, FALSE); /* Open the message window */ Width = 0; for (i = 0; i < MESSAGELINES; i++) { Length = TextLength(BabyWindow->RPort, Message[i], strlen(Message[i])); if (Length + 16 > Width) Width = Length + 16; } NewWindow.LeftEdge = WinX / 2 - Width / 2; NewWindow.TopEdge = 30; NewWindow.Width = Width; NewWindow.Height = 100; NewWindow.IDCMPFlags = NULL; NewWindow.Flags = SIMPLE_REFRESH; NewWindow.Title = (UBYTE *)ScreenTitle; NewWindow.MinWidth = Width; NewWindow.MinHeight = 100; NewWindow.MaxWidth = Width; NewWindow.MaxHeight = 100; if (IntroWindow = (struct Window *)OpenWindow(&NewWindow)) { SetAPen(IntroWindow->RPort, 1); for (i = 0; i < MESSAGELINES; i++) { Length = TextLength(IntroWindow->RPort, Message[i], strlen(Message[i])); Move(IntroWindow->RPort, Width / 2 - Length / 2, 20 + 10 * i); Text(IntroWindow->RPort, Message[i], strlen(Message[i])); } } /* Open the AreaInfo and TmpRas structures used by the drawing routines */ BabyWindow->RPort->TmpRas = (struct TmpRas *)AllocRemember(&MemList, sizeof(struct TmpRas), NULL); if (!BabyWindow->RPort->TmpRas) CleanUp(FALSE); if (!(BabyTmpRaster = AllocRaster(WinX, WinY))) CleanUp(FALSE); InitTmpRas(BabyWindow->RPort->TmpRas, BabyTmpRaster, RASSIZE(WinX, WinY)); BabyWindow->RPort->AreaInfo = (struct AreaInfo *)AllocRemember(&MemList, sizeof(struct AreaInfo), NULL); if (!BabyWindow->RPort->AreaInfo) CleanUp(FALSE); if ((VectorTable = (SHORT *)AllocRemember(&MemList, VECTORS * 5, NULL))== NULL) CleanUp(FALSE); InitArea(BabyWindow->RPort->AreaInfo, VectorTable, VECTORS); /* Make the IO reply port for audio and input IO */ if ((IOReplyPort = CreatePort("KeyBangIO", 0)) == NULL) CleanUp(FALSE); /* Make the port to receive messages from the input handler */ if ((KeyPort = CreatePort("KeyBangKeys", 0)) == NULL) CleanUp(FALSE); /* Open the audio device */ LoadSounds(); if (Sounds) { AudioMsg = (struct IOAudio *)AllocRemember(&MemList, sizeof(struct IOAudio), MEMF_PUBLIC | MEMF_CLEAR); if (AudioMsg == NULL) CleanUp(FALSE); AudioMsg->ioa_Request.io_Message.mn_ReplyPort = IOReplyPort; AudioMsg->ioa_Request.io_Command = ADCMD_ALLOCATE; AudioMsg->ioa_Request.io_Flags = ADIOF_NOWAIT; AudioMsg->ioa_Data = AudioChannels; AudioMsg->ioa_Length = sizeof(AudioChannels); if (OpenDevice(AUDIONAME, 0 , AudioMsg, 0) == 0) { Audio = TRUE; Unit[0] = (int)AudioMsg->ioa_Request.io_Unit & 9; Unit[1] = (int)AudioMsg->ioa_Request.io_Unit & 6; } } /* Open the input device and install the input handler */ if (!(InputRequest = CreateStdIO(IOReplyPort))) CleanUp(FALSE); if (OpenDevice("input.device", 0, InputRequest, 0)) CleanUp(FALSE); HandlerData[0] = (int)KeyPort; HandlerData[1] = (int)SysBase; HandlerInfo.is_Data = (APTR)&HandlerData[0]; HandlerInfo.is_Code = BabyInputHandler; HandlerInfo.is_Node.ln_Name = ScreenTitle; HandlerInfo.is_Node.ln_Pri = 75; InputRequest->io_Command = IND_ADDHANDLER; InputRequest->io_Data = (APTR)&HandlerInfo; DoIO(InputRequest); } LoadSounds() /********************\ * LoadSounds * * ---------- * * Look into the sound directory and examine all the files. Those which * contain IFF 8SVX samples are loaded into an array so that they can be * played by MakeSound. \********************/ { int SoundLength; struct FileLock *SoundLock; struct FileInfoBlock *FileInfoBlock; struct FileHandle *File; UBYTE *Buffer; char FileName[128]; struct IFFHeader Header; struct SampleInfo SampleInfo; Sound = (struct SoundInfo **)AllocRemember(&MemList, 100 * 4, NULL); SoundLock = Lock(SOUNDDIRNAME, ACCESS_READ); if (!SoundLock) return; FileInfoBlock = (struct FileInfoBlock *)AllocMem(sizeof(struct FileInfoBlock), NULL); Examine(SoundLock, FileInfoBlock); while (ExNext(SoundLock, FileInfoBlock)) { strcpy(FileName, SOUNDDIRNAME); strcat(FileName, FileInfoBlock->fib_FileName); File = Open(FileName, MODE_OLDFILE); if (File) { Read(File, &Header, 8); if (Header.Name == FORM) { Read(File, &Header, 4); if (Header.Name == EIGHT_SVX) { while (Read(File, &Header, 8) > 0) { switch (Header.Name) { case VHDR: Read(File, &SampleInfo, Header.Length); break; case BODY: Buffer = (UBYTE *)AllocRemember(&MemList, Header.Length, MEMF_CHIP | MEMF_PUBLIC); if (!Buffer) return; Read(File, Buffer, Header.Length); SoundLength = Header.Length; break; default: Seek(File, Header.Length, OFFSET_CURRENT); break; } } Sound[Sounds] =(struct SoundInfo *)AllocRemember(&MemList, sizeof(struct SoundInfo), NULL); if (!Sound[Sounds]) CleanUp(FALSE); Sound[Sounds]->Buffer = Buffer; Sound[Sounds]->SamplePeriod = CLOCK / SampleInfo.SampleRate; Sound[Sounds]->Volume = SampleInfo.Volume / 1024; Sound[Sounds]->Length = SoundLength; Sounds++; } } } Close(File); } UnLock(SoundLock); FreeMem(FileInfoBlock, sizeof(struct FileInfoBlock)); } MakeSound(Sample) int Sample; /********************\ * MakeSound * * --------- * * Play the sound "Sample". "Sample" is an index into the array of * sound samples loaded by LoadSounds. If one of the two channels opened by * KeyBang are busy, the sound is played in the other. If both are busy, * no sound is played. \********************/ { BYTE *SoundData; struct IOAudio *SoundMsg; int i; static int UnitNum; if (LeftSoundPlaying) { if (RightSoundPlaying) return; else UnitNum = 0; } else { if (RightSoundPlaying) UnitNum = 1; else UnitNum = UnitNum - 2 * UnitNum + 1; } if (SoundMsg = (struct IOAudio *)AllocMem(sizeof(struct IOAudio), MEMF_PUBLIC)) { SoundMsg->ioa_Request.io_Message.mn_ReplyPort = IOReplyPort; SoundMsg->ioa_Request.io_Device = AudioMsg->ioa_Request.io_Device; SoundMsg->ioa_Request.io_Unit = (struct Unit *)Unit[UnitNum]; SoundMsg->ioa_Request.io_Command = CMD_WRITE; SoundMsg->ioa_Request.io_Flags = ADIOF_PERVOL; SoundMsg->ioa_AllocKey = AudioMsg->ioa_AllocKey; SoundMsg->ioa_Length = Sound[Sample]->Length; SoundMsg->ioa_Period = Sound[Sample]->SamplePeriod; SoundMsg->ioa_Volume = 64; SoundMsg->ioa_Cycles = 1; SoundMsg->ioa_Data = Sound[Sample]->Buffer; BeginIO(SoundMsg); if (Unit[UnitNum] & 9) RightSoundPlaying = TRUE; if (Unit[UnitNum] & 6) LeftSoundPlaying = TRUE; } } CleanUp(Flag) BOOL Flag; /********************\ * CleanUp * * ------- * * Deallocate all resources. I have tried to reply to all messages * which may be waiting on ports before deleting them. \********************/ { struct Message *TempPointer; struct Process *ThisProcess; if (Flag) puts("Usage : KeyBang [-colors {2|4|8|16}] [-oneshape]"); if (InputRequest) { InputRequest->io_Command = IND_REMHANDLER; InputRequest->io_Data = (APTR)&HandlerInfo; DoIO(InputRequest); CloseDevice(InputRequest); DeleteStdIO(InputRequest); } if (Audio) CloseDevice(AudioMsg); if (IOReplyPort) DeletePort(IOReplyPort); if (KeyPort) { while (TempPointer = GetMsg(KeyPort)) FreeMem(TempPointer, sizeof(struct MyInputEvent)); DeletePort(KeyPort); } if (BabyTmpRaster) FreeRaster(BabyTmpRaster, WinX, WinY); if (IntroWindow) CloseWindow(IntroWindow); if (BabyWindow) { ThisProcess = (struct Process *)FindTask(0); ThisProcess->pr_WindowPtr = OldWindow; CloseWindow(BabyWindow); } if (BabyScreen) CloseScreen(BabyScreen); if (MemList) FreeRemember(&MemList, TRUE); if (GfxBase) CloseLibrary(GfxBase); if (IntuitionBase) CloseLibrary(IntuitionBase); exit(0); } int Random(UserSeed) unsigned long int UserSeed; { static unsigned long int Seed; if (UserSeed) Seed = UserSeed; Seed = Seed * 1103515245 + 12345; return (unsigned int)(Seed/65536) % 32768; }