Last active
April 24, 2017 08:51
-
-
Save msmshazan/c3c20b5758f8cf877a9bf6878abcc07b to your computer and use it in GitHub Desktop.
Win32 app wrapper
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "main.h" | |
#include "resource.h" | |
global_variable bool32 GlobalRunning = 1; | |
global_variable win32_offscreen_buffer GlobalBackBuffer; | |
global_variable WINDOWPLACEMENT GlobalWindowPosition = { sizeof(GlobalWindowPosition) }; | |
global_variable HWND hDlgCurrent = NULL; | |
#define WINDOW_WIDTH 800 | |
#define WINDOW_HEIGHT 600 | |
internal void | |
Win32DisplayBufferInWindow(win32_offscreen_buffer *Buffer, HDC DeviceContext, | |
int WindowWidth, int WindowHeight) | |
{ | |
// TODO: Centering / black bars? | |
if ((WindowWidth >= Buffer->Width * 2) && | |
(WindowHeight >= Buffer->Height * 2)) { | |
StretchDIBits(DeviceContext, | |
0, 0, 2 * Buffer->Width, 2 * Buffer->Height, | |
0, 0, Buffer->Width, Buffer->Height, | |
Buffer->Memory, &Buffer->Info, | |
DIB_RGB_COLORS, SRCCOPY); | |
} | |
else { | |
#if 1 | |
int OffsetX = 0; | |
int OffsetY = 0; | |
PatBlt(DeviceContext, 0, 0, WindowWidth, OffsetY, BLACKNESS); | |
PatBlt(DeviceContext, 0, OffsetY + Buffer->Height, WindowWidth, WindowHeight, BLACKNESS); | |
PatBlt(DeviceContext, 0, 0, OffsetX, WindowHeight, BLACKNESS); | |
PatBlt(DeviceContext, OffsetX + Buffer->Width, 0, WindowWidth, WindowHeight, BLACKNESS); | |
// NOTE: For prototyping purposes, we're going to always blit | |
// 1-to-1 pixels to make sure we don't introduce artifacts with | |
// stretching while we are learning to code the renderer! | |
StretchDIBits(DeviceContext, | |
/* | |
X, Y, Width, Height, | |
X, Y, Width, Height, | |
*/ | |
OffsetX, OffsetY, Buffer->Width, Buffer->Height, | |
0, 0, Buffer->Width, Buffer->Height, | |
Buffer->Memory, &Buffer->Info, | |
DIB_RGB_COLORS, SRCCOPY); | |
#else | |
r32 HeightOverWidth = (r32)Buffer->Height / (r32)Buffer->Width; | |
s32 Width = WindowWidth; | |
s32 Height = (s32)(Width * HeightOverWidth); | |
StretchDIBits(DeviceContext, | |
0, 0, Width, Height, | |
0, 0, Buffer->Width, Buffer->Height, | |
Buffer->Memory, &Buffer->Info, | |
DIB_RGB_COLORS, SRCCOPY); | |
#endif | |
} | |
} | |
internal void | |
Win32ResizeDIBSection(win32_offscreen_buffer* Buffer, int Width, int Height) | |
{ | |
// TODO: Bulletproof this. | |
// Maybe don't free first, free after, then free first if that fails. | |
if (Buffer->Memory) { | |
VirtualFree(Buffer->Memory, 0, MEM_RELEASE); | |
} | |
Buffer->Width = Width; | |
Buffer->Height = Height; | |
int BytesPerPixel = 4; | |
Buffer->BytesPerPixel = BytesPerPixel; | |
// NOTE: When the biHeight field is negative, this is the clue to | |
// Windows to treat this bitmap as top-down, not bottom-up, meaning that | |
// the first tree bytes of the image are the color for the top left pixel | |
// in the bitmap, not the bottom left. | |
Buffer->Info.bmiHeader.biSize = sizeof(Buffer->Info.bmiHeader); | |
Buffer->Info.bmiHeader.biWidth = Buffer->Width; | |
Buffer->Info.bmiHeader.biHeight = Buffer->Height; | |
Buffer->Info.bmiHeader.biPlanes = 1; | |
Buffer->Info.bmiHeader.biBitCount = 32; | |
Buffer->Info.bmiHeader.biCompression = BI_RGB; | |
Buffer->Pitch = Align16(Width * BytesPerPixel); | |
int BitmapMemorySize = Buffer->Pitch * Buffer->Height; | |
Buffer->Memory = VirtualAlloc(0, BitmapMemorySize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); | |
// TODO: Probably clear this to black | |
} | |
internal win32_window_dimension | |
Win32GetWindowDimension(HWND Window) | |
{ | |
win32_window_dimension Result; | |
RECT ClientRect; | |
GetClientRect(Window, &ClientRect); | |
Result.Width = ClientRect.right - ClientRect.left; | |
Result.Height = ClientRect.bottom - ClientRect.top; | |
return Result; | |
} | |
BOOL CALLBACK QuitDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) | |
{ | |
switch (Message) | |
{ | |
case WM_INITDIALOG: | |
hDlgCurrent = hwnd; | |
ShowWindow(hwnd, SW_SHOW); | |
return TRUE; | |
case WM_DESTROY: | |
hDlgCurrent = NULL; | |
break; | |
case WM_COMMAND: | |
switch (LOWORD(wParam)) | |
{ | |
case IDOK: | |
EndDialog(hwnd, IDOK); | |
GlobalRunning = false; | |
break; | |
case IDCANCEL: | |
EndDialog(hwnd, IDCANCEL); | |
break; | |
} | |
break; | |
} | |
return FALSE; | |
} | |
BOOL CALLBACK AboutDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) | |
{ | |
switch (Message) | |
{ | |
case WM_INITDIALOG: | |
hDlgCurrent = hwnd; | |
ShowWindow(hwnd, SW_SHOW); | |
return TRUE; | |
case WM_DESTROY: | |
hDlgCurrent = NULL; | |
break; | |
case WM_COMMAND: | |
switch (LOWORD(wParam)) | |
{ | |
case IDOK: | |
EndDialog(hwnd, IDOK); | |
break; | |
case IDCANCEL: | |
EndDialog(hwnd, IDCANCEL); | |
break; | |
} | |
break; | |
} | |
return FALSE; | |
} | |
LRESULT CALLBACK | |
Win32MainWindowCallback(HWND Window, | |
UINT Message, | |
WPARAM WParam, | |
LPARAM LParam) | |
{ | |
LRESULT Result = 0; | |
switch (Message) { | |
case WM_CLOSE: | |
{ | |
// TODO: Handle this with a message to the user? | |
GlobalRunning = false; | |
printf("WM_CLOSE\n"); | |
} break; | |
case WM_ACTIVATEAPP: | |
{ | |
OutputDebugString("WM_ACTIVATEAPP\n"); | |
#if 1 | |
if (WParam == TRUE) { | |
SetLayeredWindowAttributes(Window, RGB(0, 0, 0), 255, LWA_ALPHA); | |
} | |
else { | |
SetLayeredWindowAttributes(Window, RGB(0, 0, 0), 80, LWA_ALPHA); | |
} | |
#endif | |
} break; | |
case WM_COMMAND: { | |
HWND dialog; | |
int wmId = LOWORD(WParam); | |
// Parse the menu selections: | |
switch (wmId) | |
{ | |
case ID_INFO_ABOUT: | |
CreateDialog(GetModuleHandle(0), MAKEINTRESOURCE(IDD_DIALOG1), Window, AboutDlgProc); | |
break; | |
case ID_FILE_EXIT: | |
CreateDialog(GetModuleHandle(0), MAKEINTRESOURCE(IDD_DIALOG2), Window, QuitDlgProc); | |
break; | |
} | |
}break; | |
case WM_SYSKEYDOWN: | |
case WM_SYSKEYUP: | |
case WM_KEYDOWN: | |
case WM_KEYUP: | |
{ | |
assert(!"Keybord input came in through a non-dispatch message"); | |
} break; | |
case WM_SIZE: | |
{ | |
}break; | |
case WM_PAINT: | |
{ | |
PAINTSTRUCT Paint; | |
HDC DeviceContext = BeginPaint(Window, &Paint); | |
int X = Paint.rcPaint.left; | |
int Y = Paint.rcPaint.top; | |
int Width = Paint.rcPaint.right - Paint.rcPaint.left; | |
int Height = Paint.rcPaint.bottom - Paint.rcPaint.top; | |
win32_window_dimension Dimension = Win32GetWindowDimension(Window); | |
Win32DisplayBufferInWindow(&GlobalBackBuffer, DeviceContext, Dimension.Width, Dimension.Height); | |
EndPaint(Window, &Paint); | |
} break; | |
default: | |
{ | |
Result = DefWindowProc(Window, Message, WParam, LParam); | |
}break; | |
} | |
return Result; | |
} | |
internal void | |
ToggleFullscreen(HWND Window) { | |
// NOTE: This follows Raymond Chen's prescription | |
// for fullscreen toggling, see: | |
// http://blogs.msdn.com/b/oldnewthing/archive/2010/04/12/9994016.aspx | |
DWORD Style = GetWindowLong(Window, GWL_STYLE); | |
if (Style & WS_OVERLAPPEDWINDOW) { | |
MONITORINFO MonitorInfo = { sizeof(MonitorInfo) }; | |
if (GetWindowPlacement(Window, &GlobalWindowPosition) && | |
GetMonitorInfo(MonitorFromWindow(Window, MONITOR_DEFAULTTOPRIMARY), &MonitorInfo)) | |
{ | |
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, &GlobalWindowPosition); | |
SetWindowPos(Window, 0, 0, 0, 0, 0, | |
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | | |
SWP_NOOWNERZORDER | SWP_FRAMECHANGED); | |
} | |
} | |
int CALLBACK WinMain(HINSTANCE Instance, | |
HINSTANCE hPrevInstance, | |
LPSTR lpCmdLine, | |
int nCmdShow) { | |
WNDCLASS wc; | |
ATOM atom; | |
RECT rect = { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT }; | |
DWORD style = WS_OVERLAPPEDWINDOW ; | |
DWORD exstyle = WS_EX_APPWINDOW ; | |
HWND window; | |
HDC dc; | |
/* Win32 */ | |
memset(&wc, 0, sizeof(wc)); | |
wc.style = 0; | |
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); | |
wc.cbClsExtra = 0; | |
wc.cbWndExtra = 0; | |
wc.lpfnWndProc = Win32MainWindowCallback; | |
wc.hInstance = GetModuleHandle(0); | |
wc.hCursor = (HCURSOR)LoadImage(Instance,MAKEINTRESOURCE(IDC_POINTER),IMAGE_CURSOR,0,0,LR_DEFAULTSIZE); | |
wc.lpszClassName = "WindowClass"; | |
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1); | |
wc.hIcon = LoadIcon(Instance, MAKEINTRESOURCE(IDI_ICON1)); // Make sure to pass instance of app | |
atom = RegisterClass(&wc); | |
window = CreateWindowEx(exstyle, wc.lpszClassName, "Win32 programming", | |
style | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, | |
rect.right - rect.left, rect.bottom - rect.top, | |
NULL, NULL, wc.hInstance, NULL); | |
dc = GetDC(window); | |
if (window) { | |
char X = 0; | |
Win32ResizeDIBSection(&GlobalBackBuffer, 1920, 1080); | |
while (GlobalRunning) | |
{ | |
InvalidateRect(window, NULL, FALSE); | |
MSG Message; | |
while(PeekMessage(&Message, 0, 0, 0, PM_REMOVE)) { | |
if (Message.message == WM_QUIT) { | |
GlobalRunning = false; | |
} | |
if (!IsDialogMessage(hDlgCurrent, &Message)) { | |
switch (Message.message) { | |
case WM_SYSKEYDOWN: | |
case WM_SYSKEYUP: | |
case WM_KEYDOWN: | |
case WM_KEYUP: { | |
uint32 VKCode = (uint32)Message.wParam; | |
// NOTE: Since we are comparing WasDown to IsDown, | |
// we MUST use == or != to convert these bit tests to actual | |
// 0 or 1 values. | |
bool WasDown = ((Message.lParam & (1 << 30)) != 0); | |
bool IsDown = ((Message.lParam & (1 << 31)) == 0); | |
if (WasDown != IsDown) { | |
if (VKCode == 'W') { | |
} | |
else if (VKCode == 'A') { | |
} | |
else if (VKCode == 'S') { | |
} | |
else if (VKCode == 'D') { | |
} | |
else if (VKCode == 'Q') { | |
} | |
else if (VKCode == 'E') { | |
} | |
else if (VKCode == VK_UP) { | |
} | |
else if (VKCode == VK_LEFT) { | |
} | |
else if (VKCode == VK_RIGHT) { | |
} | |
else if (VKCode == VK_DOWN) { | |
} | |
else if (VKCode == VK_ESCAPE) { | |
GlobalRunning = false; | |
} | |
else if (VKCode == VK_SPACE) { | |
} | |
if (IsDown) { | |
bool32 AltKeyWasDown = (Message.lParam & (1 << 29)); | |
if ((VKCode == VK_F4) && AltKeyWasDown) { | |
GlobalRunning = false; | |
} | |
if ((VKCode == VK_RETURN) && AltKeyWasDown) { | |
if (Message.hwnd) { | |
ToggleFullscreen(Message.hwnd); | |
} | |
} | |
} | |
} | |
} break; | |
default: { | |
TranslateMessage(&Message); | |
DispatchMessage(&Message); | |
}break; | |
} | |
} | |
} | |
// All Non- Win32 code go here :-) | |
memset(GlobalBackBuffer.Memory, X, GlobalBackBuffer.BytesPerPixel*GlobalBackBuffer.Height*GlobalBackBuffer.Width); | |
X += 1; | |
} | |
ReleaseDC(window, dc); | |
} | |
UnregisterClass(wc.lpszClassName, wc.hInstance); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment