Add Mac platform

This commit is contained in:
Wojciech Figat
2021-12-27 14:04:16 +01:00
parent 36a18a0b56
commit d017dd14ca
62 changed files with 2384 additions and 55 deletions

View File

@@ -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);
}

View File

@@ -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)

View File

@@ -177,6 +177,14 @@ namespace FlaxEditor.Content.Settings
public JsonAsset PS5Platform;
#endif
#if FLAX_EDITOR || PLATFORM_MAC
/// <summary>
/// Reference to <see cref="MacPlatformSettings"/> asset. Used to apply configuration on Mac platform.
/// </summary>
[EditorOrder(2090), EditorDisplay("Platform Settings", "Mac"), AssetReference(typeof(MacPlatformSettings), true), Tooltip("Reference to Mac Platform Settings asset")]
public JsonAsset MacPlatform;
#endif
/// <summary>
/// Gets the absolute path to the game settings asset file.
/// </summary>
@@ -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<MacPlatformSettings>(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;
}

View File

@@ -82,6 +82,7 @@ public:
Guid AndroidPlatform;
Guid SwitchPlatform;
Guid PS5Platform;
Guid MacPlatform;
public:

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,19 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_MAC && !USE_EDITOR
#include "../Base/GameBase.h"
/// <summary>
/// The game class implementation for Mac platform.
/// </summary>
/// <seealso cref="Game" />
class MacGame : public GameBase
{
};
typedef MacGame Game;
#endif

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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("");
}

View File

@@ -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"

View File

@@ -53,6 +53,11 @@ API_ENUM() enum class PlatformType
/// Running on PlayStation 5.
/// </summary>
PS5 = 9,
/// <summary>
/// Running on Mac.
/// </summary>
Mac = 10,
};
/// <summary>
@@ -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__)

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,91 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_MAC
#include "MacCriticalSection.h"
#include <pthread.h>
#include <sys/time.h>
/// <summary>
/// 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.
/// </summary>
class FLAXENGINE_API MacConditionVariable
{
private:
pthread_cond_t _cond;
private:
MacConditionVariable(const MacConditionVariable&);
MacConditionVariable& operator=(const MacConditionVariable&);
public:
/// <summary>
/// Initializes a new instance of the <see cref="MacConditionVariable"/> class.
/// </summary>
MacConditionVariable()
{
pthread_cond_init(&_cond, nullptr);
}
/// <summary>
/// Finalizes an instance of the <see cref="MacConditionVariable"/> class.
/// </summary>
~MacConditionVariable()
{
pthread_cond_destroy(&_cond);
}
public:
/// <summary>
/// Blocks the current thread execution until the condition variable is woken up.
/// </summary>
/// <param name="lock">The critical section locked by the current thread.</param>
void Wait(const MacCriticalSection& lock)
{
pthread_cond_wait(&_cond, lock._mutexPtr);
}
/// <summary>
/// Blocks the current thread execution until the condition variable is woken up or after the specified timeout duration.
/// </summary>
/// <param name="lock">The critical section locked by the current thread.</param>
/// <param name="timeout">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.</param>
/// <returns>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.</returns>
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;
}
/// <summary>
/// Notifies one waiting thread.
/// </summary>
void NotifyOne()
{
pthread_cond_signal(&_cond);
}
/// <summary>
/// Notifies all waiting threads.
/// </summary>
void NotifyAll()
{
pthread_cond_broadcast(&_cond);
}
};
#endif

View File

@@ -0,0 +1,92 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_MAC
#include "Engine/Platform/Platform.h"
#include <pthread.h>
class MacConditionVariable;
/// <summary>
/// Mac implementation of a critical section.
/// </summary>
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:
/// <summary>
/// Initializes a new instance of the <see cref="MacCriticalSection"/> class.
/// </summary>
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
}
/// <summary>
/// Finalizes an instance of the <see cref="MacCriticalSection"/> class.
/// </summary>
~MacCriticalSection()
{
pthread_mutex_destroy(&_mutex);
}
public:
/// <summary>
/// Locks the critical section.
/// </summary>
void Lock() const
{
pthread_mutex_lock(_mutexPtr);
#if BUILD_DEBUG
((MacCriticalSection*)this)->_owningThreadId = pthread_self();
#endif
}
/// <summary>
/// Attempts to enter a critical section without blocking. If the call is successful, the calling thread takes ownership of the critical section.
/// </summary>
/// <returns>True if calling thread took ownership of the critical section.</returns>
bool TryLock() const
{
return pthread_mutex_trylock(_mutexPtr) == 0;
}
/// <summary>
/// Releases the lock on the critical section.
/// </summary>
void Unlock() const
{
#if BUILD_DEBUG
((MacCriticalSection*)this)->_owningThreadId = 0;
#endif
pthread_mutex_unlock(_mutexPtr);
}
};
#endif

