diff --git a/misc/bdebug.bat b/misc/bdebug.bat index 123bba6..79eb751 100644 --- a/misc/bdebug.bat +++ b/misc/bdebug.bat @@ -1,2 +1,2 @@ -call build +call build || exit /b %errorlevel% devenv .\build\main.exe diff --git a/misc/brun.bat b/misc/brun.bat index 5e770b8..94748ec 100644 --- a/misc/brun.bat +++ b/misc/brun.bat @@ -1,2 +1,2 @@ -call build +call build || exit /b %errorlevel% .\build\main.exe diff --git a/misc/build.bat b/misc/build.bat index 62b50be..65a1d9d 100644 --- a/misc/build.bat +++ b/misc/build.bat @@ -4,3 +4,8 @@ pushd .\build pwd cl -FC -Zi ..\src\main.cpp user32.lib Gdi32.lib popd +exit /b + +:error +echo Failed with error #%errorlevel%. +exit /b %errorlevel% diff --git a/misc/shell.bat b/misc/shell.bat index 24e2cbc..1e54aaa 100644 --- a/misc/shell.bat +++ b/misc/shell.bat @@ -1,4 +1,5 @@ @echo off +start "ProconXInput" "C:\Program Files\ProconXInput\ProconXInput.exe" call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" set path=C:\source\repos\handmade\misc;%path% cd C:\source\repos\handmade\ \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 492d218..da3b324 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,10 +1,31 @@ +#include +#include +#include #include #include +#include +#include +#include #define internal static // for functions #define local_persist static // for static variables in a scope #define global static // for global variables +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint64_t uint64; + +typedef int8_t int8; +typedef int16_t int16; +typedef int32_t int32; +typedef int64_t int64; + +typedef float real32; +typedef double real64; + +typedef int32_t bool32; + struct Win32OffscreenBuffer { BITMAPINFO info; void *memory; @@ -18,25 +39,46 @@ struct Win32WindowDimensions { int height; }; +#define PI32 3.141592653589f + +global ATOM HH_CTRLW; global bool running; global Win32OffscreenBuffer globalBackBuffer; +global LPDIRECTSOUNDBUFFER globalSecondaryBuffer; -typedef uint8_t uint8; -typedef uint16_t uint16; -typedef uint32_t uint32; -typedef uint64_t uint64; -typedef int8_t int8; -typedef int16_t int16; -typedef int32_t int32; -typedef int64_t int64; +void debug_printf(char* format, ...) { + int bufsize = strlen(format)*10; + char *output = (char*)malloc(bufsize); + va_list list; + va_start(list, format); + vsprintf_s(output, bufsize, format, list); + OutputDebugStringA(output); + free(output); +} + +// XInputGetState +#define X_INPUT_GET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_STATE *pState) +typedef X_INPUT_GET_STATE(XInputGetStateFn); +X_INPUT_GET_STATE(XInputGetStateStub) { return ERROR_DEVICE_NOT_CONNECTED; } +global XInputGetStateFn *XInputGetStateDyn = XInputGetStateStub; +#define XInputGetState XInputGetStateDyn + +// XInputSetState +#define X_INPUT_SET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration) +typedef X_INPUT_SET_STATE(XInputSetStateFn); +X_INPUT_SET_STATE(XInputSetStateStub) { return ERROR_DEVICE_NOT_CONNECTED; } +global XInputSetStateFn *XInputSetStateDyn = XInputSetStateStub; +#define XInputSetState XInputSetStateDyn + +typedef HRESULT WINAPI DirectSoundCreateFn(LPCGUID pcGuidDevice, LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter); -internal void renderWeirdGradient(Win32OffscreenBuffer buffer, int xOffset, int yOffset) { +internal void renderWeirdGradient(Win32OffscreenBuffer *buffer, int xOffset, int yOffset) { int bytesPerPixel = 4; - int pitch = buffer.width*bytesPerPixel; - uint8 *row = (uint8*)buffer.memory; - for (int y = 0; y < buffer.height; y++) { + int pitch = buffer->width*bytesPerPixel; + uint8 *row = (uint8 *)buffer->memory; + for (int y = 0; y < buffer->height; y++) { uint32 *pixel = (uint32*)row; - for (int x = 0; x < buffer.width; x++) { + for (int x = 0; x < buffer->width; x++) { uint8 blue = x + xOffset; uint8 green = y + yOffset; *pixel++ = (green << 8) | blue; @@ -54,22 +96,18 @@ internal void resizeDIBSection(Win32OffscreenBuffer *buffer, int width, int heig buffer->height = height; buffer->bytesPerPixel = 4; - buffer->info = { - .bmiHeader = { - .biSize = sizeof(buffer->info.bmiHeader), - .biWidth = buffer->width, - .biHeight = -buffer->height, - .biPlanes = 1, - .biBitCount = 32, - .biCompression = BI_RGB, - }, - }; + buffer->info.bmiHeader.biSize = sizeof(buffer->info.bmiHeader); + buffer->info.bmiHeader.biWidth = buffer->width; + buffer->info.bmiHeader.biHeight = -buffer->height; + buffer->info.bmiHeader.biPlanes = 1; + buffer->info.bmiHeader.biBitCount = 32; + buffer->info.bmiHeader.biCompression = BI_RGB; int bitmapSize = buffer->width*buffer->height*buffer->bytesPerPixel; buffer->memory = VirtualAlloc(NULL, bitmapSize, MEM_COMMIT, PAGE_READWRITE); } -Win32WindowDimensions win32GetWindowDimensions(HWND window) { +internal Win32WindowDimensions win32GetWindowDimensions(HWND window) { Win32WindowDimensions result; RECT clientRect; GetClientRect(window, &clientRect); @@ -78,19 +116,78 @@ Win32WindowDimensions win32GetWindowDimensions(HWND window) { return result; } -void win32DrawBufferInWindow(Win32OffscreenBuffer buffer, HWND window) { +internal void win32DrawBufferInWindow(Win32OffscreenBuffer *buffer, HWND window) { Win32WindowDimensions winDims = win32GetWindowDimensions(window); StretchDIBits( GetDC(window), 0, 0, winDims.width, winDims.height, - 0, 0, buffer.width, buffer.height, - buffer.memory, - &buffer.info, + 0, 0, buffer->width, buffer->height, + buffer->memory, + &buffer->info, DIB_RGB_COLORS, SRCCOPY ); } +internal void win32LoadXInput() { + HMODULE xInputLib = LoadLibrary("xinput1_4.dll"); + if (!xInputLib) { + xInputLib = LoadLibrary("xinput1_3.dll"); + } + + if (xInputLib) { + XInputGetState = (XInputGetStateFn *)GetProcAddress(xInputLib, "XInputGetState"); + XInputSetState = (XInputSetStateFn *)GetProcAddress(xInputLib, "XInputSetState"); + } +} + +internal void win32InitSound(HWND window, int32 samplesPerSec, int bufferSize) { + HMODULE dSoundLibrary = LoadLibraryA("dsound.dll"); + if (dSoundLibrary) { + DirectSoundCreateFn *DirectSoundCreate = (DirectSoundCreateFn*)GetProcAddress(dSoundLibrary, "DirectSoundCreate"); + LPDIRECTSOUND DirectSound; + if (DirectSoundCreate && SUCCEEDED(DirectSoundCreate(0, &DirectSound, 0))) { + WAVEFORMATEX waveFormat = {}; + waveFormat.wFormatTag = WAVE_FORMAT_PCM; + waveFormat.nChannels = 2; + waveFormat.nSamplesPerSec = samplesPerSec; + waveFormat.wBitsPerSample = 16; + waveFormat.nBlockAlign = (waveFormat.nChannels*waveFormat.wBitsPerSample) / 8; + waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec*waveFormat.nBlockAlign; + waveFormat.cbSize = 0; + + if (SUCCEEDED(DirectSound->SetCooperativeLevel(window, DSSCL_PRIORITY))) { + DSBUFFERDESC bufferDescription = {}; + bufferDescription.dwSize = sizeof(bufferDescription); + bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER; + + LPDIRECTSOUNDBUFFER primaryBuffer; + if (SUCCEEDED(DirectSound->CreateSoundBuffer(&bufferDescription, &primaryBuffer, 0))) { + if (SUCCEEDED(primaryBuffer->SetFormat(&waveFormat))) { + OutputDebugStringA("Primary buffer format was set.\n"); + } else { + //No sound! (?) + } + } + } else { + // No sound! (?) + } + + DSBUFFERDESC bufferDescription = {}; + bufferDescription.dwSize = sizeof(bufferDescription); + bufferDescription.dwFlags = 0; + bufferDescription.dwBufferBytes = bufferSize; + bufferDescription.lpwfxFormat = &waveFormat; + + if (SUCCEEDED(DirectSound->CreateSoundBuffer(&bufferDescription, &globalSecondaryBuffer, 0))) { + OutputDebugStringA("Secondary buffer created successfully!\n"); + } + } else { + // No sound! (?) + } + } +} + LRESULT mainWindowCallback( HWND window, UINT message, @@ -103,7 +200,7 @@ LRESULT mainWindowCallback( } break; case WM_DESTROY: { - //OutputDebugStringA("WM_DESTROY\n"); + OutputDebugStringA("WM_DESTROY\n"); } break; case WM_CLOSE: { @@ -117,34 +214,109 @@ LRESULT mainWindowCallback( int y = paint.rcPaint.top; int width = paint.rcPaint.right - paint.rcPaint.left; int height = paint.rcPaint.bottom - paint.rcPaint.top; - win32DrawBufferInWindow(globalBackBuffer, window); + win32DrawBufferInWindow(&globalBackBuffer, window); EndPaint(window, &paint); } break; case WM_ACTIVATEAPP: { - running = false; + } break; + + case WM_HOTKEY: { + if (wParam == HH_CTRLW) { + running = false; + } + } break; + + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_KEYUP: + case WM_KEYDOWN: { + uint32 VKCode = wParam; + bool wasDown = (lParam & (1 << 30)) != 0; + bool isDown = (lParam & (1 << 31)) == 0; + if (wasDown != isDown) { + if (VKCode == 'W') { + } else if (VKCode == 'A') { + } else if (VKCode == 'S') { + } else if (VKCode == 'D') { + } else if (VKCode == 'Q') { + } else if (VKCode == 'E') { + } else if (VKCode == VK_ESCAPE) { + } else if (VKCode == VK_UP) { + } else if (VKCode == VK_LEFT) { + } else if (VKCode == VK_DOWN) { + } else if (VKCode == VK_RIGHT) { + } else if (VKCode == VK_SPACE) { + } + } + + bool32 altKeyWasDown = lParam & (1 << 29); + if (altKeyWasDown && VKCode == VK_F4) { + running = false; + } } break; default: { result = DefWindowProcA(window, message, wParam, lParam); - //OutputDebugStringA("default\n"); } break; } return result; } +struct Win32SoundOutput { + int samplesPerSecond; + int toneHz; + int wavePeriod; + int16 toneVolume; + uint32 runningSampleIndex; + int bytesPerSample; + int secondaryBufferSize; +}; + +internal void win32FillSoundBuffer(Win32SoundOutput *soundOutput, DWORD byteToLock, DWORD bytesToWrite) { + VOID *region1; + DWORD region1Size; + VOID *region2; + DWORD region2Size; + HRESULT lock = globalSecondaryBuffer->Lock(byteToLock, bytesToWrite, ®ion1, ®ion1Size, ®ion2, ®ion2Size, 0); + if (SUCCEEDED(lock)) { + DWORD region1SampleCount = region1Size/soundOutput->bytesPerSample; + int16 *sampleOut = (int16*)region1; + for (DWORD sampleIndex = 0; sampleIndex < region1SampleCount; sampleIndex++) { + real32 t = 2.0f * PI32 * (real32)soundOutput->runningSampleIndex / (real32)soundOutput->wavePeriod; + int16 sampleValue = (int16)(sin(t) * (real32)soundOutput->toneVolume); + *sampleOut++ = sampleValue; + *sampleOut++ = sampleValue; + soundOutput->runningSampleIndex++; + } + DWORD region2SampleCount = region2Size/soundOutput->bytesPerSample; + sampleOut = (int16*)region2; + for (DWORD sampleIndex = 0; sampleIndex < region2SampleCount; sampleIndex++) { + real32 t = 2.0f * PI32 * (real32)soundOutput->runningSampleIndex / (real32)soundOutput->wavePeriod; + int16 sampleValue = (int16)(sin(t) * soundOutput->toneVolume); + *sampleOut++ = sampleValue; + *sampleOut++ = sampleValue; + soundOutput->runningSampleIndex++; + } + globalSecondaryBuffer->Unlock(region1, region1Size, region2, region2Size); + } else { + OutputDebugStringA("No lock!\n"); + } +} + int APIENTRY WinMain( HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLine, int commandShow ) { - WNDCLASS windowClass = { - .style = CS_VREDRAW | CS_HREDRAW, - .lpfnWndProc = &mainWindowCallback, - .hInstance = instance, - .lpszClassName = "HandmadeHeroWindowClass", - }; + win32LoadXInput(); + + WNDCLASSA windowClass = {}; + windowClass.style = CS_VREDRAW | CS_HREDRAW; + windowClass.lpfnWndProc = &mainWindowCallback; + windowClass.hInstance = instance; + windowClass.lpszClassName = "HandmadeHeroWindowClass"; resizeDIBSection(&globalBackBuffer, 1280, 720); @@ -164,22 +336,94 @@ int APIENTRY WinMain( NULL ); if (window) { + HH_CTRLW = GlobalAddAtomA("HH_CTRLW"); + RegisterHotKey(window, HH_CTRLW, MOD_CONTROL, 'W'); + MSG message; running = true; + + // graphics test int xOffset = 0; int yOffset = 0; + + // sound test + Win32SoundOutput soundOutput = {}; + soundOutput.samplesPerSecond = 48000; + soundOutput.toneHz = 440; // A above middle C + soundOutput.wavePeriod = soundOutput.samplesPerSecond / soundOutput.toneHz; + soundOutput.toneVolume = 6000; + soundOutput.runningSampleIndex = 0; + soundOutput.bytesPerSample = sizeof(int16)*2; + soundOutput.secondaryBufferSize = soundOutput.samplesPerSecond*soundOutput.bytesPerSample; + + win32InitSound(window, soundOutput.samplesPerSecond, soundOutput.secondaryBufferSize); + win32FillSoundBuffer(&soundOutput, 0, soundOutput.secondaryBufferSize); + globalSecondaryBuffer->Play(0, 0, DSBPLAY_LOOPING); + while (running) { - while(PeekMessageA(&message, NULL, NULL, NULL, PM_REMOVE)) { + while (PeekMessageA(&message, NULL, NULL, NULL, PM_REMOVE)) { if (message.message == WM_QUIT) { running = false; } TranslateMessage(&message); DispatchMessage(&message); } - renderWeirdGradient(globalBackBuffer, xOffset, yOffset); - win32DrawBufferInWindow(globalBackBuffer, window); - xOffset++; - yOffset++; + + XINPUT_STATE controllerState; + for (DWORD controllerIndex = 0; controllerIndex < XUSER_MAX_COUNT; controllerIndex++) { + if (XInputGetState(controllerIndex, &controllerState) == ERROR_SUCCESS) { + break; + } else { + // controller not available + } + } + XINPUT_GAMEPAD *Pad = &controllerState.Gamepad; + bool up = (Pad->wButtons & XINPUT_GAMEPAD_DPAD_UP); + bool down = (Pad->wButtons & XINPUT_GAMEPAD_DPAD_DOWN); + bool left = (Pad->wButtons & XINPUT_GAMEPAD_DPAD_LEFT); + bool right = (Pad->wButtons & XINPUT_GAMEPAD_DPAD_RIGHT); + bool start = (Pad->wButtons & XINPUT_GAMEPAD_START); + bool back = (Pad->wButtons & XINPUT_GAMEPAD_BACK); + bool leftShoulder = (Pad->wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER); + bool rightShoulder = (Pad->wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER); + bool aButton = (Pad->wButtons & XINPUT_GAMEPAD_A); + bool bButton = (Pad->wButtons & XINPUT_GAMEPAD_B); + bool xButton = (Pad->wButtons & XINPUT_GAMEPAD_X); + bool yButton = (Pad->wButtons & XINPUT_GAMEPAD_Y); + int16 stickX = Pad->sThumbLX; + int16 stickY = Pad->sThumbLY; + + float stickYNorm = 0; + float stickXNorm = 0; + if (stickY != 0 && abs(stickY) > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) { + stickYNorm = stickY / 32767.0; + } + if (stickX != 0 && abs(stickX) > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) { + stickXNorm = stickX / 32767.0; + } + + yOffset += stickY/5000; + xOffset -= stickX/5000; + + renderWeirdGradient(&globalBackBuffer, xOffset, yOffset); + + // Sound test + DWORD playCursor; + DWORD writeCursor; + if (SUCCEEDED(globalSecondaryBuffer->GetCurrentPosition(&playCursor, &writeCursor))) { + DWORD byteToLock = (soundOutput.runningSampleIndex*soundOutput.bytesPerSample) % soundOutput.secondaryBufferSize; + DWORD bytesToWrite; + if (byteToLock == playCursor) { + bytesToWrite = 0; + } else if (byteToLock > playCursor) { + bytesToWrite = soundOutput.secondaryBufferSize - byteToLock + playCursor; + } else { + bytesToWrite = playCursor - byteToLock; + } + win32FillSoundBuffer(&soundOutput, byteToLock, bytesToWrite); + } + + win32DrawBufferInWindow(&globalBackBuffer, window); } } else { // failed