From 698ce4c0ce541944e9115158117c4227a87204cb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 13 Apr 2021 19:03:04 +0200 Subject: [PATCH 01/27] Fix crash on bokeh in dof on d3d12 --- Source/Engine/Renderer/DepthOfFieldPass.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Engine/Renderer/DepthOfFieldPass.cpp b/Source/Engine/Renderer/DepthOfFieldPass.cpp index 4e4ee7ed1..afa48ad13 100644 --- a/Source/Engine/Renderer/DepthOfFieldPass.cpp +++ b/Source/Engine/Renderer/DepthOfFieldPass.cpp @@ -34,6 +34,8 @@ bool DepthOfFieldPass::Init() _platformSupportsDoF = limits.HasCompute; _platformSupportsBokeh = _platformSupportsDoF && limits.HasGeometryShaders && limits.HasDrawIndirect && limits.HasAppendConsumeBuffers; + _platformSupportsBokeh &= GPUDevice::Instance->GetRendererType() != RendererType::DirectX12; // TODO: fix bokeh crash on d3d12 (driver issue probably - started to happen recently) + // Create pipeline states if (_platformSupportsDoF) { From b2729e35c25735176d5abc40062e47e40880b944 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 17 Apr 2021 19:36:38 +0200 Subject: [PATCH 02/27] Fixes for UWP build --- .../Cooker/Platform/UWP/UWPPlatformTools.cpp | 32 ++++++++----------- Source/Engine/Main/UWP/main.cpp | 21 ++---------- .../Engine/Platform/Win32/Win32Platform.cpp | 7 ++-- Source/Platforms/UWP/Binaries/Project.csproj | 3 +- .../Platforms/XboxOne/Binaries/Project.csproj | 3 +- .../Flax.Build/Platforms/UWP/UWPToolchain.cs | 2 +- .../Platforms/Windows/WindowsToolchainBase.cs | 4 ++- 7 files changed, 28 insertions(+), 44 deletions(-) diff --git a/Source/Editor/Cooker/Platform/UWP/UWPPlatformTools.cpp b/Source/Editor/Cooker/Platform/UWP/UWPPlatformTools.cpp index c8f9e45a7..7a6d6ad84 100644 --- a/Source/Editor/Cooker/Platform/UWP/UWPPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/UWP/UWPPlatformTools.cpp @@ -43,7 +43,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data) const auto uwpDataPath = platformDataPath / (isXboxOne ? TEXT("XboxOne") : TEXT("UWP")) / TEXT("Binaries"); const auto gameSettings = GameSettings::Get(); const auto platformSettings = UWPPlatformSettings::Get(); - Array fileTemplate; + StringAnsi fileTemplate; // Copy binaries const auto binPath = data.GetGameBinariesPath(); @@ -149,12 +149,11 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data) if (!FileSystem::FileExists(dstAssemblyInfoPath)) { // Get template - if (File::ReadAllBytes(srcAssemblyInfoPath, fileTemplate)) + if (File::ReadAllText(srcAssemblyInfoPath, fileTemplate)) { data.Error(TEXT("Failed to load AssemblyInfo.cs template.")); return true; } - fileTemplate[fileTemplate.Count() - 1] = 0; // Write data to file auto file = FileWriteStream::Open(dstAssemblyInfoPath); @@ -163,7 +162,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data) { auto now = DateTime::Now(); file->WriteTextFormatted( - (char*)fileTemplate.Get() + fileTemplate.Get() , gameSettings->ProductName.ToStringAnsi() , gameSettings->CompanyName.ToStringAnsi() , now.GetYear() @@ -182,12 +181,11 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data) if (!FileSystem::FileExists(dstAppPath)) { // Get template - if (File::ReadAllBytes(srcAppPath, fileTemplate)) + if (File::ReadAllText(srcAppPath, fileTemplate)) { data.Error(TEXT("Failed to load App.cs template.")); return true; } - fileTemplate[fileTemplate.Count() - 1] = 0; // Write data to file auto file = FileWriteStream::Open(dstAppPath); @@ -195,7 +193,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data) if (file) { file->WriteTextFormatted( - (char*)fileTemplate.Get() + fileTemplate.Get() , defaultNamespace.ToStringAnsi() // {0} Default Namespace ); hasError = file->HasError(); @@ -211,12 +209,11 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data) const auto srcFlaxGeneratedPath = uwpDataPath / TEXT("FlaxGenerated.cs"); { // Get template - if (File::ReadAllBytes(srcFlaxGeneratedPath, fileTemplate)) + if (File::ReadAllText(srcFlaxGeneratedPath, fileTemplate)) { data.Error(TEXT("Failed to load FlaxGenerated.cs template.")); return true; } - fileTemplate[fileTemplate.Count() - 1] = 0; // Prepare StringAnsi autoRotationPreferences; @@ -252,7 +249,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data) if (file) { file->WriteTextFormatted( - (char*)fileTemplate.Get() + fileTemplate.Get() , autoRotationPreferences.Get() , preferredLaunchWindowingMode.Get() ); @@ -272,12 +269,11 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data) if (!FileSystem::FileExists(dstSolutionPath)) { // Get template - if (File::ReadAllBytes(srcSolutionPath, fileTemplate)) + if (File::ReadAllText(srcSolutionPath, fileTemplate)) { data.Error(TEXT("Failed to load Solution.sln template.")); return true; } - fileTemplate[fileTemplate.Count() - 1] = 0; // Write data to file auto file = FileWriteStream::Open(dstSolutionPath); @@ -285,7 +281,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data) if (file) { file->WriteTextFormatted( - (char*)fileTemplate.Get() + fileTemplate.Get() , projectName.ToStringAnsi() // {0} Project Name , mode // {1} Platform Mode , projectGuid.ToStringAnsi() // {2} Project ID @@ -305,12 +301,11 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data) const auto srcProjectPath = uwpDataPath / TEXT("Project.csproj"); { // Get template - if (File::ReadAllBytes(srcProjectPath, fileTemplate)) + if (File::ReadAllText(srcProjectPath, fileTemplate)) { data.Error(TEXT("Failed to load Project.csproj template.")); return true; } - fileTemplate[fileTemplate.Count() - 1] = 0; // Build included files data StringBuilder filesInclude(2048); @@ -334,7 +329,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data) if (file) { file->WriteTextFormatted( - (char*)fileTemplate.Get() + fileTemplate.Get() , projectName.ToStringAnsi() // {0} Project Name , mode // {1} Platform Mode , projectGuid.Get() // {2} Project ID @@ -357,12 +352,11 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data) if (!FileSystem::FileExists(dstManifestPath)) { // Get template - if (File::ReadAllBytes(srcManifestPath, fileTemplate)) + if (File::ReadAllText(srcManifestPath, fileTemplate)) { data.Error(TEXT("Failed to load Package.appxmanifest template.")); return true; } - fileTemplate[fileTemplate.Count() - 1] = 0; // Build included files data StringBuilder filesInclude(2048); @@ -385,7 +379,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data) if (file) { file->WriteTextFormatted( - (char*)fileTemplate.Get() + fileTemplate.Get() , projectName.ToStringAnsi() // {0} Display Name , gameSettings->CompanyName.ToStringAnsi() // {1} Company Name , productId.ToStringAnsi() // {2} Product ID diff --git a/Source/Engine/Main/UWP/main.cpp b/Source/Engine/Main/UWP/main.cpp index 08d9cc422..e615a2b38 100644 --- a/Source/Engine/Main/UWP/main.cpp +++ b/Source/Engine/Main/UWP/main.cpp @@ -65,22 +65,6 @@ void Game::OnActivated(CoreApplicationView^ applicationView, IActivatedEventArgs void Game::OnSuspending(Platform::Object^ sender, SuspendingEventArgs^ args) { - /* - // Save app state asynchronously after requesting a deferral. Holding a deferral - // indicates that the application is busy performing suspending operations. Be - // aware that a deferral may not be held indefinitely. After about five seconds, - // the app will be forced to exit. - SuspendingDeferral^ deferral = args->SuspendingOperation->GetDeferral(); - - create_task([this, deferral]() - { - m_deviceResources->Trim(); - - m_main->Suspend(); - - deferral->Complete(); - }); - */ } void Game::OnResuming(Platform::Object^ sender, Platform::Object^ args) @@ -396,9 +380,10 @@ int PlatformImpl::GetSpecialFolderPath(const SpecialFolder type, wchar_t* buffer path = Windows::Storage::ApplicationData::Current->LocalFolder->Path; break; case SpecialFolder::ProgramData: + path = Windows::Storage::ApplicationData::Current->RoamingFolder->Path; break; - //case SpecialFolder::Temporary: path = Windows::Storage::ApplicationData::Current->TemporaryFolder->Path; break; case SpecialFolder::Temporary: + //path = Windows::Storage::ApplicationData::Current->TemporaryFolder->Path; path = Windows::Storage::ApplicationData::Current->LocalFolder->Path; break; } @@ -409,7 +394,7 @@ int PlatformImpl::GetSpecialFolderPath(const SpecialFolder type, wchar_t* buffer if (length >= bufferLength) length = bufferLength - 1; const wchar_t* data = path->Data(); - for (int i = 0; i Designer + {3} @@ -70,7 +71,7 @@ - + diff --git a/Source/Platforms/XboxOne/Binaries/Project.csproj b/Source/Platforms/XboxOne/Binaries/Project.csproj index c6c48c200..1895423a0 100644 --- a/Source/Platforms/XboxOne/Binaries/Project.csproj +++ b/Source/Platforms/XboxOne/Binaries/Project.csproj @@ -54,6 +54,7 @@ Designer + {3} @@ -70,7 +71,7 @@ - + diff --git a/Source/Tools/Flax.Build/Platforms/UWP/UWPToolchain.cs b/Source/Tools/Flax.Build/Platforms/UWP/UWPToolchain.cs index 7a76d642d..237026b32 100644 --- a/Source/Tools/Flax.Build/Platforms/UWP/UWPToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/UWP/UWPToolchain.cs @@ -67,10 +67,10 @@ namespace Flax.Build.Platforms options.CompileEnv.PreprocessorDefinitions.Add("PLATFORM_UWP"); options.CompileEnv.PreprocessorDefinitions.Add("WINAPI_FAMILY=WINAPI_FAMILY_PC_APP"); options.CompileEnv.PreprocessorDefinitions.Add("_WINRT_DLL"); + options.CompileEnv.PreprocessorDefinitions.Add("_WINDLL"); options.CompileEnv.PreprocessorDefinitions.Add("__WRL_NO_DEFAULT_LIB__"); options.LinkEnv.InputLibraries.Add("WindowsApp.lib"); - options.LinkEnv.InputLibraries.Add("dloadhelper.lib"); } } } diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs index 38446ee17..b59bcae39 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs @@ -680,6 +680,8 @@ namespace Flax.Build.Platforms args.Add("/WINMD"); args.Add(string.Format("/WINMDFILE:\"{0}\"", Path.ChangeExtension(outputFilePath, "winmd"))); args.Add("/APPCONTAINER"); + if (linkEnvironment.Output == LinkerOutput.SharedLibrary) + args.Add("/DYNAMICBASE"); } if (linkEnvironment.LinkTimeCodeGeneration) @@ -937,7 +939,7 @@ namespace Flax.Build.Platforms xmlTextWriter.WriteStartElement("Properties"); // TODO: better logo handling - var logoSrcPath = Path.Combine(Environment.CurrentDirectory, "Source", "Logo.png"); + var logoSrcPath = Path.Combine(Globals.EngineRoot, "Source", "Logo.png"); var logoDstPath = Path.Combine(options.IntermediateFolder, "Logo.png"); if (!File.Exists(logoDstPath)) Utilities.FileCopy(logoSrcPath, logoDstPath); From 841a336581c7cc14344def3774b18b59fd0885a9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 17 Apr 2021 19:37:11 +0200 Subject: [PATCH 03/27] Fix using `AssetsCache` in game code --- Source/Engine/Content/Cache/AssetsCache.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Content/Cache/AssetsCache.h b/Source/Engine/Content/Cache/AssetsCache.h index ba0ee0303..3186b3cfc 100644 --- a/Source/Engine/Content/Cache/AssetsCache.h +++ b/Source/Engine/Content/Cache/AssetsCache.h @@ -31,7 +31,7 @@ DECLARE_ENUM_OPERATORS(AssetsCacheFlags); /// /// Flax Game Engine assets cache container /// -class AssetsCache +class FLAXENGINE_API AssetsCache { public: From cf0a16e8758623df2f7eb7e12220b47f6a3d8c9e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 17 Apr 2021 19:37:51 +0200 Subject: [PATCH 04/27] Fix handling crash when loading scripting api bindings cache during build --- .../Bindings/BindingsGenerator.Cache.cs | 98 +++++++++---------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs index 27dedb6b4..8fc7d18f2 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs @@ -250,66 +250,66 @@ namespace Flax.Build.Bindings var path = GetCachePath(moduleInfo.Module, moduleOptions); if (!File.Exists(path)) return false; - using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) - using (var reader = new BinaryReader(stream, Encoding.UTF8)) + try { - // Version - var version = reader.ReadInt32(); - if (version != CacheVersion) - return false; - if (File.GetLastWriteTime(Assembly.GetExecutingAssembly().Location).Ticks != reader.ReadInt64()) - return false; - - // Build options - if (reader.ReadString() != moduleOptions.IntermediateFolder || - reader.ReadInt32() != (int)moduleOptions.Platform.Target || - reader.ReadInt32() != (int)moduleOptions.Architecture || - reader.ReadInt32() != (int)moduleOptions.Configuration) - return false; - var publicDefinitions = Read(reader, Utilities.GetEmptyArray()); - if (publicDefinitions.Length != moduleOptions.PublicDefinitions.Count || publicDefinitions.Any(x => !moduleOptions.PublicDefinitions.Contains(x))) - return false; - var privateDefinitions = Read(reader, Utilities.GetEmptyArray()); - if (privateDefinitions.Length != moduleOptions.PrivateDefinitions.Count || privateDefinitions.Any(x => !moduleOptions.PrivateDefinitions.Contains(x))) - return false; - var preprocessorDefinitions = Read(reader, Utilities.GetEmptyArray()); - if (preprocessorDefinitions.Length != moduleOptions.CompileEnv.PreprocessorDefinitions.Count || preprocessorDefinitions.Any(x => !moduleOptions.CompileEnv.PreprocessorDefinitions.Contains(x))) - return false; - - // Header files - var headerFilesCount = reader.ReadInt32(); - if (headerFilesCount != headerFiles.Count) - return false; - for (int i = 0; i < headerFilesCount; i++) + using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) + using (var reader = new BinaryReader(stream, Encoding.UTF8)) { - var headerFile = headerFiles[i]; - if (headerFile != reader.ReadString()) + // Version + var version = reader.ReadInt32(); + if (version != CacheVersion) return false; - if (File.GetLastWriteTime(headerFile).Ticks > reader.ReadInt64()) + if (File.GetLastWriteTime(Assembly.GetExecutingAssembly().Location).Ticks != reader.ReadInt64()) return false; - } - // Info - var newModuleInfo = new ModuleInfo - { - Module = moduleInfo.Module, - Name = moduleInfo.Name, - Namespace = moduleInfo.Namespace, - IsFromCache = true, - }; - try - { + // Build options + if (reader.ReadString() != moduleOptions.IntermediateFolder || + reader.ReadInt32() != (int)moduleOptions.Platform.Target || + reader.ReadInt32() != (int)moduleOptions.Architecture || + reader.ReadInt32() != (int)moduleOptions.Configuration) + return false; + var publicDefinitions = Read(reader, Utilities.GetEmptyArray()); + if (publicDefinitions.Length != moduleOptions.PublicDefinitions.Count || publicDefinitions.Any(x => !moduleOptions.PublicDefinitions.Contains(x))) + return false; + var privateDefinitions = Read(reader, Utilities.GetEmptyArray()); + if (privateDefinitions.Length != moduleOptions.PrivateDefinitions.Count || privateDefinitions.Any(x => !moduleOptions.PrivateDefinitions.Contains(x))) + return false; + var preprocessorDefinitions = Read(reader, Utilities.GetEmptyArray()); + if (preprocessorDefinitions.Length != moduleOptions.CompileEnv.PreprocessorDefinitions.Count || preprocessorDefinitions.Any(x => !moduleOptions.CompileEnv.PreprocessorDefinitions.Contains(x))) + return false; + + // Header files + var headerFilesCount = reader.ReadInt32(); + if (headerFilesCount != headerFiles.Count) + return false; + for (int i = 0; i < headerFilesCount; i++) + { + var headerFile = headerFiles[i]; + if (headerFile != reader.ReadString()) + return false; + if (File.GetLastWriteTime(headerFile).Ticks > reader.ReadInt64()) + return false; + } + + // Info + var newModuleInfo = new ModuleInfo + { + Module = moduleInfo.Module, + Name = moduleInfo.Name, + Namespace = moduleInfo.Namespace, + IsFromCache = true, + }; newModuleInfo.Read(reader); // Skip parsing and use data loaded from cache moduleInfo = newModuleInfo; return true; } - catch - { - // Skip loading cache - return false; - } + } + catch + { + // Skip loading cache + return false; } } } From c5dd3674c155c4ddd288a62e7adb7f9ace30f4f0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 17 Apr 2021 19:38:23 +0200 Subject: [PATCH 05/27] Optimize `WriteStream::WriteText` --- Source/Engine/Serialization/WriteStream.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Serialization/WriteStream.h b/Source/Engine/Serialization/WriteStream.h index 6ff65d4ec..06e3b3904 100644 --- a/Source/Engine/Serialization/WriteStream.h +++ b/Source/Engine/Serialization/WriteStream.h @@ -154,8 +154,7 @@ public: // @param length Text length void WriteText(const char* text, int32 length) { - for (int32 i = 0; i < length; i++) - WriteChar(text[i]); + WriteBytes((const void*)text, sizeof(char) * length); } // Writes text to the stream @@ -163,8 +162,7 @@ public: // @param length Text length void WriteText(const Char* text, int32 length) { - for (int32 i = 0; i < length; i++) - WriteChar(text[i]); + WriteBytes((const void*)text, sizeof(Char) * length); } template From 0d422ec169b0695945fcc86f367bf004f03fc8bb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 17 Apr 2021 19:38:31 +0200 Subject: [PATCH 06/27] Remove unused include --- Source/Engine/Scripting/Scripting.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index 7f87178fd..d53f0a7ab 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -27,7 +27,6 @@ #include "Engine/Engine/EngineService.h" #include "Engine/Graphics/RenderTask.h" #include "Engine/Serialization/JsonTools.h" -#include "Engine/Utilities/StringConverter.h" #include #include From 08d08133c17fc1f8be03012c0fdec893606cc3f6 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Sun, 18 Apr 2021 09:49:25 +0200 Subject: [PATCH 07/27] GetChildAt should do it in reverse order to respect z order --- Source/Engine/UI/GUI/ContainerControl.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Engine/UI/GUI/ContainerControl.cs b/Source/Engine/UI/GUI/ContainerControl.cs index 0a4114205..57d2fb528 100644 --- a/Source/Engine/UI/GUI/ContainerControl.cs +++ b/Source/Engine/UI/GUI/ContainerControl.cs @@ -274,7 +274,7 @@ namespace FlaxEngine.GUI public int GetChildIndexAt(Vector2 point) { int result = -1; - for (int i = 0; i < _children.Count; i++) + for (int i = _children.Count - 1; i >= 0; i--) { var child = _children[i]; @@ -296,7 +296,7 @@ namespace FlaxEngine.GUI public Control GetChildAt(Vector2 point) { Control result = null; - for (int i = 0; i < _children.Count; i++) + for (int i = _children.Count - 1; i >= 0; i--) { var child = _children[i]; @@ -322,7 +322,7 @@ namespace FlaxEngine.GUI throw new ArgumentNullException(nameof(isValid)); Control result = null; - for (int i = 0; i < _children.Count; i++) + for (int i = _children.Count - 1; i >= 0; i--) { var child = _children[i]; @@ -344,7 +344,7 @@ namespace FlaxEngine.GUI public Control GetChildAtRecursive(Vector2 point) { Control result = null; - for (int i = 0; i < _children.Count; i++) + for (int i = _children.Count - 1; i >= 0; i--) { var child = _children[i]; From 20331bc858da8cfee968e2c5e2c027e1d5cee5e1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 20 Apr 2021 10:12:57 +0200 Subject: [PATCH 08/27] Fix crash when using Actor GetScript or GetChild with invalid index #462 --- Source/Engine/Level/Actor.cpp | 13 ++++++++++++- Source/Engine/Level/Actor.h | 10 ++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index 8ab195ef1..ef5b2a554 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -347,6 +347,12 @@ void Actor::SetOrderInParent(int32 index) } } +Actor* Actor::GetChild(int32 index) const +{ + CHECK_RETURN(index >= 0 && index < Children.Count(), nullptr); + return Children[index]; +} + Actor* Actor::GetChild(const StringView& name) const { for (int32 i = 0; i < Children.Count(); i++) @@ -354,7 +360,6 @@ Actor* Actor::GetChild(const StringView& name) const if (Children[i]->GetName() == name) return Children[i]; } - return nullptr; } @@ -482,6 +487,12 @@ void Actor::SetName(const StringView& value) Level::callActorEvent(Level::ActorEventType::OnActorNameChanged, this, nullptr); } +Script* Actor::GetScript(int32 index) const +{ + CHECK_RETURN(index >= 0 && index < Scripts.Count(), nullptr); + return Scripts[index]; +} + Script* Actor::GetScript(const MClass* type) const { CHECK_RETURN(type, nullptr); diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h index 233ebe04c..560403890 100644 --- a/Source/Engine/Level/Actor.h +++ b/Source/Engine/Level/Actor.h @@ -192,10 +192,7 @@ public: /// /// The child actor index. /// The child actor (always valid). - API_FUNCTION() FORCE_INLINE Actor* GetChild(int32 index) const - { - return Children[index]; - } + API_FUNCTION() Actor* GetChild(int32 index) const; /// /// Gets the child actor with the given name. @@ -266,10 +263,7 @@ public: /// /// The script index. /// The script (always valid). - API_FUNCTION() FORCE_INLINE Script* GetScript(int32 index) const - { - return Scripts[index]; - } + API_FUNCTION() Script* GetScript(int32 index) const; /// /// Gets the script of the given type from this actor. From eabab8301d3c2eaf7dfc18ae8d952ef774192a00 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 20 Apr 2021 10:35:09 +0200 Subject: [PATCH 09/27] Fix compilation of bindings code with `API_EVENT` in some cases #459 --- Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 424286e0e..5f74e0403 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -1129,6 +1129,7 @@ namespace Flax.Build.Bindings var paramsCount = eventInfo.Type.GenericArgs?.Count ?? 0; // C# event invoking wrapper (calls C# event from C++ delegate) + CppIncludeFiles.Add("Engine/Scripting/ManagedCLR/MClass.h"); CppIncludeFiles.Add("Engine/Scripting/ManagedCLR/MEvent.h"); contents.Append(" "); if (eventInfo.IsStatic) From 1eacb36256f003080eb68ab0ba5627d46823d3a6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 20 Apr 2021 10:58:14 +0200 Subject: [PATCH 10/27] Minor tweaks --- .../Physics/Colliders/CharacterController.cpp | 16 +++++++++++++--- .../Physics/Colliders/CharacterController.h | 10 ++-------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp index 419b6b052..ef856bf79 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.cpp +++ b/Source/Engine/Physics/Colliders/CharacterController.cpp @@ -22,9 +22,9 @@ CharacterController::CharacterController(const SpawnParams& params) , _radius(50.0f) , _height(150.0f) , _minMoveDistance(0.0f) - , _upDirection(Vector3::Up) , _isUpdatingTransform(false) - , _nonWalkableMode(CharacterController::NonWalkableModes::PreventClimbing) + , _upDirection(Vector3::Up) + , _nonWalkableMode(NonWalkableModes::PreventClimbing) , _lastFlags(CollisionFlags::None) { static_assert(sizeof(_filterData) == sizeof(PxFilterData), "Invalid filter data size."); @@ -61,7 +61,12 @@ void CharacterController::SetSlopeLimit(float value) _slopeLimit = value; if (_controller) - _controller->setSlopeLimit(cosf(value * DegreesToRadians)); + _controller->setSlopeLimit(Math::Cos(value * DegreesToRadians)); +} + +CharacterController::NonWalkableModes CharacterController::GetNonWalkableMode() const +{ + return _nonWalkableMode; } void CharacterController::SetNonWalkableMode(NonWalkableModes value) @@ -72,6 +77,11 @@ void CharacterController::SetNonWalkableMode(NonWalkableModes value) _controller->setNonWalkableMode(static_cast(value)); } +float CharacterController::GetStepOffset() const +{ + return _stepOffset; +} + void CharacterController::SetStepOffset(float value) { if (Math::NearEqual(value, _stepOffset)) diff --git a/Source/Engine/Physics/Colliders/CharacterController.h b/Source/Engine/Physics/Colliders/CharacterController.h index e3f690f8c..5b4dd3f49 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.h +++ b/Source/Engine/Physics/Colliders/CharacterController.h @@ -118,10 +118,7 @@ public: /// Gets the non-walkable mode for the character controller. /// API_PROPERTY(Attributes="EditorOrder(215), DefaultValue(NonWalkableModes.PreventClimbing), EditorDisplay(\"Character Controller\")") - FORCE_INLINE NonWalkableModes GetNonWalkableMode() const - { - return _nonWalkableMode; - } + NonWalkableModes GetNonWalkableMode() const; /// /// Sets the non-walkable mode for the character controller. @@ -132,10 +129,7 @@ public: /// Gets the step height. The character will step up a stair only if it is closer to the ground than the indicated value. This should not be greater than the Character Controller’s height or it will generate an error. /// API_PROPERTY(Attributes="EditorOrder(220), DefaultValue(30.0f), Limit(0), EditorDisplay(\"Character Controller\")") - FORCE_INLINE float GetStepOffset() const - { - return _stepOffset; - } + float GetStepOffset() const; /// /// Sets the step height. The character will step up a stair only if it is closer to the ground than the indicated value. This should not be greater than the Character Controller’s height or it will generate an error. From 5b31a8222c8e2a3994f496494f0254174950e79e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 20 Apr 2021 11:47:21 +0200 Subject: [PATCH 11/27] Fix crash when using C# script that inherits from C++ script --- Source/Engine/Scripting/Script.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Engine/Scripting/Script.cpp b/Source/Engine/Scripting/Script.cpp index e1d2da73a..55e361926 100644 --- a/Source/Engine/Scripting/Script.cpp +++ b/Source/Engine/Scripting/Script.cpp @@ -178,7 +178,6 @@ void Script::SetupType() { // Enable tick functions based on the method overriden in C# or Visual Script ScriptingTypeHandle typeHandle = GetTypeHandle(); - _tickUpdate = _tickLateUpdate = _tickFixedUpdate = 0; while (typeHandle != Script::TypeInitializer) { auto& type = typeHandle.GetType(); From 9857fb12c4184389b8fcb94a18eac6b4db9b1617 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 20 Apr 2021 11:47:32 +0200 Subject: [PATCH 12/27] Fix crash when using C# script that inherits from C++ script --- Source/Engine/Scripting/Script.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Scripting/Script.cpp b/Source/Engine/Scripting/Script.cpp index 55e361926..98a88975b 100644 --- a/Source/Engine/Scripting/Script.cpp +++ b/Source/Engine/Scripting/Script.cpp @@ -181,9 +181,12 @@ void Script::SetupType() while (typeHandle != Script::TypeInitializer) { auto& type = typeHandle.GetType(); - _tickUpdate |= type.Script.ScriptVTable[8] != nullptr; - _tickLateUpdate |= type.Script.ScriptVTable[9] != nullptr; - _tickFixedUpdate |= type.Script.ScriptVTable[10] != nullptr; + if (type.Script.ScriptVTable) + { + _tickUpdate |= type.Script.ScriptVTable[8] != nullptr; + _tickLateUpdate |= type.Script.ScriptVTable[9] != nullptr; + _tickFixedUpdate |= type.Script.ScriptVTable[10] != nullptr; + } typeHandle = type.GetBaseType(); } } From 28c0552e0abadaf008c30675eeab2c1b60ce15d7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 20 Apr 2021 16:06:32 +0200 Subject: [PATCH 13/27] Fix diff-serialization and deserialization of C# lists #347 --- .../Values/ListValueContainer.cs | 5 +- .../Scripting/ManagedCLR/MAssembly.Mono.cpp | 18 +++ Source/Platforms/DotNet/Newtonsoft.Json.dll | 4 +- Source/Platforms/DotNet/Newtonsoft.Json.pdb | 4 +- Source/Platforms/DotNet/Newtonsoft.Json.xml | 132 +++++++++++++++++- .../UWP/Binaries/Newtonsoft.Json.dll | 4 +- .../XboxOne/Binaries/Newtonsoft.Json.dll | 4 +- .../Deps/Dependencies/NewtonsoftJson.cs | 6 +- 8 files changed, 158 insertions(+), 19 deletions(-) diff --git a/Source/Editor/CustomEditors/Values/ListValueContainer.cs b/Source/Editor/CustomEditors/Values/ListValueContainer.cs index f8e5dd26c..416b866cc 100644 --- a/Source/Editor/CustomEditors/Values/ListValueContainer.cs +++ b/Source/Editor/CustomEditors/Values/ListValueContainer.cs @@ -46,10 +46,7 @@ namespace FlaxEditor.CustomEditors if (values.HasReferenceValue) { - var v = (IList)values.ReferenceValue; - - // Get the reference value if collections are the same size - if (v != null && values.Count == v.Count) + if (values.ReferenceValue is IList v && values.Count == v.Count && v.Count > index) { _referenceValue = v[index]; _hasReferenceValue = true; diff --git a/Source/Engine/Scripting/ManagedCLR/MAssembly.Mono.cpp b/Source/Engine/Scripting/ManagedCLR/MAssembly.Mono.cpp index 7d00c1b8c..224565429 100644 --- a/Source/Engine/Scripting/ManagedCLR/MAssembly.Mono.cpp +++ b/Source/Engine/Scripting/ManagedCLR/MAssembly.Mono.cpp @@ -326,6 +326,24 @@ bool MAssembly::LoadWithImage(const String& assemblyPath) mono_debug_open_image_from_memory(assemblyImage, _debugData.Get(), _debugData.Count()); } } + +#if 0 + // Hack to load debug information for Newtonsoft.Json (enable it to debug C# code of json lib) + if (assemblyPath.EndsWith(TEXT("FlaxEngine.CSharp.dll"))) + { + static Array NewtonsoftJsonDebugData; + File::ReadAllBytes(StringUtils::GetDirectoryName(assemblyPath) / TEXT("Newtonsoft.Json.pdb"), NewtonsoftJsonDebugData); + if (NewtonsoftJsonDebugData.HasItems()) + { + StringAnsi tmp(StringUtils::GetDirectoryName(assemblyPath) / TEXT("Newtonsoft.Json.dll")); + MonoAssembly* a = mono_assembly_open(tmp.Get(), &status); + if (a) + { + mono_debug_open_image_from_memory(mono_assembly_get_image(a), NewtonsoftJsonDebugData.Get(), NewtonsoftJsonDebugData.Count()); + } + } + } +#endif #endif // Set state diff --git a/Source/Platforms/DotNet/Newtonsoft.Json.dll b/Source/Platforms/DotNet/Newtonsoft.Json.dll index b08068dd3..05dcf0c44 100644 --- a/Source/Platforms/DotNet/Newtonsoft.Json.dll +++ b/Source/Platforms/DotNet/Newtonsoft.Json.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ed0cb43a692d86f2d47bfc93475e554ee8a70b33187b1e1d2356a7d4f25fffa -size 629760 +oid sha256:eed043b9e8a7982f4a60ab332e833bd8834d6d43814773d30fda48a22eda1d8e +size 641536 diff --git a/Source/Platforms/DotNet/Newtonsoft.Json.pdb b/Source/Platforms/DotNet/Newtonsoft.Json.pdb index 9d8502e2c..3848081ec 100644 --- a/Source/Platforms/DotNet/Newtonsoft.Json.pdb +++ b/Source/Platforms/DotNet/Newtonsoft.Json.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e5b77e2864fee56c380d85c8d1488713c5ba5deacb9005d125d9e47629014e8 -size 249884 +oid sha256:882eecaf201c6d6e7c724e24259df9d23a6c7d8be1b9d407ff646fa24da680ca +size 253816 diff --git a/Source/Platforms/DotNet/Newtonsoft.Json.xml b/Source/Platforms/DotNet/Newtonsoft.Json.xml index c1d3cfd3d..316a82975 100644 --- a/Source/Platforms/DotNet/Newtonsoft.Json.xml +++ b/Source/Platforms/DotNet/Newtonsoft.Json.xml @@ -1865,6 +1865,15 @@ The error message that explains the reason for the exception. The exception that is the cause of the current exception, or null if no inner exception is specified. + + + Initializes a new instance of the class. + + The that holds the serialized object data about the exception being thrown. + The that contains contextual information about the source or destination. + The parameter is null. + The class name is null or is zero (0). + Instructs the to deserialize properties with no matching class member into the specified collection @@ -2471,6 +2480,15 @@ The error message that explains the reason for the exception. The exception that is the cause of the current exception, or null if no inner exception is specified. + + + Initializes a new instance of the class. + + The that holds the serialized object data about the exception being thrown. + The that contains contextual information about the source or destination. + The parameter is null. + The class name is null or is zero (0). + Initializes a new instance of the class @@ -2512,6 +2530,15 @@ The error message that explains the reason for the exception. The exception that is the cause of the current exception, or null if no inner exception is specified. + + + Initializes a new instance of the class. + + The that holds the serialized object data about the exception being thrown. + The that contains contextual information about the source or destination. + The parameter is null. + The class name is null or is zero (0). + Serializes and deserializes objects into and from the JSON format. @@ -2553,7 +2580,7 @@ Gets or sets how a type name assembly is written and resolved by the serializer. - The default value is . + The default value is . The type name assembly format. @@ -2620,6 +2647,12 @@ serializing .NET objects to JSON and vice versa. + + + Gets or sets the used by the serializer when invoking serialization callback methods. + + The context. + Indicates how JSON text output is formatted. @@ -3003,6 +3036,12 @@ The error handler called during serialization and deserialization. + + + Gets or sets the used by the serializer when invoking serialization callback methods. + + The context. + Gets or sets how and values are formatted when writing JSON text, @@ -5479,6 +5518,15 @@ The error message that explains the reason for the exception. The exception that is the cause of the current exception, or null if no inner exception is specified. + + + Initializes a new instance of the class. + + The that holds the serialized object data about the exception being thrown. + The that contains contextual information about the source or destination. + The parameter is null. + The class name is null or is zero (0). + Initializes a new instance of the class @@ -8898,6 +8946,15 @@ The error message that explains the reason for the exception. The exception that is the cause of the current exception, or null if no inner exception is specified. + + + Initializes a new instance of the class. + + The that holds the serialized object data about the exception being thrown. + The that contains contextual information about the source or destination. + The parameter is null. + The class name is null or is zero (0). + @@ -9188,10 +9245,10 @@ - Gets or sets a value indicating whether to ignore the interface when serializing and deserializing types. + Gets or sets a value indicating whether to ignore the interface when serializing and deserializing types. - true if the interface will be ignored when serializing and deserializing types; otherwise, false. + true if the interface will be ignored when serializing and deserializing types; otherwise, false. @@ -9301,6 +9358,13 @@ Type of the object. A for the given type. + + + Creates a for the given type. + + Type of the object. + A for the given type. + Creates a for the given type. @@ -9707,6 +9771,21 @@ The underlying type for the contract. + + + Handles serialization callback events. + + The object that raised the callback event. + The streaming context. + + + + Handles serialization error callback events. + + The object that raised the callback event. + The streaming context. + The error context. + Sets extension data for an object during deserialization. @@ -9750,6 +9829,36 @@ The converter. + + + Gets or sets all methods called immediately after deserialization of the object. + + The methods called immediately after deserialization of the object. + + + + Gets or sets all methods called during deserialization of the object. + + The methods called during deserialization of the object. + + + + Gets or sets all methods called after serialization of the object graph. + + The methods called after serialization of the object graph. + + + + Gets or sets all methods called before serialization of the object. + + The methods called before serialization of the object. + + + + Gets or sets all method called when an error is thrown during the serialization of the object. + + The methods called when an error is thrown during the serialization of the object. + Gets or sets the default creator method used to create the object. @@ -9826,6 +9935,23 @@ The underlying type for the contract. + + + Contract details for a used by the . + + + + + Gets or sets the object constructor. + + The object constructor. + + + + Initializes a new instance of the class. + + The underlying type for the contract. + Contract details for a used by the . diff --git a/Source/Platforms/UWP/Binaries/Newtonsoft.Json.dll b/Source/Platforms/UWP/Binaries/Newtonsoft.Json.dll index 4c908a1ce..1885aed9a 100644 --- a/Source/Platforms/UWP/Binaries/Newtonsoft.Json.dll +++ b/Source/Platforms/UWP/Binaries/Newtonsoft.Json.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a753e82f0d128171be8df8693872615974526e41200105c2853d54283014e1d3 -size 624640 +oid sha256:c43903a192d31dac91b7744e2b1a2e8cec21983ceed630261fd7b80a433fee20 +size 635392 diff --git a/Source/Platforms/XboxOne/Binaries/Newtonsoft.Json.dll b/Source/Platforms/XboxOne/Binaries/Newtonsoft.Json.dll index 4c908a1ce..1885aed9a 100644 --- a/Source/Platforms/XboxOne/Binaries/Newtonsoft.Json.dll +++ b/Source/Platforms/XboxOne/Binaries/Newtonsoft.Json.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a753e82f0d128171be8df8693872615974526e41200105c2853d54283014e1d3 -size 624640 +oid sha256:c43903a192d31dac91b7744e2b1a2e8cec21983ceed630261fd7b80a433fee20 +size 635392 diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/NewtonsoftJson.cs b/Source/Tools/Flax.Build/Deps/Dependencies/NewtonsoftJson.cs index 69d53c990..de9b115ad 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/NewtonsoftJson.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/NewtonsoftJson.cs @@ -82,10 +82,8 @@ namespace Flax.Deps.Dependencies case TargetPlatform.PS4: case TargetPlatform.XboxScarlett: { - foreach (var file in outputFileNames) - { - Utilities.FileCopy(Path.Combine(binFolder, file), Path.Combine(options.PlatformsFolder, platform.ToString(), file)); - } + var file = "Newtonsoft.Json.dll"; + Utilities.FileCopy(Path.Combine(binFolder, file), Path.Combine(options.PlatformsFolder, platform.ToString(), "Binaries", file)); break; } } From 3e73f40c0fa81722bc5cb96961303a5cf384f7b6 Mon Sep 17 00:00:00 2001 From: Tayler Mauk <44530136+TaylerMauk@users.noreply.github.com> Date: Tue, 20 Apr 2021 21:23:42 -0700 Subject: [PATCH 14/27] Fix issue #460 --- Source/Engine/UI/GUI/Tooltip.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/UI/GUI/Tooltip.cs b/Source/Engine/UI/GUI/Tooltip.cs index 1ccf0a5da..3e1fe22ac 100644 --- a/Source/Engine/UI/GUI/Tooltip.cs +++ b/Source/Engine/UI/GUI/Tooltip.cs @@ -155,7 +155,7 @@ namespace FlaxEngine.GUI /// The delta time. public void OnMouseOverControl(Control target, float dt) { - if (!Visible) + if (!Visible && _timeToPopupLeft > 0.0f) { _lastTarget = target; _timeToPopupLeft -= dt; From dc0f77ae2add3ab738ad009b68b7f79becdb832b Mon Sep 17 00:00:00 2001 From: Tayler Mauk <44530136+TaylerMauk@users.noreply.github.com> Date: Tue, 20 Apr 2021 22:24:37 -0700 Subject: [PATCH 15/27] Fix issue #468 --- Source/Engine/UI/GUI/Tooltip.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Source/Engine/UI/GUI/Tooltip.cs b/Source/Engine/UI/GUI/Tooltip.cs index 3e1fe22ac..0fd0428fb 100644 --- a/Source/Engine/UI/GUI/Tooltip.cs +++ b/Source/Engine/UI/GUI/Tooltip.cs @@ -73,13 +73,18 @@ namespace FlaxEngine.GUI Vector2 locationWS = target.PointToWindow(location); Vector2 locationSS = parentWin.PointToScreen(locationWS); Vector2 screenSize = Platform.VirtualDesktopSize; + Vector2 parentWinLocation = parentWin.PointToScreen(Vector2.Zero); + float parentWinAbsoluteRight = parentWinLocation.Y + parentWin.Size.Y; + float parentWinAbsoluteBottom = parentWinLocation.X + parentWin.Size.X; Vector2 rightBottomLocationSS = locationSS + dpiSize; - if (screenSize.Y < rightBottomLocationSS.Y) + + // Prioritize tooltip placement within parent window, fall back to virtual desktop + if (parentWinAbsoluteRight < rightBottomLocationSS.Y || screenSize.Y < rightBottomLocationSS.Y) { // Direction: up locationSS.Y -= dpiSize.Y; } - if (screenSize.X < rightBottomLocationSS.X) + if (parentWinAbsoluteBottom < rightBottomLocationSS.X || screenSize.X < rightBottomLocationSS.X) { // Direction: left locationSS.X -= dpiSize.X; From 3410bd5380669117046c6e427715e876097a6562 Mon Sep 17 00:00:00 2001 From: Tayler Mauk <44530136+TaylerMauk@users.noreply.github.com> Date: Tue, 20 Apr 2021 22:31:15 -0700 Subject: [PATCH 16/27] Standardize variable names --- Source/Engine/UI/GUI/Tooltip.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Engine/UI/GUI/Tooltip.cs b/Source/Engine/UI/GUI/Tooltip.cs index 0fd0428fb..4541124e7 100644 --- a/Source/Engine/UI/GUI/Tooltip.cs +++ b/Source/Engine/UI/GUI/Tooltip.cs @@ -73,18 +73,18 @@ namespace FlaxEngine.GUI Vector2 locationWS = target.PointToWindow(location); Vector2 locationSS = parentWin.PointToScreen(locationWS); Vector2 screenSize = Platform.VirtualDesktopSize; - Vector2 parentWinLocation = parentWin.PointToScreen(Vector2.Zero); - float parentWinAbsoluteRight = parentWinLocation.Y + parentWin.Size.Y; - float parentWinAbsoluteBottom = parentWinLocation.X + parentWin.Size.X; + Vector2 parentWinLocationSS = parentWin.PointToScreen(Vector2.Zero); + float parentWinRightSS = parentWinLocationSS.Y + parentWin.Size.Y; + float parentWinBottomSS = parentWinLocationSS.X + parentWin.Size.X; Vector2 rightBottomLocationSS = locationSS + dpiSize; // Prioritize tooltip placement within parent window, fall back to virtual desktop - if (parentWinAbsoluteRight < rightBottomLocationSS.Y || screenSize.Y < rightBottomLocationSS.Y) + if (parentWinRightSS < rightBottomLocationSS.Y || screenSize.Y < rightBottomLocationSS.Y) { // Direction: up locationSS.Y -= dpiSize.Y; } - if (parentWinAbsoluteBottom < rightBottomLocationSS.X || screenSize.X < rightBottomLocationSS.X) + if (parentWinBottomSS < rightBottomLocationSS.X || screenSize.X < rightBottomLocationSS.X) { // Direction: left locationSS.X -= dpiSize.X; From 8ba1affe9ed9004be9455d8d73e388c1d7c57222 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 21 Apr 2021 10:14:59 +0200 Subject: [PATCH 17/27] Fix `PrefabSpritesRenderer` from script list --- Source/Editor/Viewport/PrefabWindowViewport.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 1b66b8c95..d59ebb977 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -26,6 +26,7 @@ namespace FlaxEditor.Viewport /// public class PrefabWindowViewport : PrefabPreview, IEditorPrimitivesOwner { + [HideInEditor] private sealed class PrefabSpritesRenderer : MainEditorGizmoViewport.EditorSpritesRenderer { public PrefabWindowViewport Viewport; From 02856273ad701d8d7969af26a0aa5c6dd18d2970 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 20 Apr 2021 21:04:05 +0200 Subject: [PATCH 18/27] Add error message box on Editor Options saving error --- Source/Editor/Options/OptionsModule.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs index 62496823c..06cff713c 100644 --- a/Source/Editor/Options/OptionsModule.cs +++ b/Source/Editor/Options/OptionsModule.cs @@ -150,7 +150,10 @@ namespace FlaxEditor.Options private void Save() { // Update file - Editor.SaveJsonAsset(_optionsFilePath, Options); + if (Editor.SaveJsonAsset(_optionsFilePath, Options)) + { + MessageBox.Show(string.Format("Failed to save editor option to '{0}'. Ensure that directory exists and program has access to it.", _optionsFilePath), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } // Special case for editor analytics var editorAnalyticsTrackingFile = Path.Combine(Editor.LocalCachePath, "noTracking"); From a7a6dc7671c34205f68be254ec9c68a752b4c7da Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 20 Apr 2021 21:05:02 +0200 Subject: [PATCH 19/27] Add path utils to support StringView --- Source/Engine/Core/Types/StringView.cpp | 12 ++++++++++++ Source/Engine/Core/Types/StringView.h | 14 ++++++++++++++ Source/Engine/Platform/Base/StringUtilsBase.cpp | 14 ++++++-------- Source/Engine/Platform/StringUtils.h | 8 ++++---- 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/Source/Engine/Core/Types/StringView.cpp b/Source/Engine/Core/Types/StringView.cpp index d6cbb3a6a..e29afe4e8 100644 --- a/Source/Engine/Core/Types/StringView.cpp +++ b/Source/Engine/Core/Types/StringView.cpp @@ -27,6 +27,18 @@ bool StringView::operator!=(const String& other) const return StringUtils::Compare(this->GetText(), *other) != 0; } +String StringView::Left(int32 count) const +{ + const int32 countClamped = count < 0 ? 0 : count < Length() ? count : Length(); + return String(**this, countClamped); +} + +String StringView::Right(int32 count) const +{ + const int32 countClamped = count < 0 ? 0 : count < Length() ? count : Length(); + return String(**this + Length() - countClamped); +} + String StringView::Substring(int32 startIndex) const { ASSERT(startIndex >= 0 && startIndex < Length()); diff --git a/Source/Engine/Core/Types/StringView.h b/Source/Engine/Core/Types/StringView.h index 5d5ad2799..b413ff552 100644 --- a/Source/Engine/Core/Types/StringView.h +++ b/Source/Engine/Core/Types/StringView.h @@ -321,6 +321,20 @@ public: public: + /// + /// Gets the left most given number of characters. + /// + /// The characters count. + /// The substring. + String Left(int32 count) const; + + /// + /// Gets the string of characters from the right (end of the string). + /// + /// The characters count. + /// The substring. + String Right(int32 count) const; + /// /// Retrieves substring created from characters starting from startIndex to the String end. /// diff --git a/Source/Engine/Platform/Base/StringUtilsBase.cpp b/Source/Engine/Platform/Base/StringUtilsBase.cpp index b492abc9b..e8fe3ebc3 100644 --- a/Source/Engine/Platform/Base/StringUtilsBase.cpp +++ b/Source/Engine/Platform/Base/StringUtilsBase.cpp @@ -289,7 +289,7 @@ void RemoveLongPathPrefix(const String& path, String& result) result.Remove(2, 6); } -String StringUtils::GetDirectoryName(const String& path) +String StringUtils::GetDirectoryName(const StringView& path) { const int32 lastFrontSlash = path.FindLast('\\'); const int32 lastBackSlash = path.FindLast('/'); @@ -297,24 +297,22 @@ String StringUtils::GetDirectoryName(const String& path) return splitIndex != INVALID_INDEX ? path.Left(splitIndex) : String::Empty; } -String StringUtils::GetFileName(const String& path) +String StringUtils::GetFileName(const StringView& path) { Char chr; const int32 length = path.Length(); int32 num = length; - do { num--; if (num < 0) - return path; + return String(path); chr = path[num]; } while (chr != DirectorySeparatorChar && chr != AltDirectorySeparatorChar && chr != VolumeSeparatorChar); - return path.Substring(num + 1, length - num - 1); } -String StringUtils::GetFileNameWithoutExtension(const String& path) +String StringUtils::GetFileNameWithoutExtension(const StringView& path) { String filename = GetFileName(path); const int32 num = filename.FindLast('.'); @@ -325,14 +323,14 @@ String StringUtils::GetFileNameWithoutExtension(const String& path) return filename; } -String StringUtils::GetPathWithoutExtension(const String& path) +String StringUtils::GetPathWithoutExtension(const StringView& path) { const int32 num = path.FindLast('.'); if (num != -1) { return path.Substring(0, num); } - return path; + return String(path); } void StringUtils::PathRemoveRelativeParts(String& path) diff --git a/Source/Engine/Platform/StringUtils.h b/Source/Engine/Platform/StringUtils.h index 7e0b7b422..9d812bc47 100644 --- a/Source/Engine/Platform/StringUtils.h +++ b/Source/Engine/Platform/StringUtils.h @@ -207,19 +207,19 @@ public: // Returns the directory name of the specified path string // @param path The path string from which to obtain the directory name // @returns Directory name - static String GetDirectoryName(const String& path); + static String GetDirectoryName(const StringView& path); // Returns the file name and extension of the specified path string // @param path The path string from which to obtain the file name and extension // @returns File name with extension - static String GetFileName(const String& path); + static String GetFileName(const StringView& path); // Returns the file name without extension of the specified path string // @param path The path string from which to obtain the file name // @returns File name without extension - static String GetFileNameWithoutExtension(const String& path); + static String GetFileNameWithoutExtension(const StringView& path); - static String GetPathWithoutExtension(const String& path); + static String GetPathWithoutExtension(const StringView& path); static void PathRemoveRelativeParts(String& path); From 9381d0f5f19b1b83bb63369ff14fe4f0bd033a33 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 20 Apr 2021 21:05:26 +0200 Subject: [PATCH 20/27] Add automatic destination folder creation when saving Json asset --- Source/Engine/ContentImporters/CreateJson.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Source/Engine/ContentImporters/CreateJson.cpp b/Source/Engine/ContentImporters/CreateJson.cpp index 6141cb03e..fb7d3f6e7 100644 --- a/Source/Engine/ContentImporters/CreateJson.cpp +++ b/Source/Engine/ContentImporters/CreateJson.cpp @@ -41,6 +41,18 @@ bool CreateJson::Create(const StringView& path, StringAnsiView& data, StringAnsi LOG(Warning, "Asset will have different type name {0} -> {1}", typeName, String(dataTypename.Get())); } } + else + { + const String directory = StringUtils::GetDirectoryName(path); + if (!FileSystem::DirectoryExists(directory)) + { + if (FileSystem::CreateDirectory(directory)) + { + LOG(Warning, "Failed to create directory"); + return true; + } + } + } rapidjson_flax::StringBuffer buffer; From 3ed55c89f0760da14a27c730194a5d80ce898b1e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 21 Apr 2021 23:46:56 +0200 Subject: [PATCH 21/27] Add drag&drop support for Linux --- .../Engine/Platform/Linux/LinuxPlatform.cpp | 519 +++++++++++++++++- Source/Engine/Platform/Linux/LinuxWindow.cpp | 16 +- Source/Engine/Platform/Linux/LinuxWindow.h | 3 +- 3 files changed, 528 insertions(+), 10 deletions(-) diff --git a/Source/Engine/Platform/Linux/LinuxPlatform.cpp b/Source/Engine/Platform/Linux/LinuxPlatform.cpp index a49d655dd..95426b70a 100644 --- a/Source/Engine/Platform/Linux/LinuxPlatform.cpp +++ b/Source/Engine/Platform/Linux/LinuxPlatform.cpp @@ -20,6 +20,7 @@ #include "Engine/Platform/MessageBox.h" #include "Engine/Platform/WindowsManager.h" #include "Engine/Platform/Clipboard.h" +#include "Engine/Platform/IGuiData.h" #include "Engine/Utilities/StringConverter.h" #include "Engine/Threading/Threading.h" #include "Engine/Engine/Engine.h" @@ -58,6 +59,15 @@ X11::Display* xDisplay = nullptr; X11::XIM IM = nullptr; X11::XIC IC = nullptr; X11::Atom xAtomDeleteWindow; +X11::Atom xAtomXdndEnter; +X11::Atom xAtomXdndPosition; +X11::Atom xAtomXdndLeave; +X11::Atom xAtomXdndDrop; +X11::Atom xAtomXdndActionCopy; +X11::Atom xAtomXdndStatus; +X11::Atom xAtomXdndSelection; +X11::Atom xAtomXdndFinished; +X11::Atom xAtomXdndAware; X11::Atom xAtomWmState; X11::Atom xAtomWmStateHidden; X11::Atom xAtomWmStateMaxVert; @@ -65,6 +75,11 @@ X11::Atom xAtomWmStateMaxHorz; X11::Atom xAtomWmWindowOpacity; X11::Atom xAtomWmName; X11::Atom xAtomClipboard; +X11::Atom xDnDRequested = 0; +X11::Window xDndSourceWindow = 0; +DragDropEffect xDndResult; +Vector2 xDndPos; +int32 xDnDVersion = 0; int32 SystemDpi = 96; X11::Cursor Cursors[(int32)CursorType::MAX]; X11::XcursorImage* CursorsImg[(int32)CursorType::MAX]; @@ -1171,6 +1186,13 @@ public: } }; +struct Property +{ + unsigned char* data; + int format, nitems; + X11::Atom type; +}; + namespace Impl { LinuxKeyboard Keyboard; @@ -1222,6 +1244,371 @@ namespace Impl } } } + + Property ReadProperty(X11::Display* display, X11::Window window, X11::Atom property) + { + X11::Atom readType = 0; + int readFormat = 0; + unsigned long nitems = 0; + unsigned long readBytes = 0; + unsigned char* result = nullptr; + int bytesCount = 1024; + if (property != 0) + { + do + { + if (result != nullptr) + X11::XFree(result); + XGetWindowProperty(display, window, property, 0, bytesCount, 0, AnyPropertyType, &readType, &readFormat, &nitems, &readBytes, &result); + bytesCount *= 2; + } while (readBytes != 0); + } + Property p = { result, readFormat, (int)nitems, readType }; + return p; + } + + static X11::Atom SelectTargetFromList(X11::Display* display, const char* targetType, X11::Atom* list, int count) + { + for (int i = 0; i < count; i++) + { + X11::Atom atom = list[i]; + if (atom != 0 && StringAnsi(XGetAtomName(display, atom)) == targetType) + return atom; + } + return 0; + } + + static X11::Atom SelectTargetFromAtoms(X11::Display* display, const char* targetType, X11::Atom t1, X11::Atom t2, X11::Atom t3) + { + if (t1 != 0 && StringAnsi(XGetAtomName(display, t1)) == targetType) + return t1; + if (t2 != 0 && StringAnsi(XGetAtomName(display, t2)) == targetType) + return t2; + if (t3 != 0 && StringAnsi(XGetAtomName(display, t3)) == targetType) + return t3; + return 0; + } + + static X11::Window FindAppWindow(X11::Display* display, X11::Window w) + { + int nprops, i = 0; + X11::Atom* a; + if (w == 0) + return 0; + a = X11::XListProperties(display, w, &nprops); + for (i = 0; i < nprops; i++) + { + if (a[i] == xAtomXdndAware) + break; + } + if (nprops) + X11::XFree(a); + if (i != nprops) + return w; + X11::Window child, wtmp; + int tmp; + unsigned int utmp; + X11::XQueryPointer(display, w, &wtmp, &child, &tmp, &tmp, &tmp, &tmp, &utmp); + return FindAppWindow(display, child); + } +} + +class LinuxDropFilesData : public IGuiData +{ +public: + Array Files; + + Type GetType() const override + { + return Type::Files; + } + String GetAsText() const override + { + return String::Empty; + } + void GetAsFiles(Array* files) const override + { + files->Add(Files); + } +}; + +class LinuxDropTextData : public IGuiData +{ +public: + StringView Text; + + Type GetType() const override + { + return Type::Text; + } + String GetAsText() const override + { + return String(Text); + } + void GetAsFiles(Array* files) const override + { + } +}; + +DragDropEffect LinuxWindow::DoDragDrop(const StringView& data) +{ + auto cursorWrong = X11::XCreateFontCursor(xDisplay, 54); + auto cursorTransient = X11::XCreateFontCursor(xDisplay, 24); + auto cursorGood = X11::XCreateFontCursor(xDisplay, 4); + Array> formats; + formats.Add(X11::XInternAtom(xDisplay, "text/plain", 0)); + formats.Add(X11::XInternAtom(xDisplay, "TEXT", 0)); + formats.Add((X11::Atom)31); + StringAnsi dataAnsi(data); + LinuxDropTextData dropData; + dropData.Text = data; + + // Begin dragging + auto screen = X11::XDefaultScreen(xDisplay); + auto rootWindow = X11::XRootWindow(xDisplay, screen); + if (X11::XGrabPointer(xDisplay, _window, 1, Button1MotionMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, rootWindow, cursorWrong, CurrentTime) != GrabSuccess) + return DragDropEffect::None; + X11::XSetSelectionOwner(xDisplay, xAtomXdndSelection, _window, CurrentTime); + + // Process events + X11::XEvent event; + enum Status + { + Unaware, + Unreceptive, + CanDrop, + }; + int status = Unaware, previousVersion = -1; + X11::Window previousWindow = 0; + DragDropEffect result = DragDropEffect::None; + float lastDraw = Platform::GetTimeSeconds(); + while (true) + { + X11::XNextEvent(xDisplay, &event); + + if (event.type == SelectionClear) + break; + if (event.type == SelectionRequest) + { + // Extract the relavent data + X11::Window owner = event.xselectionrequest.owner; + X11::Atom selection = event.xselectionrequest.selection; + X11::Atom target = event.xselectionrequest.target; + X11::Atom property = event.xselectionrequest.property; + X11::Window requestor = event.xselectionrequest.requestor; + X11::Time timestamp = event.xselectionrequest.time; + X11::Display* disp = event.xselection.display; + X11::XEvent s; + s.xselection.type = SelectionNotify; + s.xselection.requestor = requestor; + s.xselection.selection = selection; + s.xselection.target = target; + s.xselection.property = 0; + s.xselection.time = timestamp; + if (target == X11::XInternAtom(disp, "TARGETS", 0)) + { + Array targets; + targets.Add(target); + targets.Add(X11::XInternAtom(disp, "MULTIPLE", 0)); + targets.Add(formats.Get(), formats.Count()); + X11::XChangeProperty(disp, requestor, property, (X11::Atom)4, 32, PropModeReplace, (unsigned char*)targets.Get(), targets.Count()); + s.xselection.property = property; + } + else if (formats.Contains(target)) + { + s.xselection.property = property; + X11::XChangeProperty(disp, requestor, property, target, 8, PropModeReplace, reinterpret_cast(dataAnsi.Get()), dataAnsi.Length()); + } + X11::XSendEvent(event.xselection.display, event.xselectionrequest.requestor, 1, 0, &s); + } + else if (event.type == MotionNotify) + { + // Find window under mouse + auto window = Impl::FindAppWindow(xDisplay, rootWindow); + int fmt, version = -1; + X11::Atom atmp; + unsigned long nitems, bytesLeft; + unsigned char* data = nullptr; + if (window == previousWindow) + version = previousVersion; + else if(window == 0) + ; + else if (X11::XGetWindowProperty(xDisplay, window, xAtomXdndAware, 0, 2, 0, AnyPropertyType, &atmp, &fmt, &nitems, &bytesLeft, &data) != Success) + continue; + else if (data == 0) + continue; + else if (fmt != 32) + continue; + else if (nitems != 1) + continue; + else + version = data[0]; + if (status == Unaware && version != -1) + status = Unreceptive; + else if(version == -1) + status = Unaware; + xDndPos = Vector2((float)event.xmotion.x_root, (float)event.xmotion.y_root); + + // Update mouse grab + if (status == Unaware) + X11::XChangeActivePointerGrab(xDisplay, Button1MotionMask | ButtonReleaseMask, cursorWrong, CurrentTime); + else if(status == Unreceptive) + X11::XChangeActivePointerGrab(xDisplay, Button1MotionMask | ButtonReleaseMask, cursorTransient, CurrentTime); + else + X11::XChangeActivePointerGrab(xDisplay, Button1MotionMask | ButtonReleaseMask, cursorGood, CurrentTime); + + if (window != previousWindow && previousVersion != -1) + { + // Send drag left event + auto ww = WindowsManager::GetByNativePtr((void*)previousWindow); + if (ww) + { + ww->_dragOver = false; + ww->OnDragLeave(); + } + else + { + X11::XClientMessageEvent m; + memset(&m, 0, sizeof(m)); + m.type = ClientMessage; + m.display = event.xclient.display; + m.window = previousWindow; + m.message_type = xAtomXdndLeave; + m.format = 32; + m.data.l[0] = _window; + m.data.l[1] = 0; + m.data.l[2] = 0; + m.data.l[3] = 0; + m.data.l[4] = 0; + X11::XSendEvent(xDisplay, previousWindow, 0, NoEventMask, (X11::XEvent*)&m); + X11::XFlush(xDisplay); + } + } + + if (window != previousWindow && version != -1) + { + // Send drag enter event + auto ww = WindowsManager::GetByNativePtr((void*)window); + if (ww) + { + xDndPos = ww->ScreenToClient(Platform::GetMousePosition()); + xDndResult = DragDropEffect::None; + ww->OnDragEnter(&dropData, xDndPos, xDndResult); + } + else + { + X11::XClientMessageEvent m; + memset(&m, 0, sizeof(m)); + m.type = ClientMessage; + m.display = event.xclient.display; + m.window = window; + m.message_type = xAtomXdndEnter; + m.format = 32; + m.data.l[0] = _window; + m.data.l[1] = Math::Min(5, version) << 24 | (formats.Count() > 3); + m.data.l[2] = formats.Count() > 0 ? formats[0] : 0; + m.data.l[3] = formats.Count() > 1 ? formats[1] : 0; + m.data.l[4] = formats.Count() > 2 ? formats[2] : 0; + X11::XSendEvent(xDisplay, window, 0, NoEventMask, (X11::XEvent*)&m); + X11::XFlush(xDisplay); + } + } + + if (version != -1) + { + // Send position event + auto ww = WindowsManager::GetByNativePtr((void*)window); + if (ww) + { + xDndPos = ww->ScreenToClient(Platform::GetMousePosition()); + ww->_dragOver = true; + xDndResult = DragDropEffect::None; + ww->OnDragOver(&dropData, xDndPos, xDndResult); + status = CanDrop; + } + else + { + int x, y, tmp; + unsigned int utmp; + X11::Window wtmp; + X11::XQueryPointer(xDisplay, window, &wtmp, &wtmp, &tmp, &tmp, &x, &y, &utmp); + X11::XClientMessageEvent m; + memset(&m, 0, sizeof(m)); + m.type = ClientMessage; + m.display = event.xclient.display; + m.window = window; + m.message_type = xAtomXdndPosition; + m.format = 32; + m.data.l[0] = _window; + m.data.l[1] = 0; + m.data.l[2] = (x << 16) | y; + m.data.l[3] = CurrentTime; + m.data.l[4] = xAtomXdndActionCopy; + X11::XSendEvent(xDisplay, window, 0, NoEventMask, (X11::XEvent*)&m); + X11::XFlush(xDisplay); + } + } + + previousWindow = window; + previousVersion = version; + } + else if (event.type == ClientMessage && event.xclient.message_type == xAtomXdndStatus) + { + if ((event.xclient.data.l[1]&1) && status != Unaware) + status = CanDrop; + if (!(event.xclient.data.l[1]&1) && status != Unaware) + status = Unreceptive; + } + else if (event.type == ButtonRelease && event.xbutton.button == 1) + { + if (status == CanDrop) + { + // Send drop event + auto ww = WindowsManager::GetByNativePtr((void*)previousWindow); + if (ww) + { + xDndPos = ww->ScreenToClient(Platform::GetMousePosition()); + xDndResult = DragDropEffect::None; + ww->OnDragDrop(&dropData, xDndPos, xDndResult); + ww->Focus(); + result = xDndResult; + } + else + { + X11::XClientMessageEvent m; + memset(&m, 0, sizeof(m)); + m.type = ClientMessage; + m.display = event.xclient.display; + m.window = previousWindow; + m.message_type = xAtomXdndDrop; + m.format = 32; + m.data.l[0] = _window; + m.data.l[1] = 0; + m.data.l[2] = CurrentTime; + m.data.l[3] = 0; + m.data.l[4] = 0; + X11::XSendEvent(xDisplay, previousWindow, 0, NoEventMask, (X11::XEvent*)&m); + X11::XFlush(xDisplay); + result = DragDropEffect::Copy; + } + } + break; + } + + // Redraw + const float time = Platform::GetTimeSeconds(); + if (time - lastDraw >= 1.0f / 60.0f) + { + lastDraw = time; + Engine::OnDraw(); + } + } + + // End grabbing + X11::XChangeActivePointerGrab(xDisplay, Button1MotionMask | ButtonReleaseMask, 0, CurrentTime); + XUngrabPointer(xDisplay, CurrentTime); + + return result; } void LinuxClipboard::Clear() @@ -1651,7 +2038,15 @@ bool LinuxPlatform::Init() } xAtomDeleteWindow = X11::XInternAtom(xDisplay, "WM_DELETE_WINDOW", 0); - xAtomWmState = X11::XInternAtom(xDisplay, "_NET_WM_STATE", 0); + xAtomXdndEnter = X11::XInternAtom(xDisplay, "XdndEnter", 0); + xAtomXdndPosition = X11::XInternAtom(xDisplay, "XdndPosition", 0); + xAtomXdndLeave = X11::XInternAtom(xDisplay, "XdndLeave", 0); + xAtomXdndDrop = X11::XInternAtom(xDisplay, "XdndDrop", 0); + xAtomXdndActionCopy = X11::XInternAtom(xDisplay, "XdndActionCopy", 0); + xAtomXdndStatus = X11::XInternAtom(xDisplay, "XdndStatus", 0); + xAtomXdndSelection = X11::XInternAtom(xDisplay, "XdndSelection", 0); + xAtomXdndFinished = X11::XInternAtom(xDisplay, "XdndFinished", 0); + xAtomXdndAware = X11::XInternAtom(xDisplay, "XdndAware", 0); xAtomWmStateHidden = X11::XInternAtom(xDisplay, "_NET_WM_STATE_HIDDEN", 0); xAtomWmStateMaxHorz = X11::XInternAtom(xDisplay, "_NET_WM_STATE_MAXIMIZED_HORZ", 0); xAtomWmStateMaxVert = X11::XInternAtom(xDisplay, "_NET_WM_STATE_MAXIMIZED_VERT", 0); @@ -1799,6 +2194,93 @@ void LinuxPlatform::Tick() window->Close(ClosingReason::User); } } + else if ((uint32)event.xclient.message_type == (uint32)xAtomXdndEnter) + { + // Drag&drop enter + X11::Window source = event.xclient.data.l[0]; + xDnDVersion = (int32)(event.xclient.data.l[1] >> 24); + const char* targetTypeFiles = "text/uri-list"; + if (event.xclient.data.l[1] & 1) + { + Property p = Impl::ReadProperty(xDisplay, source, XInternAtom(xDisplay, "XdndTypeList", 0)); + xDnDRequested = Impl::SelectTargetFromList(xDisplay, targetTypeFiles, (X11::Atom*)p.data, p.nitems); + X11::XFree(p.data); + } + else + { + xDnDRequested = Impl::SelectTargetFromAtoms(xDisplay, targetTypeFiles, event.xclient.data.l[2], event.xclient.data.l[3], event.xclient.data.l[4]); + } + } + else if ((uint32)event.xclient.message_type == (uint32)xAtomXdndPosition) + { + // Drag&drop move + X11::XClientMessageEvent m; + memset(&m, 0, sizeof(m)); + m.type = ClientMessage; + m.display = event.xclient.display; + m.window = event.xclient.data.l[0]; + m.message_type = xAtomXdndStatus; + m.format = 32; + m.data.l[0] = event.xany.window; + m.data.l[1] = (xDnDRequested != 0); + m.data.l[2] = 0; + m.data.l[3] = 0; + m.data.l[4] = xAtomXdndActionCopy; + X11::XSendEvent(xDisplay, event.xclient.data.l[0], 0, NoEventMask, (X11::XEvent*)&m); + X11::XFlush(xDisplay); + xDndPos = Vector2((float)(event.xclient.data.l[2] >> 16), (float)(event.xclient.data.l[2] & 0xffff)); + window = WindowsManager::GetByNativePtr((void*)event.xany.window); + if (window) + { + LinuxDropFilesData dropData; + xDndResult = DragDropEffect::None; + if (window->_dragOver) + { + window->OnDragEnter(&dropData, xDndPos, xDndResult); + } + else + { + window->_dragOver = true; + window->OnDragOver(&dropData, xDndPos, xDndResult); + } + } + } + else if ((uint32)event.xclient.message_type == (uint32)xAtomXdndLeave) + { + window = WindowsManager::GetByNativePtr((void*)event.xany.window); + if (window && window->_dragOver) + { + window->_dragOver = false; + window->OnDragLeave(); + } + } + else if ((uint32)event.xclient.message_type == (uint32)xAtomXdndDrop) + { + auto w = event.xany.window; + if (xDnDRequested != 0) + { + xDndSourceWindow = event.xclient.data.l[0]; + auto primary = XInternAtom(xDisplay, "PRIMARY", 0); + if (xDnDVersion >= 1) + XConvertSelection(xDisplay, xAtomXdndSelection, xDnDRequested, primary, w, event.xclient.data.l[2]); + else + XConvertSelection(xDisplay, xAtomXdndSelection, xDnDRequested, primary, w, CurrentTime); + } + else + { + X11::XClientMessageEvent m; + memset(&m, 0, sizeof(m)); + m.type = ClientMessage; + m.display = event.xclient.display; + m.window = event.xclient.data.l[0]; + m.message_type = xAtomXdndFinished; + m.format = 32; + m.data.l[0] = w; + m.data.l[1] = 0; + m.data.l[2] = 0; + X11::XSendEvent(xDisplay, event.xclient.data.l[0], 0, NoEventMask, (X11::XEvent*)&m); + } + } break; case MapNotify: // Auto-focus shown windows @@ -1980,6 +2462,41 @@ void LinuxPlatform::Tick() X11::XSendEvent(xDisplay, ev.requestor, 0, 0, (X11::XEvent*)&ev); break; } + case SelectionNotify: + if (event.xselection.target == xDnDRequested) + { + // Drag&drop + window = WindowsManager::GetByNativePtr((void*)event.xany.window); + if (window) + { + Property p = Impl::ReadProperty(xDisplay, event.xany.window, X11::XInternAtom(xDisplay, "PRIMARY", 0)); + if (xDndResult != DragDropEffect::None) + { + LinuxDropFilesData dropData; + const String filesList((const char*)p.data); + filesList.Split('\n', dropData.Files); + for (auto& e : dropData.Files) + { + e.Replace(TEXT("file://"), TEXT("")); + e = e.TrimTrailing(); + } + xDndResult = DragDropEffect::None; + window->OnDragDrop(&dropData, xDndPos, xDndResult); + } + } + X11::XClientMessageEvent m; + memset(&m, 0, sizeof(m)); + m.type = ClientMessage; + m.display = xDisplay; + m.window = xDndSourceWindow; + m.message_type = xAtomXdndFinished; + m.format = 32; + m.data.l[0] = event.xany.window; + m.data.l[1] = 1; + m.data.l[2] = xAtomXdndActionCopy; + XSendEvent(xDisplay, xDndSourceWindow, 0, NoEventMask, (X11::XEvent*)&m); + } + break; default: break; } diff --git a/Source/Engine/Platform/Linux/LinuxWindow.cpp b/Source/Engine/Platform/Linux/LinuxWindow.cpp index 8714a130c..04389eb29 100644 --- a/Source/Engine/Platform/Linux/LinuxWindow.cpp +++ b/Source/Engine/Platform/Linux/LinuxWindow.cpp @@ -103,7 +103,6 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings) bool Fullscreen; bool AllowMinimize; bool AllowMaximize; - bool AllowDragAndDrop; */ const X11::Window window = X11::XCreateWindow( @@ -223,6 +222,15 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings) } X11::XChangeProperty(display, window, wmState, (X11::Atom)4, 32, PropModeReplace, (unsigned char*)states, statesCount); + // Drag&drop support + if (settings.AllowDragAndDrop) + { + auto xdndVersion = 5; + auto xdndAware = XInternAtom(display, "XdndAware", 0); + if (xdndAware != 0) + X11::XChangeProperty(display, window, xdndAware, (X11::Atom)4, 32, PropModeReplace, (unsigned char*)&xdndVersion, 1); + } + // Sync X11::XFlush(display); X11::XSync(display, 0); @@ -725,12 +733,6 @@ void LinuxWindow::SetTitle(const StringView& title) _title = title; } -DragDropEffect LinuxWindow::DoDragDrop(const StringView& data) -{ - // TODO: impl drag and drop on Linux - return DragDropEffect::None; -} - void LinuxWindow::StartTrackingMouse(bool useMouseScreenOffset) { // TODO: impl this diff --git a/Source/Engine/Platform/Linux/LinuxWindow.h b/Source/Engine/Platform/Linux/LinuxWindow.h index 845fd587e..24a3d94a2 100644 --- a/Source/Engine/Platform/Linux/LinuxWindow.h +++ b/Source/Engine/Platform/Linux/LinuxWindow.h @@ -13,14 +13,13 @@ class LinuxWindow : public WindowBase { friend LinuxPlatform; - public: typedef unsigned long HandleType; private: - bool _resizeDisabled, _focusOnMapped = false; + bool _resizeDisabled, _focusOnMapped = false, _dragOver = false; float _opacity = 1.0f; HandleType _window; From d11a9d4d8fba865f1a11399f4b037334f85b294d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 21 Apr 2021 23:47:02 +0200 Subject: [PATCH 22/27] Fix typo --- Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp index aa169e8f5..07eabe814 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp @@ -1032,7 +1032,7 @@ void StagingManagerVulkan::Dispose() ScopeLock lock(_locker); #if !BUILD_RELEASE - LOG(Info, "Vulakn staging buffers peek memory usage: {0}, allocs: {1}, frees: {2}", Utilities::BytesToText(_allBuffersPeekSize), Utilities::BytesToText(_allBuffersAllocSize), Utilities::BytesToText(_allBuffersFreeSize)); + LOG(Info, "Vulkan staging buffers peek memory usage: {0}, allocs: {1}, frees: {2}", Utilities::BytesToText(_allBuffersPeekSize), Utilities::BytesToText(_allBuffersAllocSize), Utilities::BytesToText(_allBuffersFreeSize)); #endif // Release buffers and clear memory From 6821e3c3703eff0b3059f9bf999b5a27c718ad35 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 22 Apr 2021 19:25:01 +0200 Subject: [PATCH 23/27] Fix progress bar drawing precision and stability #471 --- Source/Engine/UI/GUI/Common/ProgressBar.cs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/Source/Engine/UI/GUI/Common/ProgressBar.cs b/Source/Engine/UI/GUI/Common/ProgressBar.cs index 21cce7faa..881bbc161 100644 --- a/Source/Engine/UI/GUI/Common/ProgressBar.cs +++ b/Source/Engine/UI/GUI/Common/ProgressBar.cs @@ -39,9 +39,6 @@ namespace FlaxEngine.GUI /// /// Gets a value indicating whether use progress value smoothing. /// - /// - /// true if use progress value smoothing; otherwise, false. - /// public bool UseSmoothing => !Mathf.IsZero(SmoothingScale); /// @@ -91,8 +88,6 @@ namespace FlaxEngine.GUI if (!Mathf.NearEqual(value, _value)) { _value = value; - - // Check if skip smoothing if (!UseSmoothing) { _current = _value; @@ -138,22 +133,22 @@ namespace FlaxEngine.GUI /// public override void Update(float deltaTime) { - bool isDeltaSlow = deltaTime > (1 / 20.0f); - - // Ensure progress bar is visible if (Visible) { // Value smoothing + var value = _value; if (Mathf.Abs(_current - _value) > 0.01f) { // Lerp or not if running slow - float value; + bool isDeltaSlow = deltaTime > (1 / 20.0f); if (!isDeltaSlow && UseSmoothing) value = Mathf.Lerp(_current, _value, Mathf.Saturate(deltaTime * 5.0f * SmoothingScale)); - else - value = _value; _current = value; } + else + { + _current = _value; + } } base.Update(deltaTime); From 3e945fbe34be503675bdd8915305541da56c9163 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 22 Apr 2021 20:24:21 +0200 Subject: [PATCH 24/27] Bump up version number --- Flax.flaxproj | 2 +- Source/FlaxEngine.Gen.cs | 4 ++-- Source/FlaxEngine.Gen.h | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Flax.flaxproj b/Flax.flaxproj index cc643346f..681c1dc55 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -3,7 +3,7 @@ "Version": { "Major": 1, "Minor": 1, - "Build": 6217 + "Build": 6218 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.", diff --git a/Source/FlaxEngine.Gen.cs b/Source/FlaxEngine.Gen.cs index 6f8878d25..62d40ad9a 100644 --- a/Source/FlaxEngine.Gen.cs +++ b/Source/FlaxEngine.Gen.cs @@ -13,5 +13,5 @@ using System.Runtime.InteropServices; [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] [assembly: Guid("b8442186-4a70-7c85-704a-857c262d00f6")] -[assembly: AssemblyVersion("1.1.6217")] -[assembly: AssemblyFileVersion("1.1.6217")] +[assembly: AssemblyVersion("1.1.6218")] +[assembly: AssemblyFileVersion("1.1.6218")] diff --git a/Source/FlaxEngine.Gen.h b/Source/FlaxEngine.Gen.h index 7a888ae1f..eff8449cc 100644 --- a/Source/FlaxEngine.Gen.h +++ b/Source/FlaxEngine.Gen.h @@ -3,11 +3,11 @@ #pragma once #define FLAXENGINE_NAME "FlaxEngine" -#define FLAXENGINE_VERSION Version(1, 1, 6217) -#define FLAXENGINE_VERSION_TEXT "1.1.6217" +#define FLAXENGINE_VERSION Version(1, 1, 6218) +#define FLAXENGINE_VERSION_TEXT "1.1.6218" #define FLAXENGINE_VERSION_MAJOR 1 #define FLAXENGINE_VERSION_MINOR 1 -#define FLAXENGINE_VERSION_BUILD 6217 +#define FLAXENGINE_VERSION_BUILD 6218 #define FLAXENGINE_COMPANY "Flax" #define FLAXENGINE_COPYRIGHT "Copyright (c) 2012-2021 Wojciech Figat. All rights reserved." From 2b698ca8b03f8c7894faca9019c0201270982408 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 22 Apr 2021 21:03:40 +0200 Subject: [PATCH 25/27] Add support for fixed-array fields in scripting --- Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs | 8 +++++++- Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs | 3 --- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 5f74e0403..6828a8eca 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -1252,7 +1252,13 @@ namespace Flax.Build.Bindings if (fieldInfo.Getter != null) GenerateCppWrapperFunction(buildData, contents, classInfo, fieldInfo.Getter, "{0}"); if (fieldInfo.Setter != null) - GenerateCppWrapperFunction(buildData, contents, classInfo, fieldInfo.Setter, "{0} = {1}"); + { + var callFormat = "{0} = {1}"; + var type = fieldInfo.Setter.Parameters[0].Type; + if (type.IsArray) + callFormat = $"auto __tmp = {{1}}; for (int32 i = 0; i < {type.ArraySize}; i++) {{0}}[i] = __tmp[i]"; + GenerateCppWrapperFunction(buildData, contents, classInfo, fieldInfo.Setter, callFormat); + } } // Properties diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs index 742aa6f63..f8d26314f 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs @@ -623,9 +623,6 @@ namespace Flax.Build.Bindings if (!fieldInfo.IsReadOnly) { - if (fieldInfo.Type.IsArray) - throw new NotImplementedException("Use ReadOnly on field. TODO: add support for setter for fixed-array fields."); - fieldInfo.Setter = new FunctionInfo { Name = "Set" + fieldInfo.Name, From 27fbd896f7b2ac12b7bc8683bb24b3c229698596 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 23 Apr 2021 16:35:18 +0200 Subject: [PATCH 26/27] Fix Win32 stack traces issue due to invalid search path for debug symbols #457 --- .../Platform/Windows/WindowsPlatform.cpp | 90 ++++++------------- 1 file changed, 29 insertions(+), 61 deletions(-) diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.cpp b/Source/Engine/Platform/Windows/WindowsPlatform.cpp index 1b8942dec..8bff48f18 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.cpp +++ b/Source/Engine/Platform/Windows/WindowsPlatform.cpp @@ -38,7 +38,7 @@ namespace CriticalSection SymLocker; bool SymInitialized = false; bool SymModulesDirty = true; - char* SymPath = nullptr; + Array SymbolsPath; #endif } @@ -617,9 +617,8 @@ void WindowsPlatform::Exit() { SymInitialized = false; SymCleanup(GetCurrentProcess()); - free(SymPath); - SymPath = nullptr; } + SymbolsPath.Resize(0); SymLocker.Unlock(); #endif @@ -1116,7 +1115,13 @@ void* WindowsPlatform::LoadLibrary(const Char* filename) #if CRASH_LOG_ENABLE // Refresh modules info during next stack trace collecting to have valid debug symbols information SymLocker.Lock(); - SymModulesDirty = true; + const auto folder = StringUtils::GetDirectoryName(filename); + if (!SymbolsPath.Contains(folder)) + SymbolsPath.Add(folder); + if (SymInitialized) + { + SymModulesDirty = true; + } SymLocker.Unlock(); #endif @@ -1137,80 +1142,43 @@ Array WindowsPlatform::GetStackFrames(int32 skipCount, SymInitialized = true; // Build search path - const size_t nSymPathLen = 4096; - SymPath = (char*)malloc(nSymPathLen); - SymPath[0] = 0; - strcat_s(SymPath, nSymPathLen, ".;"); - const size_t nTempLen = 1024; - char szTemp[nTempLen]; - - // Current directory path - if (GetCurrentDirectoryA(nTempLen, szTemp) > 0) + String symbolSearchPath; + TCHAR ModulePath[MAX_PATH] = { 0 }; + if (::GetModuleFileName(::GetModuleHandle(nullptr), ModulePath, MAX_PATH)) { - szTemp[nTempLen - 1] = 0; - strcat_s(SymPath, nSymPathLen, szTemp); - strcat_s(SymPath, nSymPathLen, ";"); + symbolSearchPath += StringUtils::GetDirectoryName(ModulePath); + symbolSearchPath += ";"; } - - // Main module path - if (GetModuleFileNameA(nullptr, szTemp, nTempLen) > 0) + for (auto& path : SymbolsPath) { - szTemp[nTempLen - 1] = 0; - for (char* p = (szTemp + strlen(szTemp) - 1); p >= szTemp; --p) - { - // Locate the rightmost path separator - if ((*p == '\\') || (*p == '/') || (*p == ':')) - { - *p = 0; - break; - } - } - if (strlen(szTemp) > 0) - { - strcat_s(SymPath, nSymPathLen, szTemp); - strcat_s(SymPath, nSymPathLen, ";"); - } + symbolSearchPath += path; + symbolSearchPath += ";"; } - - // System symbols paths - if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", szTemp, nTempLen) > 0) + String _NT_SYMBOL_PATH; + if (!Platform::GetEnvironmentVariable(TEXT("_NT_SYMBOL_PATH"), _NT_SYMBOL_PATH)) { - szTemp[nTempLen - 1] = 0; - strcat_s(SymPath, nSymPathLen, szTemp); - strcat_s(SymPath, nSymPathLen, ";"); + symbolSearchPath += _NT_SYMBOL_PATH; + symbolSearchPath += ";"; } - if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", szTemp, nTempLen) > 0) - { - szTemp[nTempLen - 1] = 0; - strcat_s(SymPath, nSymPathLen, szTemp); - strcat_s(SymPath, nSymPathLen, ";"); - } - if (GetEnvironmentVariableA("SYSTEMROOT", szTemp, nTempLen) > 0) - { - szTemp[nTempLen - 1] = 0; - strcat_s(SymPath, nSymPathLen, szTemp); - strcat_s(SymPath, nSymPathLen, ";"); - - strcat_s(szTemp, nTempLen, "\\system32"); - strcat_s(SymPath, nSymPathLen, szTemp); - strcat_s(SymPath, nSymPathLen, ";"); - } - - SymInitialize(process, SymPath, FALSE); + symbolSearchPath += Platform::GetWorkingDirectory(); + symbolSearchPath += ";"; DWORD options = SymGetOptions(); options |= SYMOPT_LOAD_LINES; options |= SYMOPT_FAIL_CRITICAL_ERRORS; + options |= SYMOPT_DEFERRED_LOADS; + options |= SYMOPT_EXACT_SYMBOLS; SymSetOptions(options); + + SymInitializeW(process, *symbolSearchPath, TRUE); } - // Load modules + // Refresh modules if needed if (SymModulesDirty) { SymModulesDirty = false; - GetModuleListPSAPI(process); + SymRefreshModuleList(process); } - SymRefreshModuleList(process); // Capture the context if missing /*EXCEPTION_POINTERS exceptionPointers; From 23e722a9fb059a892aa009c3c33359434f7526c4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 23 Apr 2021 22:36:56 +0200 Subject: [PATCH 27/27] Add timeout check for drag&drop on Linux to prevent deadlock --- .../Engine/Platform/Linux/LinuxPlatform.cpp | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Platform/Linux/LinuxPlatform.cpp b/Source/Engine/Platform/Linux/LinuxPlatform.cpp index 95426b70a..59066a2e7 100644 --- a/Source/Engine/Platform/Linux/LinuxPlatform.cpp +++ b/Source/Engine/Platform/Linux/LinuxPlatform.cpp @@ -1382,6 +1382,7 @@ DragDropEffect LinuxWindow::DoDragDrop(const StringView& data) X11::Window previousWindow = 0; DragDropEffect result = DragDropEffect::None; float lastDraw = Platform::GetTimeSeconds(); + float startTime = lastDraw; while (true) { X11::XNextEvent(xDisplay, &event); @@ -1559,7 +1560,7 @@ DragDropEffect LinuxWindow::DoDragDrop(const StringView& data) if (!(event.xclient.data.l[1]&1) && status != Unaware) status = Unreceptive; } - else if (event.type == ButtonRelease && event.xbutton.button == 1) + else if (event.type == ButtonRelease && event.xbutton.button == Button1) { if (status == CanDrop) { @@ -1597,11 +1598,46 @@ DragDropEffect LinuxWindow::DoDragDrop(const StringView& data) // Redraw const float time = Platform::GetTimeSeconds(); - if (time - lastDraw >= 1.0f / 60.0f) + if (time - lastDraw >= 1.0f / 20.0f) { lastDraw = time; Engine::OnDraw(); } + + // Prevent dead-loop + if (time - startTime >= 10.0f) + { + break; + } + } + + // Drag end + if (previousWindow != 0 && previousVersion != -1) + { + // Send drag left event + auto ww = WindowsManager::GetByNativePtr((void*)previousWindow); + if (ww) + { + ww->_dragOver = false; + ww->OnDragLeave(); + } + else + { + X11::XClientMessageEvent m; + memset(&m, 0, sizeof(m)); + m.type = ClientMessage; + m.display = event.xclient.display; + m.window = previousWindow; + m.message_type = xAtomXdndLeave; + m.format = 32; + m.data.l[0] = _window; + m.data.l[1] = 0; + m.data.l[2] = 0; + m.data.l[3] = 0; + m.data.l[4] = 0; + X11::XSendEvent(xDisplay, previousWindow, 0, NoEventMask, (X11::XEvent*)&m); + X11::XFlush(xDisplay); + } } // End grabbing