Rendering OpenGL Canvas in WC_TABCONTROL

My goal is to create a basic tab control using Win32 API that contains a canvas for OpenGL rendering. My tab contains a static OpenGL render control. However, the only way to get the canvas to appear in the GUI is to exclude the tab control (comment out the CREATE_TAB_PANE macro in my example to do this).

My example is shown below:

// OpenGlTabWin32.cpp
// NOTE: canvas displays fine if TabPane creation is commented out

#include <windows.h>
#include <commctrl.h>
#include <gl/GL.h>

#pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "opengl32.lib")

#define CREATE_TAB_PANE

enum { IDC_TAB = 200, IDC_CANVAS = 201 };

static HWND TabPaneId;
static HWND CanvasId;

static WNDPROC CanvasWndProc;
static HGLRC CanvasRc;
static HDC CanvasDc;

////////////////////////////////////////////////////////////////////////////////
// WndProc
////////////////////////////////////////////////////////////////////////////////
static LRESULT CALLBACK WndProc(HWND hwnd,
                                UINT msg,
                                WPARAM wParam,
                                LPARAM lParam) {

  switch (msg) {
    case WM_CREATE:
      break;

    case WM_DESTROY:
      PostQuitMessage(0);
      break;

    default:
      return DefWindowProc(hwnd, msg, wParam, lParam);
  }

  return 0;
}

////////////////////////////////////////////////////////////////////////////////
// OpenGlCanvasProc
////////////////////////////////////////////////////////////////////////////////
static LRESULT CALLBACK OpenGlCanvasProc(HWND hwnd,
                                         UINT msg,
                                         WPARAM wParam,
                                         LPARAM lParam) {

  if (msg == WM_PAINT) {
    PAINTSTRUCT ps;
    BeginPaint(hwnd, &ps);
    EndPaint(hwnd, &ps);

    wglMakeCurrent(CanvasDc, CanvasRc);
    SwapBuffers(CanvasDc);

    return 0;
  }

  return CallWindowProc(CanvasWndProc, hwnd, msg, wParam, lParam);
}

////////////////////////////////////////////////////////////////////////////////
// CanvasInit
////////////////////////////////////////////////////////////////////////////////
static void CanvasInit() {

  glClearColor(0.0, 0.0, 0.0, 0.0);
}

////////////////////////////////////////////////////////////////////////////////
// CanvasResize
////////////////////////////////////////////////////////////////////////////////
static void CanvasResize(int width, int height) {

  glViewport(0, 0, width, height);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  glOrtho(0, 1, 0, 1, -1, 1);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

////////////////////////////////////////////////////////////////////////////////
// CanvasDisplay
////////////////////////////////////////////////////////////////////////////////
static void CanvasDisplay() {

  glClear(GL_COLOR_BUFFER_BIT);

  glLoadIdentity();
  glColor3d(1, 0, 0);

  glBegin(GL_LINE_STRIP);
    glVertex2d(0.2, 0.2);
    glVertex2d(0.8, 0.8);
  glEnd();

  SwapBuffers(CanvasDc);
}

////////////////////////////////////////////////////////////////////////////////
// WinMain
////////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nShowCmd) {

  // Create window

  const char *className = "OpenGlTab";

  WNDCLASSEX wc;
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.style = 0;
  wc.lpfnWndProc = WndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
  wc.lpszMenuName = NULL;
  wc.lpszClassName = className;
  wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

  if (!RegisterClassEx(&wc)) {
    exit(0);
  }

  HWND winId = CreateWindowEx(0,
                              className,
                              "Tab Pane w/ OpenGL",
                              WS_OVERLAPPEDWINDOW,
                              100, 100, 600, 400,
                              0,
                              0,
                              hInstance,
                              0);
  if (!winId) {
    exit(0);
  }

  ShowWindow(winId, nShowCmd);
  UpdateWindow(winId);

#ifdef CREATE_TAB_PANE

  // Create tab pane

  TabPaneId = CreateWindow(WC_TABCONTROL,
                           0,
                           WS_CHILD | WS_VISIBLE,
                           10, 10, 566, 343,
                           winId,
                           HMENU(IDC_TAB),
                           hInstance,
                           0);

  TCITEM tabItem = {0};
  tabItem.mask = TCIF_TEXT;
  tabItem.pszText = "Tab";

  SendMessage(TabPaneId, TCM_INSERTITEM, 0, LPARAM(&tabItem));

#endif

  // Create OpenGL canvas

  int w = 200;
  int h = 200;

  CanvasId = CreateWindow("static",
                          "",
                          WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN |  WS_CLIPSIBLINGS,
                          20, 100, w, h,
                          winId,
                          HMENU(IDC_CANVAS),
                          hInstance,
                          0);

  CanvasWndProc = (WNDPROC)SetWindowLongPtr(CanvasId,
                                            GWLP_WNDPROC,
                                            (LONG_PTR)OpenGlCanvasProc);

  CanvasDc = GetDC(CanvasId);

  static PIXELFORMATDESCRIPTOR pfd = {
    sizeof(PIXELFORMATDESCRIPTOR),
    1, // version
    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,
    16, // depth buffer
    0,
    0,
    0,
    0,
    0, 0, 0
  };

  int pixelFormat = ChoosePixelFormat(CanvasDc, &pfd);
  SetPixelFormat(CanvasDc, pixelFormat, &pfd);

  CanvasRc = wglCreateContext(CanvasDc);

  // Render OpenGL canvas

  wglMakeCurrent(CanvasDc, CanvasRc);
  CanvasResize(w, h);
  CanvasInit();
  CanvasDisplay();
  SwapBuffers(CanvasDc);

  // Execute GUI

  MSG msg;

  while (GetMessage(&msg, 0, 0, 0) > 0) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  return 0;
}

      

