1094 lines
28 KiB
C++
1094 lines
28 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
|
|
|