View File

@@ -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

View File

@@ -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 <sys/file.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <cerrno>
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<MacFile>(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

View File

@@ -0,0 +1,56 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_MAC
#include "Engine/Platform/Base/FileBase.h"
/// <summary>
/// Mac platform file object implementation.
/// </summary>
class MacFile : public FileBase
{
protected:
int32 _handle;
public:
/// <summary>
/// Initializes a new instance of the <see cref="MacFile"/> class.
/// </summary>
/// <param name="handle">The handle.</param>
MacFile(int32 handle);
/// <summary>
/// Finalizes an instance of the <see cref="MacFile"/> class.
/// </summary>
~MacFile();
public:
/// <summary>
/// Creates or opens a file.
/// </summary>
/// <param name="path">The name of the file to be created or opened.</param>
/// <param name="mode">An action to take on a file that exists or does not exist.</param>
/// <param name="access">The requested access to the file.</param>
/// <param name="share">The requested sharing mode of the file.</param>
/// <returns>Opened file handle or null if cannot.</returns>
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

View File

@@ -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 <sys/types.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <unistd.h>
#include <stdio.h>
#include <cerrno>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
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<String>& 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<String>& 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<String>& 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<String>& 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

View File

@@ -0,0 +1,52 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_MAC
#include "Engine/Platform/Base/FileSystemBase.h"
/// <summary>
/// Mac platform implementation of filesystem service.
/// </summary>
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<String, HeapAllocation>& results, const String& path, const Char* searchPattern, DirectorySearchOption option = DirectorySearchOption::AllDirectories);
static bool GetChildDirectories(Array<String, HeapAllocation>& 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:
/// <summary>
/// Gets last time when file has been modified (in UTC).
/// </summary>
/// <param name="path">The file path to check.</param>
/// <returns>The last write time or DateTime::MinValue() if cannot get data.</returns>
static DateTime GetFileLastEditTime(const StringView& path);
/// <summary>
/// Gets the special folder path.
/// </summary>
/// <param name="type">The folder type.</param>
/// <param name="result">The result full path.</param>
static void GetSpecialFolderPath(const SpecialFolder type, String& result);
private:
static bool getFilesFromDirectoryTop(Array<String, HeapAllocation>& results, const char* path, const char* searchPattern);
static bool getFilesFromDirectoryAll(Array<String, HeapAllocation>& results, const char* path, const char* searchPattern);
};
#endif

View File

@@ -0,0 +1,7 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#if PLATFORM_MAC
// TODO: networking on Mac
#endif

View File

@@ -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

View File

@@ -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<User>(TEXT("User")));
// TODO: get UserLocale
// TODO: get ComputerName
Input::Mouse = Impl::Mouse = New<MacMouse>();
Input::Keyboard = Impl::Keyboard = New<MacKeyboard>();
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<MacWindow>(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

View File

@@ -0,0 +1,112 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_MAC
#include "../Base/PlatformBase.h"
/// <summary>
/// The Mac platform implementation and application management utilities.
/// </summary>
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<char const*>(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

View File

@@ -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"
/// <summary>
/// Mac platform settings.
/// </summary>
API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API MacPlatformSettings : public SettingsBase
{
DECLARE_SCRIPTING_TYPE_MINIMAL(MacPlatformSettings);
/// <summary>
/// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use.
/// </summary>
static MacPlatformSettings* Get();
// [SettingsBase]
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override
{
}
};
#if PLATFORM_MAC
typedef MacPlatformSettings PlatformSettings;
#endif
#endif

View File

@@ -0,0 +1,277 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#if PLATFORM_MAC
#include "Engine/Platform/StringUtils.h"
#include <wctype.h>
#include <cctype>
#include <wchar.h>
#include <cstring>
#include <stdlib.h>
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<int32>(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

View File

@@ -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

View File

@@ -0,0 +1,31 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_MAC
#include "../Base/ThreadBase.h"
/// <summary>
/// Thread object for Mac platform.
/// </summary>
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

View File

@@ -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

View File

@@ -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"
/// <summary>
/// Implementation of the window class for Mac platform.
/// </summary>
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

View File

@@ -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

View File

@@ -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"));

View File

@@ -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

View File

@@ -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

View File

@@ -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!

View File

@@ -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

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);