Click here to Skip to main content
15,919,479 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have a window made with win32 API and inside this window, there is a smaller window in which another thread draws images using opengl. I removed the default frame and painted the visible portion of the big window so I can use it as a custom titlebar. When I try to resize the window, it starts glitching for small periods of time while it keeps rendering the content in the smaller window. How can I fix that?
Here is a sample video of what happens https://imgur.io/a/sOvaEby.
C++
#include<windows.h>
#include<dwmapi.h>
#include<pthread.h>
#include<stdio.h>
#include<GL/gl.h>
#include<GL/glext.h>
#include<GL/wglext.h>
 
#define TITLEBARHEIGHT 25
#define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
#define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
 
HGLRC glcontext = {0};
HGLRC dummy_glcontext = {0};
void** newglfun = {0};
GLuint buffer = 0;
GLuint ibuffer = 0;
GLuint vao = 0;
unsigned int program = 0;
int u_location = 0;
float r = 0; 
float increment = 0.05;
 
HANDLE handle_to_this_module = {0};
HWND handle_to_window = {0};
HWND parent = {0};
HWND child = {0};
HDC device_context = {0};
 
void (*glGenBuffers)(GLsizei, GLuint*)={0};
void (*glBindBuffer)(GLenum, GLuint)={0};
void (*glBufferData)(GLenum, GLsizeiptr, const GLvoid*, GLenum)={0};
void (*glVertexAttribPointer)(GLuint, GLint, GLenum, GLboolean, GLsizei, const GLvoid*)={0};
void (*glEnableVertexAttribArray)(GLuint)={0};
GLuint (*glCreateProgram)(void)={0};
GLuint (*glCreateShader)(GLenum)={0};
void (*glShaderSource)(GLuint, GLsizei,const GLchar**, const GLint*)={0};
void (*glCompileShader)(GLuint)={0};
void (*glAttachShader)(GLuint, GLuint)={0};
void (*glLinkProgram)(GLuint)={0};
void (*glValidateProgram)(GLuint)={0};
void (*glDeleteShader)(GLuint)={0};
void (*glGetShaderiv)(GLuint,GLenum, GLint*)={0};
void (*glUseProgram)(GLuint)={0};
void (*glDeleteProgram)(GLuint)={0};
GLint (*glGetUniformLocation)(GLuint, const GLchar*)={0};
void (*glUniform4f)(GLint,GLfloat,GLfloat,GLfloat,GLfloat)={0};
const GLubyte* (*glGetStringi)(GLenum, GLuint)={0};
void (*glGenVertexArrays)(GLsizei, GLuint*)={0};
void (*glBindVertexArray)(GLuint)={0};
void (*wglSwapIntervalEXT)(int)={0};
const char* (*wglGetExtensionsStringARB)(HDC)={0};
BOOL (*wglChoosePixelFormatARB)(HDC, const int*,const FLOAT*,UINT,int*,UINT*)={0};
HGLRC (*wglCreateContextAttribsARB)(HDC,HGLRC, const int*)={0};
 
int loadModernGLFunctions()
{
    char* newglfuncnames[] = {"glGenBuffers","glBindBuffer","glBufferData",
                      "glVertexAttribPointer","glEnableVertexAttribArray",
                      "glCreateProgram", "glCreateShader",
                      "glShaderSource","glCompileShader",                                
                      "glAttachShader","glLinkProgram","glValidateProgram",
                      "glDeleteShader", "glGetShaderiv","glUseProgram",
                      "glDeleteProgram","glUniform4f","glGetUniformLocation",
             "wglSwapIntervalEXT","glGetStringi","wglGetExtensionsStringARB",
             "wglChoosePixelFormatARB","wglCreateContextAttribsARB",
             "glGenVertexArrays","glBindVertexArray"};
 
        int n_functions = sizeof(newglfuncnames)/8;
        newglfun = malloc(sizeof(newglfuncnames));
        for(int i = 0; i<n_functions; i++){
        newglfun[i] = (void*) wglGetProcAddress(newglfuncnames[i]);
    }
 
    glGenBuffers = newglfun[0];
    glBindBuffer = newglfun[1];
    glBufferData = newglfun[2];
    glVertexAttribPointer = newglfun[3];
    glEnableVertexAttribArray = newglfun[4];
    glCreateProgram = newglfun[5];
    glCreateShader = newglfun[6];
    glShaderSource = newglfun[7];
    glCompileShader = newglfun[8];
    glAttachShader = newglfun[9];
    glLinkProgram = newglfun[10];
    glValidateProgram = newglfun[11];
    glDeleteShader = newglfun[12];
    glGetShaderiv = newglfun[13];
    glUseProgram = newglfun[14];
    glDeleteProgram = newglfun[15];
    glUniform4f = newglfun[16];
    glGetUniformLocation = newglfun[17];
    wglSwapIntervalEXT = newglfun[18];
    glGetStringi = newglfun[19];
    wglGetExtensionsStringARB = newglfun[20];
    wglChoosePixelFormatARB = newglfun[21];
    wglCreateContextAttribsARB = newglfun[22];
    glGenVertexArrays = newglfun[23];
    glBindVertexArray = newglfun[24];
    return TRUE;
}
 
