#ifdef _WIN32 #include #include #include #include #include #include #include "../gl_funcs.h" #include "../pltf.h" #include "DynArray.h" #include "DynString.h" #include "internal.h" #include "log.h" #include "win_proc.h" void pltf_init() { pltf_state.exe_module = GetModuleHandle(NULL); if (pltf_state.exe_module == NULL) { LOG_FATAL("Failed to get module"); } opengl_module = LoadLibraryA("opengl32.dll"); if (opengl_module == NULL) { LOG_FATAL("Failed to load opengl32.dll"); } // Register the window class WNDCLASS wc = { }; wc.lpfnWndProc = WindowProc; wc.hInstance = pltf_state.exe_module; wc.lpszClassName = pltf_state.wc_name; wc.style = CS_OWNDC; RegisterClass(&wc); } void pltf_deinit() { UnregisterClass(pltf_state.wc_name, pltf_state.exe_module); } void pltf_window_create() { // Create the window pltf_state.hwnd = CreateWindowEx( 0, // Optional window styles pltf_state.wc_name, // Window class L"Learn to Program Windows", // Window text WS_OVERLAPPEDWINDOW, // Window style // Size and position CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, // Parent window NULL, // Menu pltf_state.exe_module, // Instance handle &pltf_state.window_state // Additional application data ); if (pltf_state.hwnd == NULL) { LOG_FATAL("ERROR: hwnd was NULL"); } pltf_state.hdc = GetDC(pltf_state.hwnd); if (pltf_state.hdc == NULL) { LOG_FATAL("Failed to get HDC"); } ShowWindow(pltf_state.hwnd, SW_SHOWDEFAULT); } void pltf_window_close() { PostMessage(pltf_state.hwnd, WM_CLOSE, 0, 0); } void pltf_window_destroy() { if (pltf_state.hwnd != NULL) { DestroyWindow(pltf_state.hwnd); pltf_state.hwnd = NULL; } } void pltf_gl_ctx_create() { const PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, 32, // Color depth 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, // Depth buffer bits 8, // Stencil buffer bits 0, }; HDC hdc = GetDC(pltf_state.hwnd); if (hdc == NULL) { LOG_ERROR("Failed to get window DC"); return; } const int pixel_format = ChoosePixelFormat(hdc, &pfd); if (SetPixelFormat(hdc, pixel_format, &pfd) == FALSE) { LOG_ERRORF("Failed to set pixel format. Error: %d", GetLastError()); return; } pltf_state.window_state.gl_ctx = wglCreateContext(hdc); if (pltf_state.window_state.gl_ctx == NULL) { LOG_ERRORF("Failed to create OpenGL context. Error: %d", GetLastError()); return; } if (wglMakeCurrent(hdc, pltf_state.window_state.gl_ctx) == FALSE) { LOG_ERRORF("Failed to make OpenGL context current. Error: %d", GetLastError()); return; } PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC) pltf_gl_function_load("wglGetExtensionsStringARB"); // Copy to prevent modifying the original string. Might be useless but doing it just in case. char* wgl_extensions_str = strdup(wglGetExtensionsStringARB(hdc)); if (!DynArray_DynString_alloc(&pltf_state.wgl_extensions)) { LOG_ERROR("Failed to allocate wgl_extensions"); return; } char* ext = strtok(wgl_extensions_str, " "); if (ext == NULL) { LOG_ERROR("wgl_extensions_str was empty"); return; } while (ext != NULL) { DynString ext_dstr = DynString_alloc(ext, strlen(ext)); if (!DynArray_DynString_push_back(&pltf_state.wgl_extensions, &ext_dstr)) { LOG_ERROR("Failed to push back extension string"); return; } ext = strtok(NULL, " "); if (ext == NULL) { break; } } free(wgl_extensions_str); if (!pltf_has_wgl_extension("WGL_ARB_pixel_format")) { LOG_FATAL("WGL_ARB_create_context extension required."); } if (!pltf_has_wgl_extension("WGL_ARB_create_context")) { LOG_FATAL("WGL_ARB_create_context extension is required"); } PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC) pltf_gl_function_load("wglChoosePixelFormatARB"); PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) pltf_gl_function_load("wglCreateContextAttribsARB"); int wgl_pixel_format = 0; { UINT num_formats = 0; const int attribIList[] = { WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, WGL_SUPPORT_OPENGL_ARB, GL_TRUE, WGL_DOUBLE_BUFFER_ARB, GL_TRUE, WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, WGL_COLOR_BITS_ARB, 32, WGL_DEPTH_BITS_ARB, 24, WGL_STENCIL_BITS_ARB, 8, 0 }; if (wglChoosePixelFormatARB(hdc, attribIList, NULL, 1, &wgl_pixel_format, &num_formats) == FALSE) { LOG_FATAL("Failed to get pixel format with wglChoosePixelFormatARB"); } } if (SetPixelFormat(hdc, wgl_pixel_format, &pfd) == FALSE) { LOG_FATAL("Failed to set wgl_pixel_format"); } if (wglDeleteContext(pltf_state.window_state.gl_ctx) == FALSE) { LOG_FATAL("Failed to delete dummy context"); } { const int attribIList[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, 4, WGL_CONTEXT_MINOR_VERSION_ARB, 6, WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB, WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB }; pltf_state.window_state.gl_ctx = wglCreateContextAttribsARB(hdc, NULL, attribIList); if (pltf_state.window_state.gl_ctx == NULL) { LOG_FATAL("wglCreateContextAttribsARB did not create a context"); } } if (wglMakeCurrent(hdc, pltf_state.window_state.gl_ctx) == FALSE) { LOG_FATAL("Failed to set proper context as current"); } } void pltf_gl_ctx_destroy() { wglDeleteContext(pltf_state.window_state.gl_ctx); pltf_state.window_state.gl_ctx = NULL; } void pltf_gl_ctx_info_print() { if (glGetIntegerv == NULL) { glGetIntegerv = (PFNGLGETINTEGERVPROC) pltf_gl_function_load("glGetIntegerv"); if (glGetIntegerv == NULL) { LOG_FATAL("Could not load glGetIntegerv"); } } int major = 0; int minor = 0; glGetIntegerv(GL_MAJOR_VERSION, &major); glGetIntegerv(GL_MINOR_VERSION, &minor); int profile_mask = 0; char* profile; glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &profile_mask); if (profile_mask & GL_CONTEXT_CORE_PROFILE_BIT) { profile = "core"; } else if (profile_mask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) { profile = "compatibility"; } else { LOG_ERRORF( "GL_CONTEXT_PROFILE_MASK did not contain either bit. profile_mask: %d", profile_mask ); return; } printf("OpenGL version %d.%d %s\n", major, minor, profile); } void pltf_swap_buffers() { wglSwapLayerBuffers(pltf_state.hdc, WGL_SWAP_MAIN_PLANE); } PltfWindowEvent pltf_window_event_handle() { MSG msg = { 0 }; if (PeekMessage(&msg, pltf_state.hwnd, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); switch (msg.message) { case WM_DESTROY: return PLTF_WE_DESTROY; } } return PLTF_WE_NONE; } #define PLTF_KEYS_X(lower, upper) \ case PLTF_KEY_##upper: { \ pltf_state.key_##lower##_cb = cb; \ pltf_state.key_##lower##_cb_user_data = (void*) user_data; \ break; \ } void pltf_key_callback_set( const PltfKey key, const PltfKeyCallback cb, void* user_data ) { switch (key) { case PLTF_KEY_NONE: break; PLTF_KEYS(PLTF_KEYS_X); } } #undef PLTF_KEYS_X #endif // _WIN32