The 2024 Wheel Reinvention Jam just concluded. See the results.

Using win32 "Caret API" with OpenGL

I'm working on some UI controls, and while I was looking for how to query the blinking time for the caret (text cursor in textboxes, GetCaretBlinkTime() ), I discovered that programs can create a caret and let windows handle the blinking. So I wanted to try it but I can't seem to make it work when the windows is updated often.

The docs says that I need to hide the caret whenever I draw outside of a paint message, and restore it after drawing. After a few tests, I found that I need to hide the caret before SwapBuffers() and restore it after. That works, as long as there aren't a lot of messages/frame update. When there are a lot of updates (constant frame rate, or mouse_move messages) then the caret is barely ever visible.

Does anybody knows how to make that works ? Should I avoid that API ?

Here is a reproduction example.

/* cl main.c -Fequick.exe -nologo -Zi -Od -link -subsystem:windows user32.lib OpenGL32.lib GDI32.lib */

#include <stdio.h>
#include <windows.h>

#include <gl/gl.h>

int running = 1;

LRESULT CALLBACK window_proc( HWND window, UINT message, WPARAM wParam, LPARAM lParam ) {
    
    LRESULT result = 0;
    
    switch ( message ) {
        
        case WM_DESTROY :
        case WM_CLOSE : {
            running = 0;
        } break;
        
        default : {
            result = DefWindowProcW( window, message, wParam, lParam );
        } break;
    }
    
    return result;
}

int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow ) {
    
    WNDCLASSW window_class = { 0 };
    window_class.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    window_class.lpfnWndProc = window_proc;
    window_class.hInstance = hInstance;
    window_class.lpszClassName = L"simple_window";
    
    RegisterClassW( &window_class );
    
    HWND handle = CreateWindowExW( 0, window_class.lpszClassName, L"Window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, window_class.hInstance, 0
                                  );
    
    PIXELFORMATDESCRIPTOR pixel_format_descriptor = {
        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
    };
    
    HDC device_context = GetDC( handle );
    int pixel_format = ChoosePixelFormat( device_context, &pixel_format_descriptor );
    SetPixelFormat( device_context, pixel_format, &pixel_format_descriptor );
    HGLRC render_context = wglCreateContext( device_context );
    wglMakeCurrent( device_context, render_context );
    
    ShowWindow( handle, SW_SHOW );
    
    CreateCaret( handle, 0, 30, 30 );
    SetCaretPos( 30, 30 );
    ShowCaret( handle );
    
    MSG message;
    
    while ( running && GetMessageW( &message, 0, 0, 0 ) ) {
        
        TranslateMessage( &message );
        DispatchMessageW( &message );
        
        while ( PeekMessageW( &message, 0, 0, 0, PM_REMOVE ) ) {
            TranslateMessage( &message );
            DispatchMessageW( &message );
        }
        
        glClearColor( 0, 0, 0, 1 );
        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

        glBegin( GL_POLYGON );
        
        glColor4f( 1, 0, 0, 1 );
        glVertex2f( -0.5f, -0.5f );
        glColor4f( 0, 1, 0, 1 );
        glVertex2f( 0.5f, -0.5f );
        glColor4f( 0, 0, 1, 1 );
        glVertex2f( 0.0f, 0.5f );
        
        glEnd( );
        
        glFlush( );
        
        HideCaret( handle );
        SwapBuffers( device_context );
        ShowCaret( handle );
    }
    
    return 0;
}

Edited by Simon Anciaux on

I don't know for sure, but my feeling is that this won't work well with OpenGL. Caret probably uses gdi to draw to window, and it is well known that gdi and opengl does not mix well together because of differences how everything is rendered - gdi renders straight to window surface, but opengl does rendering to backbuffer that is then swapped, so doing these two things at the same time will yield strange results.


Edited by Mārtiņš Možeiko on

Thanks.