vn/code/win32_main.cpp

865 lines
26 KiB
C++

#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#include "timeapi.h"
#include "vn_platform.h"
#if W32_LINK_SINGLE
#include "vn.cpp"
#endif
#include "win32_main.h"
#include "win32_opengl.cpp"
global win32_state Global_Win32State;
global WINDOWPLACEMENT Global_WindowPosition = {sizeof(Global_WindowPosition)};;
static void Win32_PlatformError(char *Message, bool IsFatal)
{
MessageBoxA(0, Message, "vn - Platform Error", MB_OK|(IsFatal?MB_ICONSTOP:MB_ICONEXCLAMATION));
if(IsFatal)
{
ExitProcess((UINT)-1);
}
}
static PLATFORM_SHOW_MESSAGE(Win32_ShowMessage)
{
DWORD Flags = MB_OK;
switch(Type)
{
case Platform_Message_Info: { Flags |= MB_ICONINFORMATION; } break;
case Platform_Message_Warning: { Flags |= MB_ICONWARNING; } break;
case Platform_Message_Error: { Flags |= MB_ICONEXCLAMATION; } break;
case Platform_Message_Fatal: { Flags |= MB_ICONSTOP; } break;
InvalidDefaultCase;
}
// sixten(NOTE): Check for null-termination.
Assert(Message.Data[Message.Count] == 0);
MessageBoxA(0, (char *)Message.Data, "vn - A message from the developer", Flags);
if(Type == Platform_Message_Fatal)
{
ExitProcess((UINT)-1);
}
}
static u64 Win32_GetPageSize(void)
{
SYSTEM_INFO Info;
GetSystemInfo(&Info);
return(Info.dwPageSize);
}
static PLATFORM_RESERVE(Win32_Reserve)
{
u64 GigabyteAlignedSize = Size+Gigabytes(1)-1;
GigabyteAlignedSize -= GigabyteAlignedSize%Gigabytes(1);
void *Result = VirtualAlloc(0, GigabyteAlignedSize, MEM_RESERVE, PAGE_NOACCESS);
return(Result);
}
static PLATFORM_RELEASE(Win32_Release)
{
VirtualFree(Pointer, 0, MEM_RELEASE);
}
static PLATFORM_COMMIT(Win32_Commit)
{
u64 PageAlignedSize = Size+Win32_GetPageSize()-1;
PageAlignedSize -= PageAlignedSize%Win32_GetPageSize();
VirtualAlloc(Pointer, PageAlignedSize, MEM_COMMIT, PAGE_READWRITE);
}
static PLATFORM_DECOMMIT(Win32_Decommit)
{
VirtualFree(Pointer, Size, MEM_DECOMMIT);
}
static PLATFORM_ALLOCATE(Win32_Allocate)
{
void *Result = VirtualAlloc(0, Size, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
return(Result);
}
static PLATFORM_DEALLOCATE(Win32_Deallocate)
{
VirtualFree(Pointer, 0, MEM_DECOMMIT|MEM_RELEASE);
}
static PLATFORM_OPEN_FILE(Win32_OpenFile)
{
DWORD DesiredAccess = 0;
if(FileAccess & PlatformAccess_Read)
{
DesiredAccess |= GENERIC_READ;
}
if(FileAccess & PlatformAccess_Write)
{
DesiredAccess |= GENERIC_WRITE;
}
DWORD CreationAttributes = 0;
if(FileAccess & PlatformAccess_Read)
{
CreationAttributes = OPEN_EXISTING;
}
if(FileAccess & PlatformAccess_Write)
{
CreationAttributes = CREATE_ALWAYS;
}
temporary_memory Scratch = GetScratch(0, 0);
string FullPath = PushFormat(Scratch.Arena, "%S\\%S", Global_Win32State.ContentsPath, Path);
HANDLE File = CreateFileA((char *)FullPath.Data, DesiredAccess, 0, 0, CreationAttributes, 0, 0);
ReleaseScratch(Scratch);
platform_file_handle Result = {};
Result.Platform = (u64)File;
Result.IsValid = (File != INVALID_HANDLE_VALUE);
return(Result);
}
static PLATFORM_CLOSE_FILE(Win32_CloseFile)
{
HANDLE File = (HANDLE)Handle.Platform;
if(File != INVALID_HANDLE_VALUE)
{
CloseHandle(File);
}
}
static PLATFORM_READ_FILE(Win32_ReadFile)
{
HANDLE File = (HANDLE)Handle.Platform;
if(File != INVALID_HANDLE_VALUE)
{
DWORD BytesRead;
ReadFile(File, Dest, Size, &BytesRead, 0);
Assert(BytesRead == Size);
}
}
static PLATFORM_WRITE_FILE(Win32_WriteFile)
{
HANDLE File = (HANDLE)Handle.Platform;
if(File != INVALID_HANDLE_VALUE)
{
DWORD BytesWritten;
WriteFile(File, Source, Size, &BytesWritten, 0);
Assert(BytesWritten == Size);
}
}
static PLATFORM_GET_FILE_SIZE(Win32_GetFileSize)
{
u64 Result = 0;
HANDLE File = (HANDLE)Handle.Platform;
if(File != INVALID_HANDLE_VALUE)
{
LARGE_INTEGER FileSize;
GetFileSizeEx(File, &FileSize);
Result = FileSize.QuadPart;
}
return(Result);
}
static PLATFORM_SET_CURSOR(Win32_SetCursor)
{
Global_Win32State.Cursor = Cursor;
}
static PLATFORM_BEGIN_FILE_ITER(Win32_BeginFileIter)
{
win32_file_find_data *FileFindData = PushStruct(Arena, win32_file_find_data);
temporary_memory Scratch = GetScratch(&Arena, 1);
Path = PushFormat(Scratch.Arena, "%S/%S*", Global_Win32State.ContentsPath, Path);
string16 Path16 = String16FromString8(Scratch.Arena, Path);
FileFindData->Handle = FindFirstFileW((WCHAR *)Path16.Data, &FileFindData->FindData);
ReleaseScratch(Scratch);
platform_file_iter *Iter = (platform_file_iter *)FileFindData;
return(Iter);
}
static PLATFORM_ADVANCE_FILE_ITER(Win32_AdvanceFileIter)
{
b32 Result = false;
win32_file_find_data *FileFindData = (win32_file_find_data *)Iter;
WIN32_FIND_DATAW FindData = {};
s64 InvalidCount = 0;
for(;;)
{
if(FileFindData->FindFirstReturned)
{
Result = FindNextFileW(FileFindData->Handle, &FindData);
}
else
{
Result = (FileFindData->Handle != 0 && FileFindData->Handle != INVALID_HANDLE_VALUE);
FindData = FileFindData->FindData;
FileFindData->FindFirstReturned = true;
}
b32 NameIsInvalid = (FindData.cFileName[0] == '.' && (FindData.cFileName[1] == 0 || FindData.cFileName[1] == '.'));
if(NameIsInvalid && InvalidCount < 10)
{
InvalidCount += 1;
continue;
}
break;
}
if(Result != 0)
{
string16 Name16 = {};
Name16.Data = (u16 *)FindData.cFileName;
for(u16 Index = 0; Index < MAX_PATH; Index += 1)
{
if(FindData.cFileName[Index] == 0)
{
break;
}
Name16.Count += 1;
}
*OutInfo = {};
OutInfo->Name = String8FromString16(Arena, Name16);
OutInfo->IsDirectory = FindData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY;
}
return(Result);
}
static PLATFORM_END_FILE_ITER(Win32_EndFileIter)
{
win32_file_find_data *FileFindData = (win32_file_find_data *)Iter;
FindClose(FileFindData->Handle);
}
inline u64 Win32_GetWallClock(void)
{
LARGE_INTEGER Query;
QueryPerformanceCounter(&Query);
u64 Result = Query.QuadPart;
return(Result);
}
inline r64 Win32_GetSecondsElapsed(u64 Start, u64 End)
{
u64 Elapsed = End - Start;
r64 Result = (r64)Elapsed/(r64)Global_Win32State.PerformanceFrequency;
return(Result);
}
inline FILETIME Win32_GetLastWriteTime(char *Path)
{
FILETIME Result = {};
HANDLE File = CreateFileA(Path, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
if(File)
{
FILETIME Creation, LastAccess, LastWrite;
GetFileTime(File, &Creation, &LastAccess, &LastWrite);
Result = LastWrite;
CloseHandle(File);
}
return(Result);
}
#if !W32_LINK_SINGLE
static win32_loaded_code Win32_LoadCode(void)
{
win32_loaded_code Code = {};
win32_state *State = &Global_Win32State;
if(CopyFile(State->DLLPath, State->TempDLLPath, FALSE))
{
Code.LastWriteTime = Win32_GetLastWriteTime(State->DLLPath);
Code.DLL = LoadLibraryA(State->TempDLLPath);
if(Code.DLL)
{
Code.UpdateAndRender = (vn_update_and_render *)GetProcAddress(Code.DLL, "VN_UpdateAndRender");
if(Code.UpdateAndRender)
{
Code.IsValid = true;
}
}
}
return(Code);
}
static void Win32_UnloadCode(win32_loaded_code *Code)
{
if(Code->DLL)
{
FreeLibrary(Code->DLL);
}
*Code = {};
}
static void Win32_UpdateCode(win32_loaded_code *Code)
{
win32_state *State = &Global_Win32State;
FILETIME LastWriteTime = Win32_GetLastWriteTime(State->DLLPath);
if(CompareFileTime(&Code->LastWriteTime, &LastWriteTime) != 0)
{
Win32_UnloadCode(Code);
*Code = Win32_LoadCode();
// sixten(NOTE): Sometimes the program decides to crash upon reloads, so we just wait for those to be over...
Sleep(500);
}
}
#endif
static PLATFORM_TOGGLE_FULLSCREEN(Win32_ToggleFullscreen)
{
HWND Window = Global_Win32State.Window;
DWORD Style = GetWindowLong(Window, GWL_STYLE);
if(Style & WS_OVERLAPPEDWINDOW)
{
MONITORINFO MonitorInfo = {sizeof(MonitorInfo)};
if(GetWindowPlacement(Window, &Global_WindowPosition) &&
GetMonitorInfo(MonitorFromWindow(Window, MONITOR_DEFAULTTOPRIMARY), &MonitorInfo))
{
// sixten(NOTE): This doesn't work when the window is maximized. One wordaround would be to set the
// window to "normal" size using ShowWindow(Window, SW_SHOWNORMAL) but it looks *very* scuffed.
SetWindowLong(Window, GWL_STYLE, Style & ~WS_OVERLAPPEDWINDOW);
SetWindowPos(Window, HWND_TOP,
MonitorInfo.rcMonitor.left, MonitorInfo.rcMonitor.top,
MonitorInfo.rcMonitor.right - MonitorInfo.rcMonitor.left,
MonitorInfo.rcMonitor.bottom - MonitorInfo.rcMonitor.top,
SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
}
}
else
{
SetWindowLong(Window, GWL_STYLE, Style | WS_OVERLAPPEDWINDOW);
SetWindowPlacement(Window, &Global_WindowPosition);
SetWindowPos(Window, 0, 0, 0, 0, 0,
SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOOWNERZORDER|SWP_FRAMECHANGED);
}
}
static PLATFORM_SET_CLIPBOARD(Win32_SetClipboard)
{
temporary_memory Scratch = GetScratch();
if(OpenClipboard(0))
{
EmptyClipboard();
string16 String16 = String16FromString8(Scratch.Arena, String);
HANDLE CopyHandle = GlobalAlloc(GMEM_MOVEABLE, String16.Count*sizeof(u16)+1);
if(CopyHandle)
{
u16 *CopyBuffer = (u16 *)GlobalLock(CopyHandle);
Copy(CopyBuffer, String16.Data, String16.Count*sizeof(u16));
CopyBuffer[String.Count] = 0;
GlobalUnlock(CopyHandle);
SetClipboardData(CF_UNICODETEXT, CopyHandle);
}
CloseClipboard();
}
ReleaseScratch(Scratch);
}
static PLATFORM_GET_CLIPBOARD(Win32_GetClipboard)
{
string Result = {};
if(IsClipboardFormatAvailable(CF_UNICODETEXT) && OpenClipboard(0))
{
HANDLE DataHandle = GetClipboardData(CF_UNICODETEXT);
if(DataHandle)
{
u16 *Data = (u16 *)GlobalLock(DataHandle);
if(Data)
{
s64 Count = StringLength16(Data);
Result = String8FromString16(Arena, MakeString16(Data, Count));
GlobalUnlock(DataHandle);
}
}
CloseClipboard();
}
return(Result);
}
inline v2 Win32_GetMouseP(HWND Window)
{
POINT Point;
GetCursorPos(&Point);
ScreenToClient(Window, &Point);
v2 Result = V2(Point.x, Point.y);
return(Result);
}
inline v2 Win32_GetWindowDim(HWND Window)
{
RECT ClientRect;
GetClientRect(Window, &ClientRect);
v2 Result = V2(ClientRect.right - ClientRect.left, ClientRect.bottom - ClientRect.top);
return(Result);
}
inline platform_modifiers Win32_GetModifiers(void)
{
platform_modifiers Modifiers = 0;
if(GetKeyState(VK_CONTROL) & 0x8000)
{
Modifiers |= PlatformModifier_Ctrl;
}
if(GetKeyState(VK_SHIFT) & 0x8000)
{
Modifiers |= PlatformModifier_Shift;
}
if(GetKeyState(VK_MENU) & 0x8000)
{
Modifiers |= PlatformModifier_Alt;
}
return(Modifiers);
}
static LRESULT Win32_WindowCallback(HWND Window, UINT Message, WPARAM WParam, LPARAM LParam)
{
LRESULT Result = 0;
win32_state *State = &Global_Win32State;
temporary_memory Scratch = GetScratch(0, 0);
platform_event *Event = 0;
b32 ButtonIsUp = false;
axis2 ScrollAxis = Axis2_Y;
switch(Message)
{
case WM_CLOSE:
{
Event = PushStruct(State->EventArena, platform_event);
Event->Type = PlatformEvent_WindowClose;
} break;
case WM_WINDOWPOSCHANGED:
{
// TODO(casey): For now, we are setting the window styles in here
// because sometimes Windows can reposition our window out of fullscreen
// without going through our ToggleFullscreen(), and we want to put our
// title bar and border back when it does!
WINDOWPOS *NewPos = (WINDOWPOS *)LParam;
b32 BecomingFullscreen = false;
MONITORINFO MonitorInfo = {sizeof(MonitorInfo)};
if(GetMonitorInfo(MonitorFromWindow(Window, MONITOR_DEFAULTTOPRIMARY),
&MonitorInfo))
{
s32 MonWidth = (MonitorInfo.rcMonitor.right - MonitorInfo.rcMonitor.left);
s32 MonHeight = (MonitorInfo.rcMonitor.bottom - MonitorInfo.rcMonitor.top);
BecomingFullscreen = ((MonitorInfo.rcMonitor.left == NewPos->x) &&
(MonitorInfo.rcMonitor.top == NewPos->y) &&
(MonWidth == NewPos->cx) &&
(MonHeight == NewPos->cy));
}
DWORD OldStyle = GetWindowLong(Window, GWL_STYLE);
DWORD FullscreenStyle = OldStyle & ~WS_OVERLAPPEDWINDOW;
DWORD WindowedStyle = OldStyle | WS_OVERLAPPEDWINDOW;
DWORD NewStyle = (BecomingFullscreen) ? FullscreenStyle : WindowedStyle;
if(NewStyle != OldStyle)
{
SetWindowLong(Window, GWL_STYLE, NewStyle);
}
Result = DefWindowProcA(Window, Message, WParam, LParam);
} break;
case WM_MOUSEHWHEEL:
{
ScrollAxis = Axis2_X;
} fallthrough;
case WM_MOUSEWHEEL:
{
Event = PushStruct(State->EventArena, platform_event);
Event->Type = PlatformEvent_MouseScroll;
Event->Scroll.E[ScrollAxis] = GET_WHEEL_DELTA_WPARAM(WParam) / 120.0;
} break;
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
{
ButtonIsUp = true;
} fallthrough;
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
{
platform_event_type Type = ButtonIsUp ? PlatformEvent_Release : PlatformEvent_Press;
platform_key Key = Key_Invalid;
switch(Message)
{
case WM_LBUTTONUP: case WM_LBUTTONDOWN: { Key = Key_MouseLeft; } break;
case WM_MBUTTONUP: case WM_MBUTTONDOWN: { Key = Key_MouseMiddle; } break;
case WM_RBUTTONUP: case WM_RBUTTONDOWN: { Key = Key_MouseRight; } break;
}
Event = PushStruct(State->EventArena, platform_event);
Event->Type = Type;
Event->Key = Key;
Event->P = Win32_GetMouseP(Window);
} break;
case WM_SYSKEYUP:
case WM_KEYUP:
{
ButtonIsUp = true;
} fallthrough;
case WM_SYSKEYDOWN:
case WM_KEYDOWN:
{
platform_event_type Type = ButtonIsUp ? PlatformEvent_Release : PlatformEvent_Press;
u32 VKCode = (u32)WParam;
platform_key Key = Key_Invalid;
if(VKCode >= 'A' && VKCode <= 'Z')
{
Key = (platform_key)(Key_A + (VKCode - 'A'));
}
else if(VKCode >= VK_F1 && VKCode <= VK_F12)
{
Key = (platform_key)(Key_F1 + (VKCode - VK_F1));
}
else if(VKCode == VK_LEFT) { Key = Key_Left; }
else if(VKCode == VK_RIGHT) { Key = Key_Right; }
else if(VKCode == VK_UP) { Key = Key_Up; }
else if(VKCode == VK_DOWN) { Key = Key_Down; }
else if(VKCode == VK_SPACE) { Key = Key_Space; }
else if(VKCode == VK_RETURN) { Key = Key_Return; }
else if(VKCode == VK_PRIOR) { Key = Key_PageUp; }
else if(VKCode == VK_NEXT) { Key = Key_PageDown; }
else if(VKCode == VK_HOME) { Key = Key_Home; }
else if(VKCode == VK_END) { Key = Key_End; }
else if(VKCode == VK_BACK) { Key = Key_Backspace; }
else if(VKCode == VK_DELETE) { Key = Key_Delete; }
else if(VKCode == VK_ESCAPE) { Key = Key_Escape; }
if(Key != Key_Invalid)
{
Event = PushStruct(State->EventArena, platform_event);
Event->Type = Type;
Event->Key = Key;
}
if(!ButtonIsUp && (VKCode == VK_RETURN) && (LParam & (1 << 29)))
{
Win32_ToggleFullscreen();
}
else
{
Result = DefWindowProc(Window, Message, WParam, LParam);
}
} break;
case WM_CHAR:
{
u32 Codepoint = (u32)WParam;
if(Codepoint == '\r')
{
Codepoint = '\n';
}
if((Codepoint >= 32 && Codepoint != 127) || Codepoint == '\t' || Codepoint == '\n')
{
Event = PushStruct(State->EventArena, platform_event);
Event->Type = PlatformEvent_Text;
Event->Codepoint = Codepoint;
}
} break;
case WM_SETCURSOR:
{
range2_r32 WindowRect = {};
WindowRect.Max = Win32_GetWindowDim(Window);
if(InRange(WindowRect, Win32_GetMouseP(Window)))
{
persist HCURSOR CursorTable[PlatformCursor_Count];
persist b32 CursorTableLoaded = false;
if(!CursorTableLoaded)
{
CursorTable[PlatformCursor_Arrow] = LoadCursor(0, IDC_ARROW);
CursorTable[PlatformCursor_Cross] = LoadCursor(0, IDC_CROSS);
CursorTable[PlatformCursor_Hand] = LoadCursor(0, IDC_HAND);
CursorTable[PlatformCursor_Help] = LoadCursor(0, IDC_HELP);
CursorTable[PlatformCursor_IBeam] = LoadCursor(0, IDC_IBEAM);
CursorTable[PlatformCursor_SlashedCircle] = LoadCursor(0, IDC_NO);
CursorTable[PlatformCursor_ArrowAll] = LoadCursor(0, IDC_SIZEALL);
CursorTable[PlatformCursor_ArrowNESW] = LoadCursor(0, IDC_SIZENESW);
CursorTable[PlatformCursor_ArrowVertical] = LoadCursor(0, IDC_SIZENS);
CursorTable[PlatformCursor_ArrowNWSE] = LoadCursor(0, IDC_SIZENWSE);
CursorTable[PlatformCursor_ArrowHorizontal] = LoadCursor(0, IDC_SIZEWE);
CursorTable[PlatformCursor_Wait] = LoadCursor(0, IDC_WAIT);
CursorTableLoaded = true;
}
SetCursor(CursorTable[Global_Win32State.Cursor]);
}
else
{
DefWindowProc(Window, Message, WParam, LParam);
}
} break;
default:
{
Result = DefWindowProc(Window, Message, WParam, LParam);
} break;
}
if(Event)
{
Event->Modifiers = Win32_GetModifiers();
DLLInsertLast(State->EventList.First, State->EventList.Last, Event);
}
ReleaseScratch(Scratch);
return(Result);
}
static void Win32_ProcessInput(vn_input *Input, HWND Window, r32 dtForFrame)
{
win32_state *State = &Global_Win32State;
ArenaClear(State->EventArena);
MSG Message;
while(PeekMessage(&Message, Window, 0, 0, PM_REMOVE))
{
TranslateMessage(&Message);
DispatchMessageA(&Message);
}
Input->EventList = &State->EventList;
v2 NewMouseP = Win32_GetMouseP(Window);
v2 OldMouseP = Input->MouseP;
Input->dMouseP = NewMouseP - OldMouseP;
Input->MouseP = NewMouseP;
Input->dtForFrame = dtForFrame;
}
static void Win32_EnforceFrameRate(u64 FrameBegin, r32 TargetFrameRate)
{
win32_state *State = &Global_Win32State;
r64 TimeElapsed = Win32_GetSecondsElapsed(FrameBegin, Win32_GetWallClock());
r64 Target = 1.0 / TargetFrameRate;
r64 ToSleep = Target - TimeElapsed;
if(ToSleep > 0)
{
u64 MSToSleep = (u64)Floor(ToSleep*1000);
if(State->SleepIsGranular)
{
Sleep(MSToSleep);
}
while(ToSleep > 0)
{
TimeElapsed = Win32_GetSecondsElapsed(FrameBegin, Win32_GetWallClock());
ToSleep = Target - TimeElapsed;
}
}
}
inline void Win32_GetRelevantPaths(win32_state *State)
{
GetModuleFileName(0, State->EXEPath, ArrayCount(State->EXEPath));
if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
Win32_PlatformError("Path to executable is too long. Try running the game from another directory", true);
}
string EXEPathString = MakeString(State->EXEPath);
s64 OnePastLastSlash = LastIndexOf(EXEPathString, '\\') + 1;
s64 BuildIndex = LastIndexOf(EXEPathString, StrLit("\\build\\")) + 1;
if(BuildIndex == 0)
{
BuildIndex = LastIndexOf(EXEPathString, StrLit("//build//")) + 1;
}
if(BuildIndex == 0)
{
State->ContentsPath = MakeString(EXEPathString.Data, OnePastLastSlash);
}
else
{
State->ContentsPath = MakeString(EXEPathString.Data, BuildIndex);
}
string DLLName = StrLit("vn.dll");
Copy(State->DLLPath, State->EXEPath, OnePastLastSlash);
Copy(State->DLLPath+OnePastLastSlash, DLLName.Data, DLLName.Count);
string TempDLLName = StrLit("temp_vn.dll");
Copy(State->TempDLLPath, State->EXEPath, OnePastLastSlash);
Copy(State->TempDLLPath+OnePastLastSlash, TempDLLName.Data, TempDLLName.Count);
}
int WinMain(HINSTANCE Instance, HINSTANCE PreviousInstance, LPSTR CommandLine, int ShowCommand)
{
RegisterPlatformFunctions(Win32);
thread_context ThreadContext = AllocateThreadContext();
SetThreadContext(&ThreadContext);
// sixten: Setup Win32 platform state.
{
win32_state *State = &Global_Win32State;
Win32_GetRelevantPaths(State);
LARGE_INTEGER FrequencyQuery;
QueryPerformanceFrequency(&FrequencyQuery);
State->PerformanceFrequency = FrequencyQuery.QuadPart;
State->EventArena = ArenaAlloc(Gigabytes(1));
State->SleepIsGranular = (timeBeginPeriod(1) == TIMERR_NOERROR);
}
WNDCLASS WindowClass = {};
WindowClass.lpszClassName = "vn-window-class";
WindowClass.lpfnWndProc = Win32_WindowCallback;
WindowClass.hCursor = LoadCursorA(0, IDC_ARROW);
WindowClass.style = CS_OWNDC;
if(RegisterClassA(&WindowClass))
{
HWND Window = CreateWindowEx(0,
WindowClass.lpszClassName,
"vn - December 2023 Build",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
0, 0, Instance, 0);
if(Window)
{
Global_Win32State.Window = Window;
vn_input Input = {};
vn_render_commands RenderCommands = {};
// sixten: Setup OpenGL
HDC DeviceContext = GetDC(Window);
Win32_CreateOpenGLContext(DeviceContext);
opengl_context OpenGLContext = OpenGL_SetupContext(&RenderCommands, 16*1024);
vn_memory Memory = {};
Memory.PlatformAPI = Platform;
#if !W32_LINK_SINGLE
win32_loaded_code LoadedCode = Win32_LoadCode();
#endif
ShowWindow(Window, SW_SHOWNORMAL);
u64 CurrentTime = Win32_GetWallClock();
while(!Input.ExitRequested)
{
u64 NewTime = Win32_GetWallClock();
r64 dtForFrame = Win32_GetSecondsElapsed(CurrentTime, NewTime);
CurrentTime = NewTime;
Win32_ProcessInput(&Input, Window, dtForFrame);
Win32_SetCursor(PlatformCursor_Arrow);
// sixten: Update and render frame.
{
v2_r32 RenderDim = Win32_GetWindowDim(Window);
OpenGL_BeginFrame(&RenderCommands, RenderDim);
#if W32_LINK_SINGLE
VN_UpdateAndRender(&ThreadContext, &Memory, &Input, &RenderCommands);
#else
Win32_UpdateCode(&LoadedCode);
if(LoadedCode.IsValid)
{
LoadedCode.UpdateAndRender(&ThreadContext, &Memory, &Input, &RenderCommands);
}
#endif
b32 UseVSync = (Input.RefreshRate == 0);
wglSwapIntervalEXT(UseVSync);
OpenGL_EndFrame(&OpenGLContext, &RenderCommands);
wglSwapLayerBuffers(DeviceContext, WGL_SWAP_MAIN_PLANE);
}
b32 ShouldLimitFrameRate = (Input.RefreshRate > 0);
if(ShouldLimitFrameRate)
{
Win32_EnforceFrameRate(CurrentTime, Input.RefreshRate);
}
}
}
else
{
Win32_PlatformError("Unable to create window.", true);
}
}
else
{
Win32_PlatformError("Unable to register window class.", true);
}
return(0);
}
#if VN_ASAN_ENABLED
int main(int ArgumentCount, char **Arguments)
{
return WinMain(GetModuleHandle(0), 0, 0, 0);
}
#endif