724 lines
20 KiB
C++
724 lines
20 KiB
C++
#define NOMINMAX
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include "windows.h"
|
|
#include "timeapi.h"
|
|
|
|
#include "vn_platform.h"
|
|
|
|
#include "win32_main.h"
|
|
#include "win32_opengl.cpp"
|
|
|
|
global b32 Global_Running;
|
|
global win32_state Global_Win32State;
|
|
global WINDOWPLACEMENT Global_WindowPosition = {sizeof(Global_WindowPosition)};;
|
|
|
|
static void Win32_PlatformError(char *Message, bool IsFatal)
|
|
{
|
|
MessageBoxA(0, Message, "nv - Platform Error", MB_OK|(IsFatal?MB_ICONSTOP:MB_ICONEXCLAMATION));
|
|
|
|
if(IsFatal)
|
|
{
|
|
ExitProcess((UINT)-1);
|
|
}
|
|
}
|
|
|
|
static PLATFORM_ALLOCATE_MEMORY(Win32_AllocateMemory)
|
|
{
|
|
win32_state *State = &Global_Win32State;
|
|
|
|
umm TotalSize = Size + sizeof(win32_memory_block);
|
|
|
|
win32_memory_block *Block =
|
|
(win32_memory_block *)VirtualAlloc(0, TotalSize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
|
|
|
|
Assert(Block);
|
|
Block->Block.Base = (u8 *)Block + sizeof(win32_memory_block);
|
|
Block->Block.Size = Size;
|
|
|
|
win32_memory_block *Sentinel = &State->MemorySentinel;
|
|
Block->Next = Sentinel;
|
|
|
|
BeginTicketMutex(&State->MemoryMutex);
|
|
Block->Prev = Sentinel->Prev;
|
|
Block->Prev->Next = Block;
|
|
Block->Next->Prev = Block;
|
|
EndTicketMutex(&State->MemoryMutex);
|
|
|
|
platform_memory_block *Result = &Block->Block;
|
|
return(Result);
|
|
}
|
|
|
|
static PLATFORM_DEALLOCATE_MEMORY(Win32_DeallocateMemory)
|
|
{
|
|
win32_state *State = &Global_Win32State;
|
|
|
|
win32_memory_block *Win32Block = (win32_memory_block *)Block;
|
|
|
|
if(Block)
|
|
{
|
|
BeginTicketMutex(&State->MemoryMutex);
|
|
Win32Block->Prev->Next = Win32Block->Next;
|
|
Win32Block->Next->Prev = Win32Block->Prev;
|
|
EndTicketMutex(&State->MemoryMutex);
|
|
}
|
|
|
|
BOOL Result = VirtualFree(Block, 0, MEM_RELEASE);
|
|
return(Result);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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_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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
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))
|
|
{
|
|
<<<<<<< HEAD
|
|
MONITORINFO MonitorInfo = {sizeof(MonitorInfo)};
|
|
if(GetWindowPlacement(Window, &Global_WindowPosition) &&
|
|
GetMonitorInfo(MonitorFromWindow(Window, MONITOR_DEFAULTTOPRIMARY), &MonitorInfo))
|
|
{
|
|
// sixten: 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);
|
|
=======
|
|
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);
|
|
>>>>>>> 376349b92227dcf5369dee77701a9ad8570ab639
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetWindowPlacement(Window, &Global_WindowPosition);
|
|
SetWindowPos(Window, 0, 0, 0, 0, 0,
|
|
SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOOWNERZORDER|SWP_FRAMECHANGED);
|
|
}
|
|
}
|
|
|
|
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:
|
|
{
|
|
Global_Running = false;
|
|
} 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_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; }
|
|
|
|
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;
|
|
|
|
{
|
|
if(State->EventArenaTemp.Arena)
|
|
{
|
|
EndTemporaryMemory(State->EventArenaTemp);
|
|
}
|
|
|
|
State->EventArenaTemp = BeginTemporaryMemory(&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 = MakeStringFromCString(State->EXEPath);
|
|
|
|
s64 OnePastLastSlash = LastIndexOf(EXEPathString, '\\') + 1;
|
|
s64 BuildIndex = LastIndexOf(EXEPathString, StrLit("build"));
|
|
|
|
if(BuildIndex == -1)
|
|
{
|
|
State->ContentsPath = MakeString((char *)EXEPathString.Data, OnePastLastSlash);
|
|
}
|
|
else
|
|
{
|
|
State->ContentsPath = MakeString((char *)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)
|
|
{
|
|
thread_context ThreadContext = {};
|
|
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->MemorySentinel.Next = &State->MemorySentinel;
|
|
State->MemorySentinel.Prev = &State->MemorySentinel;
|
|
|
|
State->SleepIsGranular = (timeBeginPeriod(1) != TIMERR_NOERROR);
|
|
}
|
|
|
|
// sixten: Setup platform layer
|
|
{
|
|
Platform.AllocateMemory = Win32_AllocateMemory;
|
|
Platform.DeallocateMemory = Win32_DeallocateMemory;
|
|
Platform.OpenFile = Win32_OpenFile;
|
|
Platform.CloseFile = Win32_CloseFile;
|
|
Platform.ReadFile = Win32_ReadFile;
|
|
Platform.GetFileSize = Win32_GetFileSize;
|
|
Platform.SetCursor = Win32_SetCursor;
|
|
Platform.ToggleFullscreen = Win32_ToggleFullscreen;
|
|
}
|
|
|
|
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 - June 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 = {};
|
|
|
|
// sixten: Setup OpenGL
|
|
vn_render_commands RenderCommands = {};
|
|
HDC DeviceContext = GetDC(Window);
|
|
Win32_CreateOpenGLContext(DeviceContext);
|
|
opengl_context OpenGLContext = OpenGL_SetupContext(&RenderCommands, 16*1024);
|
|
|
|
vn_memory Memory = {};
|
|
Memory.PlatformAPI = Platform;
|
|
|
|
win32_loaded_code LoadedCode = Win32_LoadCode();
|
|
|
|
ShowWindow(Window, SW_SHOWNORMAL);
|
|
|
|
u64 CurrentTime = Win32_GetWallClock();
|
|
|
|
Global_Running = true;
|
|
while(Global_Running)
|
|
{
|
|
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.
|
|
{
|
|
Win32_UpdateCode(&LoadedCode);
|
|
|
|
OpenGL_BeginFrame(&RenderCommands, Win32_GetWindowDim(Window));
|
|
|
|
if(LoadedCode.IsValid)
|
|
{
|
|
LoadedCode.UpdateAndRender(&ThreadContext, &Memory, &Input, &RenderCommands);
|
|
}
|
|
|
|
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);
|
|
} |