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.
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,
BorderlessWindow/BorderlessWindow/src/BorderlessWindow.cpp
Lines 147 to 152 in 3b6978d
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.