void end_opengl(void)
{
    glDeleteProgram(program);
}
 
int make_shaders(void)
{
    int compileresult;
    program = (unsigned int) glCreateProgram();
    unsigned int id_vert = (unsigned int) glCreateShader(GL_VERTEX_SHADER);
    const char* vert_shader_source = "\n\
                     #version 330 core \n\
                     layout(location = 0) in vec4 position;\n\
                     void main()\n\
                     {\n\
                         gl_Position = position;\n\
                     }\n\
                     ";
    glShaderSource(id_vert, 1, &vert_shader_source, NULL);
    glCompileShader(id_vert);
    glGetShaderiv(id_vert, GL_COMPILE_STATUS, &compileresult);
    if(!compileresult){
        printf("errore shader vert\n");
        return 0;
    }
    unsigned int vs = id_vert;
    unsigned int id_frag = (unsigned int) glCreateShader(GL_FRAGMENT_SHADER);
    const char* frag_shader_source = "\n\
                     #version 330 core \n\
                     layout(location = 0) out vec4 color;\n\
                     uniform vec4 u_Color;\n\
                     void main()\n\
                     {\n\
                         color = u_Color;\n\
                     }\n\
                     ";
    glShaderSource(id_frag, 1, &frag_shader_source, NULL);
    glCompileShader(id_frag);
    glGetShaderiv(id_frag, GL_COMPILE_STATUS, &compileresult);
    if(!compileresult){
        printf("errore shader frag\n");
        return 0;
    }
    unsigned int fs = id_frag;
    glAttachShader(program, vs);
    glAttachShader(program, fs);
    glLinkProgram(program);
    glValidateProgram(program);
    glUseProgram(program);
    glDeleteShader(vs);
    glDeleteShader(fs);
 
    u_location = glGetUniformLocation(program, "u_Color"); 
    glUniform4f(u_location, 0.3254f, 0.098f, 0.9843f, 1.0f);
 
    return 1;
}
 
int init_opengl(void)
{
    PIXELFORMATDESCRIPTOR pfd =
    {
        sizeof(PIXELFORMATDESCRIPTOR),
        1,
        PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
        PFD_TYPE_RGBA,        
        32,                   
        0, 0, 0, 0, 0, 0,
        0,
        0,
        0,
        0, 0, 0, 0,
        24,                   
        8,                    
        0,                    
        PFD_MAIN_PLANE,
        0,
        0, 0, 0
    };
    int dummyiPixelFormat = ChoosePixelFormat(device_context, &pfd); 
    SetPixelFormat(device_context, dummyiPixelFormat, &pfd);
    if(!(dummy_glcontext = wglCreateContext(device_context)))
        printf("errcode %d\n", GetLastError()); 
    
    if(!wglMakeCurrent(device_context, dummy_glcontext))
        return 1;
 
    if(!loadModernGLFunctions())
        return 0;
 
    int iPixelFormat;
    UINT numFormats;
    const int PixAttribList[] =
    {
        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(device_context, PixAttribList, 
        NULL, 1, &iPixelFormat, &numFormats))
    {
        return 0; 
    }
 
    const int CtxAttribList[] =
    {
        WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, 
        WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
        WGL_CONTEXT_MINOR_VERSION_ARB, 3,
        0,
    };
    if(!(glcontext = wglCreateContextAttribsARB
                     (device_context, NULL, CtxAttribList)))
        return 0;
    
    if(!wglMakeCurrent(device_context, glcontext))
        return 0;
    
    glClearColor(0.1f,0.1f,0.1f,0.1f);
 
    glGenVertexArrays(1,&vao);
    glBindVertexArray(vao);
 
    wglSwapIntervalEXT(0);
 
    float positions[8] = {
        -0.5, -0.5, 
        -0.5f, 0.5f,
        0.5f, 0.5f, 
        0.5f, -0.5f 
    };
    unsigned int indices[] = {
        0, 1, 2,
        2, 3, 0
    };
    glGenBuffers(1, &buffer);
    glBindBuffer(GL_ARRAY_BUFFER, buffer);
    glBufferData(GL_ARRAY_BUFFER, 8*sizeof(float),positions,GL_STATIC_DRAW);
    glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,sizeof(float)*2, 0);
    glEnableVertexAttribArray(0);
 
    glGenBuffers(1, &ibuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, 
                 6*sizeof(unsigned int),indices,GL_STATIC_DRAW);
 
    if(!make_shaders())
        return 0;
 
    glUseProgram(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 
    if(!wglMakeCurrent(device_context, NULL)) 
        return 0;
    
    return TRUE;
}
 
void opengl_draw(void)
{
    if (r>1.0f)
        increment = -0.05f;
    else if (r< 0.0f)
        increment = 0.05f;
    r+=increment;
 
    RECT rcCli;          
    GetClientRect(WindowFromDC(device_context), &rcCli);
    int nWidth = rcCli.right-rcCli.left; 
    int nHeight  = rcCli.bottom-rcCli.top;
    glViewport(rcCli.left,rcCli.top,nWidth,nHeight);
 
    glUseProgram(program);
    glBindVertexArray(vao);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuffer);
 
    glClear(GL_COLOR_BUFFER_BIT);
    glUniform4f(u_location, r, 0.098f, 0.9843f, 1.0f);
    glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,NULL);
    SwapBuffers(device_context);
}
 
void OnSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    int parentWidth = LOWORD(lParam);
    int parentHeight = HIWORD(lParam);
 
    RECT parentRect;
    GetClientRect(hwnd, &parentRect);
 
    RECT childRect;
    GetWindowRect(child, &childRect); 
 
    int childWidth = parentRect.right - parentRect.left;
    int childHeight = parentRect.bottom - parentRect.top - 25;
 
    int childX = parentRect.left;
    int childY = parentRect.top + 25;
 
    SetWindowPos(child, 
                 NULL, 
                 childX, childY, 
                 childWidth, childHeight, 
                 SWP_NOZORDER | SWP_NOACTIVATE);
}
 
LRESULT HitTestNCA(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
    POINT ptMouse = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
    RECT rcWindow;
    GetWindowRect(hWnd, &rcWindow);
    RECT rcFrame = { 0 };
    rcFrame.top = rcWindow.top + 5;
    rcFrame.bottom = rcWindow.bottom - 5;
    rcFrame.left = rcWindow.left + 5;
    rcFrame.right = rcWindow.right - 5;
    USHORT uRow = 1;
    USHORT uCol = 1;
    BOOL fOnResizeBorder = FALSE;
 
    if (ptMouse.y >= rcWindow.top && ptMouse.y 
                  < rcWindow.top + 25){
        fOnResizeBorder = (ptMouse.y < rcFrame.top);
        uRow = 0;
    }
    else if (ptMouse.y < rcWindow.bottom && ptMouse.y >= rcWindow.bottom - 20){
        uRow = 2;
    }
 
    if (ptMouse.x >= rcWindow.left && ptMouse.x < rcWindow.left + 8){
        uCol = 0;
    }
    else if (ptMouse.x < rcWindow.right && 
             ptMouse.x >= rcWindow.right - 8){
        uCol = 2;
    }
 
    BOOL titlebar = (ptMouse.x > rcWindow.left && 
                     ptMouse.x < rcWindow.right-25) &&
                    (ptMouse.y > rcWindow.top && 
                     ptMouse.y < rcWindow.top+25) ? 1 : 0;
    if(titlebar) 
        return HTCAPTION;
 
    BOOL client = (ptMouse.x > rcWindow.left+5 && 
                   ptMouse.x < rcWindow.right-5) &&
                  (ptMouse.y > rcWindow.top+5 && 
                   ptMouse.y < rcWindow.bottom-5) ? 1 : 0;
    if(client){
        return HTCLIENT;
    }
 
    LRESULT hitTests[3][3] =
    {
        { HTTOPLEFT,    fOnResizeBorder ? HTTOP : HTCAPTION,    HTTOPRIGHT },
        { HTLEFT,       HTNOWHERE,     HTRIGHT },
        { HTBOTTOMLEFT, HTBOTTOM, HTBOTTOMRIGHT },
    };
    return hitTests[uRow][uCol];
} 
 
