From d017dd14ca5a465167f0c1cf35417a09ac7e3263 Mon Sep 17 00:00:00 2001 From: Wojciech Figat Date: Mon, 27 Dec 2021 14:04:16 +0100 Subject: [PATCH] Add Mac platform --- .../Content/Create/SettingsCreateEntry.cs | 7 + Source/Editor/Cooker/CookingData.h | 6 + Source/Editor/Cooker/GameCooker.cpp | 11 + Source/Editor/Cooker/GameCooker.cs | 1 + .../Cooker/Steps/CompileScriptsStep.cpp | 4 + Source/Editor/Editor.Build.cs | 10 +- Source/Editor/GUI/PlatformSelector.cs | 1 + .../Editor/Modules/ContentDatabaseModule.cs | 1 + Source/Editor/Surface/Archetypes/Tools.cs | 5 +- Source/Editor/Windows/GameCookerWindow.cs | 20 +- Source/Engine/Audio/Audio.Build.cs | 3 + Source/Engine/Core/Config/GameSettings.cpp | 3 + Source/Engine/Core/Config/GameSettings.cs | 14 + Source/Engine/Core/Config/GameSettings.h | 1 + Source/Engine/Core/Config/PlatformSettings.h | 3 + Source/Engine/Engine/Game.h | 2 + Source/Engine/Engine/Mac/MacGame.h | 19 + Source/Engine/Graphics/Graphics.Build.cs | 3 + Source/Engine/Main/Mac/main.cpp | 24 + Source/Engine/Main/Main.Build.cs | 3 + Source/Engine/Platform/Base/PlatformBase.cpp | 2 + Source/Engine/Platform/CriticalSection.h | 16 +- Source/Engine/Platform/Defines.h | 12 +- Source/Engine/Platform/File.h | 16 +- Source/Engine/Platform/FileSystem.h | 2 + .../Platform/Mac/MacConditionVariable.h | 91 +++ .../Engine/Platform/Mac/MacCriticalSection.h | 92 +++ Source/Engine/Platform/Mac/MacDefines.h | 19 + Source/Engine/Platform/Mac/MacFile.cpp | 156 ++++++ Source/Engine/Platform/Mac/MacFile.h | 56 ++ Source/Engine/Platform/Mac/MacFileSystem.cpp | 523 ++++++++++++++++++ Source/Engine/Platform/Mac/MacFileSystem.h | 52 ++ Source/Engine/Platform/Mac/MacNetwork.cpp | 7 + Source/Engine/Platform/Mac/MacNetwork.h | 27 + Source/Engine/Platform/Mac/MacPlatform.cpp | 357 ++++++++++++ Source/Engine/Platform/Mac/MacPlatform.h | 112 ++++ .../Engine/Platform/Mac/MacPlatformSettings.h | 31 ++ Source/Engine/Platform/Mac/MacStringUtils.cpp | 277 ++++++++++ Source/Engine/Platform/Mac/MacThread.cpp | 23 + Source/Engine/Platform/Mac/MacThread.h | 31 ++ Source/Engine/Platform/Mac/MacWindow.cpp | 103 ++++ Source/Engine/Platform/Mac/MacWindow.h | 38 ++ Source/Engine/Platform/Network.h | 14 +- Source/Engine/Platform/Platform.Build.cs | 4 + Source/Engine/Platform/Platform.h | 2 + Source/Engine/Platform/Thread.h | 10 +- Source/Engine/Platform/Types.h | 33 +- Source/Engine/Platform/Window.h | 2 + .../Tools/TextureTool/TextureTool.Build.cs | 1 + Source/Engine/Visject/ShaderGraph.cpp | 1 + Source/Engine/Visject/VisjectGraph.cpp | 3 + Source/Tools/Flax.Build/Build/Module.cs | 1 + Source/Tools/Flax.Build/Build/Platform.cs | 2 + .../Tools/Flax.Build/Build/TargetPlatform.cs | 5 + Source/Tools/Flax.Build/Deps/Dependency.cs | 1 + Source/Tools/Flax.Build/Flax.Build.csproj | 2 + Source/Tools/Flax.Build/Globals.cs | 1 + .../Flax.Build/Platforms/Mac/MacPlatform.cs | 60 ++ .../Flax.Build/Platforms/Mac/MacToolchain.cs | 58 ++ .../Flax.Build/Projects/ProjectFormat.cs | 5 + .../Flax.Build/Projects/ProjectGenerator.cs | 1 + Source/Tools/Flax.Build/Projects/XCode.cs | 49 ++ 62 files changed, 2384 insertions(+), 55 deletions(-) create mode 100644 Source/Engine/Engine/Mac/MacGame.h create mode 100644 Source/Engine/Main/Mac/main.cpp create mode 100644 Source/Engine/Platform/Mac/MacConditionVariable.h create mode 100644 Source/Engine/Platform/Mac/MacCriticalSection.h create mode 100644 Source/Engine/Platform/Mac/MacDefines.h create mode 100644 Source/Engine/Platform/Mac/MacFile.cpp create mode 100644 Source/Engine/Platform/Mac/MacFile.h create mode 100644 Source/Engine/Platform/Mac/MacFileSystem.cpp create mode 100644 Source/Engine/Platform/Mac/MacFileSystem.h create mode 100644 Source/Engine/Platform/Mac/MacNetwork.cpp create mode 100644 Source/Engine/Platform/Mac/MacNetwork.h create mode 100644 Source/Engine/Platform/Mac/MacPlatform.cpp create mode 100644 Source/Engine/Platform/Mac/MacPlatform.h create mode 100644 Source/Engine/Platform/Mac/MacPlatformSettings.h create mode 100644 Source/Engine/Platform/Mac/MacStringUtils.cpp create mode 100644 Source/Engine/Platform/Mac/MacThread.cpp create mode 100644 Source/Engine/Platform/Mac/MacThread.h create mode 100644 Source/Engine/Platform/Mac/MacWindow.cpp create mode 100644 Source/Engine/Platform/Mac/MacWindow.h create mode 100644 Source/Tools/Flax.Build/Platforms/Mac/MacPlatform.cs create mode 100644 Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs create mode 100644 Source/Tools/Flax.Build/Projects/XCode.cs diff --git a/Source/Editor/Content/Create/SettingsCreateEntry.cs b/Source/Editor/Content/Create/SettingsCreateEntry.cs index 73b89ac8f..fa359fa32 100644 --- a/Source/Editor/Content/Create/SettingsCreateEntry.cs +++ b/Source/Editor/Content/Create/SettingsCreateEntry.cs @@ -42,6 +42,7 @@ namespace FlaxEditor.Content.Create SwitchPlatformSettings, [EditorDisplay(null, "PS5 Platform Settings")] PS5PlatformSettings, + MacPlatformSettings, } private static readonly Type[] _types = @@ -66,6 +67,7 @@ namespace FlaxEditor.Content.Create typeof(AndroidPlatformSettings), TypeUtils.GetManagedType(GameSettings.SwitchPlatformSettingsTypename), TypeUtils.GetManagedType(GameSettings.PS5PlatformSettingsTypename), + typeof(MacPlatformSettings), }; internal class Options @@ -236,6 +238,11 @@ namespace FlaxEditor.Content.Create return false; instance.PS5Platform = asset; break; + case SettingsTypes.MacPlatformSettings: + if (instance.MacPlatform != null) + return false; + instance.MacPlatform = asset; + break; } return true; } diff --git a/Source/Editor/Cooker/CookingData.h b/Source/Editor/Cooker/CookingData.h index 0d8236e83..8c1113f71 100644 --- a/Source/Editor/Cooker/CookingData.h +++ b/Source/Editor/Cooker/CookingData.h @@ -99,6 +99,12 @@ API_ENUM() enum class BuildPlatform /// API_ENUM(Attributes="EditorDisplay(null, \"PlayStation 5\")") PS5 = 11, + + /// + /// MacOS (x86-64 Intel) + /// + API_ENUM(Attributes="EditorDisplay(null, \"Mac x64\")") + MacOSx64 = 12, }; extern FLAXENGINE_API const Char* ToString(const BuildPlatform platform); diff --git a/Source/Editor/Cooker/GameCooker.cpp b/Source/Editor/Cooker/GameCooker.cpp index 452f4d230..38f94abd2 100644 --- a/Source/Editor/Cooker/GameCooker.cpp +++ b/Source/Editor/Cooker/GameCooker.cpp @@ -58,6 +58,10 @@ #if PLATFORM_TOOLS_SWITCH #include "Platforms/Switch/Editor/PlatformTools/SwitchPlatformTools.h" #endif +#if PLATFORM_TOOLS_MAC +#include "Platform/Mac/MacPlatformTools.h" +#include "Engine/Platform/Mac/MacPlatformSettings.h" +#endif namespace GameCookerImpl { @@ -129,6 +133,8 @@ const Char* ToString(const BuildPlatform platform) return TEXT("Switch"); case BuildPlatform::PS5: return TEXT("PlayStation 5"); + case BuildPlatform::MacOSx64: + return TEXT("Mac x64"); default: return TEXT("?"); } @@ -325,6 +331,11 @@ PlatformTools* GameCooker::GetTools(BuildPlatform platform) case BuildPlatform::PS5: result = New(); break; +#endif +#if PLATFORM_TOOLS_MAC + case BuildPlatform::MacOSx64: + result = New(ArchitectureType::x64); + break; #endif } Tools.Add(platform, result); diff --git a/Source/Editor/Cooker/GameCooker.cs b/Source/Editor/Cooker/GameCooker.cs index 0d83dff24..d6b1daed7 100644 --- a/Source/Editor/Cooker/GameCooker.cs +++ b/Source/Editor/Cooker/GameCooker.cs @@ -103,6 +103,7 @@ namespace FlaxEditor case BuildPlatform.AndroidARM64: return PlatformType.Android; case BuildPlatform.XboxScarlett: return PlatformType.XboxScarlett; case BuildPlatform.Switch: return PlatformType.Switch; + case BuildPlatform.MacOSx64: return PlatformType.Mac; default: throw new ArgumentOutOfRangeException(nameof(buildPlatform), buildPlatform, null); } } diff --git a/Source/Editor/Cooker/Steps/CompileScriptsStep.cpp b/Source/Editor/Cooker/Steps/CompileScriptsStep.cpp index ee7f87bd6..e00c8cd78 100644 --- a/Source/Editor/Cooker/Steps/CompileScriptsStep.cpp +++ b/Source/Editor/Cooker/Steps/CompileScriptsStep.cpp @@ -186,6 +186,10 @@ bool CompileScriptsStep::Perform(CookingData& data) platform = TEXT("PS5"); architecture = TEXT("x64"); break; + case BuildPlatform::MacOSx64: + platform = TEXT("Mac"); + architecture = TEXT("x64"); + break; default: LOG(Error, "Unknown or unsupported build platform."); return true; diff --git a/Source/Editor/Editor.Build.cs b/Source/Editor/Editor.Build.cs index c51e72fa1..9d18158d0 100644 --- a/Source/Editor/Editor.Build.cs +++ b/Source/Editor/Editor.Build.cs @@ -61,8 +61,16 @@ public class Editor : EditorModule AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "XboxScarlett", "PLATFORM_TOOLS_XBOX_SCARLETT", "PLATFORM_TOOLS_GDK"); AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Android", "PLATFORM_TOOLS_ANDROID"); AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Switch", "PLATFORM_TOOLS_SWITCH"); + AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Linux", "PLATFORM_TOOLS_LINUX"); + } + else if (options.Platform.Target == TargetPlatform.Linux) + { + AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Linux", "PLATFORM_TOOLS_LINUX"); + } + else if (options.Platform.Target == TargetPlatform.Mac) + { + AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Mac", "PLATFORM_TOOLS_MAC"); } - AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Linux", "PLATFORM_TOOLS_LINUX"); // Visual Studio integration if (options.Platform.Target == TargetPlatform.Windows && Flax.Build.Platform.BuildTargetPlatform == TargetPlatform.Windows) diff --git a/Source/Editor/GUI/PlatformSelector.cs b/Source/Editor/GUI/PlatformSelector.cs index bb02bb95c..a2089df57 100644 --- a/Source/Editor/GUI/PlatformSelector.cs +++ b/Source/Editor/GUI/PlatformSelector.cs @@ -91,6 +91,7 @@ namespace FlaxEditor.GUI new PlatformData(PlatformType.Android, icons.AndroidIcon128, "Android"), new PlatformData(PlatformType.Switch, icons.SwitchIcon128, "Switch"), new PlatformData(PlatformType.PS5, icons.PS5Icon128, "PlayStation 5"), + new PlatformData(PlatformType.Mac, icons.Flax64, "Mac"), }; const float IconSize = 64.0f; diff --git a/Source/Editor/Modules/ContentDatabaseModule.cs b/Source/Editor/Modules/ContentDatabaseModule.cs index a4fb00876..b94f5df25 100644 --- a/Source/Editor/Modules/ContentDatabaseModule.cs +++ b/Source/Editor/Modules/ContentDatabaseModule.cs @@ -945,6 +945,7 @@ namespace FlaxEditor.Modules Proxy.Add(new SettingsProxy(typeof(UWPPlatformSettings), Editor.Instance.Icons.UWPSettings128)); Proxy.Add(new SettingsProxy(typeof(LinuxPlatformSettings), Editor.Instance.Icons.LinuxSettings128)); Proxy.Add(new SettingsProxy(typeof(AndroidPlatformSettings), Editor.Instance.Icons.AndroidSettings128)); + Proxy.Add(new SettingsProxy(typeof(MacPlatformSettings), Editor.Instance.Icons.Document128)); var typePS4PlatformSettings = TypeUtils.GetManagedType(GameSettings.PS4PlatformSettingsTypename); if (typePS4PlatformSettings != null) diff --git a/Source/Editor/Surface/Archetypes/Tools.cs b/Source/Editor/Surface/Archetypes/Tools.cs index 596a7999c..3a5f87948 100644 --- a/Source/Editor/Surface/Archetypes/Tools.cs +++ b/Source/Editor/Surface/Archetypes/Tools.cs @@ -1342,13 +1342,13 @@ namespace FlaxEditor.Surface.Archetypes }, new NodeArchetype { - TypeID = 17, + TypeID = 18, Title = "Platform Switch", Description = "Gets the input value based on the runtime-platform type", Flags = NodeFlags.AllGraphs, Size = new Vector2(220, 200), ConnectionsHints = ConnectionsHint.Value, - IndependentBoxes = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, + IndependentBoxes = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }, DependentBoxes = new[] { 0 }, Elements = new[] { @@ -1363,6 +1363,7 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Input(7, "Android", true, null, 8), NodeElementArchetype.Factory.Input(8, "Switch", true, null, 9), NodeElementArchetype.Factory.Input(9, "PlayStation 5", true, null, 10), + NodeElementArchetype.Factory.Input(10, "Mac", true, null, 11), } }, new NodeArchetype diff --git a/Source/Editor/Windows/GameCookerWindow.cs b/Source/Editor/Windows/GameCookerWindow.cs index 4d9f6b3e3..61bd753af 100644 --- a/Source/Editor/Windows/GameCookerWindow.cs +++ b/Source/Editor/Windows/GameCookerWindow.cs @@ -45,6 +45,7 @@ namespace FlaxEditor.Windows { PlatformType.Android, new Android() }, { PlatformType.Switch, new Switch() }, { PlatformType.PS5, new PS5() }, + { PlatformType.Mac, new Mac() }, }; public BuildTabProxy(GameCookerWindow win, PlatformSelector platformSelector) @@ -61,6 +62,7 @@ namespace FlaxEditor.Windows PerPlatformOptions[PlatformType.Android].Init("Output/Android", "Android"); PerPlatformOptions[PlatformType.Switch].Init("Output/Switch", "Switch"); PerPlatformOptions[PlatformType.PS5].Init("Output/PS5", "PS5"); + PerPlatformOptions[PlatformType.Mac].Init("Output/Mac", "Mac"); } abstract class Platform @@ -102,7 +104,15 @@ namespace FlaxEditor.Windows // Check if can build on that platform #if PLATFORM_WINDOWS - IsSupported = true; + switch (BuildPlatform) + { + case BuildPlatform.MacOSx64: + IsSupported = false; + break; + default: + IsSupported = true; + break; + } #elif PLATFORM_LINUX switch (BuildPlatform) { @@ -202,6 +212,11 @@ namespace FlaxEditor.Windows protected override BuildPlatform BuildPlatform => BuildPlatform.PS5; } + class Mac : Platform + { + protected override BuildPlatform BuildPlatform => BuildPlatform.MacOSx64; + } + class Editor : CustomEditor { private PlatformType _platform; @@ -249,6 +264,9 @@ namespace FlaxEditor.Windows case PlatformType.PS5: name = "PlayStation 5"; break; + case PlatformType.Mac: + name = "Mac"; + break; default: name = CustomEditorsUtil.GetPropertyNameUI(_platform.ToString()); break; diff --git a/Source/Engine/Audio/Audio.Build.cs b/Source/Engine/Audio/Audio.Build.cs index 694adb742..e0bb24ed3 100644 --- a/Source/Engine/Audio/Audio.Build.cs +++ b/Source/Engine/Audio/Audio.Build.cs @@ -54,6 +54,9 @@ public class Audio : EngineModule options.SourcePaths.Add(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "PS5", "Engine", "Audio")); options.CompileEnv.PreprocessorDefinitions.Add("AUDIO_API_PS5"); break; + case TargetPlatform.Mac: + useNone = true; // TODO: Audio support on Mac + break; default: throw new InvalidPlatformException(options.Platform.Target); } diff --git a/Source/Engine/Core/Config/GameSettings.cpp b/Source/Engine/Core/Config/GameSettings.cpp index 6f21b9b73..482ef156c 100644 --- a/Source/Engine/Core/Config/GameSettings.cpp +++ b/Source/Engine/Core/Config/GameSettings.cpp @@ -65,6 +65,8 @@ IMPLEMENT_SETTINGS_GETTER(XboxScarlettPlatformSettings, XboxScarlettPlatform); IMPLEMENT_SETTINGS_GETTER(AndroidPlatformSettings, AndroidPlatform); #elif PLATFORM_SWITCH IMPLEMENT_SETTINGS_GETTER(SwitchPlatformSettings, SwitchPlatform); +#elif PLATFORM_MAC +IMPLEMENT_SETTINGS_GETTER(MacPlatformSettings, MacPlatform); #else #error Unknown platform #endif @@ -226,6 +228,7 @@ void GameSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* mo DESERIALIZE(AndroidPlatform); DESERIALIZE(SwitchPlatform); DESERIALIZE(PS5Platform); + DESERIALIZE(MacPlatform); } void LayersAndTagsSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) diff --git a/Source/Engine/Core/Config/GameSettings.cs b/Source/Engine/Core/Config/GameSettings.cs index 4c365e4b0..1b34660a0 100644 --- a/Source/Engine/Core/Config/GameSettings.cs +++ b/Source/Engine/Core/Config/GameSettings.cs @@ -177,6 +177,14 @@ namespace FlaxEditor.Content.Settings public JsonAsset PS5Platform; #endif +#if FLAX_EDITOR || PLATFORM_MAC + /// + /// Reference to asset. Used to apply configuration on Mac platform. + /// + [EditorOrder(2090), EditorDisplay("Platform Settings", "Mac"), AssetReference(typeof(MacPlatformSettings), true), Tooltip("Reference to Mac Platform Settings asset")] + public JsonAsset MacPlatform; +#endif + /// /// Gets the absolute path to the game settings asset file. /// @@ -295,6 +303,10 @@ namespace FlaxEditor.Content.Settings if (type.FullName == PS5PlatformSettingsTypename) return LoadAsset(gameSettings.PS5Platform, PS5PlatformSettingsTypename) as T; #endif +#if FLAX_EDITOR || PLATFORM_MAC + if (type == typeof(MacPlatformSettings)) + return LoadAsset(gameSettings.MacPlatform) as T; +#endif if (gameSettings.CustomSettings != null) { @@ -391,6 +403,8 @@ namespace FlaxEditor.Content.Settings return SaveAsset(gameSettings, ref gameSettings.PS5Platform, obj); if (type == typeof(AudioSettings)) return SaveAsset(gameSettings, ref gameSettings.Audio, obj); + if (type == typeof(MacPlatformSettings)) + return SaveAsset(gameSettings, ref gameSettings.MacPlatform, obj); return true; } diff --git a/Source/Engine/Core/Config/GameSettings.h b/Source/Engine/Core/Config/GameSettings.h index a9f324127..fe99f014c 100644 --- a/Source/Engine/Core/Config/GameSettings.h +++ b/Source/Engine/Core/Config/GameSettings.h @@ -82,6 +82,7 @@ public: Guid AndroidPlatform; Guid SwitchPlatform; Guid PS5Platform; + Guid MacPlatform; public: diff --git a/Source/Engine/Core/Config/PlatformSettings.h b/Source/Engine/Core/Config/PlatformSettings.h index df5dceedd..313ecf33f 100644 --- a/Source/Engine/Core/Config/PlatformSettings.h +++ b/Source/Engine/Core/Config/PlatformSettings.h @@ -32,3 +32,6 @@ #if PLATFORM_PS5 #include "Platforms/PS5/Engine/Platform/PS5PlatformSettings.h" #endif +#if PLATFORM_MAC +#include "Engine/Platform/Mac/MacPlatformSettings.h" +#endif diff --git a/Source/Engine/Engine/Game.h b/Source/Engine/Engine/Game.h index 55e1f0869..e979ebe5d 100644 --- a/Source/Engine/Engine/Game.h +++ b/Source/Engine/Engine/Game.h @@ -22,6 +22,8 @@ #include "Android/AndroidGame.h" #elif PLATFORM_SWITCH #include "Platforms/Switch/Engine/Engine/SwitchGame.h" +#elif PLATFORM_MAC +#include "Mac/MacGame.h" #else #error Missing Game implementation! #endif diff --git a/Source/Engine/Engine/Mac/MacGame.h b/Source/Engine/Engine/Mac/MacGame.h new file mode 100644 index 000000000..e6b39a134 --- /dev/null +++ b/Source/Engine/Engine/Mac/MacGame.h @@ -0,0 +1,19 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#if PLATFORM_MAC && !USE_EDITOR + +#include "../Base/GameBase.h" + +/// +/// The game class implementation for Mac platform. +/// +/// +class MacGame : public GameBase +{ +}; + +typedef MacGame Game; + +#endif diff --git a/Source/Engine/Graphics/Graphics.Build.cs b/Source/Engine/Graphics/Graphics.Build.cs index 08c9c6e83..625128ece 100644 --- a/Source/Engine/Graphics/Graphics.Build.cs +++ b/Source/Engine/Graphics/Graphics.Build.cs @@ -81,6 +81,9 @@ public class Graphics : EngineModule case TargetPlatform.Switch: options.PrivateDependencies.Add("GraphicsDeviceVulkan"); break; + case TargetPlatform.Mac: + options.PrivateDependencies.Add("GraphicsDeviceNull"); // TODO: Graphics support on Mac + break; default: throw new InvalidPlatformException(options.Platform.Target); } diff --git a/Source/Engine/Main/Mac/main.cpp b/Source/Engine/Main/Mac/main.cpp new file mode 100644 index 000000000..b51ad799c --- /dev/null +++ b/Source/Engine/Main/Mac/main.cpp @@ -0,0 +1,24 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#if PLATFORM_MAC + +#include "Engine/Engine/Engine.h" +#include "Engine/Core/Types/StringBuilder.h" + +int main(int argc, char* argv[]) +{ + // Join the arguments + StringBuilder args; + for (int i = 1; i < argc; i++) + { + args.Append(argv[i]); + + if (i + 1 != argc) + args.Append(TEXT(' ')); + } + args.Append(TEXT('\0')); + + return Engine::Main(*args); +} + +#endif diff --git a/Source/Engine/Main/Main.Build.cs b/Source/Engine/Main/Main.Build.cs index a40151b44..37adc09d9 100644 --- a/Source/Engine/Main/Main.Build.cs +++ b/Source/Engine/Main/Main.Build.cs @@ -72,6 +72,9 @@ public class Main : EngineModule case TargetPlatform.Switch: options.SourcePaths.Add(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "Switch", "Engine", "Main")); break; + case TargetPlatform.Mac: + options.SourcePaths.Add(Path.Combine(FolderPath, "Mac")); + break; default: throw new InvalidPlatformException(options.Platform.Target); } } diff --git a/Source/Engine/Platform/Base/PlatformBase.cpp b/Source/Engine/Platform/Base/PlatformBase.cpp index 382aff1b4..b10838a6a 100644 --- a/Source/Engine/Platform/Base/PlatformBase.cpp +++ b/Source/Engine/Platform/Base/PlatformBase.cpp @@ -614,6 +614,8 @@ const Char* ToString(PlatformType type) return TEXT("Switch"); case PlatformType::PS5: return TEXT("PlayStation 5"); + case PlatformType::Mac: + return TEXT("Mac"); default: return TEXT(""); } diff --git a/Source/Engine/Platform/CriticalSection.h b/Source/Engine/Platform/CriticalSection.h index 6dae6588f..f3e9c27f0 100644 --- a/Source/Engine/Platform/CriticalSection.h +++ b/Source/Engine/Platform/CriticalSection.h @@ -2,21 +2,9 @@ #pragma once -#if PLATFORM_WINDOWS +#if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT #include "Win32/Win32CriticalSection.h" -#elif PLATFORM_UWP -#include "Win32/Win32CriticalSection.h" -#elif PLATFORM_LINUX -#include "Unix/UnixCriticalSection.h" -#elif PLATFORM_PS4 -#include "Unix/UnixCriticalSection.h" -#elif PLATFORM_PS5 -#include "Unix/UnixCriticalSection.h" -#elif PLATFORM_XBOX_ONE -#include "Win32/Win32CriticalSection.h" -#elif PLATFORM_XBOX_SCARLETT -#include "Win32/Win32CriticalSection.h" -#elif PLATFORM_ANDROID +#elif PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_PS4 || PLATFORM_PS5 #include "Unix/UnixCriticalSection.h" #elif PLATFORM_SWITCH #include "Platforms/Switch/Engine/Platform/SwitchCriticalSection.h" diff --git a/Source/Engine/Platform/Defines.h b/Source/Engine/Platform/Defines.h index 2ac7f8e1f..f7f8ddf35 100644 --- a/Source/Engine/Platform/Defines.h +++ b/Source/Engine/Platform/Defines.h @@ -53,6 +53,11 @@ API_ENUM() enum class PlatformType /// Running on PlayStation 5. /// PS5 = 9, + + /// + /// Running on Mac. + /// + Mac = 10, }; /// @@ -120,9 +125,6 @@ API_ENUM() enum class ArchitectureType #if !defined(PLATFORM_MAC) #define PLATFORM_MAC 0 #endif -#if !defined(PLATFORM_OSX) -#define PLATFORM_OSX 0 -#endif #if !defined(PLATFORM_IOS) #define PLATFORM_IOS 0 #endif @@ -148,6 +150,8 @@ API_ENUM() enum class ArchitectureType #include "Android/AndroidDefines.h" #elif PLATFORM_SWITCH #include "Platforms/Switch/Engine/Platform/SwitchDefines.h" +#elif PLATFORM_MAC +#include "Mac/MacDefines.h" #else #error Missing Defines implementation! #endif @@ -189,7 +193,7 @@ API_ENUM() enum class ArchitectureType #define PLATFORM_WINDOWS_FAMILY (PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT) #define PLATFORM_MICROSOFT_FAMILY (PLATFORM_WINDOWS_FAMILY) #define PLATFORM_UNIX_FAMILY (PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_PS4 || PLATFORM_PS5) -#define PLATFORM_APPLE_FAMILY (PLATFORM_IOS || PLATFORM_OSX) +#define PLATFORM_APPLE_FAMILY (PLATFORM_MAC || PLATFORM_IOS) // SIMD defines #if defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) || defined(__SSE2__) diff --git a/Source/Engine/Platform/File.h b/Source/Engine/Platform/File.h index 98696040b..d7df5444c 100644 --- a/Source/Engine/Platform/File.h +++ b/Source/Engine/Platform/File.h @@ -2,24 +2,16 @@ #pragma once -#if PLATFORM_WINDOWS +#if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT #include "Win32/Win32File.h" -#elif PLATFORM_UWP -#include "Win32/Win32File.h" -#elif PLATFORM_LINUX +#elif PLATFORM_LINUX || PLATFORM_PS4 || PLATFORM_PS5 #include "Unix/UnixFile.h" -#elif PLATFORM_PS4 -#include "Unix/UnixFile.h" -#elif PLATFORM_PS5 -#include "Unix/UnixFile.h" -#elif PLATFORM_XBOX_ONE -#include "Win32/Win32File.h" -#elif PLATFORM_XBOX_SCARLETT -#include "Win32/Win32File.h" #elif PLATFORM_ANDROID #include "Android/AndroidFile.h" #elif PLATFORM_SWITCH #include "Platforms/Switch/Engine/Platform/SwitchFile.h" +#elif PLATFORM_MAC +#include "Mac/MacFile.h" #else #error Missing File implementation! #endif diff --git a/Source/Engine/Platform/FileSystem.h b/Source/Engine/Platform/FileSystem.h index c64be0cf7..e17552db4 100644 --- a/Source/Engine/Platform/FileSystem.h +++ b/Source/Engine/Platform/FileSystem.h @@ -20,6 +20,8 @@ #include "Android/AndroidFileSystem.h" #elif PLATFORM_SWITCH #include "Platforms/Switch/Engine/Platform/SwitchFileSystem.h" +#elif PLATFORM_MAC +#include "Mac/MacFileSystem.h" #else #error Missing File System implementation! #endif diff --git a/Source/Engine/Platform/Mac/MacConditionVariable.h b/Source/Engine/Platform/Mac/MacConditionVariable.h new file mode 100644 index 000000000..c1d60184d --- /dev/null +++ b/Source/Engine/Platform/Mac/MacConditionVariable.h @@ -0,0 +1,91 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#if PLATFORM_MAC + +#include "MacCriticalSection.h" +#include +#include + +/// +/// Mac implementation of a condition variables. Condition variables are synchronization primitives that enable threads to wait until a particular condition occurs. Condition variables enable threads to atomically release a lock and enter the sleeping state. +/// +class FLAXENGINE_API MacConditionVariable +{ +private: + + pthread_cond_t _cond; + +private: + + MacConditionVariable(const MacConditionVariable&); + MacConditionVariable& operator=(const MacConditionVariable&); + +public: + + /// + /// Initializes a new instance of the class. + /// + MacConditionVariable() + { + pthread_cond_init(&_cond, nullptr); + } + + /// + /// Finalizes an instance of the class. + /// + ~MacConditionVariable() + { + pthread_cond_destroy(&_cond); + } + +public: + + /// + /// Blocks the current thread execution until the condition variable is woken up. + /// + /// The critical section locked by the current thread. + void Wait(const MacCriticalSection& lock) + { + pthread_cond_wait(&_cond, lock._mutexPtr); + } + + /// + /// Blocks the current thread execution until the condition variable is woken up or after the specified timeout duration. + /// + /// The critical section locked by the current thread. + /// The time-out interval, in milliseconds. If the time-out interval elapses, the function re-acquires the critical section and returns zero. If timeout is zero, the function tests the states of the specified objects and returns immediately. If timeout is INFINITE, the function's time-out interval never elapses. + /// If the function succeeds, the return value is true, otherwise, if the function fails or the time-out interval elapses, the return value is false. + bool Wait(const MacCriticalSection& lock, const int32 timeout) + { + struct timeval tv; + struct timespec ts; + + gettimeofday(&tv, NULL); + ts.tv_sec = time(NULL) + timeout / 1000; + ts.tv_nsec = tv.tv_usec * 1000 + 1000 * 1000 * (timeout % 1000); + ts.tv_sec += ts.tv_nsec / (1000 * 1000 * 1000); + ts.tv_nsec %= (1000 * 1000 * 1000); + + return pthread_cond_timedwait(&_cond, lock._mutexPtr, &ts) == 0; + } + + /// + /// Notifies one waiting thread. + /// + void NotifyOne() + { + pthread_cond_signal(&_cond); + } + + /// + /// Notifies all waiting threads. + /// + void NotifyAll() + { + pthread_cond_broadcast(&_cond); + } +}; + +#endif diff --git a/Source/Engine/Platform/Mac/MacCriticalSection.h b/Source/Engine/Platform/Mac/MacCriticalSection.h new file mode 100644 index 000000000..2f83260f1 --- /dev/null +++ b/Source/Engine/Platform/Mac/MacCriticalSection.h @@ -0,0 +1,92 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#if PLATFORM_MAC + +#include "Engine/Platform/Platform.h" +#include + +class MacConditionVariable; + +/// +/// Mac implementation of a critical section. +/// +class FLAXENGINE_API MacCriticalSection +{ + friend MacConditionVariable; + +private: + + pthread_mutex_t _mutex; + pthread_mutex_t* _mutexPtr; +#if BUILD_DEBUG + pthread_t _owningThreadId; +#endif + +private: + + MacCriticalSection(const MacCriticalSection&); + MacCriticalSection& operator=(const MacCriticalSection&); + +public: + + /// + /// Initializes a new instance of the class. + /// + MacCriticalSection() + { + pthread_mutexattr_t attributes; + pthread_mutexattr_init(&attributes); + pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&_mutex, &attributes); + pthread_mutexattr_destroy(&attributes); + _mutexPtr = &_mutex; +#if BUILD_DEBUG + _owningThreadId = 0; +#endif + } + + /// + /// Finalizes an instance of the class. + /// + ~MacCriticalSection() + { + pthread_mutex_destroy(&_mutex); + } + +public: + + /// + /// Locks the critical section. + /// + void Lock() const + { + pthread_mutex_lock(_mutexPtr); +#if BUILD_DEBUG + ((MacCriticalSection*)this)->_owningThreadId = pthread_self(); +#endif + } + + /// + /// Attempts to enter a critical section without blocking. If the call is successful, the calling thread takes ownership of the critical section. + /// + /// True if calling thread took ownership of the critical section. + bool TryLock() const + { + return pthread_mutex_trylock(_mutexPtr) == 0; + } + + /// + /// Releases the lock on the critical section. + /// + void Unlock() const + { +#if BUILD_DEBUG + ((MacCriticalSection*)this)->_owningThreadId = 0; +#endif + pthread_mutex_unlock(_mutexPtr); + } +}; + +#endif diff --git a/Source/Engine/Platform/Mac/MacDefines.h b/Source/Engine/Platform/Mac/MacDefines.h new file mode 100644 index 000000000..c56284643 --- /dev/null +++ b/Source/Engine/Platform/Mac/MacDefines.h @@ -0,0 +1,19 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#if PLATFORM_MAC + +// Platform description +#define PLATFORM_64BITS 1 +#define PLATFORM_ARCH_X64 1 +#define PLATFORM_ARCH ArchitectureType::x64 +#define PLATFORM_TYPE PlatformType::Mac +#define PLATFORM_DESKTOP 1 +#define PLATFORM_CACHE_LINE_SIZE 128 +#define PLATFORM_HAS_HEADLESS_MODE 1 +#define PLATFORM_DEBUG_BREAK __builtin_trap() +#define PLATFORM_LINE_TERMINATOR "\n" +#define PLATFORM_TEXT_IS_CHAR16 1 + +#endif diff --git a/Source/Engine/Platform/Mac/MacFile.cpp b/Source/Engine/Platform/Mac/MacFile.cpp new file mode 100644 index 000000000..109306ad7 --- /dev/null +++ b/Source/Engine/Platform/Mac/MacFile.cpp @@ -0,0 +1,156 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#if PLATFORM_MAC + +#include "../File.h" +#include "Engine/Core/Log.h" +#include "Engine/Core/Types/String.h" +#include "Engine/Core/Types/DateTime.h" +#include "Engine/Core/Types/TimeSpan.h" +#include "Engine/Utilities/StringConverter.h" +#include "Engine/Core/Log.h" +#include +#include +#include +#include +#include + +MacFile::MacFile(int32 handle) + : _handle(handle) +{ +} + +MacFile::~MacFile() +{ + Close(); +} + +MacFile* MacFile::Open(const StringView& path, FileMode mode, FileAccess access, FileShare share) +{ + int flags = O_CLOEXEC; + switch (access) + { + case FileAccess::Read: + flags |= O_RDONLY; + break; + case FileAccess::Write: + flags |= O_WRONLY; + break; + case FileAccess::ReadWrite: + flags |= O_RDWR; + break; + default: ; + } + switch (mode) + { + case FileMode::CreateAlways: + flags |= O_CREAT | O_TRUNC; + break; + case FileMode::CreateNew: + flags |= O_CREAT | O_EXCL; + break; + case FileMode::OpenAlways: + break; + case FileMode::OpenExisting: + break; + case FileMode::TruncateExisting: + flags |= O_TRUNC; + break; + default: ; + } + + mode_t omode = S_IRUSR | S_IWUSR; + if ((uint32)share & (uint32)FileShare::Delete) + omode |= 0; + if ((uint32)share & (uint32)FileShare::Read) + omode |= (mode_t)(S_IRGRP | S_IROTH); + if ((uint32)share & (uint32)FileShare::Write) + omode |= (mode_t)(S_IWGRP | S_IWOTH); + if ((uint32)share & (uint32)FileShare::Delete) + omode |= 0; + + const StringAsANSI<> pathANSI(*path, path.Length()); + auto handle = open(pathANSI.Get(), flags, omode); + if (handle == -1) + { + LOG_Mac_LAST_ERROR; + return nullptr; + } + + return New(handle); +} + +bool MacFile::Read(void* buffer, uint32 bytesToRead, uint32* bytesRead) +{ + const ssize_t tmp = read(_handle, buffer, bytesToRead); + if (tmp != -1) + { + if (bytesRead) + *bytesRead = tmp; + return false; + } + if (bytesRead) + *bytesRead = 0; + LOG_Mac_LAST_ERROR; + return true; +} + +bool MacFile::Write(const void* buffer, uint32 bytesToWrite, uint32* bytesWritten) +{ + const ssize_t tmp = write(_handle, buffer, bytesToWrite); + if (tmp != -1) + { + if (bytesWritten) + *bytesWritten = tmp; + return false; + } + if (bytesWritten) + *bytesWritten = 0; + LOG_Mac_LAST_ERROR; + return true; +} + +void MacFile::Close() +{ + if (_handle != -1) + { + close(_handle); + _handle = -1; + } +} + +uint32 MacFile::GetSize() const +{ + struct stat fileInfo; + fstat(_handle, &fileInfo); + return fileInfo.st_size; +} + +DateTime MacFile::GetLastWriteTime() const +{ + struct stat fileInfo; + if (fstat(_handle, &fileInfo) == -1) + { + return DateTime::MinValue(); + } + const TimeSpan timeSinceEpoch(0, 0, fileInfo.st_mtime); + const DateTime MacEpoch(1970, 1, 1); + return MacEpoch + timeSinceEpoch; +} + +uint32 MacFile::GetPosition() const +{ + return lseek(_handle, 0, SEEK_CUR); +} + +void MacFile::SetPosition(uint32 seek) +{ + lseek(_handle, seek, SEEK_SET); +} + +bool MacFile::IsOpened() const +{ + return _handle != -1; +} + +#endif diff --git a/Source/Engine/Platform/Mac/MacFile.h b/Source/Engine/Platform/Mac/MacFile.h new file mode 100644 index 000000000..8e6741db1 --- /dev/null +++ b/Source/Engine/Platform/Mac/MacFile.h @@ -0,0 +1,56 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#if PLATFORM_MAC + +#include "Engine/Platform/Base/FileBase.h" + +/// +/// Mac platform file object implementation. +/// +class MacFile : public FileBase +{ +protected: + + int32 _handle; + +public: + + /// + /// Initializes a new instance of the class. + /// + /// The handle. + MacFile(int32 handle); + + /// + /// Finalizes an instance of the class. + /// + ~MacFile(); + +public: + + /// + /// Creates or opens a file. + /// + /// The name of the file to be created or opened. + /// An action to take on a file that exists or does not exist. + /// The requested access to the file. + /// The requested sharing mode of the file. + /// Opened file handle or null if cannot. + static MacFile* Open(const StringView& path, FileMode mode, FileAccess access = FileAccess::ReadWrite, FileShare share = FileShare::None); + +public: + + // [FileBase] + bool Read(void* buffer, uint32 bytesToRead, uint32* bytesRead = nullptr) override; + bool Write(const void* buffer, uint32 bytesToWrite, uint32* bytesWritten = nullptr) override; + void Close() override; + uint32 GetSize() const override; + DateTime GetLastWriteTime() const override; + uint32 GetPosition() const override; + void SetPosition(uint32 seek) override; + bool IsOpened() const override; +}; + +#endif diff --git a/Source/Engine/Platform/Mac/MacFileSystem.cpp b/Source/Engine/Platform/Mac/MacFileSystem.cpp new file mode 100644 index 000000000..165fa2bfb --- /dev/null +++ b/Source/Engine/Platform/Mac/MacFileSystem.cpp @@ -0,0 +1,523 @@ +// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved. + +#if PLATFORM_MAC + +#include "MacFileSystem.h" +#include "Engine/Platform/File.h" +#include "Engine/Core/Types/String.h" +#include "Engine/Core/Types/StringView.h" +#include "Engine/Core/Types/TimeSpan.h" +#include "Engine/Core/Math/Math.h" +#include "Engine/Core/Log.h" +#include "Engine/Utilities/StringConverter.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const DateTime UnixEpoch(1970, 1, 1); + +bool MacFileSystem::CreateDirectory(const StringView& path) +{ + const StringAsANSI<> pathAnsi(*path, path.Length()); + + // Skip if already exists + struct stat fileInfo; + if (stat(pathAnsi.Get(), &fileInfo) != -1 && S_ISDIR(fileInfo.st_mode)) + { + return false; + } + + // Recursively do it all again for the parent directory, if any + const int32 slashIndex = path.FindLast('/'); + if (slashIndex > 1) + { + if (CreateDirectory(path.Substring(0, slashIndex))) + { + return true; + } + } + + // Create the last directory on the path (the recursive calls will have taken care of the parent directories by now) + return mkdir(pathAnsi.Get(), 0755) != 0 && errno != EEXIST; +} + +bool DeletePathTree(const char* path) +{ + size_t pathLength; + DIR* dir; + struct stat statPath, statEntry; + struct dirent* entry; + + // Stat for the path + stat(path, &statPath); + + // If path does not exists or is not dir - exit with status -1 + if (S_ISDIR(statPath.st_mode) == 0) + { + // Is not directory + return true; + } + + // If not possible to read the directory for this user + if ((dir = opendir(path)) == NULL) + { + // Cannot open directory + return true; + } + + // The length of the path + pathLength = strlen(path); + + // Iteration through entries in the directory + while ((entry = readdir(dir)) != NULL) + { + // Skip entries "." and ".." + if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) + continue; + + // Determinate a full path of an entry + char full_path[256]; + ASSERT(pathLength + strlen(entry->d_name) < ARRAY_COUNT(full_path)); + strcpy(full_path, path); + strcat(full_path, "/"); + strcat(full_path, entry->d_name); + + // Stat for the entry + stat(full_path, &statEntry); + + // Recursively remove a nested directory + if (S_ISDIR(statEntry.st_mode) != 0) + { + if (DeletePathTree(full_path)) + return true; + continue; + } + + // Remove a file object + if (unlink(full_path) != 0) + return true; + } + + // Remove the devastated directory and close the object of it + if (rmdir(path) != 0) + return true; + + closedir(dir); + + return false; +} + +bool MacFileSystem::DeleteDirectory(const String& path, bool deleteContents) +{ + const StringAsANSI<> pathANSI(*path, path.Length()); + if (deleteContents) + return DeletePathTree(pathANSI.Get()); + return rmdir(pathANSI.Get()) != 0; +} + +bool MacFileSystem::DirectoryExists(const StringView& path) +{ + struct stat fileInfo; + const StringAsANSI<> pathANSI(*path, path.Length()); + if (stat(pathANSI.Get(), &fileInfo) != -1) + { + return S_ISDIR(fileInfo.st_mode); + } + return false; +} + +bool MacFileSystem::DirectoryGetFiles(Array& results, const String& path, const Char* searchPattern, DirectorySearchOption option) +{ + const StringAsANSI<> pathANSI(*path, path.Length()); + const StringAsANSI<> searchPatternANSI(searchPattern); + + // Check if use only top directory + if (option == DirectorySearchOption::TopDirectoryOnly) + return getFilesFromDirectoryTop(results, pathANSI.Get(), searchPatternANSI.Get()); + return getFilesFromDirectoryAll(results, pathANSI.Get(), searchPatternANSI.Get()); +} + +bool MacFileSystem::GetChildDirectories(Array& results, const String& directory) +{ + size_t pathLength; + DIR* dir; + struct stat statPath, statEntry; + struct dirent* entry; + const StringAsANSI<> pathANSI(*directory, directory.Length()); + const char* path = pathANSI.Get(); + + // Stat for the path + stat(path, &statPath); + + // If path does not exists or is not dir - exit with status -1 + if (S_ISDIR(statPath.st_mode) == 0) + { + // Is not directory + return true; + } + + // If not possible to read the directory for this user + if ((dir = opendir(path)) == NULL) + { + // Cannot open directory + return true; + } + + // The length of the path + pathLength = strlen(path); + + // Iteration through entries in the directory + while ((entry = readdir(dir)) != NULL) + { + // Skip entries "." and ".." + if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) + continue; + + // Determinate a full path of an entry + char full_path[256]; + ASSERT(pathLength + strlen(entry->d_name) < ARRAY_COUNT(full_path)); + strcpy(full_path, path); + strcat(full_path, "/"); + strcat(full_path, entry->d_name); + + // Stat for the entry + stat(full_path, &statEntry); + + // Check for directory + if (S_ISDIR(statEntry.st_mode) != 0) + { + // Add directory + results.Add(String(full_path)); + } + } + + closedir(dir); + + return false; +} + +bool MacFileSystem::FileExists(const StringView& path) +{ + struct stat fileInfo; + const StringAsANSI<> pathANSI(*path, path.Length()); + if (stat(pathANSI.Get(), &fileInfo) != -1) + { + return S_ISREG(fileInfo.st_mode); + } + return false; +} + +bool MacFileSystem::DeleteFile(const StringView& path) +{ + const StringAsANSI<> pathANSI(*path, path.Length()); + return unlink(pathANSI.Get()) == 0; +} + +uint64 MacFileSystem::GetFileSize(const StringView& path) +{ + struct stat fileInfo; + fileInfo.st_size = -1; + const StringAsANSI<> pathANSI(*path, path.Length()); + if (stat(pathANSI.Get(), &fileInfo) != -1) + { + // Check for directories + if (S_ISDIR(fileInfo.st_mode)) + { + fileInfo.st_size = -1; + } + } + return fileInfo.st_size; +} + +bool MacFileSystem::IsReadOnly(const StringView& path) +{ + const StringAsANSI<> pathANSI(*path, path.Length()); + if (access(pathANSI.Get(), W_OK) == -1) + { + return errno == EACCES; + } + return false; +} + +bool MacFileSystem::SetReadOnly(const StringView& path, bool isReadOnly) +{ + const StringAsANSI<> pathANSI(*path, path.Length()); + struct stat fileInfo; + if (stat(pathANSI.Get(), &fileInfo) != -1) + { + if (isReadOnly) + { + fileInfo.st_mode &= ~S_IWUSR; + } + else + { + fileInfo.st_mode |= S_IWUSR; + } + return chmod(pathANSI.Get(), fileInfo.st_mode) == 0; + } + return false; +} + +bool MacFileSystem::MoveFile(const StringView& dst, const StringView& src, bool overwrite) +{ + if (!overwrite && FileExists(dst)) + { + // Already exists + return true; + } + + if (overwrite) + { + unlink(StringAsANSI<>(*dst, dst.Length()).Get()); + } + if (rename(StringAsANSI<>(*src, src.Length()).Get(), StringAsANSI<>(*dst, dst.Length()).Get()) != 0) + { + if (errno == EXDEV) + { + if (!CopyFile(dst, src)) + { + unlink(StringAsANSI<>(*src, src.Length()).Get()); + return false; + } + } + return true; + } + return false; +} + +bool MacFileSystem::CopyFile(const StringView& dst, const StringView& src) +{ + const StringAsANSI<> srcANSI(*src, src.Length()); + const StringAsANSI<> dstANSI(*dst, dst.Length()); + + int srcFile, dstFile; + char buffer[4096]; + ssize_t readSize; + int cachedError; + + srcFile = open(srcANSI.Get(), O_RDONLY); + if (srcFile < 0) + return true; + dstFile = open(dstANSI.Get(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + if (dstFile < 0) + goto out_error; + + // first try the kernel method + struct stat statBuf; + fstat(srcFile, &statBuf); + readSize = 1; + while (readSize > 0) + { + readSize = sendfile(dstFile, srcFile, 0, statBuf.st_size); + } + // sendfile could fail for example if the input file is not nmap'able + // in this case we fall back to the read/write loop + if (readSize < 0) + { + while (readSize = read(srcFile, buffer, sizeof(buffer)), readSize > 0) + { + char* ptr = buffer; + ssize_t writeSize; + + do + { + writeSize = write(dstFile, ptr, readSize); + if (writeSize >= 0) + { + readSize -= writeSize; + ptr += writeSize; + } + else if (errno != EINTR) + { + goto out_error; + } + } while (readSize > 0); + } + } + + if (readSize == 0) + { + if (close(dstFile) < 0) + { + dstFile = -1; + goto out_error; + } + close(srcFile); + + // Success + return false; + } + +out_error: + cachedError = errno; + close(srcFile); + if (dstFile >= 0) + close(dstFile); + errno = cachedError; + return true; +} + +bool MacFileSystem::getFilesFromDirectoryTop(Array& results, const char* path, const char* searchPattern) +{ + size_t pathLength; + struct stat statPath, statEntry; + struct dirent* entry; + + // Stat for the path + stat(path, &statPath); + + // If path does not exists or is not dir - exit with status -1 + if (S_ISDIR(statPath.st_mode) == 0) + { + // Is not directory + return true; + } + + // If not possible to read the directory for this user + DIR* dir = opendir(path); + if (dir == NULL) + { + // Cannot open directory + return true; + } + + // The length of the path + pathLength = strlen(path); + + // Iteration through entries in the directory + while ((entry = readdir(dir)) != NULL) + { + // Skip entries "." and ".." + if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) + continue; + + // Determinate a full path of an entry + char fullPath[256]; + ASSERT(pathLength + strlen(entry->d_name) < ARRAY_COUNT(fullPath)); + strcpy(fullPath, path); + strcat(fullPath, "/"); + strcat(fullPath, entry->d_name); + + // Stat for the entry + stat(fullPath, &statEntry); + + // Check for file + if (S_ISREG(statEntry.st_mode) != 0) + { + // Validate with filter + const int32 fullPathLength = StringUtils::Length(fullPath); + const int32 searchPatternLength = StringUtils::Length(searchPattern); + if (searchPatternLength == 0 || StringUtils::Compare(searchPattern, "*") == 0) + { + // All files + } + else if (searchPattern[0] == '*' && searchPatternLength < fullPathLength && StringUtils::Compare(fullPath + fullPathLength - searchPatternLength + 1, searchPattern + 1, searchPatternLength - 1) == 0) + { + // Path ending + } + else + { + // TODO: implement all cases in a generic way + continue; + } + + // Add file + results.Add(String(fullPath)); + } + } + + closedir(dir); + + return false; +} + +bool MacFileSystem::getFilesFromDirectoryAll(Array& results, const char* path, const char* searchPattern) +{ + // Find all files in this directory + getFilesFromDirectoryTop(results, path, searchPattern); + + size_t pathLength; + DIR* dir; + struct stat statPath, statEntry; + struct dirent* entry; + + // Stat for the path + stat(path, &statPath); + + // If path does not exists or is not dir - exit with status -1 + if (S_ISDIR(statPath.st_mode) == 0) + { + // Is not directory + return true; + } + + // If not possible to read the directory for this user + if ((dir = opendir(path)) == NULL) + { + // Cannot open directory + return true; + } + + // The length of the path + pathLength = strlen(path); + + // Iteration through entries in the directory + while ((entry = readdir(dir)) != NULL) + { + // Skip entries "." and ".." + if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) + continue; + + // Determinate a full path of an entry + char full_path[256]; + ASSERT(pathLength + strlen(entry->d_name) < ARRAY_COUNT(full_path)); + strcpy(full_path, path); + strcat(full_path, "/"); + strcat(full_path, entry->d_name); + + // Stat for the entry + stat(full_path, &statEntry); + + // Check for directory + if (S_ISDIR(statEntry.st_mode) != 0) + { + if (getFilesFromDirectoryAll(results, full_path, searchPattern)) + { + closedir(dir); + return true; + } + } + } + + closedir(dir); + + return false; +} + +DateTime MacFileSystem::GetFileLastEditTime(const StringView& path) +{ + struct stat fileInfo; + const StringAsANSI<> pathANSI(*path, path.Length()); + if (stat(pathANSI.Get(), &fileInfo) == -1) + { + return DateTime::MinValue(); + } + + const TimeSpan timeSinceEpoch(0, 0, fileInfo.st_mtime); + return UnixEpoch + timeSinceEpoch; +} + +void MacFileSystem::GetSpecialFolderPath(const SpecialFolder type, String& result) +{ + MISSING_CODE("MacFileSystem::GetSpecialFolderPath"); + return; // TODO: filesystem on Mac +} + +#endif diff --git a/Source/Engine/Platform/Mac/MacFileSystem.h b/Source/Engine/Platform/Mac/MacFileSystem.h new file mode 100644 index 000000000..4ad8df0b6 --- /dev/null +++ b/Source/Engine/Platform/Mac/MacFileSystem.h @@ -0,0 +1,52 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#if PLATFORM_MAC + +#include "Engine/Platform/Base/FileSystemBase.h" + +/// +/// Mac platform implementation of filesystem service. +/// +class FLAXENGINE_API MacFileSystem : public FileSystemBase +{ +public: + + // [FileSystemBase] + static bool CreateDirectory(const StringView& path); + static bool DeleteDirectory(const String& path, bool deleteContents = true); + static bool DirectoryExists(const StringView& path); + static bool DirectoryGetFiles(Array& results, const String& path, const Char* searchPattern, DirectorySearchOption option = DirectorySearchOption::AllDirectories); + static bool GetChildDirectories(Array& results, const String& directory); + static bool FileExists(const StringView& path); + static bool DeleteFile(const StringView& path); + static uint64 GetFileSize(const StringView& path); + static bool IsReadOnly(const StringView& path); + static bool SetReadOnly(const StringView& path, bool isReadOnly); + static bool MoveFile(const StringView& dst, const StringView& src, bool overwrite = false); + static bool CopyFile(const StringView& dst, const StringView& src); + +public: + + /// + /// Gets last time when file has been modified (in UTC). + /// + /// The file path to check. + /// The last write time or DateTime::MinValue() if cannot get data. + static DateTime GetFileLastEditTime(const StringView& path); + + /// + /// Gets the special folder path. + /// + /// The folder type. + /// The result full path. + static void GetSpecialFolderPath(const SpecialFolder type, String& result); + +private: + + static bool getFilesFromDirectoryTop(Array& results, const char* path, const char* searchPattern); + static bool getFilesFromDirectoryAll(Array& results, const char* path, const char* searchPattern); +}; + +#endif diff --git a/Source/Engine/Platform/Mac/MacNetwork.cpp b/Source/Engine/Platform/Mac/MacNetwork.cpp new file mode 100644 index 000000000..11eeccb26 --- /dev/null +++ b/Source/Engine/Platform/Mac/MacNetwork.cpp @@ -0,0 +1,7 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#if PLATFORM_MAC + +// TODO: networking on Mac + +#endif diff --git a/Source/Engine/Platform/Mac/MacNetwork.h b/Source/Engine/Platform/Mac/MacNetwork.h new file mode 100644 index 000000000..1e8c16cd3 --- /dev/null +++ b/Source/Engine/Platform/Mac/MacNetwork.h @@ -0,0 +1,27 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#if PLATFORM_MAC + +#include "Engine/Platform/Base/NetworkBase.h" + +class FLAXENGINE_API MacNetwork : public NetworkBase +{ +public: + + // [NetworkBase] + static bool CreateSocket(NetworkSocket& socket, NetworkProtocol proto, NetworkIPVersion ipv); + static bool DestroySocket(NetworkSocket& socket); + static bool SetSocketOption(NetworkSocket& socket, NetworkSocketOption option, int32 value); + static bool GetSocketOption(NetworkSocket& socket, NetworkSocketOption option, int32& value); + static bool ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); + static bool BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); + static bool Listen(NetworkSocket& socket, uint16 queueSize); + static bool Accept(NetworkSocket& serverSocket, NetworkSocket& newSocket, NetworkEndPoint& newEndPoint); + static int32 WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint = nullptr); + static int32 ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint = nullptr); + static bool CreateEndPoint(const String& address, const String& port, NetworkIPVersion ipv, NetworkEndPoint& endPoint, bool bindable = true); +}; + +#endif diff --git a/Source/Engine/Platform/Mac/MacPlatform.cpp b/Source/Engine/Platform/Mac/MacPlatform.cpp new file mode 100644 index 000000000..f900b6cb3 --- /dev/null +++ b/Source/Engine/Platform/Mac/MacPlatform.cpp @@ -0,0 +1,357 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#if PLATFORM_MAC + +#include "MacPlatform.h" +#include "MacWindow.h" +#include "Engine/Core/Log.h" +#include "Engine/Core/Types/Guid.h" +#include "Engine/Core/Types/String.h" +#include "Engine/Core/Collections/HashFunctions.h" +#include "Engine/Core/Collections/Array.h" +#include "Engine/Core/Collections/Dictionary.h" +#include "Engine/Core/Collections/HashFunctions.h" +#include "Engine/Core/Math/Math.h" +#include "Engine/Core/Math/Rectangle.h" +#include "Engine/Core/Math/Color32.h" +#include "Engine/Platform/CPUInfo.h" +#include "Engine/Platform/MemoryStats.h" +#include "Engine/Platform/StringUtils.h" +#include "Engine/Platform/MessageBox.h" +#include "Engine/Platform/WindowsManager.h" +#include "Engine/Platform/Clipboard.h" +#include "Engine/Platform/IGuiData.h" +#include "Engine/Platform/Base/PlatformUtils.h" +#include "Engine/Utilities/StringConverter.h" +#include "Engine/Threading/Threading.h" +#include "Engine/Engine/Engine.h" +#include "Engine/Engine/CommandLine.h" +#include "Engine/Input/Input.h" +#include "Engine/Input/Mouse.h" +#include "Engine/Input/Keyboard.h" + +CPUInfo MacCpu; +Guid DeviceId; +String UserLocale, ComputerName; +byte MacAddress[6]; + +DialogResult MessageBox::Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon) +{ + if (CommandLine::Options.Headless) + return DialogResult::None; + todo; +} + +class MacKeyboard : public Keyboard +{ +public: + explicit MacKeyboard() + : Keyboard() + { + } +}; + +class MacMouse : public Mouse +{ +public: + explicit MacMouse() + : Mouse() + { + } + +public: + + // [Mouse] + void SetMousePosition(const Vector2& newPosition) final override + { + MacPlatform::SetMousePosition(newPosition); + + OnMouseMoved(newPosition); + } +}; + +bool MacPlatform::Is64BitPlatform() +{ + return PLATFORM_64BITS; +} + +CPUInfo MacPlatform::GetCPUInfo() +{ + return MacCpu; +} + +int32 MacPlatform::GetCacheLineSize() +{ + return MacCpu.CacheLineSize; +} + +MemoryStats MacPlatform::GetMemoryStats() +{ + MISSING_CODE("MacPlatform::GetMemoryStats"); + return MemoryStats(); // TODO: platform stats on Mac +} + +ProcessMemoryStats MacPlatform::GetProcessMemoryStats() +{ + MISSING_CODE("MacPlatform::GetProcessMemoryStats"); + return ProcessMemoryStats(); // TODO: platform stats on Mac +} + +uint64 MacPlatform::GetCurrentThreadID() +{ + MISSING_CODE("MacPlatform::GetCurrentThreadID"); + return 0; // TODO: threading on Mac +} + +void MacPlatform::SetThreadPriority(ThreadPriority priority) +{ + // TODO: impl this +} + +void MacPlatform::SetThreadAffinityMask(uint64 affinityMask) +{ + // TODO: impl this +} + +void MacPlatform::Sleep(int32 milliseconds) +{ + MISSING_CODE("MacPlatform::Sleep"); + return; // TODO: clock on Mac +} + +double MacPlatform::GetTimeSeconds() +{ + MISSING_CODE("MacPlatform::GetTimeSeconds"); + return 0.0; // TODO: clock on Mac +} + +uint64 MacPlatform::GetTimeCycles() +{ + MISSING_CODE("MacPlatform::GetTimeCycles"); + return 0; // TODO: clock on Mac +} + +uint64 MacPlatform::GetClockFrequency() +{ + MISSING_CODE("MacPlatform::GetClockFrequency"); + return 0; // TODO: clock on Mac +} + +void MacPlatform::GetSystemTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond) +{ + // Query for calendar time + struct timeval time; + gettimeofday(&time, nullptr); + + // Convert to local time + struct tm localTime; + localtime_r(&time.tv_sec, &localTime); + + // Extract time + year = localTime.tm_year + 1900; + month = localTime.tm_mon + 1; + dayOfWeek = localTime.tm_wday; + day = localTime.tm_mday; + hour = localTime.tm_hour; + minute = localTime.tm_min; + second = localTime.tm_sec; + millisecond = time.tv_usec / 1000; +} + +void MacPlatform::GetUTCTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond) +{ + // Get the calendar time + struct timeval time; + gettimeofday(&time, nullptr); + + // Convert to UTC time + struct tm localTime; + gmtime_r(&time.tv_sec, &localTime); + + // Extract time + year = localTime.tm_year + 1900; + month = localTime.tm_mon + 1; + dayOfWeek = localTime.tm_wday; + day = localTime.tm_mday; + hour = localTime.tm_hour; + minute = localTime.tm_min; + second = localTime.tm_sec; + millisecond = time.tv_usec / 1000; +} + +bool MacPlatform::Init() +{ + if (PlatformBase::Init()) + return true; + + // TODO: get MacCpu + + // TODO: get MacAddress + // TODO: get DeviceId + + // TODO: get username + OnPlatformUserAdd(New(TEXT("User"))); + + // TODO: get UserLocale + // TODO: get ComputerName + + Input::Mouse = Impl::Mouse = New(); + Input::Keyboard = Impl::Keyboard = New(); + + return false; +} + +void MacPlatform::BeforeRun() +{ +} + +void MacPlatform::Tick() +{ + // TODO: app events +} + +void MacPlatform::BeforeExit() +{ +} + +void MacPlatform::Exit() +{ +} + +int32 MacPlatform::GetDpi() +{ + return todo; +} + +String MacPlatform::GetUserLocaleName() +{ + return UserLocale; +} + +String MacPlatform::GetComputerName() +{ + return ComputerName; +} + +bool MacPlatform::GetHasFocus() +{ + // Check if any window is focused + ScopeLock lock(WindowsManager::WindowsLocker); + for (auto window : WindowsManager::Windows) + { + if (window->IsFocused()) + return true; + } + + // Default to true if has no windows open + return WindowsManager::Windows.IsEmpty(); +} + +bool MacPlatform::CanOpenUrl(const StringView& url) +{ + return false; +} + +void MacPlatform::OpenUrl(const StringView& url) +{ +} + +Vector2 MacPlatform::GetMousePosition() +{ + MISSING_CODE("MacPlatform::GetMousePosition"); + return Vector2(0, 0); // TODO: mouse on Mac +} + +void MacPlatform::SetMousePosition(const Vector2& pos) +{ + MISSING_CODE("MacPlatform::SetMousePosition"); + // TODO: mouse on Mac +} + +Vector2 MacPlatform::GetDesktopSize() +{ + MISSING_CODE("MacPlatform::GetDesktopSize"); + return Vector2(0, 0); // TODO: desktop size on Mac +} + +Rectangle MacPlatform::GetMonitorBounds(const Vector2& screenPos) +{ + // TODO: do it in a proper way + return Rectangle(Vector2::Zero, GetDesktopSize()); +} + +Rectangle MacPlatform::GetVirtualDesktopBounds() +{ + // TODO: do it in a proper way + return Rectangle(Vector2::Zero, GetDesktopSize()); +} + +String MacPlatform::GetMainDirectory() +{ + MISSING_CODE("MacPlatform::GetMainDirectory"); + return TEXT("/"); // TODO: GetMainDirectory +} + +String MacPlatform::GetExecutableFilePath() +{ + MISSING_CODE("MacPlatform::GetExecutableFilePath"); + return TEXT("/"); // TODO: GetMainDirectory +} + +Guid MacPlatform::GetUniqueDeviceId() +{ + return DeviceId; +} + +String MacPlatform::GetWorkingDirectory() +{ + char buffer[256]; + getcwd(buffer, ARRAY_COUNT(buffer)); + return String(buffer); +} + +bool MacPlatform::SetWorkingDirectory(const String& path) +{ + return chdir(StringAsANSI<>(*path).Get()) != 0; +} + +Window* MacPlatform::CreateWindow(const CreateWindowSettings& settings) +{ + return New(settings); +} + +bool MacPlatform::GetEnvironmentVariable(const String& name, String& value) +{ + char* env = getenv(StringAsANSI<>(*name).Get()); + if (env) + { + value = String(env); + return false; + } + return true; +} + +bool MacPlatform::SetEnvironmentVariable(const String& name, const String& value) +{ + return setenv(StringAsANSI<>(*name).Get(), StringAsANSI<>(*value).Get(), true) != 0; +} + +void* MacPlatform::LoadLibrary(const Char* filename) +{ + MISSING_CODE("MacPlatform::LoadLibrary"); + return nullptr; // TODO: dynamic libs on Mac +} + +void MacPlatform::FreeLibrary(void* handle) +{ + MISSING_CODE("MacPlatform::FreeLibrary"); + return; // TODO: dynamic libs on Mac +} + +void* MacPlatform::GetProcAddress(void* handle, const char* symbol) +{ + MISSING_CODE("MacPlatform::GetProcAddress"); + return nullptr; // TODO: dynamic libs on Mac +} + +#endif diff --git a/Source/Engine/Platform/Mac/MacPlatform.h b/Source/Engine/Platform/Mac/MacPlatform.h new file mode 100644 index 000000000..86bb07e63 --- /dev/null +++ b/Source/Engine/Platform/Mac/MacPlatform.h @@ -0,0 +1,112 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#if PLATFORM_MAC + +#include "../Base/PlatformBase.h" + +/// +/// The Mac platform implementation and application management utilities. +/// +class FLAXENGINE_API MacPlatform : public UnixPlatform +{ +public: + + // [UnixPlatform] + FORCE_INLINE static void MemoryBarrier() + { + __sync_synchronize(); + } + FORCE_INLINE static int64 InterlockedExchange(int64 volatile* dst, int64 exchange) + { + return __sync_lock_test_and_set(dst, exchange); + } + FORCE_INLINE static int32 InterlockedCompareExchange(int32 volatile* dst, int32 exchange, int32 comperand) + { + return __sync_val_compare_and_swap(dst, comperand, exchange); + } + FORCE_INLINE static int64 InterlockedCompareExchange(int64 volatile* dst, int64 exchange, int64 comperand) + { + return __sync_val_compare_and_swap(dst, comperand, exchange); + } + FORCE_INLINE static int64 InterlockedIncrement(int64 volatile* dst) + { + return __sync_add_and_fetch(dst, 1); + } + FORCE_INLINE static int64 InterlockedDecrement(int64 volatile* dst) + { + return __sync_sub_and_fetch(dst, 1); + } + FORCE_INLINE static int64 InterlockedAdd(int64 volatile* dst, int64 value) + { + return __sync_fetch_and_add(dst, value); + } + FORCE_INLINE static int32 AtomicRead(int32 volatile* dst) + { + int32 result; + __atomic_load(dst, &result, __ATOMIC_SEQ_CST); + return result; + } + FORCE_INLINE static int64 AtomicRead(int64 volatile* dst) + { + int64 result; + __atomic_load(dst, &result, __ATOMIC_SEQ_CST); + return result; + } + FORCE_INLINE static void AtomicStore(int32 volatile* dst, int32 value) + { + __atomic_store(dst, &value, __ATOMIC_SEQ_CST); + } + FORCE_INLINE static void AtomicStore(int64 volatile* dst, int64 value) + { + __atomic_store(dst, &value, __ATOMIC_SEQ_CST); + } + FORCE_INLINE static void Prefetch(void const* ptr) + { + __builtin_prefetch(static_cast(ptr)); + } + static bool Is64BitPlatform(); + static CPUInfo GetCPUInfo(); + static int32 GetCacheLineSize(); + static MemoryStats GetMemoryStats(); + static ProcessMemoryStats GetProcessMemoryStats(); + static uint64 GetCurrentThreadID(); + static void SetThreadPriority(ThreadPriority priority); + static void SetThreadAffinityMask(uint64 affinityMask); + static void Sleep(int32 milliseconds); + static double GetTimeSeconds(); + static uint64 GetTimeCycles(); + static uint64 GetClockFrequency(); + static void GetSystemTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond); + static void GetUTCTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond); + static bool Init(); + static void BeforeRun(); + static void Tick(); + static void BeforeExit(); + static void Exit(); + static int32 GetDpi(); + static String GetUserLocaleName(); + static String GetComputerName(); + static bool GetHasFocus(); + static bool CanOpenUrl(const StringView& url); + static void OpenUrl(const StringView& url); + static Vector2 GetMousePosition(); + static void SetMousePosition(const Vector2& pos); + static Rectangle GetMonitorBounds(const Vector2& screenPos); + static Vector2 GetDesktopSize(); + static Rectangle GetVirtualDesktopBounds(); + static String GetMainDirectory(); + static String GetExecutableFilePath(); + static Guid GetUniqueDeviceId(); + static String GetWorkingDirectory(); + static bool SetWorkingDirectory(const String& path); + static Window* CreateWindow(const CreateWindowSettings& settings); + static bool GetEnvironmentVariable(const String& name, String& value); + static bool SetEnvironmentVariable(const String& name, const String& value); + static void* LoadLibrary(const Char* filename); + static void FreeLibrary(void* handle); + static void* GetProcAddress(void* handle, const char* symbol); +}; + +#endif diff --git a/Source/Engine/Platform/Mac/MacPlatformSettings.h b/Source/Engine/Platform/Mac/MacPlatformSettings.h new file mode 100644 index 000000000..c5030fc58 --- /dev/null +++ b/Source/Engine/Platform/Mac/MacPlatformSettings.h @@ -0,0 +1,31 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#if PLATFORM_MAC || USE_EDITOR + +#include "Engine/Core/Config/PlatformSettingsBase.h" + +/// +/// Mac platform settings. +/// +API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API MacPlatformSettings : public SettingsBase +{ + DECLARE_SCRIPTING_TYPE_MINIMAL(MacPlatformSettings); + + /// + /// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use. + /// + static MacPlatformSettings* Get(); + + // [SettingsBase] + void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override + { + } +}; + +#if PLATFORM_MAC +typedef MacPlatformSettings PlatformSettings; +#endif + +#endif diff --git a/Source/Engine/Platform/Mac/MacStringUtils.cpp b/Source/Engine/Platform/Mac/MacStringUtils.cpp new file mode 100644 index 000000000..f84026c46 --- /dev/null +++ b/Source/Engine/Platform/Mac/MacStringUtils.cpp @@ -0,0 +1,277 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#if PLATFORM_MAC + +#include "Engine/Platform/StringUtils.h" +#include +#include +#include +#include +#include + +bool StringUtils::IsUpper(char c) +{ + return isupper(c) != 0; +} + +bool StringUtils::IsLower(char c) +{ + return islower(c) != 0; +} + +bool StringUtils::IsAlpha(char c) +{ + return iswalpha(c) != 0; +} + +bool StringUtils::IsPunct(char c) +{ + return ispunct(c) != 0; +} + +bool StringUtils::IsAlnum(char c) +{ + return isalnum(c) != 0; +} + +bool StringUtils::IsDigit(char c) +{ + return isdigit(c) != 0; +} + +bool StringUtils::IsHexDigit(char c) +{ + return isxdigit(c) != 0; +} + +bool StringUtils::IsWhitespace(char c) +{ + return isspace(c) != 0; +} + +char StringUtils::ToUpper(char c) +{ + return toupper(c); +} + +char StringUtils::ToLower(char c) +{ + return tolower(c); +} + +bool StringUtils::IsUpper(Char c) +{ + return iswupper(c) != 0; +} + +bool StringUtils::IsLower(Char c) +{ + return iswlower(c) != 0; +} + +bool StringUtils::IsAlpha(Char c) +{ + return iswalpha(c) != 0; +} + +bool StringUtils::IsPunct(Char c) +{ + return iswpunct(c) != 0; +} + +bool StringUtils::IsAlnum(Char c) +{ + return iswalnum(c) != 0; +} + +bool StringUtils::IsDigit(Char c) +{ + return iswdigit(c) != 0; +} + +bool StringUtils::IsHexDigit(Char c) +{ + return iswxdigit(c) != 0; +} + +bool StringUtils::IsWhitespace(Char c) +{ + return iswspace(c) != 0; +} + +Char StringUtils::ToUpper(Char c) +{ + return towupper(c); +} + +Char StringUtils::ToLower(Char c) +{ + return towlower(c); +} + +int32 StringUtils::Compare(const Char* str1, const Char* str2) +{ + Char c1, c2; + int32 i; + do + { + c1 = *str1++; + c2 = *str2++; + i = (int32)c1 - (int32)c2; + } while (i == 0 && c1 && c2); + return i; +} + +int32 StringUtils::Compare(const Char* str1, const Char* str2, int32 maxCount) +{ + Char c1, c2; + int32 i; + if (maxCount == 0) + return 0; + do + { + c1 = *str1++; + c2 = *str2++; + i = (int32)c1 - (int32)c2; + maxCount--; + } while (i == 0 && c1 && c2 && maxCount); + return i; +} + +int32 StringUtils::CompareIgnoreCase(const Char* str1, const Char* str2) +{ + Char c1, c2; + int32 i; + do + { + c1 = ToLower(*str1++); + c2 = ToLower(*str2++); + i = (int32)c1 - (int32)c2; + } while (i == 0 && c1 && c2); + return i; +} + +int32 StringUtils::CompareIgnoreCase(const Char* str1, const Char* str2, int32 maxCount) +{ + Char c1, c2; + int32 i; + if (maxCount == 0) + return 0; + do + { + c1 = ToLower(*str1++); + c2 = ToLower(*str2++); + i = (int32)c1 - (int32)c2; + maxCount--; + } while (i == 0 && c1 && c2 && maxCount); + return i; +} + +int32 StringUtils::Length(const Char* str) +{ + if (!str) + return 0; + const Char* ptr = str; + for (; *ptr; ++ptr) + { + } + return ptr - str; +} + +int32 StringUtils::Length(const char* str) +{ + if (!str) + return 0; + return static_cast(strlen(str)); +} + +int32 StringUtils::Compare(const char* str1, const char* str2) +{ + return strcmp(str1, str2); +} + +int32 StringUtils::Compare(const char* str1, const char* str2, int32 maxCount) +{ + return strncmp(str1, str2, maxCount); +} + +int32 StringUtils::CompareIgnoreCase(const char* str1, const char* str2) +{ + return strcasecmp(str1, str2); +} + +int32 StringUtils::CompareIgnoreCase(const char* str1, const char* str2, int32 maxCount) +{ + return strncasecmp(str1, str2, maxCount); +} + +Char* StringUtils::Copy(Char* dst, const Char* src) +{ + Char* q = dst; + const Char* p = src; + Char ch; + do + { + *q++ = ch = *p++; + } while (ch); + return dst; +} + +Char* StringUtils::Copy(Char* dst, const Char* src, int32 count) +{ + Char* q = dst; + const Char* p = src; + char ch; + while (count) + { + count--; + *q++ = ch = *p++; + if (!ch) + break; + } + *q = 0; + return dst; +} + +const Char* StringUtils::Find(const Char* str, const Char* toFind) +{ + while (*str) + { + const Char* start = str; + const Char* sub = toFind; + + // If first character of sub string match, check for whole string + while (*str && *sub && *str == *sub) + { + str++; + sub++; + } + + // If complete substring match, return starting address + if (!*sub) + return (Char*)start; + + // Increment main string + str = start + 1; + } + + // No matches + return nullptr; +} + +const char* StringUtils::Find(const char* str, const char* toFind) +{ + return strstr(str, toFind); +} + +void StringUtils::ConvertANSI2UTF16(const char* from, Char* to, int32 len) +{ + todo; +} + +void StringUtils::ConvertUTF162ANSI(const Char* from, char* to, int32 len) +{ + todo; +} + +#endif diff --git a/Source/Engine/Platform/Mac/MacThread.cpp b/Source/Engine/Platform/Mac/MacThread.cpp new file mode 100644 index 000000000..dba77dda6 --- /dev/null +++ b/Source/Engine/Platform/Mac/MacThread.cpp @@ -0,0 +1,23 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#if PLATFORM_MAC + +#include "MacThread.h" +#include "Engine/Threading/IRunnable.h" +#include "Engine/Threading/ThreadRegistry.h" + +MacThread::MacThread(IRunnable* runnable, const String& name, ThreadPriority priority) + : ThreadBase(runnable, name, priority) +{ +} + +MacThread::~MacThread() +{ +} + +MacThread* Create(IRunnable* runnable, const String& name, ThreadPriority priority = ThreadPriority::Normal, uint32 stackSize = 0) +{ + // TODO: imp this +} + +#endif diff --git a/Source/Engine/Platform/Mac/MacThread.h b/Source/Engine/Platform/Mac/MacThread.h new file mode 100644 index 000000000..d30ba696b --- /dev/null +++ b/Source/Engine/Platform/Mac/MacThread.h @@ -0,0 +1,31 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#if PLATFORM_MAC + +#include "../Base/ThreadBase.h" + +/// +/// Thread object for Mac platform. +/// +class MacThread : public ThreadBase +{ +public: + + MacThread(IRunnable* runnable, const String& name, ThreadPriority priority); + ~MacThread(); + static MacThread* Create(IRunnable* runnable, const String& name, ThreadPriority priority = ThreadPriority::Normal, uint32 stackSize = 0); + + // [ThreadBase] + void Join() override; + +protected: + + // [ThreadBase] + void ClearHandleInternal(); + void SetPriorityInternal(ThreadPriority priority); + void KillInternal(bool waitForJoin); +}; + +#endif diff --git a/Source/Engine/Platform/Mac/MacWindow.cpp b/Source/Engine/Platform/Mac/MacWindow.cpp new file mode 100644 index 000000000..7afa8176e --- /dev/null +++ b/Source/Engine/Platform/Mac/MacWindow.cpp @@ -0,0 +1,103 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#if PLATFORM_MAC + +#include "../Window.h" + +MacWindow::MacWindow(const CreateWindowSettings& settings) + : WindowBase(settings) +{ + int32 x = Math::TruncToInt(settings.Position.X); + int32 y = Math::TruncToInt(settings.Position.Y); + int32 clientWidth = Math::TruncToInt(settings.Size.X); + int32 clientHeight = Math::TruncToInt(settings.Size.Y); + int32 windowWidth = clientWidth; + int32 windowHeight = clientHeight; + _clientSize = Vector2((float)clientWidth, (float)clientHeight); + + // TODO: setup window +} + +MacWindow::~MacWindow() +{ + // TODO: close window +} + +void* MacWindow::GetNativePtr() const +{ + // TODO: return window handle + return nullptr; +} + +void MacWindow::Show() +{ + if (!_visible) + { + InitSwapChain(); + if (_showAfterFirstPaint) + { + if (RenderTask) + RenderTask->Enabled = true; + return; + } + + // Show + // TODO: show window + _focused = true; + + // Base + WindowBase::Show(); + } +} + +void MacWindow::Hide() +{ + if (_visible) + { + // Hide + // TODO: hide window + + // Base + WindowBase::Hide(); + } +} + +void MacWindow::Minimize() +{ + // TODO: Minimize +} + +void MacWindow::Maximize() +{ + // TODO: Maximize +} + +void MacWindow::Restore() +{ + // TODO: Restore +} + +bool MacWindow::IsClosed() const +{ + return false; +} + +bool MacWindow::IsForegroundWindow() const +{ + return Platform::GetHasFocus(); +} + +void MacWindow::SetIsFullscreen(bool isFullscreen) +{ +} + +void MacWindow::GetScreenInfo(int32& x, int32& y, int32& width, int32& height) const +{ + // TODO: proper screen info + x = 0; + y = 0; + width = (int32)_clientSize.X; + height = (int32)_clientSize.Y; +} + +#endif diff --git a/Source/Engine/Platform/Mac/MacWindow.h b/Source/Engine/Platform/Mac/MacWindow.h new file mode 100644 index 000000000..1bdd1ff55 --- /dev/null +++ b/Source/Engine/Platform/Mac/MacWindow.h @@ -0,0 +1,38 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#if PLATFORM_MAC + +#include "Engine/Platform/Base/WindowBase.h" +#include "Engine/Platform/Platform.h" + +/// +/// Implementation of the window class for Mac platform. +/// +class MacWindow : public WindowBase +{ +private: + + Vector2 _clientSize; + +public: + + MacWindow(const CreateWindowSettings& settings); + ~MacWindow(); + +public: + + // [Window] + void* GetNativePtr() const override; + void Show() override; + void Hide() override; + void Minimize() override; + void Maximize() override; + void Restore() override; + bool IsClosed() const override; + bool IsForegroundWindow() const override; + void SetIsFullscreen(bool isFullscreen) override; +}; + +#endif diff --git a/Source/Engine/Platform/Network.h b/Source/Engine/Platform/Network.h index 1e04b185d..03435c4e0 100644 --- a/Source/Engine/Platform/Network.h +++ b/Source/Engine/Platform/Network.h @@ -2,24 +2,18 @@ #pragma once -#if PLATFORM_WINDOWS +#if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT #include "Win32/Win32Network.h" -#elif PLATFORM_UWP -#include "Win32/Win32Network.h" -#elif PLATFORM_LINUX +#elif PLATFORM_LINUX || PLATFORM_ANDROID #include "Unix/UnixNetwork.h" #elif PLATFORM_PS4 #include "Platforms/PS4/Engine/Platform/PS4Network.h" #elif PLATFORM_PS5 #include "Platforms/PS5/Engine/Platform/PS5Network.h" -#elif PLATFORM_XBOX_ONE -#include "Win32/Win32Network.h" -#elif PLATFORM_XBOX_SCARLETT -#include "Win32/Win32Network.h" -#elif PLATFORM_ANDROID -#include "Unix/UnixNetwork.h" #elif PLATFORM_SWITCH #include "Platforms/Switch/Engine/Platform/SwitchNetwork.h" +#elif PLATFORM_MAC +#include "Mac/MacNetwork.h" #else #error Missing Network implementation! #endif diff --git a/Source/Engine/Platform/Platform.Build.cs b/Source/Engine/Platform/Platform.Build.cs index b8f006a25..05f6078b5 100644 --- a/Source/Engine/Platform/Platform.Build.cs +++ b/Source/Engine/Platform/Platform.Build.cs @@ -77,6 +77,9 @@ public class Platform : EngineModule case TargetPlatform.Switch: options.SourcePaths.Add(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "Switch", "Engine", "Platform")); break; + case TargetPlatform.Mac: + options.SourcePaths.Add(Path.Combine(FolderPath, "Mac")); + break; default: throw new InvalidPlatformException(options.Platform.Target); } if (options.Target.IsEditor) @@ -87,6 +90,7 @@ public class Platform : EngineModule options.SourceFiles.Add(Path.Combine(FolderPath, "Linux", "LinuxPlatformSettings.h")); options.SourceFiles.Add(Path.Combine(FolderPath, "Android", "AndroidPlatformSettings.h")); options.SourceFiles.Add(Path.Combine(FolderPath, "GDK", "GDKPlatformSettings.h")); + options.SourceFiles.Add(Path.Combine(FolderPath, "Mac", "MacPlatformSettings.h")); AddSourceFileIfExists(options, Path.Combine(Globals.EngineRoot, "Source", "Platforms", "XboxOne", "Engine", "Platform", "XboxOnePlatformSettings.h")); AddSourceFileIfExists(options, Path.Combine(Globals.EngineRoot, "Source", "Platforms", "XboxScarlett", "Engine", "Platform", "XboxScarlettPlatformSettings.h")); AddSourceFileIfExists(options, Path.Combine(Globals.EngineRoot, "Source", "Platforms", "PS4", "Engine", "Platform", "PS4PlatformSettings.h")); diff --git a/Source/Engine/Platform/Platform.h b/Source/Engine/Platform/Platform.h index 0b61945e5..5c07844b3 100644 --- a/Source/Engine/Platform/Platform.h +++ b/Source/Engine/Platform/Platform.h @@ -26,6 +26,8 @@ #include "Android/AndroidPlatform.h" #elif PLATFORM_SWITCH #include "Platforms/Switch/Engine/Platform/SwitchPlatform.h" +#elif PLATFORM_MAC +#include "Mac/MacPlatform.h" #else #error Missing Platform implementation! #endif diff --git a/Source/Engine/Platform/Thread.h b/Source/Engine/Platform/Thread.h index feb242ebb..b9a4f8103 100644 --- a/Source/Engine/Platform/Thread.h +++ b/Source/Engine/Platform/Thread.h @@ -2,9 +2,7 @@ #pragma once -#if PLATFORM_WINDOWS -#include "Win32/Win32Thread.h" -#elif PLATFORM_UWP +#if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT #include "Win32/Win32Thread.h" #elif PLATFORM_LINUX #include "Linux/LinuxThread.h" @@ -12,14 +10,12 @@ #include "Platforms/PS4/Engine/Platform/PS4Thread.h" #elif PLATFORM_PS5 #include "Platforms/PS5/Engine/Platform/PS5Thread.h" -#elif PLATFORM_XBOX_ONE -#include "Win32/Win32Thread.h" -#elif PLATFORM_XBOX_SCARLETT -#include "Win32/Win32Thread.h" #elif PLATFORM_ANDROID #include "Android/AndroidThread.h" #elif PLATFORM_SWITCH #include "Platforms/Switch/Engine/Platform/SwitchThread.h" +#elif PLATFORM_MAC +#include "Mac/MacThread.h" #else #error Missing Thread implementation! #endif diff --git a/Source/Engine/Platform/Types.h b/Source/Engine/Platform/Types.h index 3529dcf47..e82bdac71 100644 --- a/Source/Engine/Platform/Types.h +++ b/Source/Engine/Platform/Types.h @@ -147,8 +147,8 @@ class Win32Thread; typedef Win32Thread Thread; class GDKWindow; typedef GDKWindow Window; -class NetworkBase; -typedef NetworkBase Network; +class Win32Network; +typedef Win32Network Network; class GDKUser; typedef GDKUser User; @@ -172,8 +172,8 @@ class Win32Thread; typedef Win32Thread Thread; class GDKWindow; typedef GDKWindow Window; -class NetworkBase; -typedef NetworkBase Network; +class Win32Network; +typedef Win32Network Network; class GDKUser; typedef GDKUser User; @@ -227,6 +227,31 @@ typedef SwitchNetwork Network; class UserBase; typedef UserBase User; +#elif PLATFORM_MAC + +class ClipboardBase; +typedef ClipboardBase Clipboard; +class MacCriticalSection; +typedef MacCriticalSection CriticalSection; +class MacConditionVariable; +typedef MacConditionVariable ConditionVariable; +class MacFileSystem; +typedef MacFileSystem FileSystem; +class FileSystemWatcherBase; +typedef FileSystemWatcherBase FileSystemWatcher; +class MacFile; +typedef MacFile File; +class MacPlatform; +typedef MacPlatform Platform; +class MacThread; +typedef MacThread Thread; +class MacWindow; +typedef MacWindow Window; +class MacNetwork; +typedef MacNetwork Network; +class UserBase; +typedef UserBase User; + #else #error Missing Types implementation! diff --git a/Source/Engine/Platform/Window.h b/Source/Engine/Platform/Window.h index ea2bb6e8e..5e1797727 100644 --- a/Source/Engine/Platform/Window.h +++ b/Source/Engine/Platform/Window.h @@ -20,6 +20,8 @@ #include "Platforms/Switch/Engine/Platform/SwitchWindow.h" #elif PLATFORM_PS5 #include "Platforms/PS5/Engine/Platform/PS5Window.h" +#elif PLATFORM_MAC +#include "Mac/MacWindow.h" #else #error Missing Window implementation! #endif diff --git a/Source/Engine/Tools/TextureTool/TextureTool.Build.cs b/Source/Engine/Tools/TextureTool/TextureTool.Build.cs index d1acb0b4b..bbc2cb8df 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.Build.cs +++ b/Source/Engine/Tools/TextureTool/TextureTool.Build.cs @@ -35,6 +35,7 @@ public class TextureTool : EngineModule case TargetPlatform.PS5: case TargetPlatform.Android: case TargetPlatform.Switch: + case TargetPlatform.Mac: useStb = true; break; default: throw new InvalidPlatformException(options.Platform.Target); diff --git a/Source/Engine/Visject/ShaderGraph.cpp b/Source/Engine/Visject/ShaderGraph.cpp index e572f4db2..b2a1c1b9a 100644 --- a/Source/Engine/Visject/ShaderGraph.cpp +++ b/Source/Engine/Visject/ShaderGraph.cpp @@ -769,6 +769,7 @@ void ShaderGenerator::ProcessGroupTools(Box* box, Node* node, Value& value) PLATFORM_CASE(8, "PLATFORM_ANDROID"); PLATFORM_CASE(9, "PLATFORM_SWITCH"); PLATFORM_CASE(10, "PLATFORM_PS5"); + PLATFORM_CASE(11, "PLATFORM_MAC"); #undef PLATFORM_CASE break; } diff --git a/Source/Engine/Visject/VisjectGraph.cpp b/Source/Engine/Visject/VisjectGraph.cpp index 9e98fa5f5..e3ab770b5 100644 --- a/Source/Engine/Visject/VisjectGraph.cpp +++ b/Source/Engine/Visject/VisjectGraph.cpp @@ -917,6 +917,9 @@ void VisjectExecutor::ProcessGroupTools(Box* box, Node* node, Value& value) case PlatformType::PS5: boxId = 10; break; + case PlatformType::Mac: + boxId = 11; + break; default: ; } value = tryGetValue(node->GetBox(node->GetBox(boxId)->HasConnection() ? boxId : 1), Value::Zero); diff --git a/Source/Tools/Flax.Build/Build/Module.cs b/Source/Tools/Flax.Build/Build/Module.cs index 942cc8e2b..bf1c6df43 100644 --- a/Source/Tools/Flax.Build/Build/Module.cs +++ b/Source/Tools/Flax.Build/Build/Module.cs @@ -97,6 +97,7 @@ namespace Flax.Build case TargetPlatform.XboxScarlett: return "PLATFORM_XBOX_SCARLETT"; case TargetPlatform.Android: return "PLATFORM_ANDROID"; case TargetPlatform.Switch: return "PLATFORM_SWITCH"; + case TargetPlatform.Mac: return "PLATFORM_MAC"; default: throw new InvalidPlatformException(platform); } } diff --git a/Source/Tools/Flax.Build/Build/Platform.cs b/Source/Tools/Flax.Build/Build/Platform.cs index ebc4e9023..a2c875eb0 100644 --- a/Source/Tools/Flax.Build/Build/Platform.cs +++ b/Source/Tools/Flax.Build/Build/Platform.cs @@ -34,6 +34,7 @@ namespace Flax.Build case PlatformID.Win32Windows: case PlatformID.WinCE: return TargetPlatform.Windows; case PlatformID.Unix: return TargetPlatform.Linux; + case PlatformID.MacOSX: return TargetPlatform.Mac; default: throw new NotImplementedException(string.Format("Unsupported build platform {0}.", platformId)); } } @@ -242,6 +243,7 @@ namespace Flax.Build case TargetPlatform.PS5: return targetArchitecture == TargetArchitecture.x64; case TargetPlatform.Android: return targetArchitecture == TargetArchitecture.ARM64; case TargetPlatform.Switch: return targetArchitecture == TargetArchitecture.ARM64; + case TargetPlatform.Mac: return targetArchitecture == TargetArchitecture.x64; default: return false; } } diff --git a/Source/Tools/Flax.Build/Build/TargetPlatform.cs b/Source/Tools/Flax.Build/Build/TargetPlatform.cs index 81b84b6e9..da850ed20 100644 --- a/Source/Tools/Flax.Build/Build/TargetPlatform.cs +++ b/Source/Tools/Flax.Build/Build/TargetPlatform.cs @@ -51,6 +51,11 @@ namespace Flax.Build /// Running on PlayStation 5. /// PS5 = 9, + + /// + /// Running on Mac. + /// + Mac = 10, } /// diff --git a/Source/Tools/Flax.Build/Deps/Dependency.cs b/Source/Tools/Flax.Build/Deps/Dependency.cs index 0a873334e..3ace8dd8d 100644 --- a/Source/Tools/Flax.Build/Deps/Dependency.cs +++ b/Source/Tools/Flax.Build/Deps/Dependency.cs @@ -239,6 +239,7 @@ namespace Flax.Deps case TargetPlatform.Linux: case TargetPlatform.PS4: case TargetPlatform.PS5: + case TargetPlatform.Mac: { cmdLine = "CMakeLists.txt"; break; diff --git a/Source/Tools/Flax.Build/Flax.Build.csproj b/Source/Tools/Flax.Build/Flax.Build.csproj index abf2bfd86..fdfce10d2 100644 --- a/Source/Tools/Flax.Build/Flax.Build.csproj +++ b/Source/Tools/Flax.Build/Flax.Build.csproj @@ -139,6 +139,7 @@ + @@ -160,6 +161,7 @@ + diff --git a/Source/Tools/Flax.Build/Globals.cs b/Source/Tools/Flax.Build/Globals.cs index a9a29af88..61d2382d3 100644 --- a/Source/Tools/Flax.Build/Globals.cs +++ b/Source/Tools/Flax.Build/Globals.cs @@ -36,6 +36,7 @@ namespace Flax.Build TargetPlatform.XboxScarlett, TargetPlatform.Android, TargetPlatform.Switch, + TargetPlatform.Mac, }; /// diff --git a/Source/Tools/Flax.Build/Platforms/Mac/MacPlatform.cs b/Source/Tools/Flax.Build/Platforms/Mac/MacPlatform.cs new file mode 100644 index 000000000..ab21dc54b --- /dev/null +++ b/Source/Tools/Flax.Build/Platforms/Mac/MacPlatform.cs @@ -0,0 +1,60 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +using Flax.Build.Projects; + +namespace Flax.Build.Platforms +{ + /// + /// The build platform for all Mac systems. + /// + /// + public sealed class MacPlatform : Platform + { + /// + public override TargetPlatform Target => TargetPlatform.Mac; + + /// + public override bool HasRequiredSDKsInstalled { get; } + + /// + public override bool HasSharedLibrarySupport => true; + + /// + public override string ExecutableFileExtension => string.Empty; + + /// + public override string SharedLibraryFileExtension => ".dylib"; + + /// + public override string StaticLibraryFileExtension => ".a"; + + /// + public override string ProgramDatabaseFileExtension => ".dSYM"; + + /// + public override string SharedLibraryFilePrefix => string.Empty; + + /// + public override string StaticLibraryFilePrefix => "lib"; + + /// + public override ProjectFormat DefaultProjectFormat => ProjectFormat.XCode; + + /// + /// Initializes a new instance of the class. + /// + public MacPlatform() + { + if (Platform.BuildTargetPlatform != TargetPlatform.Mac) + return; + + throw new System.NotImplementedException("TODO: detect MacSDK installation"); + } + + /// + protected override Toolchain CreateToolchain(TargetArchitecture architecture) + { + return new MacToolchain(this, architecture); + } + } +} diff --git a/Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs b/Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs new file mode 100644 index 000000000..1d1e88d3e --- /dev/null +++ b/Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs @@ -0,0 +1,58 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +using System; +using System.Collections.Generic; +using Flax.Build.Graph; +using Flax.Build.NativeCpp; + +namespace Flax.Build.Platforms +{ + /// + /// The build toolchain for all Mac systems. + /// + /// + public sealed class MacToolchain : Toolchain + { + /// + /// Initializes a new instance of the class. + /// + /// The platform. + /// The target architecture. + public MacToolchain(MacPlatform platform, TargetArchitecture architecture) + : base(platform, architecture) + { + } + + /// + public override string DllExport => "__attribute__((__visibility__(\\\"default\\\")))"; + + /// + public override string DllImport => ""; + + /// + public override void LogInfo() + { + throw new NotImplementedException("TODO: MacToolchain.LogInfo"); + } + + /// + public override void SetupEnvironment(BuildOptions options) + { + base.SetupEnvironment(options); + + options.CompileEnv.PreprocessorDefinitions.Add("PLATFORM_MAC"); + } + + /// + public override CompileOutput CompileCppFiles(TaskGraph graph, BuildOptions options, List sourceFiles, string outputPath) + { + throw new NotImplementedException("TODO: MacToolchain.CompileCppFiles"); + } + + /// + public override void LinkFiles(TaskGraph graph, BuildOptions options, string outputFilePath) + { + throw new NotImplementedException("TODO: MacToolchain.LinkFiles"); + } + } +} diff --git a/Source/Tools/Flax.Build/Projects/ProjectFormat.cs b/Source/Tools/Flax.Build/Projects/ProjectFormat.cs index 4014ddfa9..6ab86b2d5 100644 --- a/Source/Tools/Flax.Build/Projects/ProjectFormat.cs +++ b/Source/Tools/Flax.Build/Projects/ProjectFormat.cs @@ -41,5 +41,10 @@ namespace Flax.Build.Projects /// Visual Studio Code. /// VisualStudioCode, + + /// + /// XCode. + /// + XCode, } } diff --git a/Source/Tools/Flax.Build/Projects/ProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/ProjectGenerator.cs index 820e0d020..f218a9a4f 100644 --- a/Source/Tools/Flax.Build/Projects/ProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/ProjectGenerator.cs @@ -139,6 +139,7 @@ namespace Flax.Build.Projects } } case ProjectFormat.VisualStudioCode: return new VisualStudioCodeProjectGenerator(); + case ProjectFormat.XCode: return new XCodeProjectGenerator(); case ProjectFormat.Custom: if (CustomProjectTypes.TryGetValue(Configuration.ProjectFormatCustom, out var factory)) return factory(type); diff --git a/Source/Tools/Flax.Build/Projects/XCode.cs b/Source/Tools/Flax.Build/Projects/XCode.cs new file mode 100644 index 000000000..56fdd894e --- /dev/null +++ b/Source/Tools/Flax.Build/Projects/XCode.cs @@ -0,0 +1,49 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +using System; + +namespace Flax.Build.Projects +{ + /// + /// Project generator for XCode. + /// + public class XCodeProjectGenerator : ProjectGenerator + { + /// + /// Initializes a new instance of the class. + /// + public XCodeProjectGenerator() + { + } + + /// + public override string ProjectFileExtension => "xcodeproj"; + + /// + public override string SolutionFileExtension => string.Empty; + + /// + public override TargetType? Type => null; + + /// + public override Project CreateProject() + { + return new Project + { + Generator = this, + }; + } + + /// + public override void GenerateProject(Project project) + { + throw new NotImplementedException("TODO: XCode"); + } + + /// + public override void GenerateSolution(Solution solution) + { + throw new NotImplementedException("TODO: XCode"); + } + } +}