#include "Framework.h" #define DEFAULT_FRAMERATE 60 #define DEFAULT_WIDTH 800 #define DEFAULT_HEIGHT 600 // Reference to ourselves - primarily used to access the message handler correctly // This is initialised in the constructor Framework * _thisFramework = NULL; // Forward declaration of our window procedure LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // We can only run if an instance of a class that inherits from Framework // has been created if (_thisFramework) { return _thisFramework->Run(hInstance, nCmdShow); } return -1; } Framework::Framework() : Framework(DEFAULT_WIDTH, DEFAULT_HEIGHT) { } Framework::Framework(unsigned int width, unsigned int height) { _thisFramework = this; _width = width; _height = height; } Framework::~Framework() { } int Framework::Run(HINSTANCE hInstance, int nCmdShow) { int returnValue; _hInstance = hInstance; if (!InitialiseMainWindow(nCmdShow)) { return -1; } returnValue = MainLoop(); Shutdown(); return returnValue; } // Main program loop. int Framework::MainLoop() { MSG msg; HACCEL hAccelTable = LoadAccelerators(_hInstance, MAKEINTRESOURCE(IDC_GRAPHICS2)); LARGE_INTEGER counterFrequency; LARGE_INTEGER nextTime; LARGE_INTEGER currentTime; LARGE_INTEGER lastTime; bool updateFlag = true; // Initialise timer QueryPerformanceFrequency(&counterFrequency); DWORD msPerFrame = (DWORD)(counterFrequency.QuadPart / DEFAULT_FRAMERATE); double timeFactor = 1.0 / counterFrequency.QuadPart; QueryPerformanceCounter(&nextTime); lastTime = nextTime; // Main message loop: msg.message = WM_NULL; while (msg.message != WM_QUIT) { if (updateFlag) { QueryPerformanceCounter(¤tTime); _timeSpan = (currentTime.QuadPart - lastTime.QuadPart) * timeFactor; lastTime = currentTime; Update(); updateFlag = false; } QueryPerformanceCounter(¤tTime); // Is it time to render the frame? if (currentTime.QuadPart > nextTime.QuadPart) { Render(); // Set time for next frame nextTime.QuadPart += msPerFrame; // If we get more than a frame ahead, allow one to be dropped // Otherwise, we will never catch up if we let the error accumulate // and message handling will suffer if (nextTime.QuadPart < currentTime.QuadPart) { nextTime.QuadPart = currentTime.QuadPart + msPerFrame; } updateFlag = true; } else { if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } } } return (int)msg.wParam; } // Register the window class, create the window and // create the bitmap that we will use for rendering bool Framework::InitialiseMainWindow(int nCmdShow) { #define MAX_LOADSTRING 100 WCHAR windowTitle[MAX_LOADSTRING]; WCHAR windowClass[MAX_LOADSTRING]; LoadStringW(_hInstance, IDS_APP_TITLE, windowTitle, MAX_LOADSTRING); LoadStringW(_hInstance, IDC_GRAPHICS2, windowClass, MAX_LOADSTRING); WNDCLASSEXW wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = _hInstance; wcex.hIcon = LoadIcon(_hInstance, MAKEINTRESOURCE(IDI_GRAPHICS2)); wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wcex.lpszMenuName = nullptr; wcex.lpszClassName = windowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); if (!RegisterClassExW(&wcex)) { MessageBox(0, L"Unable to register window class", 0, 0); return false; } // Now work out how large the window needs to be for our required client window size RECT windowRect = { 0, 0, static_cast(_width), static_cast(_height) }; AdjustWindowRect(&windowRect, WS_OVERLAPPEDWINDOW, FALSE); _width = windowRect.right - windowRect.left; _height = windowRect.bottom - windowRect.top; _hWnd = CreateWindowW(windowClass, windowTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, _width, _height, nullptr, nullptr, _hInstance, nullptr); if (!_hWnd) { MessageBox(0, L"Unable to create window", 0, 0); return false; } if (!Initialise()) { return false; } ShowWindow(_hWnd, nCmdShow); UpdateWindow(_hWnd); return true; } // The WndProc for the current window. This cannot be a method, but we can // redirect all messages to a method. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { if (_thisFramework != NULL) { // If framework is started, then we can call our own message proc return _thisFramework->MsgProc(hWnd, message, wParam, lParam); } else { // otherwise, we just pass control to the default message proc return DefWindowProc(hWnd, message, wParam, lParam); } } // Our main WndProc LRESULT Framework::MsgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_PAINT: break; case WM_KEYDOWN: OnKeyDown(wParam); return 0; case WM_KEYUP: OnKeyUp(wParam); return 0; case WM_DESTROY: PostQuitMessage(0); break; case WM_MOVE: Render(); break; case WM_SIZE: _width = LOWORD(lParam); _height = HIWORD(lParam); OnResize(wParam); Render(); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }