Skip to content

Instantly share code, notes, and snippets.

@bplaat
Created May 30, 2022 20:53
Show Gist options
  • Save bplaat/96c7031470ddd6a14f7d6f1c506026a6 to your computer and use it in GitHub Desktop.
Save bplaat/96c7031470ddd6a14f7d6f1c506026a6 to your computer and use it in GitHub Desktop.
Win32 OpenGL 1.0 Example
// Win32 OpenGL 1.0 Example
// gcc opengl.c -c -o opengl.o && ld -s opengl.o -lkernel32 -luser32 -lgdi32 -lopengl32 -o opengl.exe -e _start && ./opengl
// --subsystem windows
#define UNICODE
#include <windows.h>
#include <GL/gl.h>
// Window state
wchar_t *windowTitle = L"Win32 OpenGL Example";
UINT windowWidth = 1280;
UINT windowHeight = 720;
#define windowMinWidth 320
#define windowMinHeight 240
UINT windowDpi;
UINT windowRealWidth;
UINT windowRealHeight;
// Dpi helpers
#define PROCESS_PER_MONITOR_DPI_AWARE 2
typedef int (STDMETHODCALLTYPE *_SetProcessDpiAwarenessContext)(void *value);
typedef int (STDMETHODCALLTYPE *_SetProcessDpiAwareness)(UINT value);
typedef BOOL (STDMETHODCALLTYPE *_SetProcessDPIAware)(void);
void SetDpiAware(void) {
HMODULE huser32 = LoadLibrary(L"user32.dll");
_SetProcessDpiAwarenessContext SetProcessDpiAwarenessContext = (_SetProcessDpiAwarenessContext)GetProcAddress(huser32, "SetProcessDpiAwarenessContext");
if (SetProcessDpiAwarenessContext != NULL) {
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
return;
}
HMODULE hshcore = LoadLibrary(L"shcore.dll");
_SetProcessDpiAwareness SetProcessDpiAwareness = (_SetProcessDpiAwareness)GetProcAddress(hshcore, "SetProcessDpiAwareness");
if (SetProcessDpiAwareness != NULL) {
SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
return;
}
_SetProcessDPIAware SetProcessDPIAware = (_SetProcessDPIAware)GetProcAddress(huser32, "SetProcessDPIAware");
if (SetProcessDPIAware != NULL) SetProcessDPIAware();
}
int GetDesktopDpi(void) {
HDC hdc = GetDC(HWND_DESKTOP);
int dpi = GetDeviceCaps(hdc, LOGPIXELSY);
ReleaseDC(HWND_DESKTOP, hdc);
return dpi;
}
typedef BOOL (STDMETHODCALLTYPE *_AdjustWindowRectExForDpi)(RECT *lpRect, UINT dwStyle, BOOL bMenu, UINT dwExStyle, UINT dpi);
BOOL AdjustWindowRectExForDpi(RECT *lpRect, UINT dwStyle, BOOL bMenu, UINT dwExStyle, UINT dpi) {
HMODULE huser32 = LoadLibrary(L"user32.dll");
_AdjustWindowRectExForDpi AdjustWindowRectExForDpi = (_AdjustWindowRectExForDpi)GetProcAddress(huser32, "AdjustWindowRectExForDpi");
if (AdjustWindowRectExForDpi != NULL) return AdjustWindowRectExForDpi(lpRect, dwStyle, bMenu, dwExStyle, dpi);
return AdjustWindowRectEx(lpRect, dwStyle, bMenu, dwExStyle);
}
// Dwm helpers
#define DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 19
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
typedef HRESULT (STDMETHODCALLTYPE *_DwmSetWindowAttribute)(HWND hwnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute);
void SetWindowImmersiveDarkMode(HWND hwnd, BOOL enabled) {
HMODULE hdwmapi = LoadLibrary(L"dwmapi.dll");
_DwmSetWindowAttribute DwmSetWindowAttribute = (_DwmSetWindowAttribute)GetProcAddress(hdwmapi, "DwmSetWindowAttribute");
if (DwmSetWindowAttribute != NULL) {
if (FAILED(DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &enabled, sizeof(BOOL)))) {
DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1, &enabled, sizeof(BOOL));
}
}
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
if (msg == WM_DPICHANGED) {
windowDpi = HIWORD(wParam);
RECT *window_rect = (RECT *)lParam;
SetWindowPos(hwnd, NULL, window_rect->left, window_rect->top, window_rect->right - window_rect->left,
window_rect->bottom - window_rect->top, SWP_NOZORDER | SWP_NOACTIVATE);
return 0;
}
if (msg == WM_SIZE) {
windowRealWidth = LOWORD(lParam);
windowRealHeight = HIWORD(lParam);
windowWidth = MulDiv(windowRealWidth, USER_DEFAULT_SCREEN_DPI, windowDpi);
windowHeight = MulDiv(windowRealHeight, USER_DEFAULT_SCREEN_DPI, windowDpi);
wchar_t windowTitleBuffer[512];
wsprintf(windowTitleBuffer, L"%s (%dx%d@%d -> %dx%d)", windowTitle, windowWidth, windowHeight, windowDpi, windowRealWidth, windowRealHeight);
SetWindowText(hwnd, windowTitleBuffer);
return 0;
}
if (msg == WM_GETMINMAXINFO) {
RECT windowRect = {0};
windowRect.right = MulDiv(windowMinWidth, windowDpi, USER_DEFAULT_SCREEN_DPI);
windowRect.bottom = MulDiv(windowMinHeight, windowDpi, USER_DEFAULT_SCREEN_DPI);
AdjustWindowRectExForDpi(&windowRect, WS_OVERLAPPEDWINDOW, FALSE, 0, windowDpi);
MINMAXINFO *minMaxInfo = (MINMAXINFO *)lParam;
minMaxInfo->ptMinTrackSize.x = windowRect.right - windowRect.left;
minMaxInfo->ptMinTrackSize.y = windowRect.bottom - windowRect.top;
return 0;
}
if (msg == WM_PAINT) {
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
glViewport(0, 0, windowRealWidth, windowRealHeight);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_QUADS);
glColor3f(1, 1, 1);
glVertex2f(-0.5, 0.5);
glColor3f(1, 0, 0);
glVertex2f(0.5, 0.5);
glColor3f(0, 1, 0);
glVertex2f(0.5, -0.5);
glColor3f(0, 0, 1);
glVertex2f(-0.5, -0.5);
glEnd();
glFlush();
SwapBuffers(ps.hdc);
EndPaint(hwnd, &ps);
return 0;
}
if (msg == WM_ERASEBKGND) {
return FALSE;
}
if (msg == WM_DESTROY) {
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
void _start(void) {
SetDpiAware();
WNDCLASSEX wc = {0};
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.hInstance = GetModuleHandle(NULL);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = L"opengl-example";
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassEx(&wc);
windowDpi = GetDesktopDpi();
windowRealWidth = MulDiv(windowWidth, windowDpi, USER_DEFAULT_SCREEN_DPI);
windowRealHeight = MulDiv(windowHeight, windowDpi, USER_DEFAULT_SCREEN_DPI);
RECT windowRect;
windowRect.left = (GetSystemMetrics(SM_CXSCREEN) - windowRealWidth) / 2;
windowRect.top = (GetSystemMetrics(SM_CYSCREEN) - windowRealHeight) / 2;
windowRect.right = windowRect.left + windowRealWidth;
windowRect.bottom = windowRect.top + windowRealHeight;
AdjustWindowRectExForDpi(&windowRect, WS_OVERLAPPEDWINDOW, FALSE, 0, windowDpi);
HWND hwnd = CreateWindowEx(0, wc.lpszClassName, windowTitle, WS_OVERLAPPEDWINDOW,
windowRect.left, windowRect.top, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top,
NULL, NULL, wc.hInstance, NULL);
SetWindowImmersiveDarkMode(hwnd, TRUE);
HDC hdc = GetDC(hwnd);
PIXELFORMATDESCRIPTOR pixelFormatDesc = {0};
pixelFormatDesc.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pixelFormatDesc.nVersion = 1;
pixelFormatDesc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pixelFormatDesc.iPixelType = PFD_TYPE_RGBA;
pixelFormatDesc.cColorBits = 32;
pixelFormatDesc.cAlphaBits = 8;
pixelFormatDesc.cDepthBits = 24;
SetPixelFormat(hdc, ChoosePixelFormat(hdc, &pixelFormatDesc), &pixelFormatDesc);
HGLRC renderContext = wglCreateContext(hdc);
wglMakeCurrent(hdc, renderContext);
ReleaseDC(hwnd, hdc);
wchar_t openglInfoBuffer[512];
int length = wsprintf(openglInfoBuffer, L"[INFO] Using %hs on %hs\r\n", glGetString(GL_VERSION), glGetString(GL_RENDERER));
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), openglInfoBuffer, length, NULL, NULL);
ShowWindow(hwnd, SW_SHOWDEFAULT);
UpdateWindow(hwnd);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
ExitProcess(msg.wParam);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment