I have just started on NeHe's OpenGl tutorials and I have written all the code from the first tutorial.
The program didn't run how i wanted it to run so i started to debug it and found out that a WM_QUIT message was posted somewhere.
I didn't post it anywhere exept from "case close:"(which never happens, I don't get that far).
So i just wondered, what could possibly post a WM_QUIT message. I am not that familiar with c++, but i have done a lot of java before.
Thanks in advance and sorry about my bad English.
Code:
#include <Windows.h>
#include <gl\GL.h>
#include <gl\GLU.h>
#include <glaux.h>
#include <iostream>
// Window/Rendering vars
HGLRC hRC = NULL; // Handle to the rendering context
HDC hDC = NULL; // Handle to Device context
HWND hWnd = NULL; // Handle to the Window
HINSTANCE hInstance = NULL; // Handle to a instance of the application
bool active = true; // Is window active
bool fullscreen = true; // Is window fullscreen
bool keys[256]; // Array for keypresses
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Declaration of the window procedure
GLvoid ResizeGLScene(GLsizei width, GLsizei height) //resize and initialize GL window
{
if(height == 0)
{
height = 1;
}
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION); // Code after this will affect the projection matrix
glLoadIdentity(); // Reset the current matrix(projection)
gluPerspective(45.0f, width/height, 0.1f, 100.0f); // setting the perspective and calculating aspect ratio
glMatrixMode(GL_MODELVIEW); // Code after this will affect the modelview matrix
glLoadIdentity(); // Reset the current matrix(modelview)
}
int InitGL(GLvoid) //Setup and initialize openGL
{
glShadeModel(GL_SMOOTH); //Set shademodel to smooth shading
glClearColor(1.0f, 0.5f, 0.5f, 0.0f); //set the screen default color rgb
glClearDepth(1.0f); // set default depth to 1 and setup
glEnable(GL_DEPTH_TEST); // Enables depth test
glDepthFunc(GL_LEQUAL); // indicates the type of depth test
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // nicest perspective calculations
return true;
}
int DrawGLScene(GLvoid) //DRAWING
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // set the screen to the clear color and clears the depth buffer
glLoadIdentity();
glBegin(GL_QUADS);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex2f(100, 200);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex2f(200, 200);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex2f(200, 100);
glColor3f(0.0f, 0.0f, 0.0f);
glVertex2f(100, 100);
glEnd();
return true;
}
GLvoid KillGLWindow(GLvoid)
{
if(fullscreen) // is fuulscreen active
{
ChangeDisplaySettings(NULL, 0); // use default displaysetting(no fullscreen)
ShowCursor(true);
}
if(hRC) // is a rendering context present?
{
if(!wglMakeCurrent(NULL, NULL)) // can device context and rendering context be released?
{
MessageBox(NULL, "Couldn't Release DC And RC", "Shutdown Error", MB_OK | MB_ICONINFORMATION);
}
if(!wglDeleteContext(hRC)) // can rendering context be deleted
{
MessageBox(NULL, "Couldn't Release DC And RC", "Shutdown Error", MB_OK | MB_ICONINFORMATION);MessageBox(NULL, "Couldn't Delete Rendering Context", "Shutdown Error", MB_OK | MB_ICONINFORMATION);
}
hRC = NULL;
}
if(hDC && !ReleaseDC(hWnd, hDC)) // Can device context be released
{
MessageBox(NULL, "Couldn't Release Device Context", "Shutdown Error", MB_OK | MB_ICONINFORMATION);
}
if(hWnd && !DestroyWindow(hWnd)) // can window be destroyed
{
MessageBox(NULL, "Couldn't destroy window handle", "Shutdown Error", MB_OK | MB_ICONINFORMATION);
hWnd = NULL;
}
if(!UnregisterClass("OpenGL", hInstance))
{
MessageBox(NULL, "Couldn't Unregister Class", "Shutdown Error", MB_OK | MB_ICONINFORMATION);
hInstance = NULL;
}
}
BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
{
GLuint PixelFormat;
WNDCLASS wc;
DWORD dwExStyle; //Window Extended Style
DWORD dwStyle; //Window Style
RECT WindowRect;
WindowRect.left = 0;
WindowRect.right = width;
WindowRect.top = 0;
WindowRect.bottom = height;
fullscreen = fullscreenflag;
hInstance = GetModuleHandle(NULL);
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = (WNDPROC) WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = "OpenGL";
if(!RegisterClass(&wc))
{
MessageBox(NULL, "Couldn't Register The Window Class", "ERROR", MB_OK | MB_ICONEXCLAMATION);
return false;
}
if(fullscreen)
{
DEVMODE dmScreenSettings; //Device Mode
memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
dmScreenSettings.dmSize = sizeof(dmScreenSettings);
dmScreenSettings.dmPelsHeight = width;
dmScreenSettings.dmPelsHeight = height;
dmScreenSettings.dmBitsPerPel = bits;
dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
if(ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
{
if(MessageBox(NULL, "Fullscreen mode not supported by Graphics Card. Do you want to use windowed mode?", "OpenGL", MB_YESNO | MB_ICONEXCLAMATION) == IDYES)
{
fullscreen = false;
}
else
{
MessageBox(NULL, "Program is closing.", "CLOSING", MB_OK | MB_ICONSTOP);
return false;
}
}
}
if(fullscreen)
{
dwExStyle = WS_EX_APPWINDOW;
dwStyle = WS_POPUP;
ShowCursor(false);
}
else
{
dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
dwStyle = WS_OVERLAPPEDWINDOW;
}
AdjustWindowRectEx(&WindowRect, dwStyle, false, dwExStyle);
if(!(hWnd = CreateWindowEx(dwExStyle,
"OpenGL",
title,
WS_CLIPSIBLINGS |
WS_CLIPCHILDREN |
dwStyle,
0, 0,
WindowRect.right - WindowRect.left,
WindowRect.bottom - WindowRect.top,
NULL,
NULL,
hInstance,
NULL)))
{
KillGLWindow();
MessageBox(NULL, "Window creation error", "Error", MB_OK | MB_ICONEXCLAMATION);
return false;
}
static PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW |
PFD_SUPPORT_OPENGL |
PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
bits,
0, 0, 0, 0, 0, 0,
0,
0,
0,
0, 0, 0, 0,
16,
0,
0,
PFD_MAIN_PLANE,
0,
0, 0, 0
};
if(!(hDC = GetDC(hWnd)))
{
KillGLWindow();
MessageBox(NULL, "Can't create a Gl Device Context.", "ERROR", MB_OK | MB_ICONEXCLAMATION);
return false;
}
if(!(PixelFormat = ChoosePixelFormat(hDC, &pfd)))
{
KillGLWindow();
MessageBox(NULL, "Couldn't find the rigth pixelformat", "ERROR", MB_OK | MB_ICONEXCLAMATION);
return false;
}
if(!SetPixelFormat(hDC, PixelFormat, &pfd))
{
KillGLWindow();
MessageBox(NULL, "Couldn't set pixelformat", "ERROR", MB_OK | MB_ICONEXCLAMATION);
return false;
}
if(!(hRC = wglCreateContext(hDC)))
{
KillGLWindow();
MessageBox(NULL, "Can't create a Gl rendering Context.", "ERROR", MB_OK | MB_ICONEXCLAMATION);
return false;
}
if(!wglMakeCurrent(hDC, hRC))
{
KillGLWindow();
MessageBox(NULL, "Can't activate a Gl rendering Context.", "ERROR", MB_OK | MB_ICONEXCLAMATION);
return false;
}
ShowWindow(hWnd, SW_SHOW);
SetForegroundWindow(hWnd);
SetFocus(hWnd);
ResizeGLScene(width, height);
if(!InitGL())
{
KillGLWindow();
MessageBox(NULL, "GL Initialization Failed", "ERROR", MB_OK | MB_ICONEXCLAMATION);
return false;
}
return true;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_ACTIVATE:
{
if(!HIWORD(wParam))
{
active = true;
}
else
{
active = false;
}
return 0;
}
case WM_SYSCOMMAND:
{
switch(wParam)
{
case SC_SCREENSAVE:
case SC_MONITORPOWER:
return 0;
}
break;
}
case WM_CLOSE:
{
std::cout << "Close" << std::endl;
PostQuitMessage(0);
return 0;
}
case WM_KEYDOWN:
{
keys[wParam] = true;
return 0;
}
case WM_KEYUP:
{
keys[wParam] = false;
return 0;
}
case WM_SIZE:
{
ResizeGLScene(LOWORD(lParam), HIWORD(lParam));
return 0;
}
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
bool done = false;
if(MessageBox(NULL, "Run application in fullscreen mode?", "Initate Fullscreen", MB_YESNO | MB_ICONQUESTION) == IDNO)
{
fullscreen = false;
}
if(!CreateGLWindow("NeHe's OpenGl Framework", 800, 600, 16, fullscreen))
{
MessageBox(NULL, "framwork failed", "ERROR", MB_OK | MB_ICONEXCLAMATION);
return 0;
}
while(!done)
{
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if(msg.message = WM_QUIT)
{
std::cout << "EXIT" << std::endl;
done = false;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
std::cout << "Translate and Dispatch Msg" << std::endl;
}
}
else
{
if(active)
{
if(keys[VK_ESCAPE])
{
done = true;
}
else
{
DrawGLScene();
SwapBuffers(hDC);
}
}
if(keys[VK_F1])
{
keys[VK_F1] = false;
KillGLWindow();
fullscreen =! fullscreen;
if(!CreateGLWindow("NeHe's OpenGL Framework", 800, 600, 16, fullscreen))
{
return 0;
}
}
}
}
KillGLWindow();
return (msg.wParam);
}
In the case of this program the thing that's setting the message to WM_QUIT is this:
if(msg.message = WM_QUIT)
You've accidentally set the message to WM_QUIT instead of testing for the value by using ==, and because the result of the operation is a non-zero value it always evaluates the true section. In other words it terminates as soon as it receives even a single message rather than pumping the message loop.
If you change the line to:
if(msg.message == WM_QUIT)
Then it tests, instead of assigning the value, and should work correctly.
In general, compiling with full warnings will point this one out.
From the MSDN docs:
WM_QUIT message
Indicates a request to terminate an application, and is generated when the application calls the PostQuitMessage function. This message causes the GetMessage function to return zero.
Related
I want to see in a single window graphics with OpenGL and have the ability to insert the button. For example: half of the screen buttons, the other half - graphics. I am trying to create a child window, but get error 1400 when creating a child window, in the CreateOpenGLChildWindow() function. The main program window is created correctly and hWnd(hWndParent for the child window) is correct. But CreateWindow() in the CreateOpenGLChildWindow() function does not create a window. What's wrong? I apologize for such bad code and explanation of the problem.
My code:
#include <Windows.h>
#include <GL/GL.h>
#include <GL/GLU.h>
#include <strsafe.h>
#pragma comment(lib, "OpenGL32.lib")
void ErrorExit(LPTSTR lpszFunction)
{
// Retrieve the system error message for the last-error code
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)& lpMsgBuf,
0, NULL);
// Display the error message and exit the process
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
ExitProcess(dw);
}
HWND CreateOpenGLChildWindow(wchar_t* title, int x, int y, int width, int height,
BYTE type, DWORD flags, HWND hWndParent)
{
int pf;
HDC hDC;
HWND hWnd;
PIXELFORMATDESCRIPTOR pfd;
//error here
hWnd = CreateWindow(
L"OpenGL",
title,
CS_OWNDC | WS_CLIPSIBLINGS,
x,
y,
width,
height,
hWndParent,
NULL,
NULL,
NULL
);
if (!hWnd) {
MessageBox(NULL, L"CreateWindow() failed: Cannot create a window.",
L"Error", MB_OK);
ErrorExit((LPTSTR)L"CreateWindow");
return NULL;
}
hDC = GetDC(hWnd);
/* there is no guarantee that the contents of the stack that become
the pfd are zeroed, therefore _make sure_ to clear these bits. */
memset(&pfd, 0, sizeof(pfd));
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | flags;
pfd.iPixelType = type;
pfd.cColorBits = 32;
pf = ChoosePixelFormat(hDC, &pfd);
if (pf == 0) {
MessageBox(NULL, L"ChoosePixelFormat() failed: "
"Cannot find a suitable pixel format.", L"Error", MB_OK);
return 0;
}
if (SetPixelFormat(hDC, pf, &pfd) == FALSE) {
MessageBox(NULL, L"SetPixelFormat() failed: "
"Cannot set format specified.", L"Error", MB_OK);
return 0;
}
DescribePixelFormat(hDC, pf, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
ReleaseDC(hWnd, hDC);
return hWnd;
}
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
wchar_t WinName[] = L"MainFrame";
int WINAPI WinMain(
HINSTANCE This,
HINSTANCE Prev,
LPSTR cmd,
int mode
)
{
HDC hDC; /* device context */
HGLRC hRC; /* opengl context */
HWND hWnd;
MSG msg;
WNDCLASS wc;
wc.hInstance = This;
wc.lpszClassName = WinName;
wc.lpfnWndProc = WndProc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszMenuName = NULL;
wc.cbClsExtra = NULL;
wc.cbWndExtra = NULL;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
if (!RegisterClass(&wc)) return NULL;
int windowWidth = 800;
int windowHeight = 800;
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
hWnd = CreateWindow(
WinName,
L"Title",
WS_VISIBLE | WS_SYSMENU | WS_MINIMIZEBOX,
(screenWidth - windowWidth) / 2,
(screenHeight - windowHeight) / 2,
windowWidth,
windowHeight,
HWND_DESKTOP,
NULL,
This,
NULL
);
if (!hWnd)
{
MessageBox(NULL, L"MAIN HWND ERROR!!!",
L"Error", MB_OK);
ErrorExit((LPTSTR)L"CreateWindow");
exit(1);
}
HWND childOpenGLWindowHWND = CreateOpenGLChildWindow(
WinName,
0,
0,
600,
800,
NULL,
NULL,
hWnd
);
if (!childOpenGLWindowHWND)
MessageBox(NULL, L"Child window init error", L"Error", MB_OK);
hDC = GetDC(hWnd);
hRC = wglCreateContext(hDC);
wglMakeCurrent(hDC, hRC);
SendMessage(hWnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_SET, UISF_HIDEFOCUS), NULL);
ShowWindow(hWnd, mode);
while (GetMessage(&msg, NULL, NULL, NULL))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
wglMakeCurrent(NULL, NULL);
ReleaseDC(hWnd, hDC);
wglDeleteContext(hRC);
DestroyWindow(hWnd);
return msg.wParam;
//return NULL;
}
void display()
{
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
glFlush();
}
LRESULT CALLBACK WndProc(
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
PAINTSTRUCT ps;
switch (message)
{
case WM_PAINT:
display();
BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(NULL);
break;
default: return DefWindowProc(hWnd, message, wParam, lParam);
}
return NULL;
}
Call GetLastError() immediately after the CreateWindow() in CreateOpenGLChildWindow(). You will get error 1407 (ERROR_CANNOT_FIND_WND_CLASS).
hWnd = CreateWindow(
L"OpenGL",
title,
CS_OWNDC | WS_CLIPSIBLINGS,
x,
y,
width,
height,
hWndParent,
NULL,
NULL,
NULL
);
if (!hWnd) {
DWORD dw = GetLastError();//error code: 1407
MessageBox(NULL, L"CreateWindow() failed: Cannot create a window.",
L"Error", MB_OK);
ErrorExit((LPTSTR)L"CreateWindow");
return NULL;
}
That indicates the class name L"OpenGL" is not registered. You need to register the child window class like you did for the main window.
CS_OWNDC is a class style, not a window style. To assign a style to a window class, assign the style to the style member of the WNDCLASSEX structure.
You need to add the WS_CHILD style when creating a child window.
We have an application where we use both GDI and OpenGL to draw to the same HWND, but exclusively.
Example:
initially we are in 2d mode, so we draw on it using GDI
then we switch to 3d mode and we draw on it using OpenGL
then we switch back to 2d mode and we draw on it using GDI
When switching to 3d mode, we just create an OpenGL context for that HWND and we can draw on it using OpenGL. When switching back to 2d mode, we just destroy the OpenGL context and we can draw to the HWND using GDI.
This worked very well until recently. On Windows 10, for some NVidia cards, this does not work any more if the driver is more recent than 382.05. In this case, when we delete the OpenGL context and draw on the HWND using GDI, the window still displays the last content from OpenGL.
I have checked all available pixel formats. All have the same issue.
Are we doing something wrong, or is it an NVidia bug? Do you see solutions / workarounds?
There is possibility that it is related to NVidia + Intel dual GPU setups, but there is at least one counterexample. Cards on for which we have feedback:
NOT REPRODUCED:
GTX 980M, single GPU
GTX 1060, single GPU
REPRODUCED:
GTX 1060 (Forceware 397.31) + Intel HD Graphics 630
Quadro M3000M (Forceware 387.95) + Intel HD Graphics P530
Qudrao K110M + Intel HD 4600
Quadro P3000 + Intel HD 630
Quadro M4000 (Forceware 385.90), single GPU
It is not an option to draw 2d content in OpenGL or vice versa. Also, the application is very performance sensitive, such that it is not an option to draw 2d content to an offscreen GDI image in order to draw it as an OpenGL quad. It is also not an option to destroy and recreate the HWND.
Below is a sample application to reproduce the issue. By default, the application shows a blue background with some text in GDI mode. In OpenGL mode a rotating triangle is shown. Space key is used to switch between modes.
#include <windows.h>
#include <GL/gl.h>
#include <iostream>
#pragma comment( lib, "OpenGL32.lib" )
HWND s_hwnd = 0;
HDC s_hdc = 0;
HGLRC s_hglrc = 0;
bool s_quit = false;
static HGLRC createContext(HWND hwnd, HDC hdc)
{
PIXELFORMATDESCRIPTOR pfd;
memset(&pfd, 0, sizeof(pfd));
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL |
PFD_GENERIC_ACCELERATED /*| PFD_DOUBLEBUFFER*/;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
int pf = ChoosePixelFormat(hdc, &pfd);
SetPixelFormat(hdc, pf, &pfd);
return wglCreateContext(hdc);
}
static void display()
{
if (s_hglrc)
{
/* rotate a triangle around */
glClear(GL_COLOR_BUFFER_BIT);
glRotatef(1.0f, 0.0f, 0.0f, 1.0f);
glBegin(GL_TRIANGLES);
glIndexi(1);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex2f(0.0f, 0.8f);
glIndexi(2);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex2f(-0.8f, -0.8f);
glIndexi(3);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex2f(0.8f, -0.8f);
glEnd();
glFlush();
SwapBuffers(s_hdc);
}
else
{
HBRUSH brush = CreateSolidBrush(RGB(0, 0, 255));
RECT rect;
GetClientRect(s_hwnd, &rect);
FillRect(s_hdc, &rect, brush);
DeleteObject(brush);
DrawText(s_hdc, L"This is GDI", -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
GdiFlush();
}
}
static void toggle_between_GDI_and_OpenGL()
{
if (!s_hglrc)
{
s_hglrc = createContext(s_hwnd, s_hdc);
wglMakeCurrent(s_hdc, s_hglrc);
std::cout << "Renderer: " << glGetString(GL_RENDERER) << std::endl;
}
else
{
wglMakeCurrent(NULL, NULL);
wglDeleteContext(s_hglrc);
s_hglrc = 0;
}
}
LONG WINAPI WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_ERASEBKGND:
return 0;
case WM_PAINT:
display();
PAINTSTRUCT ps;
BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
return 0;
case WM_TIMER:
display();
return 0;
case WM_SIZE:
glViewport(0, 0, LOWORD(lParam), HIWORD(lParam));
PostMessage(hWnd, WM_PAINT, 0, 0);
return 0;
case WM_CHAR:
switch (wParam) {
case 27: /* ESC key */
s_quit = true;
break;
case ' ':
toggle_between_GDI_and_OpenGL();
PostMessage(hWnd, WM_PAINT, 0, 0);
break;
}
return 0;
case WM_CLOSE:
s_quit = true;
return 0;
case WM_QUIT:
s_quit = true;
return 0;
}
return (LONG)DefWindowProc(hWnd, uMsg, wParam, lParam);
}
static HWND CreateOpenGLWindow()
{
HWND hWnd;
WNDCLASS wc;
static HINSTANCE hInstance = 0;
/* only register the window class once - use hInstance as a flag. */
if (!hInstance) {
hInstance = GetModuleHandle(NULL);
wc.style = CS_OWNDC;
wc.lpfnWndProc = (WNDPROC)WindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = L"OpenGL";
if (!RegisterClass(&wc)) {
MessageBox(NULL, L"RegisterClass() failed: Cannot register window class.", L"Error", MB_OK);
return NULL;
}
}
hWnd = CreateWindow(L"OpenGL", L"GDI / OpenGL switching", WS_OVERLAPPEDWINDOW |
WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
0, 0, 256, 256, NULL, NULL, hInstance, NULL);
if (hWnd == NULL) {
MessageBox(NULL, L"CreateWindow() failed: Cannot create a window.",
L"Error", MB_OK);
return NULL;
}
return hWnd;
}
void executeApplication()
{
s_hwnd = CreateOpenGLWindow();
if (s_hwnd == NULL)
exit(1);
s_hdc = GetDC(s_hwnd);
//toggle_between_GDI_and_OpenGL(); // initialize OpenGL
ShowWindow(s_hwnd, SW_SHOW);
UpdateWindow(s_hwnd);
SetTimer(s_hwnd, 1, 50, NULL);
while (1) {
MSG msg;
while (PeekMessage(&msg, s_hwnd, 0, 0, PM_NOREMOVE)) {
if (!s_quit && GetMessage(&msg, s_hwnd, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else {
goto quit;
}
}
if (s_quit)
goto quit;
}
quit:
wglMakeCurrent(NULL, NULL);
if (s_hglrc)
toggle_between_GDI_and_OpenGL(); // uninitialize OpenGL
DestroyWindow(s_hwnd);
DeleteDC(s_hdc);
}
int APIENTRY WinMain(HINSTANCE hCurrentInst, HINSTANCE hPreviousInst, LPSTR lpszCmdLine, int nCmdShow)
{
executeApplication();
return 0;
}
int main()
{
executeApplication();
return 0;
}
Update: #Ripi2 and #datenwolf suggested that switching back to GDI is not allowed once we set a pixel format for the window that does not have PFD_SUPPORT_GDI flag. This is an excerpt from SetPixelFormat documentation:
Setting the pixel format of a window more than once can lead to significant complications for the Window Manager and for multithread applications, so it is not allowed.
That is a strong sign that they are correct.
Update 2: I had stated that it is not an option to recreate the HWND. But after rethinking, that seems to be the easiest solution to me.
The code:
#include <windows.h>
#include <GL/gl.h>
#include <iostream>
#pragma comment( lib, "OpenGL32.lib" )
HWND s_mainWnd = 0;
HWND s_childWnd = 0;
HGLRC s_hglrc = 0;
bool s_isOpenGLMode = false;
bool s_quit = false;
static HWND CreateChildWindow(HWND hWndParent);
static HGLRC createContext(HWND hwnd, HDC hdc)
{
PIXELFORMATDESCRIPTOR pfd;
memset(&pfd, 0, sizeof(pfd));
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL |
PFD_GENERIC_ACCELERATED /*| PFD_DOUBLEBUFFER*/;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
int pf = ChoosePixelFormat(hdc, &pfd);
SetPixelFormat(hdc, pf, &pfd);
return wglCreateContext(hdc);
}
static void display()
{
HDC hdc = GetDC(s_childWnd);
if (s_isOpenGLMode)
{
/* rotate a triangle around */
glClear(GL_COLOR_BUFFER_BIT);
glRotatef(1.0f, 0.0f, 0.0f, 1.0f);
glBegin(GL_TRIANGLES);
glIndexi(1);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex2f(0.0f, 0.8f);
glIndexi(2);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex2f(-0.8f, -0.8f);
glIndexi(3);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex2f(0.8f, -0.8f);
glEnd();
glFlush();
SwapBuffers(hdc);
}
else
{
HBRUSH brush = CreateSolidBrush(RGB(0, 0, 255));
RECT rect;
GetClientRect(s_childWnd, &rect);
FillRect(hdc, &rect, brush);
DeleteObject(brush);
DrawText(hdc, L"This is GDI", -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
GdiFlush();
}
DeleteDC(hdc);
}
static void toggle_between_GDI_and_OpenGL()
{
if (!s_isOpenGLMode)
{
DestroyWindow(s_childWnd);
s_childWnd = CreateChildWindow(s_mainWnd);
ShowWindow(s_childWnd, SW_SHOW);
HDC hdc = GetDC(s_childWnd);
s_hglrc = createContext(s_childWnd, hdc);
wglMakeCurrent(hdc, s_hglrc);
DeleteDC(hdc);
std::cout << "Renderer: " << glGetString(GL_RENDERER) << std::endl;
RECT rect;
GetClientRect(s_childWnd, &rect);
glViewport(0, 0, max(rect.left, rect.right), max(rect.top, rect.bottom));
}
else
{
if (s_hglrc)
{
wglMakeCurrent(NULL, NULL);
wglDeleteContext(s_hglrc);
s_hglrc = 0;
}
DestroyWindow(s_childWnd);
s_childWnd = CreateChildWindow(s_mainWnd);
ShowWindow(s_childWnd, SW_SHOW);
}
s_isOpenGLMode = !s_isOpenGLMode;
}
LONG WINAPI WindowProc_MainWnd(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_TIMER:
display();
return 0;
case WM_CHAR:
switch (wParam) {
case 27: /* ESC key */
s_quit = true;
break;
case ' ':
toggle_between_GDI_and_OpenGL();
PostMessage(hWnd, WM_PAINT, 0, 0);
break;
}
return 0;
case WM_CLOSE:
case WM_QUIT:
s_quit = true;
return 0;
case WM_SIZE:
if (s_childWnd)
MoveWindow(s_childWnd, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
break;
}
return (LONG)DefWindowProc(hWnd, uMsg, wParam, lParam);
}
LONG WINAPI WindowProc_ChildWnd(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_ERASEBKGND:
return 0;
case WM_PAINT:
display();
PAINTSTRUCT ps;
BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
return 0;
case WM_SIZE:
if (s_hglrc && s_isOpenGLMode)
glViewport(0, 0, LOWORD(lParam), HIWORD(lParam));
PostMessage(hWnd, WM_PAINT, 0, 0);
return 0;
}
return (LONG)DefWindowProc(hWnd, uMsg, wParam, lParam);
}
static HWND CreateMainWindow()
{
static HINSTANCE hInstance = 0;
if (!hInstance)
{
hInstance = GetModuleHandle(NULL);
WNDCLASS wc;
wc.style = CS_VREDRAW | CS_HREDRAW;
wc.lpfnWndProc = (WNDPROC)WindowProc_MainWnd;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = L"MainWindow";
if (!RegisterClass(&wc)) {
MessageBox(NULL, L"RegisterClass() failed: Cannot register window class.", L"Error", MB_OK);
return NULL;
}
}
HWND hWnd = CreateWindow(L"MainWindow", L"GDI / OpenGL switching", WS_OVERLAPPEDWINDOW |
WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
300, 300, 256, 256, NULL, NULL, hInstance, NULL);
if (hWnd == NULL) {
MessageBox(NULL, L"CreateWindow() failed: Cannot create a window.",
L"Error", MB_OK);
return NULL;
}
return hWnd;
}
static HWND CreateChildWindow(HWND hWndParent)
{
static HINSTANCE hInstance = 0;
/* only register the window class once - use hInstance as a flag. */
if (!hInstance)
{
hInstance = GetModuleHandle(NULL);
WNDCLASS wc;
wc.style = CS_OWNDC;
wc.lpfnWndProc = (WNDPROC)WindowProc_ChildWnd;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = L"ChildWindow";
if (!RegisterClass(&wc)) {
MessageBox(NULL, L"RegisterClass() failed: Cannot register window class.", L"Error", MB_OK);
return NULL;
}
}
RECT rect;
GetClientRect(hWndParent, &rect);
HWND hWnd = CreateWindow(L"ChildWindow", L"ChildWindow", WS_CHILD,
0, 0, max(rect.left, rect.right), max(rect.top, rect.bottom), hWndParent, NULL, hInstance, NULL);
if (hWnd == NULL) {
MessageBox(NULL, L"CreateWindow() failed: Cannot create a window.",
L"Error", MB_OK);
return NULL;
}
return hWnd;
}
void executeApplication()
{
s_mainWnd = CreateMainWindow();
if (s_mainWnd == NULL)
exit(1);
s_childWnd = CreateChildWindow(s_mainWnd);
//toggle_between_GDI_and_OpenGL(); // initialize OpenGL
ShowWindow(s_mainWnd, SW_SHOW);
ShowWindow(s_childWnd, SW_SHOW);
UpdateWindow(s_mainWnd);
UpdateWindow(s_childWnd);
SetTimer(s_mainWnd, 1, 50, NULL);
while (1) {
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
if (!s_quit && GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else {
goto quit;
}
}
if (s_quit)
goto quit;
}
quit:
if (s_hglrc)
{
wglMakeCurrent(NULL, NULL);
wglDeleteContext(s_hglrc);
}
DestroyWindow(s_childWnd);
DestroyWindow(s_mainWnd);
}
int APIENTRY WinMain(HINSTANCE hCurrentInst, HINSTANCE hPreviousInst, LPSTR lpszCmdLine, int nCmdShow)
{
executeApplication();
return 0;
}
int main()
{
executeApplication();
return 0;
}
What happened to you is, that up until now you relied on undefined behaviour – actually it never was supposed to work in the first place, and you were just lucky. Once you've set a doublebuffered pixelformat on a window that does not set the PFD_SUPPORT_GDI flag, you no longer can use it for GDI operations.
The OpenGL rendering context does not matter! A lot of people – it behooves me for what reason those who believe it do believe it – suffer from the misconception, that OpenGL render contexts in some way were tied to a particular HDC or HWND. Nothing could be further from the truth. As long as a drawables pixelformat is compatible to a given OpenGL context, that context can be bound to it.
And because there're no ties between your OpenGL render contexts and the windows, all that little dance of destroying and re-creating the context has no meaningful effect whatsoever. Maybe, just maybe, that little dance triggered some codepath in the driver, which made your illegal actions somehow work. But what's more likely is, that you just did the raindance in expectation for it to do something useful, while it was completely bogus in the first place, and you could just have not done it in the first place, to the same effect.
Are we doing something wrong,
Yes, yes you are. You are doing something, that never was allowed or supposed to work in the first place. You just didn't notice it, because so far OSs/drivers didn't make use of the wiggle room afforded to them by this not being allowed for you. However recent developments in GPU/OS/drivers now do make use of the wiggle room, and just steamroll over your code.
It is not an option to draw 2d content in OpenGL .
Why?
It is not an option to draw 2d content in OpenGL or vice versa. Also, the application is very performance sensitive, such that it is not an option to draw 2d content to an offscreen GDI image in order to draw it as an OpenGL quad
Did you actually try and profile it? 10 bucks says this would perform just fine.
A bit of search at OpenGL on Windows - Generic Implementation and Hardware Implementations reveals:
OpenGL and GDI graphics cannot be mixed in a double-buffered window.
An application can directly draw both OpenGL graphics and GDI graphics
into a single-buffered window, but not into a double-buffered window.
And also from PIXELFORMATDESCRIPTOR structure
PFD_SUPPORT_GDI: The buffer supports GDI drawing. This flag and
PFD_DOUBLEBUFFER are mutually exclusive in the current generic
implementation.
Since you are using OpenGL 1.1, just add PFD_SUPPORT_GDI flag to your PIXELFORMATDESCRIPTOR pfd
I would not destroy the GL context. Instead I would try to change the
glViewport(x0,y0,xs,ys);
to small area in some corner or even hidden like
glViewport(0,0,1,1);
That way even if bug persist the user will see only pixel artifact in corner somwhere most likely not even notice it (especially if rendered with the same color as background).
I was reading NeHe's first guide to OpenGL programming, and when it came down to compiling the result of completing his first tutorial, I was stumped to find errors throughout. Here's the entire source file where the errors occur (Created in Eclipse CDT with 'opengl32' 'glaux' 'glut32' 'glu32' linked successfully):
#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glaux.h>
#define GLEW_STATIC
HGLRC hRC = NULL;
HDC hDC = NULL;
HWND hWnd = NULL;
HINSTANCE hInstance;
bool keys[256];
bool active = TRUE;
bool fullscreen = TRUE;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
GLvoid ResizeGLScene(GLsizei width, GLsizei height)
{
if(height == 0) height = 1;
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f, (GLfloat) width / (GLfloat) height, 0.1f, 100.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
int InitGL()
{
glShadeModel(GL_SMOOTH);
glClearColor(0, 0, 0, 0);
glClearDepth(1);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
return TRUE;
}
int DrawGLScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
return TRUE;
}
void KillGLWindow()
{
if(fullscreen)
{
ChangeDisplaySettings(NULL, 0);
ShowCursor(TRUE);
}
if(hRC)
{
if(!wglMakeCurrent(NULL, NULL))
{
MessageBox(NULL, "Release of DC and RC failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
}
if(!wglDeleteContext(hRC))
{
MessageBox(NULL, "Release rendering context failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
}
hRC = NULL;
}
if(hDC && !ReleaseDC(hWnd, hDC))
{
MessageBox(NULL, "Release device context failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
hDC = NULL;
}
if(hWnd && !DestroyWindow(hWnd))
{
MessageBox(NULL, "Could not release hWnd.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
hWnd = NULL;
}
if(!UnregisterClass("OpenGL", hInstance))
{
MessageBox(NULL, "Could not unregister class.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
hInstance = NULL;
}
}
BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenFlag)
{
GLuint pixelFormat;
WNDCLASS wc;
DWORD dwExStyle;
DWORD dwStyle;
RECT windowRect;
windowRect.left = 0;
windowRect.right = width;
windowRect.top = 0;
windowRect.bottom = height;
fullscreen = fullscreenFlag;
hInstance = GetModuleHandle(NULL);
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = "OpenGL";
if(!RegisterClass(&wc))
{
MessageBox(NULL, "Failed to register the window class.", "ERROR", MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
if(fullscreen)
{
DEVMODE dmScreenSettings;
memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
dmScreenSettings.dmSize = sizeof(dmScreenSettings);
dmScreenSettings.dmPelsWidth = width;
dmScreenSettings.dmPelsHeight = height;
dmScreenSettings.dmBitsPerPel = bits;
dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
if(ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
{
if(MessageBox(NULL, "The requested fullscreen mode is not supported by\nyour video card. Use windowed mode instead?", "OpenGL", MB_YESNO | MB_ICONEXCLAMATION) == IDYES)
{
fullscreen = FALSE;
}
else
{
MessageBox(NULL, "Program will now close.", "ERROR", MB_OK | MB_ICONSTOP);
return FALSE;
}
}
}
if(fullscreen)
{
dwExStyle = WS_EX_APPWINDOW;
dwStyle = WS_POPUP;
ShowCursor(FALSE);
}
else
{
dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
dwStyle = WS_OVERLAPPEDWINDOW;
}
AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle);
if(!(hWnd = CreateWindowEx(dwExStyle,
"OpenGL",
title,
WS_CLIPSIBLINGS |
WS_CLIPCHILDREN |
dwStyle,
0,
0,
windowRect.right - windowRect.left,
windowRect.bottom - windowRect.top,
NULL,
NULL,
hInstance,
NULL)))
{
KillGLWindow();
MessageBox(NULL, "Window creation error.", "ERROR", MB_OK | MB_ICONINFORMATION);
return FALSE;
}
static PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_BITMAP |
PFD_SUPPORT_OPENGL |
PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
bits,
0, 0, 0, 0, 0, 0,
0,
0,
0,
0, 0, 0, 0,
16,
0,
0,
PFD_MAIN_PLANE,
0,
0, 0, 0
};
if(!(hDC = GetDC(hWnd)))
{
KillGLWindow();
MessageBox(NULL, "Cannot create a GL device context.", "ERROR", MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
if(!(pixelFormat = ChoosePixelFormat(hDC, &pfd)))
{
ChoosePixelFormat(hDC, &pfd);
KillGLWindow();
MessageBox(NULL, "Cannot find a suitable pixel format.", "ERROR", MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
if(!SetPixelFormat(hDC, pixelFormat, &pfd))
{
KillGLWindow();
MessageBox(NULL, "Cannot set the pixel format.", "ERROR", MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
if(!(hRC = wglCreateContext(hDC)))
{
KillGLWindow();
MessageBox(NULL, "Cannot create a GL rendering context.", "ERROR", MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
if(!wglMakeCurrent(hDC, hRC))
{
KillGLWindow();
MessageBox(NULL, "Cannot activate the GL rendering context.", "ERROR", MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
ShowWindow(hWnd, SW_SHOW);
SetForegroundWindow(hWnd);
SetFocus(hWnd);
ResizeGLScene(width, height);
if(!InitGL())
{
KillGLWindow();
MessageBox(NULL, "Initialization failed.", "ERROR", MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_ACTIVATE:
{
if(!HIWORD(wParam))
{
active = TRUE;
}
else
{
active = FALSE;
}
return 0;
}
case WM_SYSCOMMAND:
{
switch(wParam)
{
case SC_SCREENSAVE:
case SC_MONITORPOWER:
return 0;
}
break;
}
case WM_CLOSE:
{
PostQuitMessage(0);
return 0;
}
case WM_KEYDOWN:
{
keys[wParam] = TRUE;
return 0;
}
case WM_KEYUP:
{
keys[wParam] = FALSE;
return 0;
}
case WM_SIZE:
{
ResizeGLScene(LOWORD(lParam), HIWORD(lParam));
return 0;
}
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
BOOL done = FALSE;
if(MessageBox(NULL, "Would you like to run in fullscreen mode?", "Start Fullscreen", MB_YESNO | MB_ICONQUESTION) == IDNO)
{
fullscreen = false;
}
if(!CreateGLWindow("OpenGL", 640, 480, 16, fullscreen))
{
return 0;
}
while(!done)
{
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if(msg.message == WM_QUIT)
{
done = TRUE;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
if(active)
{
if(keys[VK_ESCAPE])
{
done = TRUE;
}
else
{
DrawGLScene();
SwapBuffers(hDC);
}
}
if(keys[VK_F1])
{
keys[VK_F1] = FALSE;
KillGLWindow();
fullscreen = !fullscreen;
if(!CreateGLWindow("OpenGL", 640, 480, 16, fullscreen))
{
return 0;
}
}
}
}
KillGLWindow();
return msg.wParam;
}
...and here is the resulting compile (Using MinGW, opengl, glut, glaux added):
03:52:04 **** Incremental Build of configuration Debug for project GL ****
Info: Internal Builder is used for build
g++ -O0 -g3 -Wall -c -fmessage-length=0 -o "src\\Main.o" "..\\src\\Main.cpp"
..\src\Main.cpp: In function 'BOOL CreateGLWindow(char*, int, int, int, bool)':
..\src\Main.cpp:216:2: warning: narrowing conversion of 'bits' from 'int' to 'BYTE {aka unsigned char}' inside { } is ill-formed in C++11 [-Wnarrowing]
};
^
..\src\Main.cpp: In function 'int WinMain(HINSTANCE, HINSTANCE, LPSTR, int)':
..\src\Main.cpp:356:55: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
if(!CreateGLWindow("OpenGL", 640, 480, 16, fullscreen))
^
..\src\Main.cpp:397:58: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
if(!CreateGLWindow("OpenGL", 640, 480, 16, fullscreen))
^
g++ -o GL.exe "src\\Main.o" -lopengl32 -lglaux -lglaux -lglu32 -lfreeglut
src\Main.o: In function `Z14CreateGLWindowPciiib':
C:\Users\Michael\Desktop\LiTHiA\C++\GL\Debug/../src/Main.cpp:227: undefined reference to `_imp__ChoosePixelFormat#8'
C:\Users\Michael\Desktop\LiTHiA\C++\GL\Debug/../src/Main.cpp:230: undefined reference to `_imp__ChoosePixelFormat#8'
C:\Users\Michael\Desktop\LiTHiA\C++\GL\Debug/../src/Main.cpp:238: undefined reference to `_imp__SetPixelFormat#12'
src\Main.o: In function `WinMain#16':
C:\Users\Michael\Desktop\LiTHiA\C++\GL\Debug/../src/Main.cpp:386: undefined reference to `_imp__SwapBuffers#4'
collect2.exe: error: ld returned 1 exit status
03:52:05 Build Finished (took 1s.598ms)
I apologize that the code is quite extensive. Any help / advice? Any is appreciated. Thanks in advance.
Link against gdi32 (i.e. add -lgdi32 to the linker command line). This library defines the functions ChoosePixelFormat, SetPixelFormat and SwapBuffers which are reported as undefined references in the linker error messages.
I am learning opengl for the first time, and i am trying to make a simple program. My program runs in two threads, one thread renders the program on the screen, while the other thread updates the data of the program. However, sometimes when i try to close my program i get a nasty error message. I don't understand why, although i feel like it is a concurrency error. Here is my code.
Main.cpp
#define WIN32_LEAN_AND_MEAN
#define WIN32_EXTRA_LEAN
#define GLX_GLXEXT_LEGACY
#include <windows.h>
#include "glwindow.h"
#include "example.h"
#include "util.h"
void updateThread(Example* example);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmdLine, int cmdShow)
{
const int windowWidth = 1024;
const int windowHeight = 768;
const int windowBPP = 16;
const int windowFullscreen = false;
GLWindow programWindow(hInstance);
Example example;
programWindow.attachExample(&example);
if (!programWindow.create(windowWidth, windowHeight, windowBPP, windowFullscreen))
{
MessageBox(NULL, "Unable to create the OpenGL Window", "An error occurred", MB_ICONERROR | MB_OK);
programWindow.destroy();
return 1;
}
if (!example.init())
{
MessageBox(NULL, "Could not initialize the application", "An error occurred", MB_ICONERROR | MB_OK);
programWindow.destroy();
return 1;
}
HANDLE thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) updateThread, &example, 0, 0);
example.setThread(&thread);
while(programWindow.isRunning())
{
programWindow.processEvents();
example.render();
programWindow.swapBuffers();
}
example.shutdown();
programWindow.destroy();
return 0;
}
void updateThread(Example* example)
{
setFPS(2000);
while(true)
{
example->update();
sync();
}
}
Util.cpp
#include "util.h"
int fps;
long timeThen;
void sync()
{
while(fps == 0);
long gapTo = 1000 / fps + timeThen;
long timeNow = time(nullptr);
while (gapTo > timeNow)
{
timeNow = time(nullptr);
}
timeThen = timeNow;
}
void setFPS(int FPS)
{
fps = FPS;
}
glwindow.cpp
#include <ctime>
#include <iostream>
#include <windows.h>
#include <GL/gl.h>
#include "wglext.h"
#include "glwindow.h"
#include "example.h"
typedef HGLRC (APIENTRYP PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int*);
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL;
GLWindow::GLWindow(HINSTANCE hInstance):
m_isRunning(false),
m_example(NULL),
m_hinstance(hInstance),
m_lastTime(0)
{
}
bool GLWindow::create(int width, int height, int bpp, bool fullscreen)
{
DWORD dwExStyle;
DWORD dwStyle;
m_isFullscreen = fullscreen;
m_windowRect.left = (long)0;
m_windowRect.right = (long)width;
m_windowRect.top = (long)0;
m_windowRect.bottom = (long)height;
m_windowClass.cbSize = sizeof(WNDCLASSEX);
m_windowClass.style = CS_HREDRAW | CS_VREDRAW;
m_windowClass.lpfnWndProc = GLWindow::StaticWndProc;
m_windowClass.cbClsExtra = 0;
m_windowClass.cbWndExtra = 0;
m_windowClass.hInstance = m_hinstance;
m_windowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
m_windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
m_windowClass.hbrBackground = NULL;
m_windowClass.lpszMenuName = NULL;
m_windowClass.lpszClassName = "GLClass";
m_windowClass.hIconSm = LoadIcon(NULL, IDI_WINLOGO);
if(!RegisterClassEx(&m_windowClass))
{
return false;
}
if(m_isFullscreen)
{
DEVMODE dmScreenSettings;
memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
dmScreenSettings.dmSize = sizeof(dmScreenSettings);
dmScreenSettings.dmPelsWidth = width;
dmScreenSettings.dmPelsHeight = height;
dmScreenSettings.dmBitsPerPel = bpp;
dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
{
MessageBox(NULL, "Display mode failed", NULL, MB_OK);
m_isFullscreen = false;
}
}
if (m_isFullscreen)
{
dwExStyle = WS_EX_APPWINDOW;
dwStyle = WS_POPUP;
ShowCursor(false);
}
else
{
dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
dwStyle = WS_OVERLAPPEDWINDOW;
}
AdjustWindowRectEx(&m_windowRect, dwStyle, false, dwExStyle);
m_hwnd = CreateWindowEx(NULL, "GLClass", "BOGLGP - Chapter 2 - Simple OpenGL Application", dwStyle | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
0, 0, m_windowRect.right - m_windowRect.left, m_windowRect.bottom - m_windowRect.top, NULL, NULL, m_hinstance, this);
if (!m_hwnd)
{
MessageBox(NULL, "Window Creation Failed", NULL, MB_OK);
return 1;
}
m_hdc = GetDC(m_hwnd);
ShowWindow(m_hwnd, SW_SHOW);
UpdateWindow(m_hwnd);
m_lastTime = GetTickCount() / 1000.0f;
return true;
}
void GLWindow::destroy()
{
if (m_isFullscreen)
{
ChangeDisplaySettings(NULL, 0);
ShowCursor(true);
}
}
void GLWindow::attachExample(Example* example)
{
m_example = example;
}
bool GLWindow::isRunning()
{
return m_isRunning;
}
void GLWindow::processEvents()
{
MSG msg;
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
void GLWindow::setupPixelFormat(void)
{
int pixelFormat;
PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_SUPPORT_OPENGL |
PFD_DRAW_TO_WINDOW |
PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
32,
0, 0, 0, 0, 0, 0,
0,
0,
0,
0, 0, 0, 0,
16,
0,
0,
PFD_MAIN_PLANE,
0,
0, 0, 0,
};
pixelFormat = ChoosePixelFormat(m_hdc, &pfd);
SetPixelFormat(m_hdc, pixelFormat, &pfd);
}
LRESULT GLWindow::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_CREATE:
{
m_hdc = GetDC(hWnd);
setupPixelFormat();
int attribs[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
WGL_CONTEXT_MINOR_VERSION_ARB, 0,
0};
HGLRC tmpContext = wglCreateContext(m_hdc);
wglMakeCurrent(m_hdc, tmpContext);
wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) wglGetProcAddress("wglCreateContextAttribsARB");
if (!wglCreateContextAttribsARB)
{
MessageBox(NULL, "Open GL 3.0 Is Not Supported", NULL, MB_OK);
m_hglrc = tmpContext;
DestroyWindow(hWnd);
return 0;
}
else
{
m_hglrc = wglCreateContextAttribsARB(m_hdc, 0, attribs);
wglDeleteContext(tmpContext);
}
wglMakeCurrent(m_hdc, m_hglrc);
m_isRunning = true;
}
break;
case WM_DESTROY:
case WM_CLOSE:
wglMakeCurrent(m_hdc, NULL);
wglDeleteContext(m_hglrc);
m_isRunning = false;
PostQuitMessage(0);
return 0;
break;
case WM_SIZE:
{
int height = HIWORD(lParam);
int width = LOWORD(lParam);
getAttachedExample()->onResize(width, height);
}
break;
case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
{
DestroyWindow(m_hwnd);
}
break;
default:
break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
LRESULT CALLBACK GLWindow::StaticWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
GLWindow* window = NULL;
if(uMsg == WM_CREATE)
{
window = (GLWindow*)((LPCREATESTRUCT)lParam)->lpCreateParams;
SetWindowLongPtr(hWnd, GWL_USERDATA, (LONG_PTR)window);
}
else
{
window = (GLWindow*)GetWindowLongPtr(hWnd, GWL_USERDATA);
if(!window)
{
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}
return window->WndProc(hWnd, uMsg, wParam, lParam);
}
float GLWindow::getElapsedSeconds()
{
float currentTime = float(GetTickCount()) / 1000.0f;
float seconds = float(currentTime - m_lastTime);
m_lastTime = currentTime;
return seconds;
}
example.cpp
#include <windows.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include "example.h"
Example::Example()
{
m_rotationAngle = 0.0f;
}
bool Example::init()
{
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
return true;
}
void Example::update()
{
const float SPEED = 15.0f;
m_rotationAngle += SPEED;
if (m_rotationAngle > 360.0f)
{
m_rotationAngle -= 360.0f;
}
}
void Example::render()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glRotatef(m_rotationAngle, 0, 0, 1);
glBegin(GL_TRIANGLES);
glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
glVertex3f(-1.0f, -0.5f, -4.0f);
glColor4f(1.0f, 1.0f, 0.0f, 1.0f);
glVertex3f(1.0f, -0.5f, -4.0f);
glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
glVertex3f(0.0f, 0.5f, -4.0f);
glEnd();
}
void Example::shutdown()
{
TerminateThread(thread, 0);
}
void Example::onResize(int width, int height)
{
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f, float(width) / float(height), 1.0f, 100.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
EDIT
I am convinced that my problem is in the threading with example.cpp and main.cpp however i included all of the code to give more context to the situation.
Here is the error i get.
I don't see how your thread can exit cleanly.
In your while loop you should have something like
while (true) {
...
if (IShouldExit()) break;
}
That can be something simple like a bool or similar.
Then in your main you can set the flag then simply join with your thread, to give it time to exit cleanly.
My problem is that my program isn't displaying anything on screen.
This is the "main.cpp" code:
#include "paStdAfx.h"
#include "OpenGL.h"
HDC hDC = NULL;
HGLRC hRC = NULL;
HWND hwnd = NULL;
HINSTANCE hInstance = GetModuleHandle(NULL);
const wchar_t* szClassName = _T("*project name :3*");
static std::wstring Titles[] = {
_T("*project name :3* - 100% free!"),
_T("*project name :3* - 100% OpenGL!"),
_T("*project name :3* - Not cross platform!"),
_T("*project name :3* - Rawr"),
_T("*project name :3* - Entirely C++!"),
_T("*project name :3* - Woo, /r/gamedev!"),
_T("*project name :3* - Platypi, platypi everywhere."),
_T("*project name :3* - Nom nom nom"),
_T("*project name :3* - Thanks, StackExchange!"),
_T("*project name :3* - DRM Free!"),
_T("*project name :3* - <3"),
_T("*project name :3* - Minecraft is also fun!")
};
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL CreateOpenGLWindow(const wchar_t*, int, int, int);
OpenGL ogl;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow)
{
srand(time(NULL));
const std::wstring wtt = Titles[rand() % 11];
const wchar_t* WindowTitle = wtt.c_str();
BOOL done = FALSE;
MSG msg;
if(!CreateOpenGLWindow(WindowTitle, 800, 600, 32)){ MessageBox(NULL, _T("Could not create window :("), _T("Error!"), MB_OK | MB_ICONERROR); exit(EXIT_FAILURE); }
while(!done)
{
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if(msg.message == WM_QUIT)
done = TRUE;
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
ogl.RenderGLScene();
SwapBuffers(hDC);
}
}
ogl.KillOpenGL(hwnd, hDC, hRC);
return 0;
}
BOOL CreateOpenGLWindow(const wchar_t* title, int width, int height, int bits)
{
WNDCLASSEX wcx = {0};
DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
DWORD dwStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.style = CS_OWNDC;
wcx.lpfnWndProc = WndProc;
wcx.cbClsExtra = 0;
wcx.cbWndExtra = 0;
wcx.hInstance = hInstance;
wcx.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PROGRAMICON));
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
wcx.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
wcx.lpszClassName = szClassName;
wcx.lpszMenuName = NULL;
wcx.hIconSm = (HICON) LoadImage(hInstance, MAKEINTRESOURCE(IDI_PROGRAMICON), IMAGE_ICON, 16, 16, 0);
if(!RegisterClassEx(&wcx)){ MessageBox(NULL, _T("Failed to register window!"), _T("Error! :("), MB_OK | MB_ICONERROR); exit(EXIT_FAILURE); }
if(!(hwnd = CreateWindowEx(dwExStyle, szClassName, title, dwStyle, 200, 69, width, height, NULL, NULL, hInstance, NULL))){ MessageBox(NULL, _T("Failed to create the window!"), _T("Error! :("), MB_OK | MB_ICONERROR); exit(EXIT_FAILURE); }
ogl.CreateOpenGLContext(hwnd, &hDC, &hRC);
ogl.PrepareOpenGLScene();
ogl.ResizeGLScene(width, height);
ShowWindow(hwnd, SW_SHOW);
SetForegroundWindow(hwnd);
SetFocus(hwnd);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
ogl.ProgramIcon(hwnd);
break;
case WM_KEYDOWN:
switch(wParam)
{
case VK_ESCAPE:
PostQuitMessage(0);
break;
}
break;
case WM_CLOSE:
PostQuitMessage(0);
break;
case WM_DESTROY:
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
and this is the "OpenGL.cpp" code:
#include "paStdAfx.h"
#include "OpenGL.h"
GLvoid OpenGL::CreateOpenGLContext(HWND hwnd, HDC* hDC, HGLRC* hRC)
{
PIXELFORMATDESCRIPTOR pfd;
HGLRC tempContext;
memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cDepthBits = 32;
pfd.iLayerType = PFD_MAIN_PLANE;
*hDC = GetDC(hwnd);
int PixelFormat = ChoosePixelFormat(*hDC, &pfd);
if(PixelFormat == 0){ MessageBox(NULL, _T("Could not choose pixel format :("), _T("Error!"), MB_OK | MB_ICONERROR); exit(EXIT_FAILURE); }
if(!SetPixelFormat(*hDC, PixelFormat, &pfd)){ MessageBox(NULL, _T("Could not set pixel format :("), _T("Error!"), MB_OK | MB_ICONERROR); exit(3); }
tempContext = wglCreateContext(*hDC);
wglMakeCurrent(*hDC, tempContext);
GLenum err = glewInit();
if(GLEW_OK != err){ MessageBox(NULL, _T("Failed to initialize GLEW! :("), _T("Warning!"), MB_OK | MB_ICONINFORMATION); }
int attribs[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
WGL_CONTEXT_MINOR_VERSION_ARB, 1,
WGL_CONTEXT_FLAGS_ARB,
WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
0
};
if(!glewIsSupported("GL_VERSION_3_1")){ MessageBox(NULL, _T("OpenGL 3.1 not supported :("), _T("Warning!"), MB_OK | MB_ICONERROR); }
if(wglewIsSupported("WGL_ARB_create_context") == 1)
{
*hRC = wglCreateContextAttribsARB(*hDC, 0, attribs);
wglMakeCurrent(NULL, NULL);
wglDeleteContext(tempContext);
wglMakeCurrent(*hDC, *hRC);
}
else{ *hRC = tempContext; }
const char* GLVersionString = (char*) glGetString(GL_VERSION);
int OpenGLVersion[2];
glGetIntegerv(GL_MAJOR_VERSION, &OpenGLVersion[0]);
glGetIntegerv(GL_MINOR_VERSION, &OpenGLVersion[1]);
}
GLvoid OpenGL::PrepareOpenGLScene(GLvoid)
{
glClearColor(0.0f, 0.6f, 1.0f, 0.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
}
GLvoid OpenGL::RenderGLScene(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
/* Begin OpenGL Rendering */
/* End OpenGL Rendering */
}
GLvoid OpenGL::ResizeGLScene(int w, int h)
{
float ratio = 1.0 * w / h;
if(h == 0)
h = 1;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f, ratio, 0.1f, 1000.0f);
glViewport(0, 0, w, h);
}
GLvoid OpenGL::ProgramIcon(HWND hwnd)
{
HICON hIcon, hIconSm;
hIcon = (HICON) LoadImage(NULL, _T("data/icon.ico"), IMAGE_ICON, 32, 32, LR_LOADFROMFILE);
if(hIcon)
SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
else
MessageBox(NULL, _T("Could not load the big icon! :("), _T("Error!"), MB_OK | MB_ICONERROR);
//---------------------------------------------------------------------------------------------//
hIconSm = (HICON) LoadImage(NULL, _T("data/icon.ico"), IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
if(hIconSm)
SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSm);
else
MessageBox(NULL, _T("Could not load the small icon! :("), _T("Error!"), MB_OK | MB_ICONERROR);
}
GLvoid OpenGL::KillOpenGL(HWND hwnd, HDC hDC, HGLRC hRC)
{
wglMakeCurrent(NULL, NULL);
wglDeleteContext(hRC);
ReleaseDC(hwnd, hDC);
}
All the rendering code would go inside the "RenderGLScene" function inside of OpenGL.cpp, but when I place code to render basic things like trianlges or squares on the screen nothing appears. I have tried tinkering with the "gluLookAt()" function and the "gluPerspective()" function as well because I think those might be the source of my problem. I have tried both VBO and the older method that uses glBegin()/glEnd().
You create a double buffered context, but I don't see you performing a buffer swap (wglSwapBuffers) after finishing the rendering. Without a buffer swap you won't see anything.
What does your render code look like. Remember that the camera always is placed in the origin, which means that you need to draw things at negative z coordinates.
If you created a core context all drawing must be done using a VAO. Check the glError() return codes to see what the glDraw*() reports back.