Making a custom titlebar

I want to disable the current titlebar and draw my own with opengl.

I decided to use GLFW_DECORATED → GLFW_FALSE, but this disable everything.

So I had the idea of making the window transparent and draw the screen, reimplement dragging and resizing and all that basic stuff.

My question is, is it even possible to do all the above and if yes, can I still snap the window on my screen in windows 10-11?

Is there a better option to make a custom titlebar?

There is a similar question I answered about this here:

So you do currently loose aero snap on Windows.

If you know Win32 code it is possible to alter GLFW to get borderless with snapping to work, but you’ll need to implement your own move and size controls.

See:

The basic changes in win32_window.c were:

static DWORD getWindowStyle(const _GLFWwindow* window)
{
    DWORD style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;

    if (window->monitor)
        style |= WS_POPUP;
    else
    {
        style |= WS_SYSMENU | WS_MINIMIZEBOX;

        if (window->decorated)
        {
            style |= WS_CAPTION;

            if (window->resizable)
                style |= WS_MAXIMIZEBOX | WS_THICKFRAME;
        }
        else
            style |= WS_POPUP | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CAPTION;
    }

    return style;
}

and also add the following on lines 1118 in win32_window.c just before the case WM_PAINT line.

        case WM_NCCALCSIZE:
        if (!window->decorated) {
            return 0;
        }
        break;

This maximizes slightly weirdly for me so it may need some further work.

I’m not sure if this helps, but I find this GitHub - rossy/borderless-window: A minimal borderless window with the Windows API a really good example on how to do borderless window with all contents custom draw, and window obeying regular rules about shortcuts and interaction with desktop. There are a lot of tiny things you need to take care for window to behave reasonably well.

Is there any documentation to add something like this for linux and macos as well?

Hey so on windows i added this code:

#define GLFW_EXPOSE_NATIVE_WIN32
#include <GLFW/glfw3.h>
#include <GLFW/glfw3native.h>

#include <Windows.h>
#include <dwmapi.h>

    void disableTitlebar(GLFWwindow* window)
    {
        HWND hWnd = glfwGetWin32Window(window);

        // Remove the title bar
        LONG_PTR lStyle = GetWindowLongPtr(hWnd, GWL_STYLE);
        lStyle &= ~WS_CAPTION;
        SetWindowLongPtr(hWnd, GWL_STYLE, lStyle);

        // Set the window shape and rounded corners
        DWMNCRENDERINGPOLICY policy = DWMNCRP_ENABLED;
        DwmSetWindowAttribute(hWnd, DWMWA_NCRENDERING_POLICY, &policy, sizeof(policy));

        // Extend the frame into the client area
        MARGINS margins = { 0 };
        DwmExtendFrameIntoClientArea(hWnd, &margins);

        // Adjust the window size to remove the thin frame at the top
        RECT windowRect;
        GetWindowRect(hWnd, &windowRect);
        SetWindowPos(hWnd, NULL, 0, 0, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, SWP_FRAMECHANGED | SWP_NOMOVE);
    }

With this, the titlebar disappear and im able to keep resizing and rounded corners. Snapping the window also works on windows 11-10.

Unfortunately there is a small issue. In the place where there was the title bar, there is a very thin white frame which does not have any purpose. It’s just there and it’s really annoying.
I cant find a way to remove it :frowning:

Hey, so finally I was able to find a soluion. Figuring out a solution completely drained my energy :sweat_smile: :sweat_smile: .

Anyway here is the final code that I used to remove the titlebar on windows. I also implemented the same thing for macos and linux but I wont post them since I dont know if they work.

WNDPROC original_proc;

LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_NCCALCSIZE:
        {
            // Remove the window's standard sizing border
            if (wParam == TRUE && lParam != NULL)
            {
                NCCALCSIZE_PARAMS* pParams = reinterpret_cast<NCCALCSIZE_PARAMS*>(lParam);
                pParams->rgrc[0].top += 1;
                pParams->rgrc[0].right -= 2;
                pParams->rgrc[0].bottom -= 2;
                pParams->rgrc[0].left += 2;
            }
            return 0;
        }
        case WM_NCPAINT:
        {
            // Prevent the non-client area from being painted
            return 0;
        }
        case WM_NCHITTEST:
        {
            // Expand the hit test area for resizing
            const int borderWidth = 8; // Adjust this value to control the hit test area size

            POINTS mousePos = MAKEPOINTS(lParam);
            POINT clientMousePos = { mousePos.x, mousePos.y };
            ScreenToClient(hWnd, &clientMousePos);

            RECT windowRect;
            GetClientRect(hWnd, &windowRect);

            if (clientMousePos.y >= windowRect.bottom - borderWidth)
            {
                if (clientMousePos.x <= borderWidth)
                    return HTBOTTOMLEFT;
                else if (clientMousePos.x >= windowRect.right - borderWidth)
                    return HTBOTTOMRIGHT;
                else
                    return HTBOTTOM;
            }
            else if (clientMousePos.y <= borderWidth)
            {
                if (clientMousePos.x <= borderWidth)
                    return HTTOPLEFT;
                else if (clientMousePos.x >= windowRect.right - borderWidth)
                    return HTTOPRIGHT;
                else
                    return HTTOP;
            }
            else if (clientMousePos.x <= borderWidth)
            {
                return HTLEFT;
            }
            else if (clientMousePos.x >= windowRect.right - borderWidth)
            {
                return HTRIGHT;
            }

            break;
        }
    }
    
    return CallWindowProc(original_proc, hWnd, uMsg, wParam, lParam);
}
void disableTitlebar(GLFWwindow* window)
{
    HWND hWnd = glfwGetWin32Window(window);

    LONG_PTR lStyle = GetWindowLongPtr(hWnd, GWL_STYLE);
    lStyle |= WS_THICKFRAME;
    lStyle &= ~WS_CAPTION;
    SetWindowLongPtr(hWnd, GWL_STYLE, lStyle);

    RECT windowRect;
    GetWindowRect(hWnd, &windowRect);
    int width = windowRect.right - windowRect.left;
    int height = windowRect.bottom - windowRect.top;

    original_proc = (WNDPROC)GetWindowLongPtr(hWnd, GWLP_WNDPROC);
    (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(WindowProc));
    SetWindowPos(hWnd, NULL, 0, 0, width, height, SWP_FRAMECHANGED | SWP_NOMOVE);
}

With this code, you can basically keep all the native functionnality of glfw and windows like snapping the window or resizing and I think you still have rounded corners in windows11(I have to verify that).

@BleastT can you please attach your win32_window.c file here or if possible link to your github repo where you implemented it would be great… That top resize border is draining my energy now :sweat_smile: :sweat_smile:. I am working on windows 10.

Hey sorry for taking so long to respond . I stopped working on this project so I don’t have the GitHub
Repo but I remember how it works . So basically you don’t need to recompile the glfw source code yourself . I did not touch the win32_window.c. I simply added that code in a Cpp file and it overwrite the window procedure used by glfw . So just copy the copy put in a Cpp file and you can just call the disable title bar function with no other work needed