LRESULT CALLBACK childProc(HWND chwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{
    switch(uMsg)
    {
        case WM_NCHITTEST:
            return HTTRANSPARENT;
        default:
            return DefWindowProc(chwnd, uMsg, wParam, lParam);
    }
} 
 
LRESULT CALLBACK myProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_SETCURSOR:
        {
           int ht_result = LOWORD(lParam);
           if(ht_result == HTCLIENT){
               SetCursor(LoadCursor(NULL, IDC_ARROW));
               return TRUE;
           }
           else 
               return DefWindowProc(hwnd, uMsg, wParam, lParam);
        }
        case WM_DESTROY:
        {
           PostQuitMessage(0);
           return 0;
        }
        case WM_SIZING:
        {
           RECT rect;
           GetClientRect(hwnd, &rect);
           InvalidateRect(hwnd, &rect, TRUE); 
           return DefWindowProc(hwnd, uMsg, wParam, lParam);
        }
        case WM_SIZE:
        {
           OnSize(hwnd, wParam, lParam);
           return DefWindowProc(hwnd, uMsg, wParam, lParam);
        }
        case WM_LBUTTONDOWN:
        {
           POINT ptMouse = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
           RECT rc;
           GetClientRect(hwnd, &rc);
           BOOL close = (ptMouse.x >= rc.right-25 && 
                         ptMouse.x < rc.right) &&
                        (ptMouse.y > rc.top && 
                         ptMouse.y < rc.top+25) ? 1 : 0;
           if(close) 
               DestroyWindow(hwnd);
           InvalidateRect(hwnd, NULL, TRUE);
           return DefWindowProc(hwnd, uMsg, wParam, lParam);
        }
        case WM_PAINT:
        {
           RECT rect;
           GetClientRect(hwnd, &rect);
           RECT titlebar;
           RECT closebutton;
           PAINTSTRUCT ps;
           HDC hdc = BeginPaint(hwnd, &ps);
           
           titlebar.top = 0;
           titlebar.bottom = 25;
           titlebar.left = 0;
           titlebar.right = rect.right - rect.left;
 
           closebutton.top = 0;
           closebutton.bottom = 25;
           closebutton.left = rect.right - 25;
           closebutton.right = rect.right;
 
           HBRUSH hBrush = CreateSolidBrush(RGB(255, 0, 0));
           FillRect(hdc, &titlebar, hBrush);
           DeleteObject(hBrush);
 
           HBRUSH hBrush3 = CreateSolidBrush(RGB(10, 10, 10));
           FillRect(hdc, &closebutton, hBrush3);
           DeleteObject(hBrush3);
 
           HBRUSH hBrush2 = CreateSolidBrush(RGB(100, 100, 100));
           RECT main;
           main.top = 25;
           main.bottom = rect.bottom;
           main.left = 0;
           main.right = titlebar.right;
           FillRect(hdc, &main, hBrush2);
           DeleteObject(hBrush2);
 
           EndPaint(hwnd, &ps);
           return DefWindowProc(hwnd, uMsg, wParam, lParam);
        }
        case WM_NCHITTEST:
        {
           return HitTestNCA(hwnd, wParam, lParam);
        }
        case WM_ACTIVATE:
        {
           MARGINS margins;
           margins.cxLeftWidth = 0;
           margins.cxRightWidth = 0;
           margins.cyBottomHeight = 0;
           margins.cyTopHeight = 0;
           HRESULT hr = DwmExtendFrameIntoClientArea(hwnd, &margins);
           return DefWindowProc(hwnd, uMsg, wParam, lParam);
        }
        case WM_CREATE:
        {
           SetWindowPos(hwnd,
                        NULL, 
                        0, 0, 
                        0, 0, 
                        SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
           return DefWindowProc(hwnd, uMsg, wParam, lParam);
        }
        case WM_NCCALCSIZE:
        {
           if(wParam == TRUE) return 0;
           else return DefWindowProc(hwnd, uMsg, wParam, lParam);
        }
        default: 
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
}
 
int init_win32(void)
{
    handle_to_this_module = GetModuleHandle(NULL);
    if(!handle_to_this_module)
        return 0;
 
    WNDCLASS class = {0};
    class.lpfnWndProc = myProc;                                      
    class.hInstance = handle_to_this_module;                         
    class.lpszClassName = (LPCSTR) L"mainclass";                     
    class.hbrBackground = NULL;
    RegisterClass(&class);
 
    handle_to_window = CreateWindowExA(
                           0,                      
                           (LPCSTR) L"mainclass",    
                           "mainwin",             
                           WS_OVERLAPPEDWINDOW |   
                           WS_VISIBLE,
                           CW_USEDEFAULT,          
                           CW_USEDEFAULT,          
                           420,                    
                           420,                    
                           (HWND) NULL,            
                           (HMENU) NULL,           
                           handle_to_this_module,  
                           NULL                    
                           );
 
    if(!handle_to_window)
        return 0;
 
    parent = handle_to_window;
 
    WNDCLASS childclass = {0};
    childclass.lpfnWndProc = childProc;    
    childclass.hInstance = handle_to_this_module; 
    childclass.lpszClassName = (LPCSTR) L"childclass";
    childclass.hCursor = class.hCursor;      
    childclass.hbrBackground = NULL;
    RegisterClass(&childclass);
    child = CreateWindowExA(
                0,
                (LPCSTR) L"childclass",
                "childwin", 
                WS_CHILD | WS_VISIBLE,
                0, TITLEBARHEIGHT, 
                420, 420-TITLEBARHEIGHT, 
                parent, 
                0, 
                handle_to_this_module, 
                NULL);
 
    UpdateWindow(handle_to_window);
    device_context = GetDC(child);
    return TRUE;
}
 
void init(void)
{
    if(!init_win32())
        exit(1);
    if(!init_opengl())
        exit(2);
}
 
void run(void)
{
    MSG msg = {0};
    while(msg.message != WM_QUIT){
        if(PeekMessageW(&msg, NULL, 0x0,0x0, PM_REMOVE))
            DispatchMessage(&msg);
    } 
}
 
void* drawing_loop(void* arg)
{
    if(!wglMakeCurrent(device_context, glcontext))
        return 0;
    
    while(1){
        opengl_draw();
    }
} 
 
void start_drawing_thread(void)
{
    pthread_t rendering_thread;
    pthread_create(&rendering_thread, NULL, drawing_loop, NULL);
}
 
void quit(void)
{
    end_opengl();
}
 
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
                   LPSTR lpCmdLine, int nShowCmd)
{
    init(); 
    start_drawing_thread();
    run();
    quit();
}
 
