diff --git a/Engine/Build/workspace.lua b/Engine/Build/workspace.lua index b638a57..8df855e 100644 --- a/Engine/Build/workspace.lua +++ b/Engine/Build/workspace.lua @@ -17,6 +17,7 @@ function coruvs_global_setup() -- Global Configuration set_targetdir('$(projectdir)/Binaries/Engine/$(plat)-$(arch)/$(mode)') set_objectdir('$(projectdir)/Intermediates/Engine/$(plat)-$(arch)/$(mode)') + set_dependir('$(projectdir)/Engine/Build/Output/.deps') set_warnings('error') set_exceptions('none') @@ -46,7 +47,7 @@ function coruvs_global_setup() -- Platform Configuration if is_plat('windows') then - add_cxflags('/EHsc', '/Zc:preprocessor', '/wd5103', {force = true}) + add_cxflags('/EHsc', '/Zc:preprocessor', '/wd5103', '/wd4005', {force = true}) add_defines('WIN32_LEAN_AND_MEAN', 'NOMINMAX', 'WIN32_DEFAULT_LIBS') end end \ No newline at end of file diff --git a/Engine/Source/Applications/Editor/xmake.lua b/Engine/Source/Applications/Editor/xmake.lua index fdf1dd7..3ec546e 100644 --- a/Engine/Source/Applications/Editor/xmake.lua +++ b/Engine/Source/Applications/Editor/xmake.lua @@ -2,4 +2,5 @@ local module_name = 'Editor' corvus_application_target(module_name) add_deps('Core') + add_deps('Platform') corvus_target_end() \ No newline at end of file diff --git a/Engine/Source/Runtime/Core/Public/Core/Assertion/Assertion.hpp b/Engine/Source/Runtime/Core/Public/Core/Assertion/Assertion.hpp index 5fbfbeb..edc722b 100644 --- a/Engine/Source/Runtime/Core/Public/Core/Assertion/Assertion.hpp +++ b/Engine/Source/Runtime/Core/Public/Core/Assertion/Assertion.hpp @@ -2,12 +2,13 @@ #pragma once +#include "ExceptionHandler.hpp" + #include "Core/Containers/String.hpp" #include "Core/Logging/LogChannel.hpp" #include "Core/Utility/SourceLocation.hpp" #include "Core/Utility/StringUtils.hpp" -struct FExceptionMetadata; CORE_API DECLARE_LOG_CHANNEL_EXTERN(Assert) namespace Assertion diff --git a/Engine/Source/Runtime/Core/Public/Core/Assertion/ExceptionHandler.hpp b/Engine/Source/Runtime/Core/Public/Core/Assertion/ExceptionHandler.hpp index 4df8016..bcd0400 100644 --- a/Engine/Source/Runtime/Core/Public/Core/Assertion/ExceptionHandler.hpp +++ b/Engine/Source/Runtime/Core/Public/Core/Assertion/ExceptionHandler.hpp @@ -2,29 +2,28 @@ #pragma once -#include - #include "Core/Containers/String.hpp" #include "Core/Utility/SourceLocation.hpp" +#include "Core/Utility/WindowsDefinitions.hpp" -#define ERROR_CODE_UNKNOWN MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x0001) -#define ERROR_CODE_ASSERTION MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x0002) +#define ERROR_CODE_UNKNOWN CREATE_HRESULT(1, 4, 0x0001) +#define ERROR_CODE_ASSERTION CREATE_HRESULT(1, 4, 0x0002) struct CORE_API FExceptionMetadata { - HRESULT ReportCode = ERROR_CODE_UNKNOWN; + Windows::HRESULT ReportCode = ERROR_CODE_UNKNOWN; FString AssertionCondition; // Only valid if report code is ERROR_CODE_ASSERTION FString Message; - HRESULT CustomCode = 0; + Windows::HRESULT CustomCode = 0; FSourceLocation SourceLocation; }; class CORE_API FExceptionHandler { public: - [[nodiscard]] static long HandleException(const EXCEPTION_POINTERS* ExceptionInfo); + [[nodiscard]] static long HandleException(const Windows::EXCEPTION_POINTERS* ExceptionInfo); static void Report(const FExceptionMetadata& Metadata); - [[nodiscard]] static FString GetResultDescription(HRESULT Result); + [[nodiscard]] static FString GetResultDescription(Windows::HRESULT Result); [[nodiscard]] static int32 GetExitCode(); @@ -32,7 +31,7 @@ class CORE_API FExceptionHandler static int32 ExitCode; }; -[[nodiscard]] CORE_API long HandleException(const EXCEPTION_POINTERS* ExceptionInfo); +[[nodiscard]] CORE_API long HandleException(const Windows::EXCEPTION_POINTERS* ExceptionInfo); #define TRY __try #define CATCH() __except(HandleException(GetExceptionInformation())) diff --git a/Engine/Source/Runtime/Core/Public/Core/Containers/String.hpp b/Engine/Source/Runtime/Core/Public/Core/Containers/String.hpp index 7c1cdef..29c04e2 100644 --- a/Engine/Source/Runtime/Core/Public/Core/Containers/String.hpp +++ b/Engine/Source/Runtime/Core/Public/Core/Containers/String.hpp @@ -38,3 +38,13 @@ using FStringView = FWideStringView; template using TFormatString = TWideFormatString; #endif + +#ifndef TEXT +# if CV_USE_ANSI_STRINGS +# define INNER_TEXT(String) String +# define TEXT(String) INNER_TEXT(String) +# else +# define INNER_TEXT(String) L##String +# define TEXT(String) INNER_TEXT(String) +# endif +#endif diff --git a/Engine/Source/Runtime/Core/Public/Core/CoreDefinitions.hpp b/Engine/Source/Runtime/Core/Public/Core/CoreDefinitions.hpp index f5c1ca0..c179a9f 100644 --- a/Engine/Source/Runtime/Core/Public/Core/CoreDefinitions.hpp +++ b/Engine/Source/Runtime/Core/Public/Core/CoreDefinitions.hpp @@ -50,8 +50,8 @@ DEFAULT_MOVEABLE_PREFIX(Class,) #define DEFAULT_COPY_MOVEABLE_PREFIX(Class, Prefix) \ - DEFAULT_COPYABLE(Class, Prefix) \ - DEFAULT_MOVEABLE(Class, Prefix) + DEFAULT_COPYABLE_PREFIX(Class, Prefix) \ + DEFAULT_MOVEABLE_PREFIX(Class, Prefix) #define INNER_APPEND(X, Y) X##Y #define APPEND(X, Y) INNER_APPEND(X, Y) diff --git a/Engine/Source/Runtime/Core/Public/Core/Utility/WindowsDefinitions.hpp b/Engine/Source/Runtime/Core/Public/Core/Utility/WindowsDefinitions.hpp new file mode 100644 index 0000000..422ebef --- /dev/null +++ b/Engine/Source/Runtime/Core/Public/Core/Utility/WindowsDefinitions.hpp @@ -0,0 +1,34 @@ +// RavenStorm Copyright @ 2025-2025 + +#pragma once + +struct HINSTANCE__; +struct HWND__; +struct HKEY__; +struct HMONITOR__; +struct _EXCEPTION_POINTERS; +struct tagRECT; + +namespace Windows +{ + using LONG = long; + using DWORD = unsigned long; + using HRESULT = long; + + using HANDLE = void*; + using HINSTANCE = HINSTANCE__*; + using HWND = HWND__*; + using HMONITOR = HMONITOR__*; + using RECT = tagRECT; + using HMODULE = HINSTANCE; + + using EXCEPTION_POINTERS = _EXCEPTION_POINTERS; + + using HKEY = HKEY__*; + + using LRESULT = int64; + using WPARAM = uint64; + using LPARAM = int64; +} + +#define CREATE_HRESULT(sev,fac,code) ((Windows::HRESULT) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code))) ) diff --git a/Engine/Source/Runtime/Platform/Private/Platform/CPUDetection.cpp b/Engine/Source/Runtime/Platform/Private/Platform/CPUDetection.cpp new file mode 100644 index 0000000..f019426 --- /dev/null +++ b/Engine/Source/Runtime/Platform/Private/Platform/CPUDetection.cpp @@ -0,0 +1,181 @@ +// RavenStorm Copyright @ 2025-2025 + +#include "Platform/CPUDetection.hpp" + +#include "Core/Logging/LogChannel.hpp" +#include "Core/Logging/LogManager.hpp" + +#include + +#include + +#include "Core/Memory/Memory.hpp" + +DEFINE_LOG_CHANNEL(CPUDetection, All) + +namespace +{ + struct FCPUIDResult + { + int32 EAX; + int32 EBX; + int32 ECX; + int32 EDX; + }; + + FCPUIDResult CallCPUID(const int32 FunctionId, const int32 SubFunctionId = 0) + { + FCPUIDResult Result; + int32 CPUInfo[4]; + __cpuidex(CPUInfo, FunctionId, SubFunctionId); + Result.EAX = CPUInfo[0]; + Result.EBX = CPUInfo[1]; + Result.ECX = CPUInfo[2]; + Result.EDX = CPUInfo[3]; + return Result; + } +} + +FCPUInfo FCPUDetection::CPUInfo = {}; + +void FCPUDetection::Initialize() +{ + DetectVendor(); + DetectBrand(); + DetectCPUTopology(); + + CVLOG(LogCPUDetection, Info, "CPU: {}", CPUInfo.BrandString); + CVLOG(LogCPUDetection, Info, "\t- Vendor: {}", CPUInfo.VendorString); + CVLOG(LogCPUDetection, Info, "\t- Family: {}", CPUInfo.Family); + CVLOG(LogCPUDetection, Info, "\t- Model: {}", CPUInfo.Model); + CVLOG(LogCPUDetection, Info, "\t- Stepping: {}", CPUInfo.Stepping); + CVLOG(LogCPUDetection, Info, "\t- Physical Cores: {}, Logical Cores: {}", CPUInfo.PhysicalCores, CPUInfo.LogicalCores); + CVLOG(LogCPUDetection, Info, "\t- Logical Cores: {}", CPUInfo.LogicalCores); + CVLOG(LogCPUDetection, Info, "\t- Cache Line Size: {} bytes", CPUInfo.CacheLine); + CVLOG(LogCPUDetection, Info, "\t- L1 Cache: {} KB", CPUInfo.L1CacheSize / 1024); + CVLOG(LogCPUDetection, Info, "\t- L2 Cache: {} MB", CPUInfo.L2CacheSize / (1024 * 1024)); + CVLOG(LogCPUDetection, Info, "\t- L3 Cache: {} MB", CPUInfo.L3CacheSize / (1024 * 1024)); +} + +FCPUInfo FCPUDetection::GetCPUInfo() noexcept +{ + return CPUInfo; +} + +void FCPUDetection::DetectVendor() +{ + const FCPUIDResult Result = CallCPUID(0, 0); + + char Vendor[13] = {}; + *reinterpret_cast(Vendor) = Result.EBX; + *reinterpret_cast(Vendor + 4) = Result.EDX; + *reinterpret_cast(Vendor + 8) = Result.ECX; + Vendor[12] = '\0'; + + CPUInfo.VendorString = FAnsiString(Vendor); + CPUInfo.IsIntel = CPUInfo.VendorString == "GenuineIntel"; + CPUInfo.IsAMD = CPUInfo.VendorString == "AuthenticAMD"; +} + +void FCPUDetection::DetectBrand() +{ + char Brand[49] = {}; + + const FCPUIDResult Result = CallCPUID(static_cast(0x80000000), 0); + if (Result.EAX >= static_cast(0x80000004)) + { + const FCPUIDResult Brand0 = CallCPUID(static_cast(0x80000002), 0); + const FCPUIDResult Brand1 = CallCPUID(static_cast(0x80000003), 0); + const FCPUIDResult Brand2 = CallCPUID(static_cast(0x80000004), 0); + + std::memcpy(Brand, &Brand0, 16); + std::memcpy(Brand + 16, &Brand1, 16); + std::memcpy(Brand + 32, &Brand2, 16); + Brand[48] = '\0'; + + // Trim leading spaces + char* Start = Brand; + while (*Start == ' ') ++Start; + + CPUInfo.BrandString = FAnsiString(Start); + } +} + +void FCPUDetection::DetectCPUTopology() +{ + const FCPUIDResult Result1 = CallCPUID(1, 0); + CPUInfo.Family = ((Result1.EAX >> 8) & 0xF) + ((Result1.EAX >> 20) & 0xFF); + CPUInfo.Model = ((Result1.EAX >> 4) & 0xF) | ((Result1.EAX >> 12) & 0xF0); + CPUInfo.Stepping = Result1.EAX & 0xF; + + SYSTEM_INFO SystemInfo; + GetSystemInfo(&SystemInfo); + CPUInfo.LogicalCores = static_cast(SystemInfo.dwNumberOfProcessors); + + DWORD BufferSize = 0; + GetLogicalProcessorInformation(nullptr, &BufferSize); + if (BufferSize > 0) + { + const PSYSTEM_LOGICAL_PROCESSOR_INFORMATION Buffer = static_cast(FMemory::Allocate(BufferSize)); + if (GetLogicalProcessorInformation(Buffer, &BufferSize)) + { + const DWORD NumEntries = BufferSize / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); + int32 PhysicalCores = 0; + int32 CacheLine = 0; + int32 L1CacheSize = 0; + int32 L2CacheSize = 0; + int32 L3CacheSize = 0; + for (DWORD Index = 0; Index < NumEntries; ++Index) + { + if (Buffer[Index].Relationship == RelationProcessorCore) + { + ++PhysicalCores; + } + else if (Buffer[Index].Relationship == RelationCache) + { + if (const CACHE_DESCRIPTOR& Cache = Buffer[Index].Cache; Cache.Level == 1) + { + if (CacheLine == 0) + { + CacheLine = static_cast(Cache.LineSize); + } + if (Cache.Type == CacheData || Cache.Type == CacheUnified) + { + L1CacheSize += static_cast(Cache.Size); + } + else if (Cache.Type == CacheInstruction) + { + L1CacheSize += static_cast(Cache.Size); + } + } + else if (Cache.Level == 2) + { + L2CacheSize += static_cast(Cache.Size); + } + else if (Cache.Level == 3) + { + if (L3CacheSize == 0) + { + L3CacheSize = static_cast(Cache.Size); + } + } + } + } + CPUInfo.PhysicalCores = PhysicalCores; + CPUInfo.CacheLine = CacheLine > 0 ? CacheLine : 64; + CPUInfo.L1CacheSize = L1CacheSize; + CPUInfo.L2CacheSize = L2CacheSize; + CPUInfo.L3CacheSize = L3CacheSize; + } + FMemory::Free(Buffer); + } + else + { + CPUInfo.PhysicalCores = CPUInfo.LogicalCores; + CPUInfo.CacheLine = 64; + CPUInfo.L1CacheSize = 0; + CPUInfo.L2CacheSize = 0; + CPUInfo.L3CacheSize = 0; + } + CPUInfo.IsHyperThreaded = CPUInfo.LogicalCores > CPUInfo.PhysicalCores; +} diff --git a/Engine/Source/Runtime/Platform/Private/Platform/Platform.cpp b/Engine/Source/Runtime/Platform/Private/Platform/Platform.cpp new file mode 100644 index 0000000..667db3c --- /dev/null +++ b/Engine/Source/Runtime/Platform/Private/Platform/Platform.cpp @@ -0,0 +1,157 @@ +// RavenStorm Copyright @ 2025-2025 + +#include "Platform/Platform.hpp" +#include "Core/Logging/LogManager.hpp" +#include "Platform/CPUDetection.hpp" + +#include + +#include + +DEFINE_LOG_CHANNEL(Platform, All) + +FSystemInfo FPlatform::SystemInfo = {}; + +bool8 FDynamicModuleHandle::IsValid() const noexcept +{ + return Module != nullptr; +} + +void FPlatform::Initialize() +{ + DetectOperatingSystem(); + DetectComputerInfo(); + + CVLOG(LogPlatform, Info, "OS: {} ({})", SystemInfo.OSName, SystemInfo.OSVersion); + CVLOG(LogPlatform, Info, "\t- Build: {}", SystemInfo.OSBuildNumber); + CVLOG(LogPlatform, Info, "\t- Computer Name: {}", SystemInfo.ComputerName); + + FCPUDetection::Initialize(); +} + +void FPlatform::Shutdown() +{ +} + +bool8 FPlatform::HasEnvVariable(const FString& VariableName) noexcept +{ + return GetEnvironmentVariable(VariableName.c_str(), nullptr, 0) > 0; +} + +FString FPlatform::GetEnvVariable(const FString& VariableName) noexcept +{ + const DWORD BufferSize = GetEnvironmentVariable(VariableName.c_str(), nullptr, 0); + if (BufferSize == 0) + { + return {}; + } + FString Result(BufferSize, '\0'); + GetEnvironmentVariable(VariableName.c_str(), Result.data(), BufferSize); + return Result; +} + +FString FPlatform::GetEnvVariable(const FString& VariableName, const FString& DefaultValue) noexcept +{ + FString Value = GetEnvVariable(VariableName); + return Value.empty() ? DefaultValue : Value; +} + +bool8 FPlatform::SetEnvVariable(const FString& VariableName, const FString& Value) noexcept +{ + return SetEnvironmentVariable(VariableName.c_str(), Value.c_str()) != 0; +} + +FDynamicModuleHandle FPlatform::LoadDynamicModule(const FString& Name) noexcept +{ + const HMODULE Module = LoadLibrary(Name.c_str()); + if (Module == nullptr) + { + CVLOG(LogPlatform, Error, "Failed to load module: {}", Name); + return {}; + } + return {.Module = Module}; +} + +void FPlatform::UnloadDynamicModule(FDynamicModuleHandle&& Module) noexcept +{ + if (!Module.IsValid()) + { + CVLOG(LogPlatform, Warning, "Attempting to unload invalid module"); + return; + } + if (!FreeLibrary(std::move(Module).Module)) + { + CVLOG(LogPlatform, Error, "Failed to unload module"); + } +} + +FCPUInfo FPlatform::GetCPUInfo() noexcept +{ + return FCPUDetection::GetCPUInfo(); +} + +FSystemInfo FPlatform::GetSystemInfo() noexcept +{ + return SystemInfo; +} + +void FPlatform::DetectOperatingSystem() +{ + using RtlGetVersionPtr = LONG(WINAPI*)(PRTL_OSVERSIONINFOW); + + RTL_OSVERSIONINFOW VersionInfo = {}; + VersionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOW); + + if (const HMODULE NtDllModule = GetModuleHandleW(L"ntdll.dll")) + { + if (const auto RtlGetVersion = reinterpret_cast(GetProcAddress(NtDllModule, "RtlGetVersion"))) + { + RtlGetVersion(&VersionInfo); + } + } + + const uint32 MajorVersion = VersionInfo.dwMajorVersion; + const uint32 MinorVersion = VersionInfo.dwMinorVersion; + const uint32 BuildNumber = VersionInfo.dwBuildNumber; + + SystemInfo.WindowsVersion = DetermineWindowsVersion(MajorVersion, BuildNumber); + SystemInfo.OSName = GetWindowsVersionString(MajorVersion, BuildNumber); + SystemInfo.OSVersion = std::format("{}.{}.{}", MajorVersion, MinorVersion, BuildNumber); + SystemInfo.OSBuildNumber = std::format("{}", BuildNumber); +} + +void FPlatform::DetectComputerInfo() +{ + CHAR ComputerNameBuffer[MAX_COMPUTERNAME_LENGTH + 1]; + DWORD ComputerNameSize = MAX_COMPUTERNAME_LENGTH + 1; + if (GetComputerName(ComputerNameBuffer, &ComputerNameSize)) + { + SystemInfo.ComputerName = FString(ComputerNameBuffer); + } +} + +EWindowsVersion FPlatform::DetermineWindowsVersion(const uint32 MajorVersion, const uint32 BuildNumber) +{ + if (MajorVersion == 10) + { + if (BuildNumber >= 22000) + { + return EWindowsVersion::Windows11; + } + return EWindowsVersion::Windows10; + } + return EWindowsVersion::Unknown; +} + +FString FPlatform::GetWindowsVersionString(const uint32 MajorVersion, const uint32 BuildNumber) +{ + if (MajorVersion == 10) + { + if (BuildNumber >= 22000) + { + return "Windows 11"; + } + return "Windows 10"; + } + return "Windows (Unknown Version)"; +} diff --git a/Engine/Source/Runtime/Platform/Private/Platform/Registry.cpp b/Engine/Source/Runtime/Platform/Private/Platform/Registry.cpp new file mode 100644 index 0000000..7674498 --- /dev/null +++ b/Engine/Source/Runtime/Platform/Private/Platform/Registry.cpp @@ -0,0 +1,526 @@ +// RavenStorm Copyright @ 2025-2025 + +#include "Platform/Registry.hpp" + +#include "Core/Assertion/Assertion.hpp" +#include "Core/Logging/LogManager.hpp" + +#include + +DEFINE_LOG_CHANNEL(Registry, All) + +namespace +{ + HKEY GetHiveHandle(const ERegistryHive Hive) + { + switch (Hive) + { + case ERegistryHive::ClassesRoot: return HKEY_CLASSES_ROOT; + case ERegistryHive::CurrentUser: return HKEY_CURRENT_USER; + case ERegistryHive::CurrentConfig: return HKEY_CURRENT_CONFIG; + case ERegistryHive::LocalMachine: return HKEY_LOCAL_MACHINE; + case ERegistryHive::Users: return HKEY_USERS; + case ERegistryHive::Unknown: + break; + } + return nullptr; + } + + FString GetHiveName(const ERegistryHive Hive) + { + switch (Hive) + { + case ERegistryHive::ClassesRoot: return TEXT("HKEY_CLASSES_ROOT"); + case ERegistryHive::CurrentUser: return TEXT("HKEY_CURRENT_USER"); + case ERegistryHive::CurrentConfig: return TEXT("HKEY_CURRENT_CONFIG"); + case ERegistryHive::LocalMachine: return TEXT("HKEY_LOCAL_MACHINE"); + case ERegistryHive::Users: return TEXT("HKEY_USERS"); + case ERegistryHive::Unknown: + break; + } + return TEXT("Unknown"); + } +} + +FString FRegistryValue::AsString() const +{ + if (Type == ERegistryValueType::String || Type == ERegistryValueType::ExpandString) + { + return {reinterpret_cast(Data.GetData()), Data.Num() / sizeof(FChar)}; + } + return {}; +} + +uint32 FRegistryValue::AsDWord() const +{ + if (Type == ERegistryValueType::DWord && Data.Num() >= sizeof(uint32)) + { + return *reinterpret_cast(Data.GetData()); + } + return 0; +} + +uint64 FRegistryValue::AsQWord() const +{ + if (Type == ERegistryValueType::QWord && Data.Num() >= sizeof(uint64)) + { + return *reinterpret_cast(Data.GetData()); + } + return 0; +} + +TArray FRegistryValue::AsMultiString() const +{ + TArray Result; + if (Type == ERegistryValueType::MultiString) + { + const FChar* Current = reinterpret_cast(Data.GetData()); + const FChar* End = Current + (Data.Num() / sizeof(FChar)); + while (Current < End && *Current) + { + FString String(Current); + Current += String.length() + 1; + Result.PushBack(std::move(String)); + } + } + return Result; +} + +TArray FRegistryValue::AsBinary() const +{ + return Data; +} + +FRegistryKey::FRegistryKey(const ERegistryHive InHive, FString InSubKey) + : Hive(InHive), SubKey(std::move(InSubKey)) +{ +} + +FRegistryKey::~FRegistryKey() +{ + Close(); +} + +bool8 FRegistryKey::IsValid() const noexcept +{ + return Hive != ERegistryHive::Unknown && !SubKey.empty(); +} + +bool8 FRegistryKey::Exists() const noexcept +{ + if (!IsValid()) + { + return false; + } + HKEY TempKey; + const LONG Result = RegOpenKeyEx(GetHiveHandle(), SubKey.c_str(), 0, KEY_READ, &TempKey); + if (Result == ERROR_SUCCESS) + { + RegCloseKey(TempKey); + return true; + } + return false; +} + +bool8 FRegistryKey::Create() +{ + if (!IsValid()) + { + CVLOG(LogRegistry, Error, TEXT("Cannot create invalid registry key")); + return false; + } + if (IsOpen) + { + Close(); + } + DWORD Disposition; + const LONG Result = RegCreateKeyEx(GetHiveHandle(), SubKey.c_str(), 0, nullptr, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, nullptr, &KeyHandle, &Disposition); + if (Result == ERROR_SUCCESS) + { + IsOpen = true; + if (Disposition == REG_CREATED_NEW_KEY) + { + CVLOG(LogRegistry, Info, TEXT("Created new registry key: {}\\{}"), GetHiveName(Hive), SubKey); + } + return true; + } + CVLOG(LogRegistry, Error, TEXT("Failed to create registry key: {}\\{} (Error: {} -> \"{}\")"), GetHiveName(Hive), SubKey, Result, FExceptionHandler::GetResultDescription(Result)); + return false; +} + +bool8 FRegistryKey::Open(const bool8 ReadOnly) +{ + if (!IsValid()) + { + CVLOG(LogRegistry, Error, TEXT("Cannot open invalid registry key")); + return false; + } + if (IsOpen) + { + return true; + } + const REGSAM Access = ReadOnly ? KEY_READ : (KEY_READ | KEY_WRITE); + const LONG Result = RegOpenKeyEx(GetHiveHandle(), SubKey.c_str(), 0, Access, &KeyHandle); + if (Result == ERROR_SUCCESS) + { + IsOpen = true; + return true; + } + CVLOG(LogRegistry, Error, TEXT("Failed to open registry key: {}\\{} (Error: {})"), GetHiveName(Hive), SubKey, Result); + return false; +} + +void FRegistryKey::Close() +{ + if (IsOpen && KeyHandle) + { + RegCloseKey(KeyHandle); + KeyHandle = nullptr; + IsOpen = false; + } +} + +bool8 FRegistryKey::Delete() +{ + if (!IsValid()) + { + return false; + } + Close(); + const LONG Result = RegDeleteKey(GetHiveHandle(), SubKey.c_str()); + return Result == ERROR_SUCCESS; +} + +bool8 FRegistryKey::HasValue(const FString& ValueName) const +{ + if (!IsOpen) + { + return false; + } + const LONG Result = RegQueryValueEx(KeyHandle, ValueName.c_str(), nullptr, nullptr, nullptr, nullptr); + return Result == ERROR_SUCCESS; +} + +FRegistryValue FRegistryKey::ReadValue(const FString& ValueName) const +{ + FRegistryValue Value; + Value.Name = ValueName; + if (!IsOpen) + { + CVLOG(LogRegistry, Warning, TEXT("Attempting to read from closed registry key")); + return Value; + } + DWORD Type = 0; + DWORD DataSize = 0; + LONG Result = RegQueryValueEx(KeyHandle, ValueName.c_str(), nullptr, &Type, nullptr, &DataSize); + if (Result != ERROR_SUCCESS) + { + return Value; + } + Value.Type = static_cast(Type); + Value.Data.Resize(DataSize); + Result = RegQueryValueEx(KeyHandle, ValueName.c_str(), nullptr, &Type, Value.Data.GetData(), &DataSize); + if (Result != ERROR_SUCCESS) + { + Value.Data.Clear(); + Value.Type = ERegistryValueType::None; + } + return Value; +} + +bool8 FRegistryKey::WriteValue(const FString& ValueName, const FRegistryValue& Value) const +{ + if (!IsOpen) + { + CVLOG(LogRegistry, Warning, TEXT("Attempting to write to closed registry key")); + return false; + } + const LONG Result = RegSetValueEx(KeyHandle, ValueName.c_str(), 0, static_cast(Value.Type), Value.Data.GetData(), static_cast(Value.Data.Num())); + if (Result != ERROR_SUCCESS) + { + CVLOG(LogRegistry, Error, TEXT("Failed to write registry value: {} (Error: {})"), ValueName, Result); + return false; + } + return true; +} + +bool8 FRegistryKey::DeleteValue(const FString& ValueName) const +{ + if (!IsOpen) + { + return false; + } + const LONG Result = RegDeleteValue(KeyHandle, ValueName.c_str()); + return Result == ERROR_SUCCESS; +} + +bool8 FRegistryKey::WriteString(const FString& ValueName, const FString& Value) const +{ + FRegistryValue RegValue; + RegValue.Name = ValueName; + RegValue.Type = ERegistryValueType::String; + const size64 ByteSize = (Value.length() + 1) * sizeof(FChar); + RegValue.Data.Resize(ByteSize); + FMemory::Copy(Value.c_str(), RegValue.Data.GetData(), ByteSize); + return WriteValue(ValueName, RegValue); +} + +bool8 FRegistryKey::WriteDWord(const FString& ValueName, const uint32 Value) const +{ + FRegistryValue RegValue; + RegValue.Name = ValueName; + RegValue.Type = ERegistryValueType::DWord; + RegValue.Data.Resize(sizeof(uint32)); + FMemory::Copy(&Value, RegValue.Data.GetData(), sizeof(uint32)); + return WriteValue(ValueName, RegValue); +} + +bool8 FRegistryKey::WriteQWord(const FString& ValueName, const uint64 Value) const +{ + FRegistryValue RegValue; + RegValue.Name = ValueName; + RegValue.Type = ERegistryValueType::QWord; + RegValue.Data.Resize(sizeof(uint64)); + FMemory::Copy(&Value, RegValue.Data.GetData(), sizeof(uint64)); + return WriteValue(ValueName, RegValue); +} + +bool8 FRegistryKey::WriteBinary(const FString& ValueName, const TArray& Value) const +{ + FRegistryValue RegValue; + RegValue.Name = ValueName; + RegValue.Type = ERegistryValueType::Binary; + RegValue.Data = Value; + return WriteValue(ValueName, RegValue); +} + +bool8 FRegistryKey::WriteMultiString(const FString& ValueName, const TArray& Values) const +{ + FRegistryValue RegValue; + RegValue.Name = ValueName; + RegValue.Type = ERegistryValueType::MultiString; + size64 TotalSize = sizeof(FChar); + for (const auto& Str : Values) + { + TotalSize += (Str.length() + 1) * sizeof(FChar); + } + RegValue.Data.Resize(TotalSize); + uint8* Current = RegValue.Data.GetData(); + for (const auto& Str : Values) + { + const size64 ByteSize = (Str.length() + 1) * sizeof(FChar); + FMemory::Copy(Str.c_str(), Current, ByteSize); + Current += ByteSize; + } + *reinterpret_cast(Current) = 0; + return WriteValue(ValueName, RegValue); +} + +FString FRegistryKey::ReadString(const FString& ValueName, const FString& DefaultValue) const +{ + const FRegistryValue Value = ReadValue(ValueName); + if (Value.Type == ERegistryValueType::String || Value.Type == ERegistryValueType::ExpandString) + { + return Value.AsString(); + } + return DefaultValue; +} + +uint32 FRegistryKey::ReadDWord(const FString& ValueName, const uint32 DefaultValue) const +{ + const FRegistryValue Value = ReadValue(ValueName); + if (Value.Type == ERegistryValueType::DWord) + { + return Value.AsDWord(); + } + return DefaultValue; +} + +uint64 FRegistryKey::ReadQWord(const FString& ValueName, const uint64 DefaultValue) const +{ + const FRegistryValue Value = ReadValue(ValueName); + if (Value.Type == ERegistryValueType::QWord) + { + return Value.AsQWord(); + } + return DefaultValue; +} + +TArray FRegistryKey::ReadBinary(const FString& ValueName) const +{ + const FRegistryValue Value = ReadValue(ValueName); + if (Value.Type == ERegistryValueType::Binary) + { + return Value.AsBinary(); + } + return {}; +} + +TArray FRegistryKey::ReadMultiString(const FString& ValueName) const +{ + const FRegistryValue Value = ReadValue(ValueName); + if (Value.Type == ERegistryValueType::MultiString) + { + return Value.AsMultiString(); + } + return {}; +} + +TArray FRegistryKey::GetSubKeyNames() const +{ + TArray SubKeys; + if (!IsOpen) + { + return SubKeys; + } + FChar KeyName[256]; + DWORD Index = 0; + while (true) + { + DWORD NameSize = 256; + const LONG Result = RegEnumKeyEx(KeyHandle, Index++, KeyName, &NameSize, nullptr, nullptr, nullptr, nullptr); + if (Result == ERROR_NO_MORE_ITEMS) + { + break; + } + if (Result == ERROR_SUCCESS) + { + SubKeys.PushBack(FString(KeyName)); + } + } + return SubKeys; +} + +TArray FRegistryKey::GetValueNames() const +{ + TArray ValueNames; + if (!IsOpen) + { + return ValueNames; + } + FChar ValueName[16384]; + DWORD Index = 0; + while (true) + { + DWORD NameSize = 16384; + const LONG Result = RegEnumValue(KeyHandle, Index++, ValueName, &NameSize, nullptr, nullptr, nullptr, nullptr); + if (Result == ERROR_NO_MORE_ITEMS) + { + break; + } + if (Result == ERROR_SUCCESS) + { + ValueNames.PushBack(FString(ValueName)); + } + } + return ValueNames; +} + +TArray FRegistryKey::GetValues() const +{ + TArray Values; + const TArray ValueNames = GetValueNames(); + for (const auto& Name : ValueNames) + { + FRegistryValue Value = ReadValue(Name); + if (Value.Type != ERegistryValueType::None) + { + Values.PushBack(std::move(Value)); + } + } + return Values; +} + +FString FRegistryKey::GetFullPath() const +{ + return GetHiveName(Hive) + TEXT("\\") + SubKey; +} + +HKEY FRegistryKey::GetHiveHandle() const +{ + return ::GetHiveHandle(Hive); +} + +bool8 FRegistry::KeyExists(const ERegistryHive Hive, const FString& SubKey) +{ + const FRegistryKey Key(Hive, SubKey); + return Key.Exists(); +} + +bool8 FRegistry::CreateKey(const ERegistryHive Hive, const FString& SubKey) +{ + FRegistryKey Key(Hive, SubKey); + return Key.Create(); +} + +bool8 FRegistry::DeleteKey(const ERegistryHive Hive, const FString& SubKey) +{ + FRegistryKey Key(Hive, SubKey); + return Key.Delete(); +} + +bool8 FRegistry::DeleteKeyTree(const ERegistryHive Hive, const FString& SubKey) +{ + const LONG Result = RegDeleteTree(GetHiveHandle(Hive), SubKey.c_str()); + return Result == ERROR_SUCCESS; +} + +FString FRegistry::ReadString(const ERegistryHive Hive, const FString& SubKey, const FString& ValueName, const FString& DefaultValue) +{ + FRegistryKey Key(Hive, SubKey); + if (Key.Open(true)) + { + return Key.ReadString(ValueName, DefaultValue); + } + return DefaultValue; +} + +uint32 FRegistry::ReadDWord(const ERegistryHive Hive, const FString& SubKey, const FString& ValueName, const uint32 DefaultValue) +{ + FRegistryKey Key(Hive, SubKey); + if (Key.Open(true)) + { + return Key.ReadDWord(ValueName, DefaultValue); + } + return DefaultValue; +} + +uint64 FRegistry::ReadQWord(const ERegistryHive Hive, const FString& SubKey, const FString& ValueName, const uint64 DefaultValue) +{ + FRegistryKey Key(Hive, SubKey); + if (Key.Open(true)) + { + return Key.ReadQWord(ValueName, DefaultValue); + } + return DefaultValue; +} + +bool8 FRegistry::WriteString(const ERegistryHive Hive, const FString& SubKey, const FString& ValueName, const FString& Value) +{ + FRegistryKey Key(Hive, SubKey); + if (Key.Create() || Key.Open(false)) + { + return Key.WriteString(ValueName, Value); + } + return false; +} + +bool8 FRegistry::WriteDWord(const ERegistryHive Hive, const FString& SubKey, const FString& ValueName, const uint32 Value) +{ + FRegistryKey Key(Hive, SubKey); + if (Key.Create() || Key.Open(false)) + { + return Key.WriteDWord(ValueName, Value); + } + return false; +} + +bool8 FRegistry::WriteQWord(const ERegistryHive Hive, const FString& SubKey, const FString& ValueName, const uint64 Value) +{ + FRegistryKey Key(Hive, SubKey); + if (Key.Create() || Key.Open(false)) + { + return Key.WriteQWord(ValueName, Value); + } + return false; +} diff --git a/Engine/Source/Runtime/Platform/Private/Platform/Window/Behaviours/WindowCloseBehaviour.cpp b/Engine/Source/Runtime/Platform/Private/Platform/Window/Behaviours/WindowCloseBehaviour.cpp new file mode 100644 index 0000000..aa60bb2 --- /dev/null +++ b/Engine/Source/Runtime/Platform/Private/Platform/Window/Behaviours/WindowCloseBehaviour.cpp @@ -0,0 +1,52 @@ +// RavenStorm Copyright @ 2025-2025 + +#include "Platform/Window/Behaviours/WindowCloseBehaviour.hpp" + +#include "Platform/Window/Window.hpp" + +#include + +bool8 PromptUserForClose(const FWindow* Window) +{ + const int32 Result = MessageBox(Window->GetHandle(), TEXT("Are you sure you want to close this window?"), TEXT("Corvus"), MB_YESNO | MB_ICONQUESTION); + return Result == IDYES; +} + +FWindowCloseBehaviour::FWindowCloseBehaviour(const ECloseAction InCloseAction) + : CloseAction(InCloseAction) +{ +} + +bool8 FWindowCloseBehaviour::OnWindowEvent(const EWindowEvent Event, FWindow* Window, FWindowState& WindowState) +{ + switch (Event) + { + case EWindowEvent::Close: + return HandleCloseEvent(Window, WindowState); + default: + break; + } + return false; +} + +bool8 FWindowCloseBehaviour::HandleCloseEvent(const FWindow* Window, FWindowState& WindowState) const +{ + switch (CloseAction) + { + case ECloseAction::Allow: + WindowState.bIsClosed = true; + return false; + case ECloseAction::Prevent: + WindowState.bIsClosed = false; + return true; + case ECloseAction::PromptUser: + if (PromptUserForClose(Window)) + { + WindowState.bIsClosed = true; + return false; + } + WindowState.bIsClosed = false; + return true; + } + return false; +} diff --git a/Engine/Source/Runtime/Platform/Private/Platform/Window/Behaviours/WindowCoreStateBehaviour.cpp b/Engine/Source/Runtime/Platform/Private/Platform/Window/Behaviours/WindowCoreStateBehaviour.cpp new file mode 100644 index 0000000..deddba8 --- /dev/null +++ b/Engine/Source/Runtime/Platform/Private/Platform/Window/Behaviours/WindowCoreStateBehaviour.cpp @@ -0,0 +1,97 @@ +// RavenStorm Copyright @ 2025-2025 + +#include "Platform/Window/Behaviours/WindowCoreStateBehaviour.hpp" + +#include "Platform/Window/Window.hpp" + +#include + + +bool8 FWindowCoreStateBehaviour::OnWindowEvent(const EWindowEvent Event, FWindow* Window, FWindowState& WindowState) +{ + switch (Event) + { + case EWindowEvent::Destroy: + { + DestroyWindow(Window->GetHandle()); + Window->WindowHandle = nullptr; + return false; + } + case EWindowEvent::Resize: + { + if (!bIsInModalLoop) + { + return true; + } + const HWND WindowHandle = Window->GetHandle(); + + RECT ClientRect = {}; + GetClientRect(WindowHandle, &ClientRect); + WindowState.ClientWidth = ClientRect.right - ClientRect.left; + WindowState.ClientHeight = ClientRect.bottom - ClientRect.top; + + RECT WindowRect = {}; + GetWindowRect(WindowHandle, &WindowRect); + WindowState.WindowWidth = WindowRect.right - WindowRect.left; + WindowState.WindowHeight = WindowRect.bottom - WindowRect.top; + return false; + } + case EWindowEvent::Move: + { + if (!bIsInModalLoop) + { + return true; + } + const HWND WindowHandle = Window->GetHandle(); + RECT WindowRect = {}; + GetWindowRect(WindowHandle, &WindowRect); + + WindowState.PositionX = WindowRect.left; + WindowState.PositionY = WindowRect.top; + return false; + } + case EWindowEvent::EnterSizeMove: + { + bIsInModalLoop = true; + ModalState.Width = WindowState.WindowWidth; + ModalState.Height = WindowState.WindowHeight; + ModalState.PositionX = WindowState.PositionX; + ModalState.PositionY = WindowState.PositionY; + return false; + } + case EWindowEvent::ExitSizeMove: + { + bIsInModalLoop = false; + + const HWND WindowHandle = Window->GetHandle(); + RECT WindowRect = {}; + GetWindowRect(WindowHandle, &WindowRect); + + FModalState AfterModalState; + AfterModalState.Width = WindowRect.right - WindowRect.left; + AfterModalState.Height = WindowRect.bottom - WindowRect.top; + AfterModalState.PositionX = WindowRect.left; + AfterModalState.PositionY = WindowRect.top; + + if (AfterModalState.Width != ModalState.Width || AfterModalState.Height != ModalState.Height) + { + WindowState.WindowWidth = AfterModalState.Width; + WindowState.WindowHeight = AfterModalState.Height; + + RECT ClientRect = {}; + GetClientRect(WindowHandle, &ClientRect); + WindowState.ClientWidth = ClientRect.right - ClientRect.left; + WindowState.ClientHeight = ClientRect.bottom - ClientRect.top; + } + if (AfterModalState.PositionX != ModalState.PositionX || AfterModalState.PositionY != ModalState.PositionY) + { + WindowState.PositionX = AfterModalState.PositionX; + WindowState.PositionY = AfterModalState.PositionY; + } + return false; + } + default: + break; + } + return false; +} diff --git a/Engine/Source/Runtime/Platform/Private/Platform/Window/Monitor.cpp b/Engine/Source/Runtime/Platform/Private/Platform/Window/Monitor.cpp new file mode 100644 index 0000000..d2d513d --- /dev/null +++ b/Engine/Source/Runtime/Platform/Private/Platform/Window/Monitor.cpp @@ -0,0 +1,138 @@ +// RavenStorm Copyright @ 2025-2025 + +#include "Platform/Window/Monitor.hpp" + +#include + +#include "Core/Containers/Ranges.hpp" +#include "Core/Logging/LogManager.hpp" + +int32 FMonitorRect::GetWidth() const noexcept +{ + return Right - Left; +} + +int32 FMonitorRect::GetHeight() const noexcept +{ + return Bottom - Top; +} + +FMonitorRect FMonitorRect::FromNativeRect(const Windows::RECT Rect) +{ + FMonitorRect Result; + Result.Left = Rect.left; + Result.Top = Rect.top; + Result.Right = Rect.right; + Result.Bottom = Rect.bottom; + return Result; +} + +float64 FMonitorDisplayMode::GetAspectRatio() const +{ + return static_cast(Width) / static_cast(Height); +} + +uint64 FMonitorDisplayMode::GetPixelCount() const +{ + return static_cast(Width) * static_cast(Height); +} + +FMonitor::FMonitor(const Windows::HMONITOR InMonitorHandle) + : MonitorHandle(InMonitorHandle) +{ + QueryMonitorInfo(); + QueryDisplayModes(); +} + +FMonitor::~FMonitor() = default; + +FMonitorDisplayMode FMonitor::GetActiveDisplayMode() const +{ + DEVMODE DevMode = {}; + DevMode.dmSize = sizeof(DEVMODE); + + FMonitorDisplayMode DisplayMode = {}; + while (EnumDisplaySettings(MonitorInfo.DeviceName.c_str(), ENUM_CURRENT_SETTINGS, &DevMode)) + { + DisplayMode.Width = DevMode.dmPelsWidth; + DisplayMode.Height = DevMode.dmPelsHeight; + DisplayMode.RefreshRate = DevMode.dmDisplayFrequency; + DisplayMode.BitsPerPixel = DevMode.dmBitsPerPel; + break; + } + return DisplayMode; +} + +bool8 FMonitor::SetActiveDisplayMode(FMonitorDisplayMode Mode) const +{ + DEVMODE DevMode = {}; + DevMode.dmSize = sizeof(DEVMODE); + DevMode.dmPelsWidth = Mode.Width; + DevMode.dmPelsHeight = Mode.Height; + DevMode.dmDisplayFrequency = Mode.RefreshRate; + DevMode.dmBitsPerPel = Mode.BitsPerPixel; + DevMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_BITSPERPEL; + + LONG Result = ChangeDisplaySettingsEx(MonitorInfo.DeviceName.c_str(), &DevMode, nullptr, CDS_UPDATEREGISTRY, nullptr); + if (Result != DISP_CHANGE_SUCCESSFUL) + { + CVLOG(LogTemp, Error, TEXT("Failed to set display mode: {}x{} @{}Hz (Error: {})"), Mode.Width, Mode.Height, Mode.RefreshRate, Result); + return false; + } + return true; +} + +void FMonitor::QueryMonitorInfo() +{ + MONITORINFOEX NativeMonitorInfo = {}; + NativeMonitorInfo.cbSize = sizeof(MONITORINFOEX); + + if (GetMonitorInfo(MonitorHandle, &NativeMonitorInfo)) + { + MonitorInfo.DeviceName = NativeMonitorInfo.szDevice; + MonitorInfo.MonitorRect = FMonitorRect::FromNativeRect(NativeMonitorInfo.rcMonitor); + MonitorInfo.WorkAreaRect = FMonitorRect::FromNativeRect(NativeMonitorInfo.rcWork); + MonitorInfo.bIsPrimary = (NativeMonitorInfo.dwFlags & MONITORINFOF_PRIMARY) != 0; + + DISPLAY_DEVICE DisplayDevice = {}; + DisplayDevice.cb = sizeof(DISPLAY_DEVICE); + if (EnumDisplayDevices(NativeMonitorInfo.szDevice, 0, &DisplayDevice, 0)) + { + MonitorInfo.FriendlyName = DisplayDevice.DeviceString; + MonitorInfo.AdapterName = DisplayDevice.DeviceName; + MonitorInfo.DeviceId = DisplayDevice.DeviceID; + MonitorInfo.DeviceKey = DisplayDevice.DeviceKey; + MonitorInfo.StateFlags = DisplayDevice.StateFlags; + } + } +} + +void FMonitor::QueryDisplayModes() +{ + DEVMODE DevMode = {}; + DevMode.dmSize = sizeof(DEVMODE); + + DWORD ModeIndex = 0; + while (EnumDisplaySettings(MonitorInfo.DeviceName.c_str(), ModeIndex++, &DevMode)) + { + FMonitorDisplayMode DisplayMode = {}; + DisplayMode.Width = DevMode.dmPelsWidth; + DisplayMode.Height = DevMode.dmPelsHeight; + DisplayMode.RefreshRate = DevMode.dmDisplayFrequency; + DisplayMode.BitsPerPixel = DevMode.dmBitsPerPel; + if (!Ranges::Contains(DisplayModes, DisplayMode)) + { + DisplayModes.PushBack(DisplayMode); + } + } +} + +bool operator==(const FMonitorDisplayMode& Lhs, const FMonitorDisplayMode& Rhs) +{ + return Lhs.Width == Rhs.Width && Lhs.Height == Rhs.Height && Lhs.RefreshRate == Rhs.RefreshRate && Lhs.BitsPerPixel == Rhs.BitsPerPixel; +} + +bool operator!=(const FMonitorDisplayMode& Lhs, const FMonitorDisplayMode& Rhs) +{ + return !(Lhs == Rhs); +} diff --git a/Engine/Source/Runtime/Platform/Private/Platform/Window/Window.cpp b/Engine/Source/Runtime/Platform/Private/Platform/Window/Window.cpp new file mode 100644 index 0000000..bfe3be8 --- /dev/null +++ b/Engine/Source/Runtime/Platform/Private/Platform/Window/Window.cpp @@ -0,0 +1,247 @@ +// RavenStorm Copyright @ 2025-2025 + +#include "Platform/Window/Window.hpp" + +#include "Core/Assertion/Assertion.hpp" + +#include + +#include "Core/Containers/Ranges.hpp" + +#include "Platform/Window/Behaviours/WindowCloseBehaviour.hpp" +#include "Platform/Window/Behaviours/WindowCoreStateBehaviour.hpp" + +LRESULT MainWindowProcess(const HWND ProcessWindowHandle, const uint32 Message, const WPARAM WParam, const LPARAM LParam) +{ + FWindow* Window; + if (Message == WM_NCCREATE) + { + const CREATESTRUCT* CreateStruct = reinterpret_cast(LParam); + Window = static_cast(CreateStruct->lpCreateParams); + SetWindowLongPtr(ProcessWindowHandle, GWLP_USERDATA, reinterpret_cast(Window)); + } + else + { + Window = reinterpret_cast(GetWindowLongPtr(ProcessWindowHandle, GWLP_USERDATA)); + } + + if (Window) + { + return Window->WindowProcess(ProcessWindowHandle, Message, WParam, LParam); + } + return DefWindowProc(ProcessWindowHandle, Message, WParam, LParam); +} + +FWindow::FWindow(const FWindowSettings& InWindowSettings) +{ + WNDCLASSEX WindowClass = {}; + WindowClass.cbSize = sizeof(WNDCLASSEX); + WindowClass.style = CS_HREDRAW | CS_VREDRAW; + WindowClass.lpfnWndProc = MainWindowProcess; + WindowClass.hInstance = GetModuleHandle(nullptr); + WindowClass.hCursor = LoadCursor(nullptr, IDC_ARROW); + WindowClass.lpszClassName = TEXT("CorvusWindowClass"); + RegisterClassEx(&WindowClass); + + State.WindowWidth = InWindowSettings.Width; + State.WindowHeight = InWindowSettings.Height; + State.ClientWidth = InWindowSettings.Width; + State.ClientHeight = InWindowSettings.Height; + State.PositionX = InWindowSettings.StartPositionX; + State.PositionY = InWindowSettings.StartPositionY; + State.Title = InWindowSettings.Title; + State.WindowMode = InWindowSettings.WindowMode; + State.ParentWindow = InWindowSettings.ParentWindow; + + AddBehaviour(); + + FWindowStyle WindowStyle = GetWindowStyle(InWindowSettings.WindowMode); + if (WindowStyle.Style & WS_BORDER) + { + RECT WindowRect = {0, 0, static_cast(State.ClientWidth), static_cast(State.ClientHeight)}; + AdjustWindowRectEx(&WindowRect, WindowStyle.Style, false, WindowStyle.ExStyle); + State.WindowWidth = WindowRect.right - WindowRect.left; + State.WindowHeight = WindowRect.bottom - WindowRect.top; + } + + HWND ParentWindowHandle = nullptr; + if (InWindowSettings.ParentWindow) + { + ParentWindowHandle = InWindowSettings.ParentWindow->GetHandle(); + WindowStyle.Style |= WS_CHILD; + } + WindowHandle = CreateWindowEx(WindowStyle.ExStyle, + WindowClass.lpszClassName, + State.Title.c_str(), + WindowStyle.Style, + State.PositionX, + State.PositionY, + static_cast(State.WindowWidth), + static_cast(State.WindowHeight), + ParentWindowHandle, + nullptr, + GetModuleHandle(nullptr), + this); + if (WindowHandle == nullptr) + { + const HRESULT Result = static_cast(GetLastError()); + ASSERT_RESULT(Result); + } + + if (InWindowSettings.bShowOnCreation) + { + Show(); + } +} + +FWindow::~FWindow() +{ + if (WindowHandle) + { + Close(); + DestroyWindow(WindowHandle); + WindowHandle = nullptr; + } +} + +bool8 FWindow::IsValid() const noexcept +{ + return WindowHandle != nullptr; +} + +void FWindow::Show() const +{ + ShowWindowAsync(WindowHandle, SW_SHOW); +} + +void FWindow::Hide() const +{ + ShowWindowAsync(WindowHandle, SW_HIDE); +} + +void FWindow::Close() const +{ + CloseWindow(WindowHandle); +} + +void FWindow::Maximize() const +{ + ShowWindowAsync(WindowHandle, SW_MAXIMIZE); +} + +void FWindow::Minimize() const +{ + ShowWindowAsync(WindowHandle, SW_MINIMIZE); +} + +void FWindow::Restore() const +{ + ShowWindowAsync(WindowHandle, SW_RESTORE); +} + +void FWindow::Focus() const +{ + SetForegroundWindow(WindowHandle); + SetFocus(WindowHandle); +} + +void FWindow::SetWindowSize(const uint32 NewWidth, const uint32 NewHeight) const +{ + SetWindowPos(WindowHandle, nullptr, 0, 0, static_cast(NewWidth), static_cast(NewHeight), SWP_NOMOVE | SWP_NOZORDER); +} + +void FWindow::SetClientSize(const uint32 NewWidth, const uint32 NewHeight) const +{ + RECT WindowRect = {0, 0, static_cast(NewWidth), static_cast(NewHeight)}; + if (AdjustWindowRect(&WindowRect, GetWindowLong(WindowHandle, GWL_STYLE), false)) + { + const int32 AdjustedWidth = WindowRect.right - WindowRect.left; + const int32 AdjustedHeight = WindowRect.bottom - WindowRect.top; + SetWindowPos(WindowHandle, nullptr, 0, 0, AdjustedWidth, AdjustedHeight, SWP_NOMOVE); + } +} + +void FWindow::SetPosition(const int32 NewPositionX, const int32 NewPositionY) const +{ + SetWindowPos(WindowHandle, nullptr, NewPositionX, NewPositionY, 0, 0, SWP_NOSIZE | SWP_NOZORDER); +} + +void FWindow::SetTitle(const FString& NewTitle) +{ + State.Title = NewTitle; + SetWindowText(WindowHandle, State.Title.c_str()); +} + +void FWindow::AppendTitle(const FString& NewTitle) +{ + State.Title += NewTitle; + SetWindowText(WindowHandle, State.Title.c_str()); +} + +void FWindow::SetParentWindow(const TSharedPtr& NewParentWindow) +{ + State.ParentWindow = NewParentWindow; + if (State.ParentWindow != nullptr) + { + SetParent(WindowHandle, State.ParentWindow->GetHandle()); + } + else + { + SetParent(WindowHandle, nullptr); + } +} + +void FWindow::AddBehaviour(TSharedPtr&& Behaviour) +{ + Behaviours.EmplaceBack(std::move(Behaviour)); +} + +Windows::LRESULT FWindow::WindowProcess(const Windows::HWND ProcessWindowHandle, const uint32 Message, const Windows::WPARAM WParam, const Windows::LPARAM LParam) +{ + switch (Message) + { + case WM_CLOSE: + ExecuteBehaviours(EWindowEvent::Close); + return 0; + case WM_DESTROY: + ExecuteBehaviours(EWindowEvent::Destroy); + return 0; + case WM_SIZE: + ExecuteBehaviours(EWindowEvent::Resize); + return 0; + case WM_MOVE: + ExecuteBehaviours(EWindowEvent::Move); + return 0; + case WM_ENTERSIZEMOVE: + ExecuteBehaviours(EWindowEvent::EnterSizeMove); + return 0; + case WM_EXITSIZEMOVE: + ExecuteBehaviours(EWindowEvent::ExitSizeMove); + return 0; + default: + break; + } + return DefWindowProc(ProcessWindowHandle, Message, WParam, LParam); +} + +bool8 FWindow::ExecuteBehaviours(const EWindowEvent Event) +{ + return Ranges::AnyOf(Behaviours, [&](const TSharedPtr& Behaviour) + { + return Behaviour->OnWindowEvent(Event, this, State); + }); +} + +FWindow::FWindowStyle FWindow::GetWindowStyle(const EWindowMode& WindowMode) +{ + switch (WindowMode) + { + case EWindowMode::Windowed: + return {.Style = WS_OVERLAPPEDWINDOW, .ExStyle = WS_EX_OVERLAPPEDWINDOW}; + case EWindowMode::BorderlessFullscreen: + return {.Style = 0, .ExStyle = WS_EX_OVERLAPPEDWINDOW}; + case EWindowMode::Fullscreen: + return {.Style = 0, .ExStyle = WS_EX_OVERLAPPEDWINDOW}; + } + return {.Style = 0, .ExStyle = 0}; +} diff --git a/Engine/Source/Runtime/Platform/Public/Platform/CPUDetection.hpp b/Engine/Source/Runtime/Platform/Public/Platform/CPUDetection.hpp new file mode 100644 index 0000000..1591f0a --- /dev/null +++ b/Engine/Source/Runtime/Platform/Public/Platform/CPUDetection.hpp @@ -0,0 +1,41 @@ +// RavenStorm Copyright @ 2025-2025 + +#pragma once + +#include "Core/Containers/String.hpp" + +struct PLATFORM_API FCPUInfo +{ + FAnsiString VendorString; + FAnsiString BrandString; + int32 Family; + int32 Model; + int32 Stepping; + int32 PhysicalCores; + int32 LogicalCores; + int32 CacheLine; + int32 L1CacheSize; + int32 L2CacheSize; + int32 L3CacheSize; + bool8 IsIntel; + bool8 IsAMD; + bool8 IsHyperThreaded; +}; + +class PLATFORM_API FCPUDetection +{ +private: + static void Initialize(); + + [[nodiscard]] static FCPUInfo GetCPUInfo() noexcept; + +private: + static void DetectVendor(); + static void DetectBrand(); + static void DetectCPUTopology(); + +private: + static FCPUInfo CPUInfo; + + friend class PLATFORM_API FPlatform; +}; diff --git a/Engine/Source/Runtime/Platform/Public/Platform/Platform.hpp b/Engine/Source/Runtime/Platform/Public/Platform/Platform.hpp new file mode 100644 index 0000000..086e49a --- /dev/null +++ b/Engine/Source/Runtime/Platform/Public/Platform/Platform.hpp @@ -0,0 +1,59 @@ +// RavenStorm Copyright @ 2025-2025 + +#pragma once + +#include "CPUDetection.hpp" + +#include "Window/Window.hpp" + +enum class PLATFORM_API EWindowsVersion : uint8 +{ + Unknown = 0, + Windows10, + Windows11, +}; + +struct PLATFORM_API FSystemInfo +{ + FString OSName; + FString OSVersion; + FString OSBuildNumber; + EWindowsVersion WindowsVersion; + FString ComputerName; +}; + +struct PLATFORM_API FDynamicModuleHandle +{ + Windows::HMODULE Module = nullptr; + +public: + [[nodiscard]] bool8 IsValid() const noexcept; +}; + +class PLATFORM_API FPlatform +{ +public: + static void Initialize(); + static void Shutdown(); + + [[nodiscard]] static bool8 HasEnvVariable(const FString& VariableName) noexcept; + [[nodiscard]] static FString GetEnvVariable(const FString& VariableName) noexcept; + [[nodiscard]] static FString GetEnvVariable(const FString& VariableName, const FString& DefaultValue) noexcept; + static bool8 SetEnvVariable(const FString& VariableName, const FString& Value) noexcept; + + [[nodiscard]] static FDynamicModuleHandle LoadDynamicModule(const FString& Name) noexcept; + static void UnloadDynamicModule(FDynamicModuleHandle&& Module) noexcept; + + [[nodiscard]] static FCPUInfo GetCPUInfo() noexcept; + [[nodiscard]] static FSystemInfo GetSystemInfo() noexcept; + +private: + static void DetectOperatingSystem(); + static void DetectComputerInfo(); + + static EWindowsVersion DetermineWindowsVersion(uint32 MajorVersion, uint32 BuildNumber); + static FString GetWindowsVersionString(uint32 MajorVersion, uint32 BuildNumber); + +private: + static FSystemInfo SystemInfo; +}; diff --git a/Engine/Source/Runtime/Platform/Public/Platform/Registry.hpp b/Engine/Source/Runtime/Platform/Public/Platform/Registry.hpp new file mode 100644 index 0000000..e66ada1 --- /dev/null +++ b/Engine/Source/Runtime/Platform/Public/Platform/Registry.hpp @@ -0,0 +1,118 @@ +// RavenStorm Copyright @ 2025-2025 + +#pragma once + +#include "Core/CoreDefinitions.hpp" +#include "Core/Containers/Array.hpp" +#include "Core/Containers/String.hpp" +#include "Core/Utility/WindowsDefinitions.hpp" + +enum class PLATFORM_API ERegistryHive : uint32 +{ + Unknown = 0, + ClassesRoot = 0x80000000, + CurrentUser = 0x80000001, + CurrentConfig = 0x80000005, + LocalMachine = 0x80000002, + Users = 0x80000003, +}; + +enum class PLATFORM_API ERegistryValueType : uint8 +{ + None = 0ul, + String = 1ul, + ExpandString = 2ul, + Binary = 3ul, + DWord = 4ul, + QWord = 11ul, + MultiString = 7ul, +}; + +struct PLATFORM_API FRegistryValue +{ +public: + FString Name; + ERegistryValueType Type = ERegistryValueType::None; + TArray Data; + +public: + [[nodiscard]] FString AsString() const; + [[nodiscard]] uint32 AsDWord() const; + [[nodiscard]] uint64 AsQWord() const; + [[nodiscard]] TArray AsMultiString() const; + [[nodiscard]] TArray AsBinary() const; +}; + +struct PLATFORM_API FRegistryKey +{ +public: + FRegistryKey() = default; + FRegistryKey(ERegistryHive InHive, FString InSubKey); + ~FRegistryKey(); + + NON_COPYABLE(FRegistryKey) + DEFAULT_MOVEABLE(FRegistryKey) + +public: + [[nodiscard]] bool8 IsValid() const noexcept; + [[nodiscard]] bool8 Exists() const noexcept; + + bool8 Create(); + bool8 Open(bool8 ReadOnly = false); + void Close(); + bool8 Delete(); + + [[nodiscard]] bool8 HasValue(const FString& ValueName) const; + [[nodiscard]] FRegistryValue ReadValue(const FString& ValueName) const; + bool8 WriteValue(const FString& ValueName, const FRegistryValue& Value) const; + bool8 DeleteValue(const FString& ValueName) const; + + bool8 WriteString(const FString& ValueName, const FString& Value) const; + bool8 WriteDWord(const FString& ValueName, uint32 Value) const; + bool8 WriteQWord(const FString& ValueName, uint64 Value) const; + bool8 WriteBinary(const FString& ValueName, const TArray& Value) const; + bool8 WriteMultiString(const FString& ValueName, const TArray& Values) const; + + [[nodiscard]] FString ReadString(const FString& ValueName, const FString& DefaultValue = {}) const; + [[nodiscard]] uint32 ReadDWord(const FString& ValueName, uint32 DefaultValue = 0) const; + [[nodiscard]] uint64 ReadQWord(const FString& ValueName, uint64 DefaultValue = 0) const; + [[nodiscard]] TArray ReadBinary(const FString& ValueName) const; + [[nodiscard]] TArray ReadMultiString(const FString& ValueName) const; + + [[nodiscard]] TArray GetSubKeyNames() const; + [[nodiscard]] TArray GetValueNames() const; + [[nodiscard]] TArray GetValues() const; + + [[nodiscard]] FString GetFullPath() const; + +public: + [[nodiscard]] ERegistryHive GetHive() const { return Hive; } + [[nodiscard]] FString GetSubKey() const { return SubKey; } + +private: + [[nodiscard]] Windows::HKEY GetHiveHandle() const; + +private: + ERegistryHive Hive = ERegistryHive::Unknown; + FString SubKey; + Windows::HKEY KeyHandle = nullptr; + bool8 IsOpen = false; +}; + +class PLATFORM_API FRegistry +{ +public: + [[nodiscard]] static bool8 KeyExists(ERegistryHive Hive, const FString& SubKey); + + static bool8 CreateKey(ERegistryHive Hive, const FString& SubKey); + static bool8 DeleteKey(ERegistryHive Hive, const FString& SubKey); + static bool8 DeleteKeyTree(ERegistryHive Hive, const FString& SubKey); + + [[nodiscard]] static FString ReadString(ERegistryHive Hive, const FString& SubKey, const FString& ValueName, const FString& DefaultValue = {}); + [[nodiscard]] static uint32 ReadDWord(ERegistryHive Hive, const FString& SubKey, const FString& ValueName, uint32 DefaultValue = 0); + [[nodiscard]] static uint64 ReadQWord(ERegistryHive Hive, const FString& SubKey, const FString& ValueName, uint64 DefaultValue = 0); + + static bool8 WriteString(ERegistryHive Hive, const FString& SubKey, const FString& ValueName, const FString& Value); + static bool8 WriteDWord(ERegistryHive Hive, const FString& SubKey, const FString& ValueName, uint32 Value); + static bool8 WriteQWord(ERegistryHive Hive, const FString& SubKey, const FString& ValueName, uint64 Value); +}; diff --git a/Engine/Source/Runtime/Platform/Public/Platform/Window/Behaviours/WindowCloseBehaviour.hpp b/Engine/Source/Runtime/Platform/Public/Platform/Window/Behaviours/WindowCloseBehaviour.hpp new file mode 100644 index 0000000..38dce79 --- /dev/null +++ b/Engine/Source/Runtime/Platform/Public/Platform/Window/Behaviours/WindowCloseBehaviour.hpp @@ -0,0 +1,31 @@ +// RavenStorm Copyright @ 2025-2025 + +#pragma once + +#include "Platform/Window/WindowBehaviour.hpp" + +class PLATFORM_API FWindowCloseBehaviour : public IWindowBehaviour +{ +public: + enum class ECloseAction : uint8 + { + Allow, + Prevent, + PromptUser, + }; + +public: + FWindowCloseBehaviour(ECloseAction InCloseAction = ECloseAction::Allow); + ~FWindowCloseBehaviour() override = default; + + NON_COPY_MOVEABLE(FWindowCloseBehaviour) + +public: + [[nodiscard]] bool8 OnWindowEvent(EWindowEvent Event, FWindow* Window, FWindowState& WindowState) override; + +private: + bool8 HandleCloseEvent(const FWindow* Window, FWindowState& WindowState) const; + +private: + ECloseAction CloseAction = ECloseAction::Allow; +}; diff --git a/Engine/Source/Runtime/Platform/Public/Platform/Window/Behaviours/WindowCoreStateBehaviour.hpp b/Engine/Source/Runtime/Platform/Public/Platform/Window/Behaviours/WindowCoreStateBehaviour.hpp new file mode 100644 index 0000000..0c8b709 --- /dev/null +++ b/Engine/Source/Runtime/Platform/Public/Platform/Window/Behaviours/WindowCoreStateBehaviour.hpp @@ -0,0 +1,30 @@ +// RavenStorm Copyright @ 2025-2025 + +#pragma once + +#include + +class PLATFORM_API FWindowCoreStateBehaviour : public IWindowBehaviour +{ +private: + struct FModalState + { + uint32 Width = 0; + uint32 Height = 0; + int32 PositionX = 0; + int32 PositionY = 0; + }; + +public: + FWindowCoreStateBehaviour() = default; + ~FWindowCoreStateBehaviour() override = default; + + NON_COPY_MOVEABLE(FWindowCoreStateBehaviour) + +public: + [[nodiscard]] bool8 OnWindowEvent(EWindowEvent Event, FWindow* Window, FWindowState& WindowState) override; + +private: + bool8 bIsInModalLoop = false; + FModalState ModalState = {}; +}; diff --git a/Engine/Source/Runtime/Platform/Public/Platform/Window/Monitor.hpp b/Engine/Source/Runtime/Platform/Public/Platform/Window/Monitor.hpp new file mode 100644 index 0000000..8f533ac --- /dev/null +++ b/Engine/Source/Runtime/Platform/Public/Platform/Window/Monitor.hpp @@ -0,0 +1,83 @@ +// RavenStorm Copyright @ 2025-2025 + +#pragma once + +#include "Core/CoreDefinitions.hpp" +#include "Core/Containers/Array.hpp" +#include "Core/Containers/String.hpp" +#include "Core/Utility/WindowsDefinitions.hpp" + +struct PLATFORM_API FMonitorRect +{ + int32 Left = 0; + int32 Right = 0; + int32 Top = 0; + int32 Bottom = 0; + +public: + [[nodiscard]] int32 GetWidth() const noexcept; + [[nodiscard]] int32 GetHeight() const noexcept; + +public: + [[nodiscard]] static FMonitorRect FromNativeRect(Windows::RECT Rect); +}; + +struct PLATFORM_API FMonitorInfo +{ + FString FriendlyName; + FString DeviceName; + FMonitorRect MonitorRect = {}; + FMonitorRect WorkAreaRect = {}; + bool8 bIsPrimary = false; + + FString AdapterName; + FString DeviceId; + FString DeviceKey; + Windows::DWORD StateFlags; +}; + +struct PLATFORM_API FMonitorDisplayMode +{ + uint32 Width = 0; + uint32 Height = 0; + uint32 RefreshRate = 0; + uint32 BitsPerPixel = 0; + +public: + [[nodiscard]] float64 GetAspectRatio() const; + [[nodiscard]] uint64 GetPixelCount() const; + +public: + friend bool operator==(const FMonitorDisplayMode& Lhs, const FMonitorDisplayMode& Rhs); + friend bool operator!=(const FMonitorDisplayMode& Lhs, const FMonitorDisplayMode& Rhs); +}; + +class PLATFORM_API FMonitor +{ +public: + FMonitor(Windows::HMONITOR InMonitorHandle); + ~FMonitor(); + + NON_COPY_MOVEABLE(FMonitor) + +public: + [[nodiscard]] FMonitorDisplayMode GetActiveDisplayMode() const; + + bool8 SetActiveDisplayMode(FMonitorDisplayMode Mode) const; + +public: + [[nodiscard]] Windows::HMONITOR GetNativeHandle() const { return MonitorHandle; } + [[nodiscard]] FMonitorInfo GetInfo() const { return MonitorInfo; } + [[nodiscard]] TArray GetDisplayModes() const { return DisplayModes; } + +private: + void QueryMonitorInfo(); + void QueryDisplayModes(); + +private: + Windows::HMONITOR MonitorHandle = nullptr; + + FMonitorInfo MonitorInfo = {}; + + TArray DisplayModes; +}; diff --git a/Engine/Source/Runtime/Platform/Public/Platform/Window/Window.hpp b/Engine/Source/Runtime/Platform/Public/Platform/Window/Window.hpp new file mode 100644 index 0000000..3d4a607 --- /dev/null +++ b/Engine/Source/Runtime/Platform/Public/Platform/Window/Window.hpp @@ -0,0 +1,123 @@ +// RavenStorm Copyright @ 2025-2025 + +#pragma once + +#include "Core/CoreDefinitions.hpp" +#include "Core/Containers/Array.hpp" +#include "Core/Containers/String.hpp" +#include "Core/Memory/SmartPointers.hpp" +#include "Core/Utility/WindowsDefinitions.hpp" + +#include "Platform/Window/WindowBehaviour.hpp" + +class FWindow; + +enum class PLATFORM_API EWindowMode : uint8 +{ + Windowed, + BorderlessFullscreen, + Fullscreen, +}; + +struct PLATFORM_API FWindowSettings +{ + uint32 Width = 1280; + uint32 Height = 720; + int32 StartPositionX = 100; + int32 StartPositionY = 100; + FString Title = TEXT("Corvus"); + EWindowMode WindowMode = EWindowMode::Windowed; + TSharedPtr ParentWindow = nullptr; + bool8 bShowOnCreation = true; +}; + +struct PLATFORM_API FWindowState +{ + uint32 WindowWidth = 0; + uint32 WindowHeight = 0; + uint32 ClientWidth = 0; + uint32 ClientHeight = 0; + int32 PositionX = 0; + int32 PositionY = 0; + FString Title = TEXT("Corvus"); + EWindowMode WindowMode = EWindowMode::Windowed; + bool8 bIsClosed = false; + TSharedPtr ParentWindow = nullptr; +}; + +class PLATFORM_API FWindow +{ +private: + struct FWindowStyle + { + Windows::DWORD Style = 0; + Windows::DWORD ExStyle = 0; + }; + + struct FModalState + { + uint32 Width = 0; + uint32 Height = 0; + int32 PositionX = 0; + int32 PositionY = 0; + }; + +public: + FWindow(const FWindowSettings& InWindowSettings); + ~FWindow(); + + NON_COPY_MOVEABLE(FWindow) + +public: + [[nodiscard]] bool8 IsValid() const noexcept; + + void Show() const; + void Hide() const; + void Close() const; + void Maximize() const; + void Minimize() const; + void Restore() const; + void Focus() const; + + void SetWindowSize(uint32 NewWidth, uint32 NewHeight) const; + void SetClientSize(uint32 NewWidth, uint32 NewHeight) const; + void SetPosition(int32 NewPositionX, int32 NewPositionY) const; + void SetTitle(const FString& NewTitle); + void AppendTitle(const FString& NewTitle); + void SetParentWindow(const TSharedPtr& NewParentWindow); + + void AddBehaviour(TSharedPtr&& Behaviour); + +public: + template requires std::is_base_of_v && std::is_constructible_v + void AddBehaviour(TArguments&&... Arguments) + { + AddBehaviour(std::move(MakeShared(std::forward(Arguments)...))); + } + +public: + [[nodiscard]] Windows::HWND GetHandle() const { return WindowHandle; } + + [[nodiscard]] FWindowState GetState() const { return State; } + +private: + Windows::LRESULT WindowProcess(Windows::HWND ProcessWindowHandle, uint32 Message, Windows::WPARAM WParam, Windows::LPARAM LParam); + + bool8 ExecuteBehaviours(EWindowEvent Event); + +private: + static FWindowStyle GetWindowStyle(const EWindowMode& WindowMode); + +private: + FWindowState State = {}; + + Windows::HWND WindowHandle = nullptr; + + bool8 bIsInModalLoop = false; + FModalState ModalState = {}; + + TArray> Behaviours; + + friend Windows::LRESULT MainWindowProcess(Windows::HWND, uint32, Windows::WPARAM, Windows::LPARAM); + friend class PLATFORM_API FWindowCoreStateBehaviour; +}; diff --git a/Engine/Source/Runtime/Platform/Public/Platform/Window/WindowBehaviour.hpp b/Engine/Source/Runtime/Platform/Public/Platform/Window/WindowBehaviour.hpp new file mode 100644 index 0000000..dac02a9 --- /dev/null +++ b/Engine/Source/Runtime/Platform/Public/Platform/Window/WindowBehaviour.hpp @@ -0,0 +1,31 @@ +// RavenStorm Copyright @ 2025-2025 + +#pragma once + +#include "Core/CoreDefinitions.hpp" + +class FWindow; +struct FWindowState; + +enum class PLATFORM_API EWindowEvent : uint8 +{ + None = 0, + Close, + Destroy, + Resize, + Move, + EnterSizeMove, + ExitSizeMove, +}; + +class PLATFORM_API IWindowBehaviour +{ +public: + IWindowBehaviour() = default; + virtual ~IWindowBehaviour() = default; + + NON_COPY_MOVEABLE(IWindowBehaviour) + +public: + [[nodiscard]] virtual bool8 OnWindowEvent(EWindowEvent Event, FWindow* Window, FWindowState& WindowState) = 0; +}; diff --git a/Engine/Source/Runtime/Platform/Public/PlatformAPI.hpp b/Engine/Source/Runtime/Platform/Public/PlatformAPI.hpp new file mode 100644 index 0000000..bfb291b --- /dev/null +++ b/Engine/Source/Runtime/Platform/Public/PlatformAPI.hpp @@ -0,0 +1,13 @@ +// RavenStorm Copyright @ 2025-2025 + +#pragma once + +#ifdef CORVUS_BUILD_MODULAR +# ifdef CORVUS_BUILD_PLATFORM +# define PLATFORM_API __declspec(dllexport) +# else +# define PLATFORM_API __declspec(dllimport) +# endif +#else +# define PLATFORM_API +#endif diff --git a/Engine/Source/Runtime/Platform/xmake.lua b/Engine/Source/Runtime/Platform/xmake.lua new file mode 100644 index 0000000..dd548d3 --- /dev/null +++ b/Engine/Source/Runtime/Platform/xmake.lua @@ -0,0 +1,11 @@ +local module_name = 'Platform' + +corvus_engine_target(module_name) + add_deps('Core') +corvus_target_end() + +-- Tests +-- corvus_test_target(module_name) +-- add_deps('Platform') +-- corvus_target_end() +