My updated example with changes for Chris:

// OpenGlTabWin32.cpp
// NOTE: canvas displays fine if TabPane creation is commented out

#include <windows.h>
#include <commctrl.h>
#include <gl/GL.h>

#pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "opengl32.lib")

#define CREATE_TAB_PANE

enum { IDC_TAB = 200, IDC_CANVAS = 201 };

static HWND TabPaneId;
static HWND CanvasId;

static WNDPROC CanvasWndProc;
static HGLRC CanvasRc;

////////////////////////////////////////////////////////////////////////////////
// WndProc
////////////////////////////////////////////////////////////////////////////////
static LRESULT CALLBACK WndProc(HWND hwnd,
                                UINT msg,
                                WPARAM wParam,
                                LPARAM lParam) {

  switch (msg) {
    case WM_CREATE:
      break;

    case WM_DESTROY:
      PostQuitMessage(0);
      break;

    default:
      return DefWindowProc(hwnd, msg, wParam, lParam);
  }

  return 0;
}

////////////////////////////////////////////////////////////////////////////////
// CanvasInit
////////////////////////////////////////////////////////////////////////////////
static void CanvasInit() {

  glClearColor(0.0, 0.0, 0.0, 0.0);
}

////////////////////////////////////////////////////////////////////////////////
// CanvasResize
////////////////////////////////////////////////////////////////////////////////
static void CanvasResize(int width, int height) {

  glViewport(0, 0, width, height);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  glOrtho(0, 1, 0, 1, -1, 1);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

////////////////////////////////////////////////////////////////////////////////
// CanvasDisplay
////////////////////////////////////////////////////////////////////////////////
static void CanvasDisplay() {

  glClear(GL_COLOR_BUFFER_BIT);

  glLoadIdentity();
  glColor3d(1, 0, 0);

  glBegin(GL_LINE_STRIP);
    glVertex2d(0.2, 0.2);
    glVertex2d(0.8, 0.8);
  glEnd();
}

////////////////////////////////////////////////////////////////////////////////
// OpenGlCanvasProc
////////////////////////////////////////////////////////////////////////////////
static LRESULT CALLBACK OpenGlCanvasProc(HWND hwnd,
                                         UINT msg,
                                         WPARAM wParam,
                                         LPARAM lParam) {

  switch (msg) {
    case WM_PAINT:
    {
      PAINTSTRUCT ps;
      HDC hdc = BeginPaint(hwnd, &ps);

      wglMakeCurrent(hdc, CanvasRc);
      CanvasDisplay();
      SwapBuffers(hdc);
      wglMakeCurrent(NULL, NULL);
      ReleaseDC(hwnd, hdc);

      EndPaint(hwnd, &ps);

      return 0;
    }

    case WM_SIZE:
    {
      HDC hdc = GetDC(hwnd);
      wglMakeCurrent(hdc, CanvasRc);
      CanvasResize(LOWORD(lParam), HIWORD(lParam));
      wglMakeCurrent(NULL, NULL);
      ReleaseDC(hwnd, hdc);

      return 0;
    }
  }

  return CallWindowProc(CanvasWndProc, hwnd, msg, wParam, lParam);
}

////////////////////////////////////////////////////////////////////////////////
// WinMain
////////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nShowCmd) {

  // Create window

  const char *className = "OpenGlTab";

  WNDCLASSEX wc;
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.style = 0;
  wc.lpfnWndProc = WndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
  wc.lpszMenuName = NULL;
  wc.lpszClassName = className;
  wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

  if (!RegisterClassEx(&wc)) {
    exit(0);
  }

  HWND winId = CreateWindowEx(0,
                              className,
                              "Tab Pane w/ OpenGL",
                              WS_OVERLAPPEDWINDOW,
                              100, 100, 600, 400,
                              0,
                              0,
                              hInstance,
                              0);
  if (!winId) {
    exit(0);
  }

  ShowWindow(winId, nShowCmd);
  UpdateWindow(winId);

#ifdef CREATE_TAB_PANE

  // Create tab pane

  TabPaneId = CreateWindow(WC_TABCONTROL,
                           0,
                           WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
                           10, 10, 566, 343,
                           winId,
                           HMENU(IDC_TAB),
                           hInstance,
                           0);

  TCITEM tabItem = {0};
  tabItem.mask = TCIF_TEXT;
  tabItem.pszText = "Tab";

  SendMessage(TabPaneId, TCM_INSERTITEM, 0, LPARAM(&tabItem));

#endif

  // Create OpenGL canvas

  int w = 200;
  int h = 200;

  CanvasId = CreateWindow("static",
                          "",
                          WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
                          20, 100, w, h,
                          winId,
                          HMENU(IDC_CANVAS),
                          hInstance,
                          0);

  CanvasWndProc = (WNDPROC)SetWindowLongPtr(CanvasId,
                                            GWLP_WNDPROC,
                                            (LONG_PTR)OpenGlCanvasProc);

  HDC hdc = GetDC(CanvasId);

  static PIXELFORMATDESCRIPTOR pfd = {
    sizeof(PIXELFORMATDESCRIPTOR),
    1, // version
    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,
    16, // depth buffer
    0,
    0,
    0,
    0,
    0, 0, 0
  };

  int pixelFormat = ChoosePixelFormat(hdc, &pfd);
  SetPixelFormat(hdc, pixelFormat, &pfd);

  CanvasRc = wglCreateContext(hdc);

  // Render OpenGL canvas

  wglMakeCurrent(hdc, CanvasRc);
  CanvasResize(w, h);
  CanvasInit();
  CanvasDisplay();
  SwapBuffers(hdc);
  wglMakeCurrent(NULL, NULL);
  ReleaseDC(CanvasId, hdc);

  // Execute GUI

  MSG msg;

  while (GetMessage(&msg, 0, 0, 0) > 0) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  return 0;
}

      

