/* * Bounce - Creates a new thread each time the letter 'a' is typed. Each * thread bounces a happy face of a different color around the screen. All * threads are terminated when the letter 'Q' is entered. * * This program requires the multithread library. For example, compile with the * following command line: CL /MT BOUNCE.C */ #include <windows.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <conio.h> #include <process.h> const int MAX_THREADS=32; /* * getrandom returns a random number between min and max, which must be in * integer range. */ static int getrandom(int min, int max) { return ((rand() % (int)(((max) + 1) - (min))) + (min)); } void main(void); /* Thread 1: main */ void KbdFunc(void); /* Keyboard input, thread dispatch */ void BounceProc(char *MyID); /* Threads 2 to n: display */ void ClearScreen(void); /* Screen clear */ void ShutDown(void); /* Program shutdown */ void WriteTitle(int ThreadNum); /* Display title bar information */ HANDLE hConsoleOut; /* Handle to the console */ HANDLE hRunMutex; /* "Keep Running" mutex */ HANDLE hScreenMutex; /* "Screen update" mutex */ int ThreadNr; /* Number of threads started */ CONSOLE_SCREEN_BUFFER_INFO csbiInfo; /* Console information */ void main() { /* Thread One */ /* Get display screen information & clear the screen. */ hConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE); GetConsoleScreenBufferInfo(hConsoleOut, &csbiInfo); ClearScreen(); WriteTitle(0); /* Create the mutexes and reset thread count. */ hScreenMutex = CreateMutex(NULL, FALSE, NULL); /* Cleared */ hRunMutex = CreateMutex(NULL, TRUE, NULL); /* Set */ ThreadNr = 0; /* Start waiting for keyboard input to dispatch threads or exit. */ KbdFunc(); /* All threads done. Clean up handles. */ CloseHandle(hScreenMutex); CloseHandle(hRunMutex); CloseHandle(hConsoleOut); } /* * Finish processing */ void ShutDown(void) { /* Shut down threads */ while (ThreadNr > 0) { /* Tell thread to die and record its death. */ ReleaseMutex(hRunMutex); ThreadNr--; } /* Clean up display when done */ WaitForSingleObject(hScreenMutex, INFINITE); ClearScreen(); } /* * Read an process keyboard commands */ void KbdFunc(void) { /* Dispatch and count threads. */ int KeyInfo; do { KeyInfo = _getch(); if (tolower(KeyInfo) == 'a' && ThreadNr < MAX_THREADS) { ThreadNr++; _beginthread(BounceProc, 0, &ThreadNr); WriteTitle(ThreadNr); } } while (tolower(KeyInfo) != 'q'); ShutDown(); } /* * Bounce the face around the screen. * This procedure is run by each thread. */ void BounceProc(char *MyID) { char MyCell, OldCell; WORD MyAttrib, OldAttrib; char BlankCell = 0x20; COORD Coords, Delta; COORD Old = {0, 0}; DWORD Dummy; /* Generate update increments and initial display coordinates. */ srand((unsigned) *MyID * 3); Coords.X = getrandom(0, csbiInfo.dwSize.X - 1); Coords.Y = getrandom(0, csbiInfo.dwSize.Y - 1); Delta.X = getrandom(-3, 3); Delta.Y = getrandom(-3, 3); /* Set up "happy face" & generate color attribute from thread number. */ if (*MyID > 16) MyCell = 0x01; /* outline face */ else MyCell = 0x02; /* solid face */ MyAttrib = *MyID & 0x0F;/* force black background */ do { /* Wait for display to be available, then lock it. */ WaitForSingleObject(hScreenMutex, INFINITE); /* If we still occupy the old screen position, blank it out. */ ReadConsoleOutputCharacter(hConsoleOut, &OldCell, 1, Old, &Dummy); ReadConsoleOutputAttribute(hConsoleOut, &OldAttrib, 1, Old, &Dummy); if ((OldCell == MyCell) && (OldAttrib == MyAttrib)) WriteConsoleOutputCharacter(hConsoleOut, &BlankCell, 1, Old, &Dummy); /* Draw new face, then clear screen lock */ WriteConsoleOutputCharacter(hConsoleOut, &MyCell, 1, Coords, &Dummy); WriteConsoleOutputAttribute(hConsoleOut, &MyAttrib, 1, Coords, &Dummy); ReleaseMutex(hScreenMutex); /* Increment the coordinates for next placement of the block. */ Old.X = Coords.X; Old.Y = Coords.Y; Coords.X += Delta.X; Coords.Y += Delta.Y; /* If we are about to go off the screen, reverse direction */ if (Coords.X < 0 || Coords.X >= csbiInfo.dwSize.X) { Delta.X = -Delta.X; Beep(400, 50); } if (Coords.Y < 0 || Coords.Y > csbiInfo.dwSize.Y) { Delta.Y = -Delta.Y; Beep(600, 50); } } /* Repeat while RunMutex is still taken. */ while (WaitForSingleObject(hRunMutex, 75L) == WAIT_TIMEOUT); } void WriteTitle(int ThreadNum) { char NThreadMsg[80]; sprintf(NThreadMsg, "Threads running: %02d. Press 'A' to start a thread,'Q' to quit.", ThreadNum); SetConsoleTitle(NThreadMsg); } void ClearScreen(void) { DWORD dummy; COORD Home = {0, 0}; FillConsoleOutputCharacter(hConsoleOut, ' ', csbiInfo.dwSize.X * csbiInfo.dwSize.Y, Home, &dummy); }