//Process all events and handle recording/playback void platform_process_events() { //Process events SDL_Event event; while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { platform::services.config.quit = true; } if (event.type == SDL_KEYUP && event.key.keysym.scancode == SDL_SCANCODE_F11) { InputRecordStart(&platform::app_state); } else if (event.type == SDL_KEYUP && event.key.keysym.scancode == SDL_SCANCODE_F9) { InputRecordEnd(&platform::app_state); } else if (event.type == SDL_KEYUP && event.key.keysym.scancode == SDL_SCANCODE_F7) { PlaybackInputStart(&platform::app_state); } else if (event.type == SDL_KEYUP && event.key.keysym.scancode == SDL_SCANCODE_F5) { PlaybackInputEnd(&platform::app_state); } else { platform::services.events.push_back(event); } } if (platform::app_state.is_recording) { //Record inputs received this frame InputRecord(&platform::app_state, platform::services.events); } else if (platform::app_state.is_playback) { //Discard current events, replace with recored input PlaybackInput(&platform::app_state, platform::services.events); } }
// WINDOWS MAIN FUNCTION // hInst = current instance of the program // hPrevInst = previous instance which is not used anymore. // cmdLine = holds command line arguments to be passed in to the program // cmdShow = holds an integer to specify if we want to show this window. int CALLBACK WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdLine, int cmdShow) { WNDCLASS wc = {0}; wc.style = CS_HREDRAW | CS_VREDRAW; wc.hInstance = hInst; wc.hbrBackground = (HBRUSH)COLOR_WINDOW; wc.lpszClassName = wndClassName; wc.hCursor = 0; //TODO: Add cursors and icons to this program. wc.hIcon = 0; wc.lpfnWndProc = (WNDPROC)wndProc; RegisterClass(&wc); HWND window = CreateWindow(wndClassName, wndTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, wndWidth, wndHeight, 0, 0, hInst, 0); if(window) { ShowWindow(window, SW_SHOW); UpdateWindow(window); // NOTE: Initializing SDL if(SDL_Init(SDL_INIT_VIDEO) != 0 ) { DestroyWindow(window); return -2; } IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG); sdlWindow = SDL_CreateWindowFrom((void*)window); char error[MAX_PATH]; stringCopy(error, SDL_GetError()); OutputDebugStringA(error); if(!sdlWindow) { SDL_Quit(); DestroyWindow(window); return -3; } renderer = SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); if(!renderer) { SDL_DestroyWindow(sdlWindow); SDL_Quit(); DestroyWindow(window); return -4; } i32 RefreshRate = 0; HDC dc = GetDC(window); i32 winRefreshRate = GetDeviceCaps(dc, VREFRESH); if( winRefreshRate > 1 ) { RefreshRate = winRefreshRate / 2; } else { RefreshRate = 30; } r32 targetSecsPerFrame = 1.0f / RefreshRate; GameMemory memory = {}; memory.permanentSize = Megabytes(64); memory.transientSize = Megabytes(64); memory.totalSize = memory.permanentSize + memory.transientSize; gameMemoryBlock = VirtualAlloc( 0, memory.totalSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if(!gameMemoryBlock) { SDL_DestroyRenderer(renderer); SDL_DestroyWindow(sdlWindow); SDL_Quit(); DestroyWindow(window); return -5; } memory.permanentBlock = gameMemoryBlock; memory.transientBlock = (i8*)gameMemoryBlock + memory.permanentSize; memory.readEntireFile = ReadEntireFile; memory.freeFile = FreeFile; memory.writeEntireFile = WriteEntireFile; Win32Dims windowDims = GetWindowDimensions(window); Render render = {}; render.renderer = renderer; render.screenW = windowDims.width; render.screenH = windowDims.height; GameController input = {0}; input.dt = targetSecsPerFrame; Win32State state = {0}; state.memoryBlock = gameMemoryBlock; state.memorySize = memory.totalSize; GameCodeDLL gameCode = Win32LoadGameCode("Game.dll", "Game_temp.dll", "lock.tmp"); isRunning = true; r32 sleepIsGranular = (timeBeginPeriod(1) == TIMERR_NOERROR); LARGE_INTEGER lastCounter = Win32GetClock(); i64 lastCycles = __rdtsc(); LARGE_INTEGER performanceFreqPerSecRes; QueryPerformanceFrequency(&performanceFreqPerSecRes); globalPerformanceCountFreq = performanceFreqPerSecRes.QuadPart; // NOTE: PROGRAM LOOP!! while(isRunning) { // NOTE: compare File times for us to be able to reload the new game code FILETIME currentFileTime = Win32GetLastWriteTime("Game.dll"); if(CompareFileTime(¤tFileTime, &gameCode.lastWriteTime) != 0) { Win32UnloadGameCode(&gameCode); gameCode = Win32LoadGameCode("Game.dll", "Game_temp.dll", "lock.tmp"); } MSG msg = {0}; while(PeekMessage(&msg, window, 0, 0, PM_REMOVE)) { switch(msg.message) { case WM_CLOSE: { isRunning = false; }break; case WM_KEYUP: case WM_KEYDOWN: case WM_SYSKEYUP: case WM_SYSKEYDOWN: { u32 vkCode = (u32)msg.wParam; // This contains the keycode for the key that the user pressed. bool isDown = ((msg.lParam & (1 << 31)) == 0); // Check to see if the key is down now. bool wasDown = ((msg.lParam & (1 << 30)) != 0); // Check to see if the key was down previously. if(isDown != wasDown) { if(vkCode == 'W') { input.moveUp.isDown = isDown; } else if(vkCode == 'S') { input.moveDown.isDown = isDown; } else if(vkCode == 'A') { input.moveLeft.isDown = isDown; } else if(vkCode == 'D') { input.moveRight.isDown = isDown; } if(vkCode == VK_UP) { input.actionUp.isDown = isDown; } else if(vkCode == VK_DOWN) { input.actionDown.isDown = isDown; } else if(vkCode == VK_LEFT) { input.actionLeft.isDown = isDown; } else if(vkCode == VK_RIGHT) { input.actionRight.isDown = isDown; } else if(vkCode == VK_ESCAPE) { input.back.isDown = isDown; } else if(vkCode == 'O') { if(isDown) { if(state.recordingIndex == 0) { BeginRecording(&state, 1); } else { EndRecording(&state); BeginPlayback(&state, 1); } } } else if(vkCode == 'P') { if(isDown) { if(state.playbackIndex > 0) { EndPlayback(&state); ZeroMemory(&input, sizeof(input)); } } } } if(isDown) { bool AltKeyWasDown = ((msg.lParam & (1 << 29)) != 0); if(vkCode == VK_RETURN && AltKeyWasDown) { if(msg.hwnd) { isFullscreen = !isFullscreen; Win32FullscreenToggle(msg.hwnd); } } } }break; default: { TranslateMessage(&msg); DispatchMessage(&msg); } } } if(input.back.isDown) { isRunning = false; PostQuitMessage(0); } if(state.recordingIndex > 0) { RecordingInput(&state, &input); } else if(state.playbackIndex > 0) { PlaybackInput(&state, &input); } Win32Dims windowDims = GetWindowDimensions(window); Win32UpdateWindow(windowDims, (i32*)&render.screenW, (i32*)&render.screenH); if(gameCode.UpdateRender) { gameCode.UpdateRender(&memory, &input, &render); } LARGE_INTEGER workCounter = Win32GetClock(); r32 workSecsElapsed = Win32GetSecondsElapsed(lastCounter, workCounter); r32 secondsElapsed = workSecsElapsed; if(secondsElapsed < targetSecsPerFrame) { if(sleepIsGranular) { DWORD sleepMS = (DWORD)(1000.0f * (targetSecsPerFrame - secondsElapsed)); if(sleepMS > 0) { Sleep(sleepMS); } } r32 testSecsElapsed = Win32GetSecondsElapsed(lastCounter, Win32GetClock()); if(testSecsElapsed < targetSecsPerFrame) { //TODO: LOG MISSED SLEEP HERE!! } while(secondsElapsed < targetSecsPerFrame) { secondsElapsed = Win32GetSecondsElapsed(lastCounter, Win32GetClock()); } } else { //TODO: MISSED FRAME RATE!! } LARGE_INTEGER endCounter = Win32GetClock(); i64 endCycles = __rdtsc(); r64 elapsedCounts = (r64)(endCounter.QuadPart - lastCounter.QuadPart); r64 elapsedCycles = (r64)(endCycles - lastCycles); r32 MSperFrame = ((1000.0f * Win32GetSecondsElapsed(lastCounter, endCounter))); r32 FPS = (r32)(globalPerformanceCountFreq / elapsedCounts); r32 MegaCyclesPerFrame = (r32)(elapsedCycles / (1000.0f * 1000.0f)); char buffer[256]; sprintf_s(buffer, "%.02fms, %.02ffps, %.02fmcpf\n", MSperFrame, FPS, MegaCyclesPerFrame); OutputDebugStringA(buffer); lastCounter = endCounter; lastCycles = endCycles; } // NOTE: END OF WHILE LOOP //IMPORTANT: Unload this when we exit the program. Win32UnloadGameCode(&gameCode); } else { // TODO: Handle Error Loggin here!! return -1; } if(gameMemoryBlock) { VirtualFree(gameMemoryBlock, 0, MEM_RELEASE); } //Close SDL_DestroyRenderer(renderer); SDL_DestroyWindow(sdlWindow); IMG_Quit(); SDL_Quit(); DestroyWindow(window); return 0; }