// > gcc .\file.c -lgdi32 -ldwmapi -lopengl32 -o file


What I have tried:

I tried handling various messages I suspected to be the cause of the issue (like size and sizing) in many different ways, but it didn't work. I've also tried to make the drawing loop slower and I noticed that the titlebar becomes invisible only in moments that the rendering thread draws in the child window so my guess is that it may be a conflict between the threads, but still I have no idea know how to fix it.
Posted
Updated 24-Oct-23 0:56am
v3
Comments
Richard MacCutchan 24-Oct-23 4:43am    
I have not looked at all your code, but the video suggests you have a conflict between the threads. It could be that your main window is being updated at the same time as the second thread is drawing its window. So each update causes the other thread to try and refresh its window. You need to synchronise the two threads, or pause updating the main window while the other thread is drawing.
samas69420 24-Oct-23 7:20am    
i was also suspecting something linked with threads but i tried to remove the code for the handling of "WM_NCCALCSIZE" and now even if i have two titlebars (the default one and the custom one) surprisingly i don't see the flashing problem anymore, i left the rest of the code almost untouched including the threads stuff, this is kinda weird because that part was directly copied from the msdn guide to remove the standard frame (https://learn.microsoft.com/en-us/windows/win32/dwm/customframe), should i be thinking that the guide has mistakes or i am missing something?
Richard MacCutchan 24-Oct-23 7:50am    
I don't think that MSDN article is likely to be incorrect. But the fact that you are mixing the use of two different Windows managers makes things more complicated. But I am afraid I have no experience of opengl so cannot make any other useful suggestions.
samas69420 24-Oct-23 8:06am    
well to be honest i tried to replicate the same article before using opengl and it actually worked as intended, i could resize and move the window without having any trouble then i used opengl and even tho the rendering works it broke the titlebar, after some experiments i guess that the problem is handling nccalcsize message in the same way as in the article while using opengl
Richard MacCutchan 24-Oct-23 8:59am    
That makes sense. But you could also try using a standard Window (without DWM) and see if you can implement the opengl stuff in that situation.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900