867 lines
22 KiB
C++
867 lines
22 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;
|
|
}
|
|
|
|
temp 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);
|
|
temp 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)
|
|
{
|
|
temp 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;
|
|
|
|
temp 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_OEM_PLUS) { Key = Key_Plus; }
|
|
else if(VKCode == VK_OEM_MINUS) { Key = Key_Minus; }
|
|
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 |