melak47/BorderlessWindow

Noob question: can't think of a good title

amrbashir opened this issue · 5 comments

The window will be borderless only if userdata is passed to CreateWindowExW which if I understand correctly is a void pointer to the BorderlessWindow object.

1280, 720, nullptr, nullptr, nullptr, userdata

my noob question is why is that necessary ?

if I used the following code in a plain wWinMain, the window won't be borderless.

CreateWindowExW( 
             0, window_class(wndproc), L"Borderless Window", 
             WS_POPUP | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, 
             1280, 720, nullptr, nullptr, nullptr, nullptr
         );

From what I understand, according to the docs. Passing "userdata" which is the lpParam , is necessary to send a WM_CREATE message as stated by the documentation available here

You can see here as to why that is neccesary,

auto CALLBACK BorderlessWindow::WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) noexcept -> LRESULT {
if (msg == WM_NCCREATE) {
auto userdata = reinterpret_cast<CREATESTRUCTW*>(lparam)->lpCreateParams;
// store window instance pointer in window user data
::SetWindowLongPtrW(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(userdata));
}

The SetWindowLongPtrW is what I think is setting the window to borderless.

If you are passing null, I think you can sequentially call ::SetWindowLongPtrW(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(userdata)) to set the window borderless.

I hope this resolves your question.

I am trying to recreate a minimal demo but unfortunately, I still can't create a borderless window.
here is my code so far

#include <Windows.h>

LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if (msg == WM_NCCREATE)
    {
        auto userdata = reinterpret_cast<CREATESTRUCTW *>(lParam)->lpCreateParams;
        SetWindowLongPtrW(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(userdata));
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
    WNDCLASSEXW wcx{};
    wcx.cbSize = sizeof(wcx);
    wcx.style = CS_HREDRAW | CS_VREDRAW;
    wcx.hInstance = nullptr;
    wcx.lpfnWndProc = wndProc;
    wcx.lpszClassName = L"BorderlessWindowClass";
    wcx.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1);
    wcx.hCursor = ::LoadCursorW(nullptr, reinterpret_cast<LPCWSTR>(IDC_ARROW));

    RegisterClassExW(&wcx);

    struct UserData
    {
    };

    UserData *userdata = new UserData;

    auto hwnd = CreateWindowExW(
        0, wcx.lpszClassName, L"Borderless Window",
        static_cast<DWORD>(WS_POPUP | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX), 100, 100,
        1280, 720, nullptr, nullptr, nullptr, userdata);

    SetWindowLongPtrW(hwnd, GWL_STYLE, static_cast<LONG>(WS_POPUP | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX));
    SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
    ShowWindow(hwnd, SW_SHOW);

    MSG msg = {0};
    while (GetMessage(&msg, nullptr, NULL, NULL))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
};

I use the userdata parameter associate the BorderlessWindow instance pointer with the window itself,
so that the window procedure callback can access it.
This is entirely unrelated to the borderless window creation.

The key things are the choice of window style, and the handling of certain messages in the WndProc.
WM_NCCALCSIZE controls what windows considers the non-client and client area of our window.
By overriding the default behavior here, we can tell it to use the whole window area as the client area.
(See MSDN for details of the parameters to this message).

Handling WM_NCHITTEST tells windows which regions to consider the borders, corners, titlebar and client area, for purposes of resizing and dragging the window.

WM_NCCALCSIZE controls what windows considers the non-client and client area of our window.

I remember using this in the frameless implementation in Webview-rust. Handling resizable frameless windows is still a pain there.

Indeed, The missing bit for my minimal example was return 0 in WM_NCCALCSIZE msg.
Thanks for help guys.