Files
FlaxEngine/Source/Engine/Platform/GDK/GDKWindow.cpp

395 lines
9.1 KiB
C++

// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#if PLATFORM_GDK
#undef _GAMING_XBOX
#include "GDKWindow.h"
#include "GDKPlatform.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Math/Math.h"
#include "Engine/Graphics/GPUSwapChain.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Platform/Win32/IncludeWindowsHeaders.h"
extern void OnMainWindowCreated(HWND hWnd);
GDKWindow::GDKWindow(const CreateWindowSettings& settings)
: WindowBase(settings)
{
int32 x = Math::TruncToInt(settings.Position.X);
int32 y = Math::TruncToInt(settings.Position.Y);
int32 clientWidth = Math::TruncToInt(settings.Size.X);
int32 clientHeight = Math::TruncToInt(settings.Size.Y);
int32 windowWidth = clientWidth;
int32 windowHeight = clientHeight;
_clientSize = Float2((float)clientWidth, (float)clientHeight);
// Setup window style
uint32 style = WS_POPUP, exStyle = 0;
if (settings.SupportsTransparency)
exStyle |= WS_EX_LAYERED;
if (!settings.ActivateWhenFirstShown)
exStyle |= WS_EX_NOACTIVATE;
if (settings.ShowInTaskbar)
exStyle |= WS_EX_APPWINDOW;
else
exStyle |= WS_EX_TOOLWINDOW;
if (settings.IsTopmost)
exStyle |= WS_EX_TOPMOST;
if (!settings.AllowInput)
exStyle |= WS_EX_TRANSPARENT;
if (settings.AllowMaximize)
style |= WS_MAXIMIZEBOX;
if (settings.AllowMinimize)
style |= WS_MINIMIZEBOX;
if (settings.HasSizingFrame)
style |= WS_THICKFRAME;
// Check if window should have a border
if (settings.HasBorder)
{
// Create window style flags
style |= WS_OVERLAPPED | WS_SYSMENU | WS_BORDER | WS_CAPTION;
exStyle |= 0;
// Adjust window size and positions to take into account window border
RECT winRect = { 0, 0, clientWidth, clientHeight };
AdjustWindowRectEx(&winRect, style, FALSE, exStyle);
x += winRect.left;
y += winRect.top;
windowWidth = winRect.right - winRect.left;
windowHeight = winRect.bottom - winRect.top;
}
else
{
// Create window style flags
style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
exStyle |= WS_EX_WINDOWEDGE;
}
// Creating the window
_handle = CreateWindowExW(
exStyle,
Platform::ApplicationClassName,
settings.Title.GetText(),
style,
x,
y,
windowWidth,
windowHeight,
settings.Parent ? static_cast<HWND>(settings.Parent->GetNativePtr()) : nullptr,
nullptr,
(HINSTANCE)Platform::Instance,
nullptr);
// Validate result
if (!HasHWND())
{
LOG_WIN32_LAST_ERROR;
Platform::Fatal(TEXT("Cannot create window."));
}
OnMainWindowCreated(_handle);
}
GDKWindow::~GDKWindow()
{
if (HasHWND())
{
// Destroy window
if (DestroyWindow(_handle) == 0)
{
LOG(Warning, "DestroyWindow failed! Error: {0:#x}", GetLastError());
}
// Clear
_handle = nullptr;
_visible = false;
}
}
void* GDKWindow::GetNativePtr() const
{
return _handle;
}
void GDKWindow::Show()
{
if (!_visible)
{
InitSwapChain();
if (_showAfterFirstPaint)
{
if (RenderTask)
RenderTask->Enabled = true;
return;
}
ASSERT(HasHWND());
// Show
ShowWindow(_handle, (_settings.AllowInput && _settings.ActivateWhenFirstShown) ? SW_SHOW : SW_SHOWNA);
// Base
WindowBase::Show();
_focused = true; // TODO: remove it and check if the initial focus works when game starts with rendering, do we get WM_ACTIVATEAPP?
}
}
void GDKWindow::Hide()
{
if (_visible)
{
ASSERT(HasHWND());
// Hide
ShowWindow(_handle, SW_HIDE);
// Base
WindowBase::Hide();
}
}
void GDKWindow::Minimize()
{
ASSERT(HasHWND());
ShowWindow(_handle, SW_MINIMIZE);
}
void GDKWindow::Maximize()
{
ASSERT(HasHWND());
ShowWindow(_handle, SW_MAXIMIZE);
}
void GDKWindow::Restore()
{
ASSERT(HasHWND());
ShowWindow(_handle, SW_RESTORE);
}
bool GDKWindow::IsClosed() const
{
return !HasHWND();
}
bool GDKWindow::IsForegroundWindow() const
{
return Platform::GetHasFocus();
}
void GDKWindow::SetIsFullscreen(bool isFullscreen)
{
}
void GDKWindow::GetScreenInfo(int32& x, int32& y, int32& width, int32& height) const
{
x = 0;
y = 0;
width = (int32)_clientSize.X;
height = (int32)_clientSize.Y;
}
void GDKWindow::SetCursor(CursorType type)
{
// Base
WindowBase::SetCursor(type);
UpdateCursor();
}
void GDKWindow::CheckForWindowResize()
{
// Skip for minimized window (GetClientRect for minimized window returns 0)
if (_minimized)
return;
ASSERT(HasHWND());
// Cache client size
RECT rect;
GetClientRect(_handle, &rect);
const int32 width = Math::Max(rect.right - rect.left, 0L);
const int32 height = Math::Max(rect.bottom - rect.top, 0L);
_clientSize = Float2(static_cast<float>(width), static_cast<float>(height));
// Check if window size has been changed
if (width > 0 && height > 0 && (_swapChain == nullptr || width != _swapChain->GetWidth() || height != _swapChain->GetHeight()))
{
OnResize(width, height);
}
}
void GDKWindow::UpdateCursor() const
{
if (_cursor == CursorType::Hidden)
{
::SetCursor(nullptr);
return;
}
int32 index = 0;
switch (_cursor)
{
case CursorType::Default:
break;
case CursorType::Cross:
index = 1;
break;
case CursorType::Hand:
index = 2;
break;
case CursorType::Help:
index = 3;
break;
case CursorType::IBeam:
index = 4;
break;
case CursorType::No:
index = 5;
break;
case CursorType::Wait:
index = 11;
break;
case CursorType::SizeAll:
index = 6;
break;
case CursorType::SizeNESW:
index = 7;
break;
case CursorType::SizeNS:
index = 8;
break;
case CursorType::SizeNWSE:
index = 9;
break;
case CursorType::SizeWE:
index = 10;
break;
}
static const LPCWSTR cursors[] =
{
IDC_ARROW,
IDC_CROSS,
IDC_HAND,
IDC_HELP,
IDC_IBEAM,
IDC_NO,
IDC_SIZEALL,
IDC_SIZENESW,
IDC_SIZENS,
IDC_SIZENWSE,
IDC_SIZEWE,
IDC_WAIT,
};
ASSERT(index >= 0 && index < ARRAY_COUNT(cursors));
const HCURSOR cursor = LoadCursorW(nullptr, cursors[index]);
::SetCursor(cursor);
}
Windows::LRESULT GDKWindow::WndProc(Windows::UINT msg, Windows::WPARAM wParam, Windows::LPARAM lParam)
{
switch (msg)
{
case WM_SETCURSOR:
{
if (LOWORD(lParam) == HTCLIENT)
{
UpdateCursor();
return true;
}
break;
}
case WM_CREATE:
{
return 0;
}
case WM_SIZE:
{
if (SIZE_MINIMIZED == wParam)
{
// Set flags
_minimized = true;
_maximized = false;
}
else
{
RECT rcCurrentClient;
GetClientRect(_handle, &rcCurrentClient);
if (rcCurrentClient.top == 0 && rcCurrentClient.bottom == 0)
{
// Rapidly clicking the task bar to minimize and restore a window can cause a WM_SIZE message with SIZE_RESTORED when
// the window has actually become minimized due to rapid change so just ignore this message.
}
else if (SIZE_MAXIMIZED == wParam)
{
// Set flags
_minimized = false;
_maximized = true;
// Check size
CheckForWindowResize();
}
else if (SIZE_RESTORED == wParam)
{
if (_maximized)
{
// Clear flag
_maximized = false;
// Check size
CheckForWindowResize();
}
else if (_minimized)
{
// Clear flag
_minimized = false;
// Check size
CheckForWindowResize();
}
else
{
// This WM_SIZE come from resizing the window via an API like SetWindowPos() so resize
CheckForWindowResize();
}
}
}
break;
}
case WM_SETFOCUS:
OnGotFocus();
break;
case WM_KILLFOCUS:
OnLostFocus();
break;
case WM_ACTIVATEAPP:
if (wParam == TRUE && !_focused)
{
OnGotFocus();
}
else if (wParam == FALSE && _focused)
{
OnLostFocus();
}
break;
case WM_CLOSE:
Close(ClosingReason::User);
return 0;
case WM_DESTROY:
{
// Quit
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(_handle, msg, wParam, lParam);
}
#endif