// ////////////////////////////////////////////////////////// // GLWindow.cpp // Copyright (c) 2004 Stephan Brumme. All rights reserved. // #include "GLWindow.h" // ... and OpenGL itself ! #include // basic string processing #include // debugging #include // Static variables /// Initialize singleton GLWindow* GLWindow::singleton = NULL; /// Number of frames drawn at last update /** Used to compute the number of frames drawn in the last interval: it's just (frames-framesAtLastFPSUpdate) **/ unsigned int GLWindow::framesAtLastFPSUpdate = 0; /// Set up a new window GLWindow::GLWindow(bool fullscreen_, const char* title_, unsigned int width_, unsigned int height_, bool doublebuffer_, unsigned char colorbuffer_, unsigned char depthbuffer_, unsigned char accumbuffer_) : fullscreen(fullscreen_), title(title_), width(width_), height(height_), doublebuffer(doublebuffer_), accumbuffer(accumbuffer_), colorbuffer(colorbuffer_), depthbuffer(depthbuffer_), active(true), timeInactive(0), frames(0) { // singleton pattern assert(singleton == NULL); singleton = this; // create window hInstance = GetModuleHandle(NULL); // register window class WNDCLASSEX window; window.cbSize = sizeof(WNDCLASSEX); window.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS; window.lpfnWndProc = message; window.cbClsExtra = 0; window.cbWndExtra = 0; window.hInstance = hInstance; window.hIcon = window.hIconSm = LoadIcon(NULL, IDI_APPLICATION); window.hCursor = LoadCursor(NULL, IDC_ARROW); window.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); window.lpszMenuName = NULL; window.lpszClassName = title; // register my window bool registerClass = RegisterClassEx(&window) != 0; assert(registerClass); // window style depends whether window is in fullscreen of windowed DWORD dwStyle = WS_OVERLAPPEDWINDOW; DWORD dwExStyle = WS_EX_APPWINDOW; if (fullscreen) { dwStyle = WS_POPUP | WS_MAXIMIZE; dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; } // ensure window size RECT size; size.left = 0; size.top = 0; size.right = width; size.bottom = height; AdjustWindowRectEx(&size, dwStyle, FALSE, dwExStyle); // create main window hWnd = CreateWindowEx(dwExStyle, title, title, dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 256,128, size.right-size.left, size.bottom-size.top, NULL, NULL, hInstance, NULL); assert(hWnd); // get the device context hDC = GetDC(hWnd); assert(hDC); // set pixel format PIXELFORMATDESCRIPTOR pfd; ZeroMemory(&pfd, sizeof(pfd)); pfd.nSize = sizeof(pfd); pfd.nVersion = 1; pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | (doublebuffer ? PFD_DOUBLEBUFFER : 0); pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = colorbuffer; pfd.cDepthBits = depthbuffer; pfd.cAccumBits = accumbuffer; pfd.iLayerType = PFD_MAIN_PLANE; // set buffers' depths int format = ChoosePixelFormat(hDC, &pfd); assert(format > 0); bool setPixelFormat = SetPixelFormat(hDC, format, &pfd) != 0; assert(setPixelFormat); // create and enable the render context hRC = wglCreateContext(hDC); assert(hRC); wglMakeCurrent(hDC, hRC); // start timer SetTimer(hWnd, FPS_TIMER_ID, FPS_TIMER_INTERVAL, 0); // disable mouse cursor in fullscreen mode if (fullscreen) ShowCursor(FALSE); // show window AnimateWindow(hWnd, 200, AW_BLEND | AW_ACTIVATE); SetForegroundWindow(hWnd); SetFocus(hWnd); // switch off vsync if possible typedef GLboolean (__stdcall * SwapProc) (GLint interval); SwapProc wglSwapIntervalEXT = (SwapProc) wglGetProcAddress("wglSwapIntervalEXT"); if (wglSwapIntervalEXT) wglSwapIntervalEXT(0); // ensure proper timing timeStarted = GetTickCount(); } /// Destroy window GLWindow::~GLWindow() { // yeah, it's time to say good-bye ... // delete timer KillTimer(hWnd, FPS_TIMER_ID); // free device context wglMakeCurrent(NULL, NULL); wglDeleteContext(hRC); ReleaseDC(hWnd, hDC); // show cursor if (fullscreen) ShowCursor(TRUE); // properly remove window from screen AnimateWindow(hWnd, 500, AW_BLEND | AW_HIDE); DestroyWindow(hWnd); UnregisterClass(title, hInstance); } /// Main loop int GLWindow::run() { // run forever :-) while (true) { MSG msg; // quit ? while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { // window closed, the only way of escaping the "run" method if (msg.message == WM_QUIT) return (int)msg.wParam; // keep message pipeline alive TranslateMessage(&msg); DispatchMessage(&msg); } // redraw window if (active) paint(); else // idle, so don't waste CPU time WaitMessage(); } } /// Resize window void GLWindow::resize(int newWidth, int newHeight) { // store new window size width = newWidth; height = newHeight; // adjust OpenGL view glViewport(0, 0, width, height); } /// Toggle fullscreen/windowed void GLWindow::toggleFullscreen() { // default windowed size if programs starts in fullscreen mode static int windowedWidth = 512; static int windowedHeight = 512; if (fullscreen) { // switch from fullscreen to windowed mode // set new window styles SetWindowLong(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); SetWindowLong(hWnd, GWL_EXSTYLE, WS_EX_APPWINDOW); // from maximized to normal mode ShowWindow(hWnd, SW_RESTORE); UpdateWindow(hWnd); // ensure window size width = windowedWidth; height = windowedHeight; RECT size; size.left = 0; size.top = 0; size.right = width; size.bottom = height; AdjustWindowRectEx(&size, WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, FALSE, WS_EX_APPWINDOW); // restore old window size SetWindowPos(hWnd, HWND_TOP, 256,128, size.right-size.left, size.bottom-size.top, SWP_SHOWWINDOW); // show mouse cursor ShowCursor(TRUE); } else { // switch from windowed mode to fullscreen windowedWidth = width; windowedHeight = height; // set new window styles SetWindowLong(hWnd, GWL_STYLE, WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); SetWindowLong(hWnd, GWL_EXSTYLE, WS_EX_APPWINDOW | WS_EX_WINDOWEDGE); // show window maximized ShowWindow(hWnd, SW_SHOWMAXIMIZED); UpdateWindow(hWnd); // hide mouse cursor ShowCursor(FALSE); } fullscreen = !fullscreen; } /// Toggle active/stopped void GLWindow::toggleActive() { setActive(!active); } /// Set active/stopped void GLWindow::setActive(bool newState) { // same state ? if (active == newState) // do nothing ... return; // well, the state changed indeed active = newState; if (!active) // entering inactive mode inactiveSince = GetTickCount(); else // going back to active mode, compute time spent being inactive timeInactive += GetTickCount() - inactiveSince; } /// Paint window void GLWindow::paint() { // redraw window contents redraw(); // frame counter frames++; // double-buffering: switch front and back buffer if (doublebuffer) SwapBuffers(hDC); } /// Returns seconds since was was opened float GLWindow::seconds() const { if (active) return (GetTickCount() - (timeStarted + timeInactive)) / 1000.0f; else return (inactiveSince - (timeStarted + timeInactive)) / 1000.0f; } /// Process message queue LRESULT CALLBACK GLWindow::message(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_SYSCOMMAND: { switch (wParam) { // disable screensavers or monitor standby // idea taken from http://nehe.gamedev.net case SC_SCREENSAVE: case SC_MONITORPOWER: return 0; } break; } // restart/pause execution //case WM_ACTIVATE: //{ // singleton->setActive(!HIWORD(wParam)); // return 0; //} case WM_KEYDOWN: { switch (wParam) { // quit case VK_ESCAPE: PostMessage(hWnd, WM_CLOSE, 0, 0); return 0; // toggle fullscreen/windowed case VK_F5: singleton->toggleFullscreen(); return 0; // pause execution case VK_PAUSE: case VK_SPACE: singleton->toggleActive(); return 0; } break; } // quit case WM_CLOSE: { PostQuitMessage(0); return 0; } // redraw window case WM_PAINT: { singleton->paint(); // required to avoid artefacts static PAINTSTRUCT ps; BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps); break; } // resize case WM_SIZE: { singleton->resize(LOWORD(lParam), HIWORD(lParam)); PostMessage(hWnd, WM_PAINT, 0, 0); return 0; } // timer case WM_TIMER: // framerate timer if(wParam == FPS_TIMER_ID) { // compute current framerate float fps = (singleton->frames - framesAtLastFPSUpdate) * (1000.0f / FPS_TIMER_INTERVAL); framesAtLastFPSUpdate = singleton->frames; // update caption bar static char newCaption[257]; sprintf(newCaption, "%s (%.0f fps)", singleton->title, fps); SetWindowText(hWnd, newCaption); return 0; } break; } // default handler return DefWindowProc(hWnd, uMsg, wParam, lParam); }