+3


source to share


1 answer


There are several things in this example that can contribute:

  • The most immediate problem might be that the tab window is closing the canvas window and you are not actually drawing anything in response to the WM_PAINT. The tab control will draw your canvas as soon as it is invalid, since Windows usually allows child windows to draw on top of each other; so adding WS_CLIPSIBLINGS to the tab control might help.

  • You grab the HDC for static control and maintain it after linking it to the current wgl context. You shouldn't do this unless you are using a window class with CS_OWNDC, and especially not one that probably has CS_PARENTDC (because then, as soon as the parent or other child window is painted, the DC is re-associated with which was never associated with SetPixelFormat).

  • You just make your opengl context current and expect it to be installed later. This is fine - if you have a CS_OWNDC window with HDC, you can grab the front and maintain it, and also assume that you never want to create a second GL context for whatever reason.

Thus, when using OpenGL in an application where you are not in control of the window class's styles (or there may be more than one OpenGL context), you need to make sure that you always clear the current context and release the DC as soon as you execute with it.



For example, your CanvasWindowProc should look like this:

case WM_PAINT:
  {
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd,&ps);
    wglMakeCurrent(glrc,hdc);
    CanvasDisplay();
    SwapBuffers();
    wglMakeCurrent(NULL,NULL);
    EndPaint(&ps);
  }
  return 0;
case WM_SIZE:
  {
    HDC hdc = GetDC(hwnd);
    wglMakeCurrent(glrc,hdc);
    CanvasResize(LOWORD(lParam),HIWORD(lParam));
    wglMakeCurrent(NULL,NULL);
    ReleaseDC(hwnd,hdc);
  }
  break;

      

+3


source







All Articles