Last active
May 20, 2026 14:04
-
-
Save EncodeTheCode/368d79820b93c8700f66dca288d15700 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #define UNICODE | |
| #define NOMINMAX | |
| #include <windows.h> | |
| #include <windowsx.h> | |
| #include <dwmapi.h> | |
| #include <chrono> | |
| #include <thread> | |
| #include <algorithm> | |
| #include <string> | |
| #pragma comment(lib, "dwmapi.lib") | |
| class BorderlessWindow { | |
| public: | |
| bool create(HINSTANCE hInstance, const wchar_t* title, int width, int height, int targetFps = 120) { | |
| hInstance_ = hInstance; | |
| title_ = title ? title : L"Window"; | |
| width_ = width; | |
| height_ = height; | |
| setTargetFPS(targetFps); | |
| WNDCLASSEXW wc{}; | |
| wc.cbSize = sizeof(wc); | |
| wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; | |
| wc.lpfnWndProc = &BorderlessWindow::WndProc; | |
| wc.hInstance = hInstance_; | |
| wc.hCursor = LoadCursor(nullptr, IDC_ARROW); | |
| wc.hbrBackground = nullptr; | |
| wc.lpszClassName = kClassName; | |
| if (!RegisterClassExW(&wc) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) { | |
| return false; | |
| } | |
| hwnd_ = CreateWindowExW( | |
| WS_EX_APPWINDOW, | |
| kClassName, | |
| title_.c_str(), | |
| WS_POPUP | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, | |
| CW_USEDEFAULT, CW_USEDEFAULT, width_, height_, | |
| nullptr, nullptr, hInstance_, this); | |
| return hwnd_ != nullptr; | |
| } | |
| void show(int cmdShow = SW_SHOWDEFAULT) { | |
| ShowWindow(hwnd_, cmdShow); | |
| UpdateWindow(hwnd_); | |
| } | |
| int run() { | |
| MSG msg{}; | |
| int exitCode = 0; | |
| auto lastFrame = clock::now(); | |
| nextFrame_ = lastFrame; | |
| while (running_) { | |
| while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) { | |
| if (msg.message == WM_QUIT) { | |
| running_ = false; | |
| exitCode = static_cast<int>(msg.wParam); | |
| break; | |
| } | |
| TranslateMessage(&msg); | |
| DispatchMessageW(&msg); | |
| } | |
| if (!running_) break; | |
| const auto now = clock::now(); | |
| const float dt = std::chrono::duration<float>(now - lastFrame).count(); | |
| lastFrame = now; | |
| onUpdate(dt); | |
| RedrawWindow(hwnd_, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW); | |
| throttleFPS(); | |
| } | |
| return exitCode; | |
| } | |
| void setTargetFPS(int fps) { | |
| targetFps_ = std::clamp(fps, 1, 1000); | |
| frameStep_ = std::chrono::duration<double>(1.0 / static_cast<double>(targetFps_)); | |
| } | |
| void minimize() { ShowWindow(hwnd_, SW_MINIMIZE); } | |
| void maximize() { ShowWindow(hwnd_, SW_MAXIMIZE); } | |
| void restore() { ShowWindow(hwnd_, SW_RESTORE); } | |
| void toggleMaximize() { ShowWindow(hwnd_, IsZoomed(hwnd_) ? SW_RESTORE : SW_MAXIMIZE); } | |
| void close() { PostMessageW(hwnd_, WM_CLOSE, 0, 0); } | |
| protected: | |
| virtual void onUpdate(float dt) { | |
| (void)dt; | |
| } | |
| virtual void onRender(HDC hdc) { | |
| RECT rc{}; | |
| GetClientRect(hwnd_, &rc); | |
| // Background | |
| HBRUSH bg = (HBRUSH)GetStockObject(DC_BRUSH); | |
| SetDCBrushColor(hdc, RGB(18, 18, 20)); | |
| FillRect(hdc, &rc, bg); | |
| // Title bar | |
| RECT titleBar = { 0, 0, rc.right, kTitleBarHeight }; | |
| SetDCBrushColor(hdc, RGB(28, 28, 32)); | |
| FillRect(hdc, &titleBar, bg); | |
| // Title text | |
| SetBkMode(hdc, TRANSPARENT); | |
| SetTextColor(hdc, RGB(240, 240, 240)); | |
| RECT textRc = { 12, 0, rc.right - 160, kTitleBarHeight }; | |
| DrawTextW(hdc, title_.c_str(), -1, &textRc, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS); | |
| // Buttons | |
| drawButton(hdc, buttonRectClose(), L"×", RGB(180, 50, 50), RGB(255, 255, 255)); | |
| drawButton(hdc, buttonRectMax(), IsZoomed(hwnd_) ? L"❐" : L"□", RGB(48, 48, 54), RGB(255, 255, 255)); | |
| drawButton(hdc, buttonRectMin(), L"—", RGB(48, 48, 54), RGB(255, 255, 255)); | |
| } | |
| private: | |
| using clock = std::chrono::steady_clock; | |
| static constexpr const wchar_t* kClassName = L"BorderlessGameWindowClass"; | |
| static constexpr int kTitleBarHeight = 32; | |
| static constexpr int kButtonWidth = 46; | |
| static constexpr int kResizeBorder = 8; | |
| HINSTANCE hInstance_ = nullptr; | |
| HWND hwnd_ = nullptr; | |
| std::wstring title_; | |
| int width_ = 0; | |
| int height_ = 0; | |
| bool running_ = true; | |
| int targetFps_ = 120; | |
| std::chrono::duration<double> frameStep_{ 1.0 / 120.0 }; | |
| clock::time_point nextFrame_{}; | |
| static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { | |
| BorderlessWindow* self = nullptr; | |
| if (msg == WM_NCCREATE) { | |
| auto* cs = reinterpret_cast<CREATESTRUCTW*>(lParam); | |
| self = static_cast<BorderlessWindow*>(cs->lpCreateParams); | |
| SetWindowLongPtrW(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(self)); | |
| self->hwnd_ = hwnd; | |
| } else { | |
| self = reinterpret_cast<BorderlessWindow*>(GetWindowLongPtrW(hwnd, GWLP_USERDATA)); | |
| } | |
| if (self) { | |
| return self->handleMessage(msg, wParam, lParam); | |
| } | |
| return DefWindowProcW(hwnd, msg, wParam, lParam); | |
| } | |
| LRESULT handleMessage(UINT msg, WPARAM wParam, LPARAM lParam) { | |
| switch (msg) { | |
| case WM_CREATE: | |
| return 0; | |
| case WM_NCCALCSIZE: | |
| if (wParam) return 0; // Remove standard non-client area | |
| break; | |
| case WM_NCHITTEST: | |
| return hitTest(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); | |
| case WM_GETMINMAXINFO: | |
| keepMaximizeInsideMonitor(reinterpret_cast<MINMAXINFO*>(lParam)); | |
| return 0; | |
| case WM_SIZE: | |
| if (wParam == SIZE_MAXIMIZED) { | |
| maximized_ = true; | |
| } else if (wParam == SIZE_RESTORED) { | |
| maximized_ = false; | |
| } | |
| return 0; | |
| case WM_ERASEBKGND: | |
| return 1; // reduce flicker | |
| case WM_LBUTTONUP: | |
| handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); | |
| return 0; | |
| case WM_LBUTTONDBLCLK: { | |
| POINT pt{ GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; | |
| if (pointInRect(pt, buttonRectClose()) || pointInRect(pt, buttonRectMin()) || pointInRect(pt, buttonRectMax())) { | |
| return 0; | |
| } | |
| toggleMaximize(); | |
| return 0; | |
| } | |
| case WM_SYSCOMMAND: | |
| if ((wParam & 0xFFF0) == SC_KEYMENU) return 0; // blocks alt menu flash | |
| break; | |
| case WM_CLOSE: | |
| DestroyWindow(hwnd_); | |
| return 0; | |
| case WM_DESTROY: | |
| PostQuitMessage(0); | |
| running_ = false; | |
| return 0; | |
| case WM_PAINT: { | |
| PAINTSTRUCT ps{}; | |
| HDC hdc = BeginPaint(hwnd_, &ps); | |
| onRender(hdc); | |
| EndPaint(hwnd_, &ps); | |
| return 0; | |
| } | |
| } | |
| return DefWindowProcW(hwnd_, msg, wParam, lParam); | |
| } | |
| void throttleFPS() { | |
| if (targetFps_ <= 0) return; | |
| const auto now = clock::now(); | |
| if (nextFrame_ == clock::time_point{}) { | |
| nextFrame_ = now; | |
| } | |
| nextFrame_ += std::chrono::duration_cast<clock::duration>(frameStep_); | |
| if (now < nextFrame_) { | |
| const auto coarseSleepUntil = nextFrame_ - std::chrono::milliseconds(1); | |
| if (now < coarseSleepUntil) { | |
| std::this_thread::sleep_until(coarseSleepUntil); | |
| } | |
| while (clock::now() < nextFrame_) { | |
| std::this_thread::yield(); | |
| } | |
| } else { | |
| // If we fell behind, resync so we do not accumulate lag forever. | |
| nextFrame_ = now; | |
| } | |
| } | |
| void keepMaximizeInsideMonitor(MINMAXINFO* mmi) { | |
| if (!mmi || !hwnd_) return; | |
| MONITORINFO mi{}; | |
| mi.cbSize = sizeof(mi); | |
| if (GetMonitorInfoW(MonitorFromWindow(hwnd_, MONITOR_DEFAULTTONEAREST), &mi)) { | |
| mmi->ptMaxPosition.x = mi.rcWork.left - mi.rcMonitor.left; | |
| mmi->ptMaxPosition.y = mi.rcWork.top - mi.rcMonitor.top; | |
| mmi->ptMaxSize.x = mi.rcWork.right - mi.rcWork.left; | |
| mmi->ptMaxSize.y = mi.rcWork.bottom - mi.rcWork.top; | |
| } | |
| } | |
| LRESULT hitTest(int screenX, int screenY) { | |
| POINT pt{ screenX, screenY }; | |
| ScreenToClient(hwnd_, &pt); | |
| RECT rc{}; | |
| GetClientRect(hwnd_, &rc); | |
| const bool onTop = pt.y >= 0 && pt.y < kTitleBarHeight; | |
| const bool onLeft = !IsZoomed(hwnd_) && pt.x >= 0 && pt.x < kResizeBorder; | |
| const bool onRight = !IsZoomed(hwnd_) && pt.x >= rc.right - kResizeBorder && pt.x < rc.right; | |
| const bool onBottom = !IsZoomed(hwnd_) && pt.y >= rc.bottom - kResizeBorder && pt.y < rc.bottom; | |
| if (!IsZoomed(hwnd_)) { | |
| if (onTop && onLeft) return HTTOPLEFT; | |
| if (onTop && onRight) return HTTOPRIGHT; | |
| if (onBottom && onLeft) return HTBOTTOMLEFT; | |
| if (onBottom && onRight) return HTBOTTOMRIGHT; | |
| if (onLeft) return HTLEFT; | |
| if (onRight) return HTRIGHT; | |
| if (pt.y < kResizeBorder) return HTTOP; | |
| if (onBottom) return HTBOTTOM; | |
| } | |
| if (onTop && | |
| !pointInRect(pt, buttonRectClose()) && | |
| !pointInRect(pt, buttonRectMin()) && | |
| !pointInRect(pt, buttonRectMax())) { | |
| return HTCAPTION; | |
| } | |
| return HTCLIENT; | |
| } | |
| void handleClick(int x, int y) { | |
| POINT pt{ x, y }; | |
| if (pointInRect(pt, buttonRectClose())) { | |
| close(); | |
| } else if (pointInRect(pt, buttonRectMin())) { | |
| minimize(); | |
| } else if (pointInRect(pt, buttonRectMax())) { | |
| toggleMaximize(); | |
| } | |
| } | |
| RECT buttonRectClose() const { | |
| RECT rc{}; | |
| GetClientRect(hwnd_, &rc); | |
| return RECT{ | |
| rc.right - kButtonWidth, | |
| 0, | |
| rc.right, | |
| kTitleBarHeight | |
| }; | |
| } | |
| RECT buttonRectMax() const { | |
| RECT rc{}; | |
| GetClientRect(hwnd_, &rc); | |
| return RECT{ | |
| rc.right - (kButtonWidth * 2), | |
| 0, | |
| rc.right - kButtonWidth, | |
| kTitleBarHeight | |
| }; | |
| } | |
| RECT buttonRectMin() const { | |
| RECT rc{}; | |
| GetClientRect(hwnd_, &rc); | |
| return RECT{ | |
| rc.right - (kButtonWidth * 3), | |
| 0, | |
| rc.right - (kButtonWidth * 2), | |
| kTitleBarHeight | |
| }; | |
| } | |
| static bool pointInRect(const POINT& pt, const RECT& rc) { | |
| return pt.x >= rc.left && pt.x < rc.right && pt.y >= rc.top && pt.y < rc.bottom; | |
| } | |
| void drawButton(HDC hdc, const RECT& rc, const wchar_t* label, COLORREF bg, COLORREF fg) { | |
| HBRUSH brush = (HBRUSH)GetStockObject(DC_BRUSH); | |
| SetDCBrushColor(hdc, bg); | |
| FillRect(hdc, &rc, brush); | |
| SetBkMode(hdc, TRANSPARENT); | |
| SetTextColor(hdc, fg); | |
| RECT textRc = rc; | |
| DrawTextW(hdc, label, -1, &textRc, DT_CENTER | DT_VCENTER | DT_SINGLELINE); | |
| } | |
| bool maximized_ = false; | |
| }; | |
| class MyGameWindow : public BorderlessWindow { | |
| protected: | |
| void onUpdate(float dt) override { | |
| (void)dt; | |
| // Put your game update logic here. | |
| } | |
| void onRender(HDC hdc) override { | |
| BorderlessWindow::onRender(hdc); | |
| // Draw your game UI / test content here. | |
| } | |
| }; | |
| int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, int cmdShow) { | |
| MyGameWindow app; | |
| if (!app.create(hInstance, L"Custom Borderless Game Window", 1280, 720, 120)) { | |
| MessageBoxW(nullptr, L"Window creation failed.", L"Error", MB_ICONERROR); | |
| return 1; | |
| } | |
| app.show(cmdShow); | |
| return app.run(); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment