Skip to content

Commit

Permalink
Examples: Amend Win32/Winapi + OpenGL example. (ocornut#3218, ocornut…
Browse files Browse the repository at this point in the history
  • Loading branch information
ocornut authored and kjblanchard committed May 5, 2023
1 parent 828c8f4 commit 6bc23ab
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 77 deletions.
9 changes: 5 additions & 4 deletions backends/imgui_impl_win32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*);

// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2023-04-19: Added ImGui_ImplWin32_InitForOpenGL() to facilitate combining raw Win32/Winapi with OpenGL. (#3218)
// 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen. (#2702)
// 2023-02-15: Inputs: Use WM_NCMOUSEMOVE / WM_NCMOUSELEAVE to track mouse position over non-client area (e.g. OS decorations) when app is not focused. (#6045, #6162)
// 2023-02-02: Inputs: Flipping WM_MOUSEHWHEEL (horizontal mouse-wheel) value to match other backends and offer consistent horizontal scrolling direction. (#4019, #6096, #1463)
Expand Down Expand Up @@ -114,7 +115,7 @@ static ImGui_ImplWin32_Data* ImGui_ImplWin32_GetBackendData()
}

// Functions
static bool ImGui_ImplWin32_Init(void* hwnd, bool platformHasOwnDC)
static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc)
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
Expand All @@ -139,7 +140,7 @@ static bool ImGui_ImplWin32_Init(void* hwnd, bool platformHasOwnDC)

// Set platform dependent data in viewport
ImGui::GetMainViewport()->PlatformHandleRaw = (void*)hwnd;
IM_UNUSED(platformHasOwnDC); // Used in 'docking' branch
IM_UNUSED(platform_has_own_dc); // Used in 'docking' branch

// Dynamically load XInput library
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
Expand Down Expand Up @@ -167,13 +168,13 @@ static bool ImGui_ImplWin32_Init(void* hwnd, bool platformHasOwnDC)

IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd)
{
return ImGui_ImplWin32_Init(hwnd, false);
return ImGui_ImplWin32_InitEx(hwnd, false);
}

IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd)
{
// OpenGL needs CS_OWNDC
return ImGui_ImplWin32_Init(hwnd, true);
return ImGui_ImplWin32_InitEx(hwnd, true);
}

