/*********************************************************************** * Source File: * UI INTERACT * Author: * Br. Helfrich * Description: * Implement the interfaces specified in uiInterface.h. This handles * all the interfaces and events necessary to work with OpenGL. Your * program will interface with this thorough the callback function * pointer towards the bottom of the file. ************************************************************************/ #include // need you ask? #include // convert an integer into text #include // I feel the need... the need for asserts #include // for clock #include // for rand() #ifdef __APPLE__ #include // Main OpenGL library #include // Second OpenGL library #endif // __APPLE__ #ifdef __linux__ #include // Main OpenGL library #include // Second OpenGL library #endif // __linux__ #ifdef _WIN32 #include #include #include // OpenGL library we copied #include // for ::Sleep(); #include #define _USE_MATH_DEFINES #include #endif // _WIN32 #include "uiInteract.h" #include "point.h" using namespace std; /********************************************************************* * SLEEP * Pause for a while. We want to put the program to sleep until it * is time to draw again. Note that this requires us to tell the OS * that we are idle. the nanosleep function performs this task for us * INPUT: msSleep: sleep time in milliseconds *********************************************************************/ void sleep(unsigned long msSleep) { // Windows handles sleep one way #ifdef _WIN32 ::Sleep(msSleep + 35); // Unix-based operating systems (OS-X, Linux) do it another #else // LINUX, XCODE timespec req = {}; time_t sec = (int)(msSleep / 1000); msSleep -= (sec * 1000); req.tv_sec = sec; req.tv_nsec = msSleep * 1000000L; while (nanosleep(&req, &req) == -1) ; #endif // LINUX, XCODE return; } /************************************************************************ * DRAW CALLBACK * This is the main callback from OpenGL. It gets called constantly by * the graphics engine to refresh and draw the window. Here we will * clear the background buffer, draw on it, and send it to the forefront * when the appropriate time period has passsed. * * Note: This and all other callbacks can't be member functions, they must * have global scope for OpenGL to see them. *************************************************************************/ void drawCallback() { // even though this is a local variable, all the members are static Interface ui; // Prepare the background buffer for drawing glClear(GL_COLOR_BUFFER_BIT); //clear the screen glColor3f(1,1,1); //calls the client's display function assert(ui.callBack != NULL); ui.callBack(&ui, ui.p); //loop until the timer runs out if (!ui.isTimeToDraw()) sleep((unsigned long)((ui.getNextTick() - clock()) / 1000)); // from this point, set the next draw time ui.setNextDrawTime(); // bring forth the background buffer glutSwapBuffers(); // clear the space at the end ui.keyEvent(); } /************************************************************************ * KEY DOWN CALLBACK * When a key on the keyboard has been pressed, we need to pass that * on to the client. Currently, we are only registering the arrow keys * INPUT key: the key we pressed according to the GLUT_KEY_ prefix * x y: the position in the window, which we ignore *************************************************************************/ void keyDownCallback(int key, int x, int y) { // Even though this is a local variable, all the members are static // so we are actually getting the same version as in the constructor. Interface ui; ui.keyEvent(key, true /*fDown*/); } /************************************************************************ * KEY UP CALLBACK * When the user has released the key, we need to reset the pressed flag * INPUT key: the key we pressed according to the GLUT_KEY_ prefix * x y: the position in the window, which we ignore *************************************************************************/ void keyUpCallback(int key, int x, int y) { // Even though this is a local variable, all the members are static // so we are actually getting the same version as in the constructor. Interface ui; ui.keyEvent(key, false /*fDown*/); } /*************************************************************** * KEYBOARD CALLBACK * Generic callback to a regular ascii keyboard event, such as * the space bar or the letter 'q' ***************************************************************/ void keyboardCallback(unsigned char key, int x, int y) { // Even though this is a local variable, all the members are static // so we are actually getting the same version as in the constructor. Interface ui; ui.keyEvent(key, true /*fDown*/); } /*************************************************************** * INTERFACE : KEY EVENT * Either set the up or down event for a given key * INPUT key which key is pressed * fDown down or brown ****************************************************************/ void Interface::keyEvent(int key, bool fDown) { switch(key) { case GLUT_KEY_DOWN: isDownPress = fDown; break; case GLUT_KEY_UP: isUpPress = fDown; break; case GLUT_KEY_RIGHT: isRightPress = fDown; break; case GLUT_KEY_LEFT: isLeftPress = fDown; break; case GLUT_KEY_HOME: case ' ': isSpacePress = fDown; break; } } /*************************************************************** * INTERFACE : KEY EVENT * Either set the up or down event for a given key * INPUT key which key is pressed * fDown down or brown ****************************************************************/ void Interface::keyEvent() { if (isDownPress) isDownPress++; if (isUpPress) isUpPress++; if (isLeftPress) isLeftPress++; if (isRightPress) isRightPress++; isSpacePress = false; } /************************************************************************ * INTEFACE : IS TIME TO DRAW * Have we waited long enough to draw swap the background buffer with * the foreground buffer? *************************************************************************/ bool Interface::isTimeToDraw() { return ((unsigned int)clock() >= nextTick); } /************************************************************************ * INTERFACE : SET NEXT DRAW TIME * What time should we draw the buffer again? This is a function of * the current time and the frames per second. *************************************************************************/ void Interface::setNextDrawTime() { nextTick = clock() + static_cast (timePeriod * CLOCKS_PER_SEC); } /************************************************************************ * INTERFACE : SET FRAMES PER SECOND * The frames per second dictates the speed of the game. The more frames * per second, the quicker the game will appear to the user. We will default * to 30 frames/second but the client can set this at will. * INPUT value The number of frames per second. 30 is default *************************************************************************/ void Interface::setFramesPerSecond(double value) { timePeriod = (1 / value); } /*************************************************** * STATICS * All the static member variables need to be initialized * Somewhere globally. This is a good spot **************************************************/ int Interface::isDownPress = 0; int Interface::isUpPress = 0; int Interface::isLeftPress = 0; int Interface::isRightPress = 0; bool Interface::isSpacePress = false; bool Interface::initialized = false; double Interface::timePeriod = 1.0 / 30; // default to 30 frames/second unsigned int Interface::nextTick = 0; // redraw now please void * Interface::p = NULL; void (*Interface::callBack)(const Interface *, void *) = NULL; /************************************************************************ * INTERFACE : DESTRUCTOR * Nothing here! ***********************************************************************/ Interface::~Interface() { } /************************************************************************ * INTEFACE : INITIALIZE * Initialize our drawing window. This will set the size and position, * get ready for drawing, set up the colors, and everything else ready to * draw the window. All these are part of initializing Open GL. * INPUT argc: Count of command-line arguments from main * argv: The actual command-line parameters * title: The text for the titlebar of the window *************************************************************************/ void Interface::initialize(int argc, char ** argv, const char * title, Point topLeft, Point bottomRight) { if (initialized) return; // set up the random number generator srand((unsigned int)time(NULL)); // create the window glutInit(&argc, argv); Point point; glutInitWindowSize( // size of the window (int)(bottomRight.getX() - topLeft.getX()), (int)(topLeft.getY() - bottomRight.getY())); glutInitWindowPosition( 10, 10); // initial position glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); // double buffering glutCreateWindow(title); // text on titlebar glutIgnoreKeyRepeat(true); // set up the drawing style: B/W and 2D glClearColor(0, 0, 0, 0); // Black is the background color gluOrtho2D((int)topLeft.getX(), (int)bottomRight.getX(), (int)bottomRight.getY(), (int)topLeft.getY()); // 2D environment // register the callbacks so OpenGL knows how to call us glutDisplayFunc( drawCallback ); glutIdleFunc( drawCallback ); glutKeyboardFunc( keyboardCallback); glutSpecialFunc( keyDownCallback ); glutSpecialUpFunc( keyUpCallback ); initialized = true; // done return; } /************************************************************************ * INTERFACE : RUN * Start the main graphics loop and play the game * INPUT callBack: Callback function. Every time we are beginning * to draw a new frame, we first callback to the client * to see if he wants to do anything, such as move * the game pieces or respond to input * p: Void point to whatever the caller wants. You * will need to cast this back to your own data * type before using it. *************************************************************************/ void Interface::run(void (*callBack)(const Interface *, void *), void *p) { // setup the callbacks this->p = p; this->callBack = callBack; glutMainLoop(); return; }