4coder/non-source/test_data/lots_of_files/jam_win32.cpp

1094 lines
29 KiB
C++

/*
* Overreact - Mr. 4th Dimention
* Allen Webster
* 03.21.2015 (mm.dd.yyyy)
*
* Platform Layer: Win32.
*/
// TOP
// TODO(allen): memcpy sound samples for performance
// TODO(allen): migrate as much as possible to c standard lib
#include "jam_meta.h"
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#define SOFTWARE 0
#define OPENGL 1
#define DISABLE_CONTROLLER 1
#define RENDER_MODE OPENGL
#if RENDER_MODE == OPENGL
#include <GL/glew.h>
#endif
#include "jam_system.h"
#include "jam_rendering_system.h"
#include "jam_rendering.h"
#include "jam.h"
#include "jam_math.cpp"
#include "jam_graphics.h"
#include "jam_graphics.cpp"
#include "jam_audio.h"
#include "jam_audio.cpp"
#include "gjCStyle.h"
#include "jam_game.h"
#include "jam.cpp"
#include <Windows.h>
#if !DISABLE_CONTROLLER
#include <XInput.h>
#endif
#include <DSound.h>
#define WINERROR(msg)
#include "jam_rendering_win32.cpp"
internal
SIG_SYS_LOAD_FILE(system_load_file){
File result = {};
HANDLE winfile = CreateFileA(filename, GENERIC_READ,
0, 0, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, 0);
if (winfile){
LARGE_INTEGER size;
// TODO(allen): GetFileSizeEx is incompatible with windows store.
if (GetFileSizeEx(winfile, &size)){
result.size = size.QuadPart;
result.data = VirtualAlloc(0, (i32)result.size,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE);
u32 unread = (u32)result.size;
while (unread != 0){
DWORD read_size = Min(unread, max_u32);
DWORD true_read_size;
u8 *write_pos = (u8*)result.data;
if (ReadFile(winfile, write_pos, read_size, &true_read_size, 0)){
if (unread >= true_read_size){
unread -= true_read_size;
write_pos += true_read_size;
}
else{
break;
}
}
else{
break;
}
}
if (unread != 0){
system_free_file(result);
result = {};
}
}
CloseHandle(winfile);
}
return result;
}
internal
SIG_SYS_SAVE_FILE(system_save_file){
HANDLE winfile = CreateFileA(filename, GENERIC_WRITE,
0, 0, OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL, 0);
if (winfile){
u32 unwritten = (u32)file.size;
while (unwritten != 0){
DWORD write_size = Min(unwritten, max_u32);
DWORD true_write_size;
u8 *src_pos = (u8*)file.data;
if (ReadFile(winfile, src_pos, write_size, &true_write_size, 0)){
if (unwritten >= true_write_size){
unwritten -= true_write_size;
src_pos += true_write_size;
}
else{
break;
}
}
else{
break;
}
}
}
}
internal
SIG_SYS_FREE_FILE(system_free_file){
VirtualFree(file.data, 0, MEM_RELEASE);
}
#if !DISABLE_CONTROLLER
#define SIG_Input_Get_State(name) DWORD(name)(DWORD user_index, XINPUT_STATE *state)
#define SIG_Input_Set_State(name) DWORD(name)(DWORD user_index, XINPUT_VIBRATION *vibration)
typedef SIG_Input_Get_State(Input_Get_State);
typedef SIG_Input_Set_State(Input_Set_State);
struct Win32ControllerFunctions{
bool32 is_valid;
Input_Get_State *get_state;
Input_Set_State *set_state;
};
#endif
#define SIG_Sound_Create(name) HRESULT(name)(LPGUID guid, LPDIRECTSOUND *ds, LPUNKNOWN unk);
typedef SIG_Sound_Create(Sound_Create);
struct Win32SoundFunctions{
Sound_Create *create;
};
struct Win32Vars{
Game_Memory memory;
HWND winhandle;
Win32RenderingVars render_vars;
i32 min_width, min_height;
i32 min_game_width, min_game_height;
i32 pref_game_width, pref_game_height;
bool32 is_fullscreen;
bool32 has_prev_winrect;
RECT prev_winrect;
LPDIRECTSOUNDBUFFER sound_buffer;
i32 samples_per_second;
i32 bytes_per_sample;
i32 sound_buffer_size;
u32 sound_sample_index;
i16* samples;
i32 sound_safety_margin;
i16 mx, my;
i64 msecond_per_frame;
real32 update_hz;
bool32 keep_playing;
bool32 first;
Game_Input keyboard_input, controller_input;
#if 0
Win32ControllerFunctions xin;
#endif
bool32 toggle_full_screen;
char btn1, btn2;
};
globalvar Win32Vars win32;
internal
SIS_SYS_SET_KEYBIND(system_set_keybind){
Assert(key >= 'A' && key <= 'Z');
if (btn_id == 1){
win32.btn1 = key;
}
else if (btn_id == 2){
win32.btn2 = key;
}
}
#if 0
internal void
Win32InputSetup(){
HMODULE xinput = 0;
xinput = LoadLibrary("Xinput1_4.dll");
if (!xinput){
xinput = LoadLibrary("Xinput9_1_0.dll");
}
if (!xinput){
xinput = LoadLibrary("Xinput1_3.dll");
}
if (xinput){
win32.xin.get_state = (Input_Get_State*)GetProcAddress(xinput, "XInputGetState");
win32.xin.set_state = (Input_Set_State*)GetProcAddress(xinput, "XInputSetState");
if (!win32.xin.get_state || !win32.xin.set_state){
win32.xin = {};
}
else{
win32.xin.is_valid = 1;
}
}
}
struct Win32ControllerData{
real32 lstick_x, lstick_y;
real32 rstick_x, rstick_y;
bool8 plugged_in;
bool8 up, down, left, right;
bool8 start, back;
bool8 left_thumb, right_thumb;
bool8 left_shoulder, right_shoulder;
bool8 a, b, x, y;
bool8 ltrigger, rtrigger;
};
inline internal void
Win32RealStick(real32 *stick, i16 input, i16 deadzone){
if (input > deadzone){
*stick = (input - deadzone) / (32767.f - deadzone);
}
else if (input < -deadzone){
*stick = (input + deadzone) / (32768.f - deadzone);
}
else{
*stick = 0;
}
}
internal void
Win32ControllerGet(Win32ControllerData *data, i32 index){
XINPUT_STATE state;
DWORD success = win32.xin.get_state(index, &state);
data->plugged_in = (success == ERROR_SUCCESS);
if (success == ERROR_SUCCESS){
data->up = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_UP);
data->down = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_DOWN);
data->left = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_LEFT);
data->right = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_RIGHT);
data->start = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_START);
data->back = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_BACK);
data->left_thumb = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_THUMB);
data->right_thumb = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_THUMB);
data->left_shoulder = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_SHOULDER);
data->right_shoulder = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_SHOULDER);
data->a = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_A);
data->b = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_B);
data->x = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_X);
data->y = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_Y);
if (state.Gamepad.bLeftTrigger >= 224){
data->ltrigger = 1;
}
else{
data->ltrigger = 0;
}
if (state.Gamepad.bRightTrigger >= 224){
data->rtrigger = 1;
}
else{
data->rtrigger = 0;
}
Win32RealStick(&data->lstick_x, state.Gamepad.sThumbLX, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
Win32RealStick(&data->lstick_y, state.Gamepad.sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
Win32RealStick(&data->rstick_x, state.Gamepad.sThumbRX, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
Win32RealStick(&data->rstick_y, state.Gamepad.sThumbRY, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
}
}
internal bool32
Win32ControllerRumble(i32 index, real32 left, real32 right){
XINPUT_VIBRATION vals;
vals.wLeftMotorSpeed = (u16)(left*max_u16);
vals.wRightMotorSpeed = (u16)(right*max_u16);
DWORD success = win32.xin.set_state(index, &vals);
return (success == ERROR_SUCCESS);
}
#endif
internal Win32SoundFunctions
Win32SoundSetup(i32 samples_per_second, i32 buffer_size){
Win32SoundFunctions funcs = {};
HMODULE dsound = LoadLibrary("dsound.dll");
if (dsound){
funcs.create = (Sound_Create*)GetProcAddress(dsound, "DirectSoundCreate");
if (funcs.create){
LPDIRECTSOUND dsound;
if (SUCCEEDED(funcs.create(0, &dsound, 0))){
WAVEFORMATEX wave_format = {};
wave_format.wFormatTag = WAVE_FORMAT_PCM;
wave_format.nChannels = 2;
wave_format.nSamplesPerSec = samples_per_second;
wave_format.wBitsPerSample = 16;
wave_format.nBlockAlign = (wave_format.nChannels * wave_format.wBitsPerSample) / 8;
wave_format.nAvgBytesPerSec = wave_format.nSamplesPerSec*wave_format.nBlockAlign;
wave_format.cbSize = 0;
if (SUCCEEDED(dsound->SetCooperativeLevel(win32.winhandle, DSSCL_PRIORITY))){
DSBUFFERDESC buffer_description = {};
buffer_description.dwSize = sizeof(buffer_description);
buffer_description.dwFlags = DSBCAPS_PRIMARYBUFFER;
LPDIRECTSOUNDBUFFER buffer;
if (SUCCEEDED(dsound->CreateSoundBuffer(&buffer_description, &buffer, 0))){
if (buffer->SetFormat(&wave_format)){
// NOTE(allen): SUCCESS - set primary buffer format
}
else{
// NOTE(allen): FAIL - set primary buffer format
}
}
else{
// NOTE(allen): FAIL - create primary buffer
}
}
else{
// NOTE(allen): FAIL - set cooperative level
}
DSBUFFERDESC buffer_description = {};
buffer_description.dwSize = sizeof(buffer_description);
buffer_description.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
buffer_description.dwBufferBytes = buffer_size;
buffer_description.lpwfxFormat = &wave_format;
if (SUCCEEDED(dsound->CreateSoundBuffer(&buffer_description, &win32.sound_buffer, 0))){
// NOTE(allen): SUCCESS - create secondary buffer
}
else{
// NOTE(allen): FAIL - create secondary buffer
}
}
else{
// NOTE(allen): FAIL - create direct sound interface
}
}
else{
// NOTE(allen): FAIL - load direct sound creator function
}
}
else{
// NOTE(allen): FAIL - load direct sound library
}
return funcs;
}
internal void
Win32ClearSound(){
VOID *chunk1, *chunk2;
DWORD size1, size2;
if (SUCCEEDED(win32.sound_buffer->Lock(0, win32.sound_buffer_size,
&chunk1, &size1, &chunk2, &size2,
0))){
u8 *dest = (u8*)chunk1;
if (dest){
for (DWORD i = 0; i < size1; ++i){
*dest = 0;
++dest;
}
}
dest = (u8*)chunk2;
if (dest){
for (DWORD i = 0; i < size1; ++i){
*dest = 0;
++dest;
}
}
win32.sound_buffer->Unlock(chunk1, size1, chunk2, size2);
}
}
internal void
Win32FillSound(DWORD write_start, DWORD write_size, i16 *source){
VOID *chunk1, *chunk2;
DWORD size1, size2;
if (SUCCEEDED(win32.sound_buffer->Lock(write_start, write_size,
&chunk1, &size1, &chunk2, &size2,
0))){
DWORD samples = size1/win32.bytes_per_sample;
i16 *dest = (i16*)chunk1;
if (dest){
for (DWORD i = 0; i < samples; ++i){
*dest++ = *source++;
*dest++ = *source++;
++win32.sound_sample_index;
}
}
samples = size2/win32.bytes_per_sample;
dest = (i16*)chunk2;
if (dest){
for (DWORD i = 0; i < samples; ++i){
*dest++ = *source++;
*dest++ = *source++;
++win32.sound_sample_index;
}
}
win32.sound_buffer->Unlock(chunk1, size1, chunk2, size2);
}
}
inline internal void
Win32UpdateKey(bool32 press, bool32 release, bool8 *key){
if (press){
*key = 1;
}
if (release){
*key = 0;
}
}
internal LRESULT
Win32Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
LRESULT result = {};
switch (uMsg){
case WM_KEYDOWN:
case WM_KEYUP:
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
{
bool32 prev, current;
prev = ((lParam & Bit_30)?(1):(0));
current = ((lParam & Bit_31)?(0):(1));
bool32 press = current && !prev;
bool32 release = prev && !current;
switch (wParam){
case VK_UP:
{
Win32UpdateKey(press, release, &win32.keyboard_input.digital.up);
}break;
case VK_DOWN:
{
Win32UpdateKey(press, release, &win32.keyboard_input.digital.down);
}break;
case VK_LEFT:
{
Win32UpdateKey(press, release, &win32.keyboard_input.digital.left);
}break;
case VK_RIGHT:
{
Win32UpdateKey(press, release, &win32.keyboard_input.digital.right);
}break;
case VK_SPACE:
case VK_RETURN:
{
Win32UpdateKey(press, release, &win32.keyboard_input.button[0]);
if (uMsg == WM_KEYDOWN){
win32.keyboard_input.key_input = 1;
win32.keyboard_input.key_code = 1;
}
}break;
case VK_BACK:
{
Win32UpdateKey(press, release, &win32.keyboard_input.back);
if (uMsg == WM_KEYDOWN){
win32.keyboard_input.key_input = 1;
win32.keyboard_input.key_code = 0;
}
}break;
case VK_ESCAPE:
{
Win32UpdateKey(press, release, &win32.keyboard_input.pause);
}break;
case VK_F2:
{
Win32UpdateKey(press, release, &win32.keyboard_input.toggle_edit_mode);
}break;
case VK_SHIFT: break;
case VK_CONTROL:
{
#if 0
if (press){
win32.toggle_full_screen = 1;
}
#endif
}break;
case VK_MENU:
{
win32.first = 1;
}break; /*ALT*/
default:
{
if ((char)wParam == win32.btn1){
Win32UpdateKey(press, release, &win32.keyboard_input.button[1]);
}
if ((char)wParam == win32.btn2){
Win32UpdateKey(press, release, &win32.keyboard_input.button[2]);
}
if (uMsg == WM_KEYDOWN){
if ((wParam >= '0' && wParam <= '9') ||
(wParam >= 'A' && wParam <= 'Z')){
win32.keyboard_input.key_input = 1;
win32.keyboard_input.key_code = (char)wParam;
}
}
}break;
}
}break;
case WM_LBUTTONDOWN:
{
win32.mx = LOWORD(lParam);
win32.my = HIWORD(lParam);
}break;
case WM_LBUTTONUP:
{
win32.mx = LOWORD(lParam);
win32.my = HIWORD(lParam);
}break;
case WM_RBUTTONDOWN:
{
win32.mx = LOWORD(lParam);
win32.my = HIWORD(lParam);
}break;
case WM_RBUTTONUP:
{
win32.mx = LOWORD(lParam);
win32.my = HIWORD(lParam);
}break;
case WM_MOUSEMOVE:
{
win32.mx = LOWORD(lParam);
win32.my = HIWORD(lParam);
}break;
case WM_SIZE:
{
i32 w, h;
w = LOWORD(lParam);
h = HIWORD(lParam);
render_set_screen_size(&win32.render_vars, w, h, win32.pref_game_width, win32.pref_game_height);
}break;
case WM_GETMINMAXINFO:
{
MINMAXINFO* mmi = (MINMAXINFO*)lParam;
mmi->ptMinTrackSize.x = win32.min_width;
mmi->ptMinTrackSize.y = win32.min_height;
return 0;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps); AllowLocal(hdc);
//win32.render_vars.hdc = hdc;
render_redraw_screen(&win32.render_vars);
EndPaint(hwnd, &ps);
}break;
case WM_CLOSE:
case WM_DESTROY:
{
win32.keep_playing = 0;
}break;
case WM_NCLBUTTONDOWN:
{
DWORD status;
win32.sound_buffer->GetStatus(&status);
if(status & DSBSTATUS_PLAYING){
win32.sound_buffer->Stop();
}
result = DefWindowProc(hwnd, uMsg, wParam, lParam);
}break;
case WM_ENTERSIZEMOVE:
{
DWORD status;
win32.sound_buffer->GetStatus(&status);
if(status & DSBSTATUS_PLAYING){
win32.sound_buffer->Stop();
}
}break;
case WM_EXITSIZEMOVE:
{
DWORD status;
win32.sound_buffer->GetStatus(&status);
if(!(status & DSBSTATUS_PLAYING)){
win32.sound_buffer->Play(0, 0, DSBPLAY_LOOPING);
}
}break;
default:
{
result = DefWindowProc(hwnd, uMsg, wParam, lParam);
}break;
}
return result;
}
internal i64
Win32GetTime(){
i64 result = 0;
LARGE_INTEGER time;
if (QueryPerformanceCounter(&time)){
result = (i64)time.QuadPart;
}
return result;
}
internal bool32
Win32GoFullscreen(){
HMONITOR monitor = MonitorFromWindow(win32.winhandle, MONITOR_DEFAULTTOPRIMARY);
if (!monitor){
WINERROR("Failed to get monitor handle");
return 0;
}
MONITORINFO monitor_info;
monitor_info.cbSize = sizeof(monitor_info);
if (!GetMonitorInfo(monitor, &monitor_info)){
WINERROR("Failed to get monitor information");
return 0;
}
if (!GetWindowRect(win32.winhandle, &win32.prev_winrect)){
WINERROR("Failed to save previous window size");
}
else{
win32.has_prev_winrect = 1;
}
SetWindowLongPtr(win32.winhandle, GWL_STYLE,
WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE);
MoveWindow(win32.winhandle,
monitor_info.rcMonitor.left, monitor_info.rcMonitor.top,
monitor_info.rcMonitor.right - monitor_info.rcMonitor.left,
monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top,
TRUE);
win32.is_fullscreen = 1;
return 1;
}
internal bool32
Win32GoWindowed(){
SetWindowLongPtr(win32.winhandle, GWL_STYLE,
WS_OVERLAPPEDWINDOW | WS_VISIBLE);
if (win32.has_prev_winrect){
MoveWindow(win32.winhandle,
win32.prev_winrect.left, win32.prev_winrect.top,
win32.prev_winrect.right - win32.prev_winrect.left,
win32.prev_winrect.bottom - win32.prev_winrect.top,
TRUE);
}
else{
RECT winrect = {};
winrect.right = win32.min_game_width;
winrect.bottom = win32.min_game_height;
if (!AdjustWindowRect(&winrect, WS_OVERLAPPEDWINDOW, 0)){
return 0;
}
i32 win_x, win_y;
win_x = 0;
win_y = 0;
// TODO(allen): This is now duplicated... so fix that?
HMONITOR monitor = MonitorFromWindow(win32.winhandle, MONITOR_DEFAULTTOPRIMARY);
if (!monitor){
WINERROR("Failed to get monitor handle");
}
else{
MONITORINFO monitor_info;
monitor_info.cbSize = sizeof(monitor_info);
if (!GetMonitorInfo(monitor, &monitor_info)){
WINERROR("Failed to get monitor information");
}
else{
// TODO: This will be easier when the get monitor info stuff
// is pulled into it's own function so that it is only a matter
// of seeing the success or failure of that one call.
win_x =
monitor_info.rcMonitor.right -
monitor_info.rcMonitor.left -
(winrect.right - winrect.left);
win_y =
monitor_info.rcMonitor.bottom -
monitor_info.rcMonitor.top -
(winrect.bottom - winrect.top);
win_x /= 2;
win_y /= 2;
}
}
MoveWindow(win32.winhandle,
win_x, win_y,
winrect.right - winrect.left,
winrect.bottom - winrect.top,
TRUE);
}
return 1;
}
int __stdcall
WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow){
win32 = {};
win32.btn1 = 'Z';
win32.btn2 = 'X';
win32.update_hz = 30.f;
win32.msecond_per_frame = (i64)(1000.f/win32.update_hz);
win32.min_game_width = 800;
win32.min_game_height = 600;
win32.pref_game_width = 800;
win32.pref_game_height = 600;
WNDCLASSEX winclass = {};
winclass.cbSize = sizeof(WNDCLASSEX);
#if RENDER_MODE == SOFTWARE
winclass.style = CS_HREDRAW | CS_VREDRAW;
#elif RENDER_MODE == OPENGL
winclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
#endif
winclass.lpfnWndProc = (WNDPROC)&Win32Proc;
winclass.hInstance = hInstance;
winclass.hIcon = 0; // TODO(allen): Setup this icon!
winclass.hCursor = 0;
winclass.lpszClassName = "TMUND-win-class";
winclass.hIconSm = 0;
#if !DISABLE_CONTROLLER
Win32InputSetup();
#endif
ATOM classatom;
classatom = RegisterClassEx(&winclass);
if (!classatom){
WINERROR("Failed to setup window class");
return 0;
}
RECT winrect = {};
winrect.right = win32.min_game_width;
winrect.bottom = win32.min_game_height;
if (!AdjustWindowRect(&winrect, WS_OVERLAPPEDWINDOW, 0)){
WINERROR("Failed to compute window size");
return 0;
}
HWND winhandle;
winhandle = CreateWindow(winclass.lpszClassName,
"THE MOST UNDEAD", //NOTE(allen): Title
WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE
,
CW_USEDEFAULT, CW_USEDEFAULT,
winrect.right - winrect.left,
winrect.bottom - winrect.top,
0, 0, hInstance, 0
);
win32.min_width = winrect.right - winrect.left;
win32.min_height = winrect.bottom - winrect.top;
if (!winhandle){
WINERROR("Failed to setup window");
return 0;
}
win32.winhandle = winhandle;
win32.render_vars.hdc = GetDC(win32.winhandle);
render_startup(&win32.render_vars);
//ReleaseDC(win32.winhandle, win32.render_vars.hdc);
GetClientRect(winhandle, &winrect);
render_set_screen_size(&win32.render_vars,
winrect.right - winrect.left,
winrect.bottom - winrect.top,
win32.min_game_width, win32.min_game_height);
#if RENDER_MODE == SOFTWARE
ShowWindow(win32.winhandle, SW_SHOW);
UpdateWindow(win32.winhandle);
#endif
win32.samples_per_second = 48000;
win32.bytes_per_sample = sizeof(i16)*2;
win32.sound_buffer_size = win32.samples_per_second*win32.bytes_per_sample;
win32.sound_safety_margin = (i32)
(win32.samples_per_second*win32.bytes_per_sample/(win32.update_hz*3.f));
win32.sound_safety_margin = LargeRoundUp(win32.sound_safety_margin, win32.bytes_per_sample);
Win32SoundSetup(win32.samples_per_second, win32.sound_buffer_size);
Win32ClearSound();
win32.sound_buffer->Play(0, 0, DSBPLAY_LOOPING);
win32.samples = (i16*)VirtualAlloc(0, win32.sound_buffer_size,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE);
if (!win32.samples){
WINERROR("Failed to setup audio buffer memory");
return 0;
}
Game_Memory memory;
memory.size = Mbytes(256);
memory.data = VirtualAlloc(0, memory.size,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE);
win32.memory = memory;
bool32 good_sound = 0;
win32.keep_playing = 1;
timeBeginPeriod(1);
win32.first = 1;
MSG msg;
i64 prev_work_time = Win32GetTime();
i64 flip_time = prev_work_time;
i64 start_time = prev_work_time;
i64 end_time = prev_work_time;
AllowLocal(start_time);
AllowLocal(end_time);
while (win32.keep_playing){
win32.toggle_full_screen = 0;
win32.keyboard_input.key_input = 0;
while (PeekMessageA(&msg, winhandle, 0, 0, PM_REMOVE)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
#if !DISABLE_CONTROLLER
Win32ControllerData controller = {};
if (win32.xin.is_valid){
// TODO(allen): Multiple controller support?
Win32ControllerGet(&controller, 0);
}
if (controller.plugged_in){
if (controller.lstick_x != 0.f ||
controller.lstick_y != 0.f){
win32.controller_input.is_analog = 1;
win32.controller_input.analog.stick_x = controller.lstick_x;
win32.controller_input.analog.stick_y = -controller.lstick_y;
}
else{
win32.controller_input.is_analog = 0;
win32.controller_input.digital.right = controller.right;
win32.controller_input.digital.left = controller.left;
win32.controller_input.digital.up = controller.up;
win32.controller_input.digital.down = controller.down;
}
win32.controller_input.button[0] = controller.a;
win32.controller_input.button[1] = controller.b;
win32.controller_input.button[2] = controller.x;
win32.controller_input.button[3] = controller.y;
win32.controller_input.button[4] = controller.left_shoulder;
win32.controller_input.button[5] = controller.right_shoulder;
win32.controller_input.button[6] = controller.ltrigger;
win32.controller_input.button[7] = controller.rtrigger;
win32.controller_input.pause = controller.start;
win32.controller_input.back = controller.back;
}
#endif
Game_Step_Target step_target;
step_target.render = render_get_render_target(&win32.render_vars);
step_target.can_fullscreen = 1;
step_target.is_fullscreen = &win32.is_fullscreen;
step_target.audio_samples_per_second = win32.samples_per_second;
step_target.second_per_frame = 1.f/win32.update_hz;
step_target.btn1 = win32.btn1;
step_target.btn2 = win32.btn2;
bool32 was_fullscreen = win32.is_fullscreen;
#define ComposeControls(name) input.name = win32.keyboard_input.name || win32.controller_input.name
Game_Input input;
if (win32.controller_input.is_analog){
input.is_analog = 1;
input.analog.stick_x = win32.controller_input.analog.stick_x;
input.analog.stick_y = win32.controller_input.analog.stick_y;
}
else{
input.is_analog = 0;
ComposeControls(digital.up);
ComposeControls(digital.down);
ComposeControls(digital.left);
ComposeControls(digital.right);
}
ComposeControls(button[0]);
ComposeControls(button[1]);
ComposeControls(button[2]);
ComposeControls(button[3]);
ComposeControls(button[4]);
ComposeControls(button[5]);
ComposeControls(button[6]);
ComposeControls(button[7]);
ComposeControls(pause);
ComposeControls(back);
ComposeControls(toggle_edit_mode);
input.key_input = win32.keyboard_input.key_input;
input.key_code = win32.keyboard_input.key_code;
if (game_step(step_target, input, memory, win32.first)){
win32.keep_playing = 0;
}
if (win32.toggle_full_screen){
win32.is_fullscreen = !was_fullscreen;
}
win32.first = 0;
if (win32.is_fullscreen && !was_fullscreen){
if (!Win32GoFullscreen()){
win32.is_fullscreen = 0;
}
}
else if (!win32.is_fullscreen && was_fullscreen){
Win32GoWindowed();
}
i64 audio_time = Win32GetTime();
i64 time_ellapsed_before_audio = audio_time - flip_time;
DWORD play_cursor, write_cursor;
if (win32.sound_buffer->GetCurrentPosition(&play_cursor, &write_cursor) == DS_OK){
if (!good_sound){
win32.sound_sample_index = write_cursor / win32.bytes_per_sample;
good_sound = 1;
}
DWORD write_start =
((win32.sound_sample_index*win32.bytes_per_sample) % win32.sound_buffer_size);
DWORD expected_bytes_per_frame = (DWORD)
(win32.bytes_per_sample*win32.samples_per_second*win32.msecond_per_frame/1000);
i64 useconds_til_flip = (win32.msecond_per_frame*1000 - time_ellapsed_before_audio);
DWORD expected_bytes_til_flip = (DWORD)
(win32.bytes_per_sample*win32.samples_per_second*useconds_til_flip/1000000);
DWORD expected_frame_bounary = play_cursor + expected_bytes_til_flip;
DWORD safe_cursor = write_cursor;
if (safe_cursor < play_cursor){
safe_cursor += win32.sound_buffer_size;
}
safe_cursor += win32.sound_safety_margin;
bool32 low_latency = (safe_cursor < expected_frame_bounary);
DWORD target = 0;
if (low_latency){
target = (expected_frame_bounary + expected_bytes_per_frame);
}
else{
target = (write_cursor + expected_bytes_per_frame + win32.sound_safety_margin);
}
target = target % win32.sound_buffer_size;
DWORD write_size = 0;
if (write_start > target){
write_size = win32.sound_buffer_size - write_start + target;
}
else{
write_size = target - write_start;
}
Game_Audio_Target audio_target;
audio_target.samples = win32.samples;
audio_target.count_per_channel = write_size / win32.bytes_per_sample;
audio_target.channels = 2;
audio_target.samples_per_second = win32.samples_per_second;
audio_target.second_per_frame = 1.f/win32.update_hz;
game_fill_samples(audio_target, memory);
Win32FillSound(write_start, write_size, win32.samples);
}
else{
good_sound = 0;
}
i64 work_time = Win32GetTime();
i64 run_time = ((work_time - prev_work_time)/1000);
if (run_time < win32.msecond_per_frame){
Sleep((DWORD)(win32.msecond_per_frame - run_time));
work_time = Win32GetTime();
if ((DWORD)((work_time - prev_work_time)/1000) < win32.msecond_per_frame){
// NOTE(allen): MINOR - sleep did not work
}
while ((DWORD)((work_time - prev_work_time)/1000) < win32.msecond_per_frame){
work_time = Win32GetTime();
}
}
else{
// NOTE(allen): FAIL - missed frame rate
int x = 0;
AllowLocal(x);
}
prev_work_time = Win32GetTime();
//win32.render_vars.hdc = GetDC(win32.winhandle);
render_redraw_screen(&win32.render_vars);
//ReleaseDC(win32.winhandle, win32.render_vars.hdc);
flip_time = Win32GetTime();
}
// TODO(allen): Must we do this? We know we're ending, seems like
// this might be handled by OS just fine.
//win32.render_vars.hdc = GetDC(win32.winhandle);
render_shutdown(&win32.render_vars);
//ReleaseDC(win32.winhandle, win32.render_vars.hdc);
return 0;
}
// BOTTOM