void ImGui_ImplWin32_Shutdown()
Expand Down
4 changes: 4 additions & 0 deletions docs/CHANGELOG.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,12 @@ Other changes:
(#6341) [@lukaasm]
- Backends: Clear bits sets io.BackendFlags on backend Shutdown(). (#6334, #6335] [@GereonV]
Potentially this would facilitate switching runtime backend mid-session.
- Backends: Win32: Added ImGui_ImplWin32_InitForOpenGL() to facilitate combining raw
Win32/Winapi with OpenGL. (#3218)
- Backends: OpenGL3: Restore front and back polygon mode separately when supported
by context (Desktop 3.0, 3.1, or 3.2+ with compat bit). (#6333) [@GereonV]
- Examples: Added native Win32+OpenGL3 example. We don't recommend using this setup but we
provide it for completeness. (#3218, #5170, #6086, #2772, #2600, #2359, #2022, #1553) [@learn-more]



Expand Down
4 changes: 4 additions & 0 deletions docs/EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,10 @@ DirectX12 example, Windows only. <BR>
= main.cpp + imgui_impl_win32.cpp + imgui_impl_dx12.cpp <BR>
This is quite long and tedious, because: DirectX12.
[example_win32_opengl3/](https://github.com/ocornut/imgui/blob/master/examples/example_win32_opengl3/) <BR>
Raw Windows + OpenGL3 + example (modern, programmable pipeline) <BR>
= main.cpp + imgui_impl_win32.cpp + imgui_impl_opengl3.cpp <BR>
### Miscellaneous
Expand Down
128 changes: 55 additions & 73 deletions examples/example_win32_opengl3/main.cpp
Original file line number Diff line number Diff line change
@@ -1,48 +1,51 @@
// dear imgui: standalone example application for OpenGL3 with Winapi
// If you are new to dear imgui, see examples/README.txt and documentation at the top of imgui.cpp.
// Dear ImGui: standalone example application for Win32 + OpenGL 3
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs

// This is provided for completeness, however it is strogly recommended you use OpenGL with SDL or GLFW.

#include "imgui.h"
#include "imgui_impl_opengl3.h"
#include "imgui_impl_win32.h"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <GL/GL.h>
#include <tchar.h>

// Data stored per platform window
struct RendererData
{
HDC hDC;
};
struct WGL_WindowData { HDC hDC; };

// Data
HGLRC g_hRC;
RendererData g_MainWindow;
static int g_Width;
static int g_Height;
static HGLRC g_hRC;
static WGL_WindowData g_MainWindow;
static int g_Width;
static int g_Height;

// Forward declarations of helper functions
static bool CreateDeviceOpenGL3(HWND hWnd, RendererData* data);
static void CleanupDeviceOpenGL3(HWND hWnd, RendererData* data);
bool CreateDeviceWGL(HWND hWnd, WGL_WindowData* data);
void CleanupDeviceWGL(HWND hWnd, WGL_WindowData* data);
void ResetDeviceWGL();
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

// Main code
int main(int, char**)
{
// Create application window
//ImGui_ImplWin32_EnableDpiAwareness();
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_OWNDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, _T("ImGui Example"), NULL };
::RegisterClassEx(&wc);
HWND hwnd = ::CreateWindow(wc.lpszClassName, _T("Dear ImGui Winapi+OpenGL3 Example"), WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, NULL, NULL, wc.hInstance, NULL);
WNDCLASSEXW wc = { sizeof(wc), CS_OWNDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, L"ImGui Example", NULL };
::RegisterClassExW(&wc);
HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui Win32+OpenGL3 Example", WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, NULL, NULL, wc.hInstance, NULL);

// Initialize OpenGL3
if (!CreateDeviceOpenGL3(hwnd, &g_MainWindow))
// Initialize OpenGL
if (!CreateDeviceWGL(hwnd, &g_MainWindow))
{
CleanupDeviceOpenGL3(hwnd, &g_MainWindow);
CleanupDeviceWGL(hwnd, &g_MainWindow);
::DestroyWindow(hwnd);
::UnregisterClass(wc.lpszClassName, wc.hInstance);
::UnregisterClassW(wc.lpszClassName, wc.hInstance);
return 1;
}

wglMakeCurrent(g_MainWindow.hDC, g_hRC);

// Show the window
Expand All @@ -60,7 +63,7 @@ int main(int, char**)
ImGui::StyleColorsDark();
//ImGui::StyleColorsClassic();

// Setup Platform/Renderer bindings
// Setup Platform/Renderer backends
ImGui_ImplWin32_InitForOpenGL(hwnd);
ImGui_ImplOpenGL3_Init();

Expand All @@ -69,13 +72,14 @@ int main(int, char**)
// - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
// - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
// - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call.
// - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering.
// - Read 'docs/FONTS.md' for more instructions and details.
// - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
//io.Fonts->AddFontDefault();
//io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f);
//ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese());
//IM_ASSERT(font != NULL);

Expand All @@ -85,21 +89,21 @@ int main(int, char**)
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);

// Main loop
MSG msg;
ZeroMemory(&msg, sizeof(msg));
while (msg.message != WM_QUIT)
bool done = false;
while (!done)
{
// Poll and handle messages (inputs, window resize, etc.)
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
if (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
// See the WndProc() function below for our to dispatch events to the Win32 backend.
MSG msg;
while (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
continue;
if (msg.message == WM_QUIT)
done = true;
}
if (done)
break;

// Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame();
Expand All @@ -110,7 +114,7 @@ int main(int, char**)
if (show_demo_window)
ImGui::ShowDemoWindow(&show_demo_window);

// 2. Show a simple window that we create ourselves. We use a Begin/End pair to created a named window.
// 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window.
{
static float f = 0.0f;
static int counter = 0;
Expand All @@ -129,7 +133,7 @@ int main(int, char**)
ImGui::SameLine();
ImGui::Text("counter = %d", counter);

ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
ImGui::End();
}

Expand All @@ -148,84 +152,62 @@ int main(int, char**)
glViewport(0, 0, g_Width, g_Height);
glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w);
glClear(GL_COLOR_BUFFER_BIT);

// If you are using this code with non-legacy OpenGL header/contexts (which you should not, prefer using imgui_impl_opengl3.cpp!!),
// you may need to backup/reset/restore current shader using the commented lines below.
//GLint last_program;
//glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
//glUseProgram(0);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
//glUseProgram(last_program);

SwapBuffers(g_MainWindow.hDC);
// Present
::SwapBuffers(g_MainWindow.hDC);
}

ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext();

CleanupDeviceOpenGL3(hwnd, &g_MainWindow);
CleanupDeviceWGL(hwnd, &g_MainWindow);
wglDeleteContext(g_hRC);
::DestroyWindow(hwnd);
::UnregisterClass(wc.lpszClassName, wc.hInstance);
::UnregisterClassW(wc.lpszClassName, wc.hInstance);

return 0;
}

static bool ActivateOpenGL3(HWND hWnd)
// Helper functions
bool CreateDeviceWGL(HWND hWnd, WGL_WindowData* data)
{
HDC hDc = GetDC(hWnd);

HDC hDc = ::GetDC(hWnd);
PIXELFORMATDESCRIPTOR pfd = { 0 };

pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;

int pf = ChoosePixelFormat(hDc, &pfd);
const int pf = ::ChoosePixelFormat(hDc, &pfd);
if (pf == 0)
{
return false;
}

if (SetPixelFormat(hDc, pf, &pfd) == FALSE)
{
return false;
}

ReleaseDC(hWnd, hDc);
return true;
}

// Helper functions

static bool CreateDeviceOpenGL3(HWND hWnd, RendererData* data)
{
if (!ActivateOpenGL3(hWnd))
if (::SetPixelFormat(hDc, pf, &pfd) == FALSE)
return false;
::ReleaseDC(hWnd, hDc);

data->hDC = GetDC(hWnd);

data->hDC = ::GetDC(hWnd);
if (!g_hRC)
{
g_hRC = wglCreateContext(data->hDC);
}

return true;
}

static void CleanupDeviceOpenGL3(HWND hWnd, RendererData* data)
void CleanupDeviceWGL(HWND hWnd, WGL_WindowData* data)
{
wglMakeCurrent(NULL, NULL);
ReleaseDC(hWnd, data->hDC);
::ReleaseDC(hWnd, data->hDC);
}

// Forward declare message handler from imgui_impl_win32.cpp
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

// Win32 message handler
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
Expand Down

0 comments on commit 6bc23ab

Please sign in to comment.