Add iOS platform (refactor Mac into shared Apple platform impl)

This commit is contained in:
Wojtek Figat
2023-03-15 20:57:44 +01:00
parent dc29ee180e
commit 0ba261d338
84 changed files with 2806 additions and 1623 deletions

View File

@@ -996,16 +996,6 @@ Float2 AndroidPlatform::GetDesktopSize()
return Float2((float)ScreenWidth, (float)ScreenHeight);
}
Rectangle AndroidPlatform::GetMonitorBounds(const Float2& screenPos)
{
return Rectangle(Float2::Zero, GetDesktopSize());
}
Rectangle AndroidPlatform::GetVirtualDesktopBounds()
{
return Rectangle(Float2::Zero, GetDesktopSize());
}
String AndroidPlatform::GetMainDirectory()
{
return String(App->activity->internalDataPath);

View File

@@ -120,9 +120,7 @@ public:
static void OpenUrl(const StringView& url);
static Float2 GetMousePosition();
static void SetMousePosition(const Float2& pos);
static Rectangle GetMonitorBounds(const Float2& screenPos);
static Float2 GetDesktopSize();
static Rectangle GetVirtualDesktopBounds();
static String GetMainDirectory();
static String GetExecutableFilePath();
static Guid GetUniqueDeviceId();

View File

@@ -0,0 +1,532 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#if PLATFORM_MAC || PLATFORM_IOS
#include "AppleFileSystem.h"
#include "AppleUtils.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 <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 AppleFileSystem::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 AppleFileSystem::DeleteDirectory(const String& path, bool deleteContents)
{
const StringAsANSI<> pathANSI(*path, path.Length());
if (deleteContents)
return DeletePathTree(pathANSI.Get());
return rmdir(pathANSI.Get()) != 0;
}
bool AppleFileSystem::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 AppleFileSystem::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 AppleFileSystem::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 AppleFileSystem::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 AppleFileSystem::DeleteFile(const StringView& path)
{
const StringAsANSI<> pathANSI(*path, path.Length());
return unlink(pathANSI.Get()) == 0;
}
uint64 AppleFileSystem::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 AppleFileSystem::IsReadOnly(const StringView& path)
{
const StringAsANSI<> pathANSI(*path, path.Length());
if (access(pathANSI.Get(), W_OK) == -1)
{
return errno == EACCES;
}
return false;
}
bool AppleFileSystem::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 AppleFileSystem::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 AppleFileSystem::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;
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 AppleFileSystem::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 AppleFileSystem::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 AppleFileSystem::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 AppleFileSystem::GetSpecialFolderPath(const SpecialFolder type, String& result)
{
String home;
Platform::GetEnvironmentVariable(TEXT("HOME"), home);
switch (type)
{
case SpecialFolder::Desktop:
result = home / TEXT("/Desktop");
break;
case SpecialFolder::Documents:
result = home / TEXT("/Documents");
break;
case SpecialFolder::Pictures:
result = home / TEXT("/Pictures");
break;
case SpecialFolder::AppData:
case SpecialFolder::LocalAppData:
result = home / TEXT("/Library/Caches");
break;
case SpecialFolder::ProgramData:
result = home / TEXT("/Library/Application Support");
break;
case SpecialFolder::Temporary:
Platform::GetEnvironmentVariable(TEXT("TMPDIR"), result);
break;
}
}
#endif

View File

@@ -0,0 +1,52 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_MAC || PLATFORM_IOS
#include "Engine/Platform/Base/FileSystemBase.h"
/// <summary>
/// Apple platform implementation of filesystem service.
/// </summary>
class FLAXENGINE_API AppleFileSystem : 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,418 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#if PLATFORM_MAC || PLATFORM_IOS
#include "ApplePlatform.h"
#include "AppleUtils.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/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 <unistd.h>
#include <cstdint>
#include <stdlib.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <mach/mach_time.h>
#include <mach-o/dyld.h>
#include <uuid/uuid.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreGraphics/CoreGraphics.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <IOKit/IOKitLib.h>
#include <dlfcn.h>
#if CRASH_LOG_ENABLE
#include <execinfo.h>
#endif
CPUInfo Cpu;
String UserLocale;
double SecondsPerCycle;
float ApplePlatform::ScreenScale = 1.0f;
String AppleUtils::ToString(CFStringRef str)
{
if (!str)
return String::Empty;
String result;
const int32 length = CFStringGetLength(str);
if (length > 0)
{
CFRange range = CFRangeMake(0, length);
result.ReserveSpace(length);
CFStringGetBytes(str, range, kCFStringEncodingUTF16LE, '?', false, (uint8*)result.Get(), length * sizeof(Char), nullptr);
}
return result;
}
CFStringRef AppleUtils::ToString(const StringView& str)
{
return CFStringCreateWithBytes(nullptr, (const UInt8*)str.GetText(), str.Length() * sizeof(Char), kCFStringEncodingUTF16LE, false);
}
typedef uint16_t offset_t;
#define align_mem_up(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
bool ApplePlatform::Is64BitPlatform()
{
return PLATFORM_64BITS;
}
CPUInfo ApplePlatform::GetCPUInfo()
{
return Cpu;
}
int32 ApplePlatform::GetCacheLineSize()
{
return Cpu.CacheLineSize;
}
MemoryStats ApplePlatform::GetMemoryStats()
{
MemoryStats result;
int64 value64;
size_t value64Size = sizeof(value64);
if (sysctlbyname("hw.memsize", &value64, &value64Size, nullptr, 0) != 0)
value64 = 1024 * 1024;
result.TotalPhysicalMemory = value64;
int id[] = { CTL_HW, HW_MEMSIZE };
if (sysctl(id, 2, &value64, &value64Size, nullptr, 0) != 0)
value64Size = 1024;
result.UsedPhysicalMemory = value64Size;
xsw_usage swapusage;
size_t swapusageSize = sizeof(swapusage);
result.TotalVirtualMemory = result.TotalPhysicalMemory;
result.UsedVirtualMemory = result.UsedPhysicalMemory;
if (sysctlbyname("vm.swapusage", &swapusage, &swapusageSize, nullptr, 0) == 0)
{
result.TotalVirtualMemory += swapusage.xsu_total;
result.UsedVirtualMemory += swapusage.xsu_used;
}
return result;
}
ProcessMemoryStats ApplePlatform::GetProcessMemoryStats()
{
ProcessMemoryStats result;
result.UsedPhysicalMemory = 1024;
result.UsedVirtualMemory = 1024;
return result;
}
uint64 ApplePlatform::GetCurrentThreadID()
{
return (uint64)pthread_mach_thread_np(pthread_self());
}
void ApplePlatform::SetThreadPriority(ThreadPriority priority)
{
// TODO: impl this
}
void ApplePlatform::SetThreadAffinityMask(uint64 affinityMask)
{
// TODO: impl this
}
void ApplePlatform::Sleep(int32 milliseconds)
{
usleep(milliseconds * 1000);
}
double ApplePlatform::GetTimeSeconds()
{
return SecondsPerCycle * mach_absolute_time();
}
uint64 ApplePlatform::GetTimeCycles()
{
return mach_absolute_time();
}
uint64 ApplePlatform::GetClockFrequency()
{
return (uint64)(1.0 / SecondsPerCycle);
}
void ApplePlatform::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 ApplePlatform::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 ApplePlatform::Init()
{
if (UnixPlatform::Init())
return true;
// Init timing
{
mach_timebase_info_data_t info;
mach_timebase_info(&info);
SecondsPerCycle = 1e-9 * (double)info.numer / (double)info.denom;
}
// Get CPU info
int32 value32;
int64 value64;
size_t value32Size = sizeof(value32), value64Size = sizeof(value64);
if (sysctlbyname("hw.packages", &value32, &value32Size, nullptr, 0) != 0)
value32 = 1;
Cpu.ProcessorPackageCount = value32;
if (sysctlbyname("hw.physicalcpu", &value32, &value32Size, nullptr, 0) != 0)
value32 = 1;
Cpu.ProcessorCoreCount = value32;
if (sysctlbyname("hw.logicalcpu", &value32, &value32Size, nullptr, 0) != 0)
value32 = 1;
Cpu.LogicalProcessorCount = value32;
if (sysctlbyname("hw.l1icachesize", &value32, &value32Size, nullptr, 0) != 0)
value32 = 0;
Cpu.L1CacheSize = value32;
if (sysctlbyname("hw.l2cachesize", &value32, &value32Size, nullptr, 0) != 0)
value32 = 0;
Cpu.L2CacheSize = value32;
if (sysctlbyname("hw.l3cachesize", &value32, &value32Size, nullptr, 0) != 0)
value32 = 0;
Cpu.L3CacheSize = value32;
if (sysctlbyname("hw.pagesize", &value32, &value32Size, nullptr, 0) != 0)
value32 = vm_page_size;
Cpu.PageSize = value32;
if (sysctlbyname("hw.cpufrequency_max", &value64, &value64Size, nullptr, 0) != 0)
value64 = GetClockFrequency();
Cpu.ClockSpeed = value64;
if (sysctlbyname("hw.cachelinesize", &value32, &value32Size, nullptr, 0) != 0)
value32 = PLATFORM_CACHE_LINE_SIZE;
Cpu.CacheLineSize = value32;
// Get locale
{
CFLocaleRef locale = CFLocaleCopyCurrent();
CFStringRef localeLang = (CFStringRef)CFLocaleGetValue(locale, kCFLocaleLanguageCode);
CFStringRef localeCountry = (CFStringRef)CFLocaleGetValue(locale, kCFLocaleCountryCode);
UserLocale = AppleUtils::ToString(localeLang);
String localeCountryStr = AppleUtils::ToString(localeCountry);
if (localeCountryStr.HasChars())
UserLocale += TEXT("-") + localeCountryStr;
CFRelease(locale);
CFRelease(localeLang);
CFRelease(localeCountry);
}
// Init user
{
String username;
GetEnvironmentVariable(TEXT("USER"), username);
OnPlatformUserAdd(New<User>(username));
}
return false;
}
void ApplePlatform::Tick()
{
}
void ApplePlatform::BeforeExit()
{
}
void ApplePlatform::Exit()
{
}
void ApplePlatform::SetHighDpiAwarenessEnabled(bool enable)
{
// Disable resolution scaling in low dpi mode
if (!enable)
{
CustomDpiScale /= ScreenScale;
ScreenScale = 1.0f;
}
}
String ApplePlatform::GetUserLocaleName()
{
return UserLocale;
}
bool ApplePlatform::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();
}
void ApplePlatform::CreateGuid(Guid& result)
{
uuid_t uuid;
uuid_generate(uuid);
auto ptr = (uint32*)&uuid;
result.A = ptr[0];
result.B = ptr[1];
result.C = ptr[2];
result.D = ptr[3];
}
bool ApplePlatform::CanOpenUrl(const StringView& url)
{
return false;
}
void ApplePlatform::OpenUrl(const StringView& url)
{
}
String ApplePlatform::GetExecutableFilePath()
{
char buf[PATH_MAX];
uint32 size = PATH_MAX;
String result;
if (_NSGetExecutablePath(buf, &size) == 0)
result.SetUTF8(buf, StringUtils::Length(buf));
return result;
}
String ApplePlatform::GetWorkingDirectory()
{
char buffer[256];
getcwd(buffer, ARRAY_COUNT(buffer));
return String(buffer);
}
bool ApplePlatform::SetWorkingDirectory(const String& path)
{
return chdir(StringAsANSI<>(*path).Get()) != 0;
}
bool ApplePlatform::GetEnvironmentVariable(const String& name, String& value)
{
char* env = getenv(StringAsANSI<>(*name).Get());
if (env)
{
value = String(env);
return false;
}
return true;
}
bool ApplePlatform::SetEnvironmentVariable(const String& name, const String& value)
{
return setenv(StringAsANSI<>(*name).Get(), StringAsANSI<>(*value).Get(), true) != 0;
}
void* ApplePlatform::LoadLibrary(const Char* filename)
{
const StringAsANSI<> filenameANSI(filename);
void* result = dlopen(filenameANSI.Get(), RTLD_LAZY | RTLD_LOCAL);
if (!result)
{
LOG(Error, "Failed to load {0} because {1}", filename, String(dlerror()));
}
return result;
}
void ApplePlatform::FreeLibrary(void* handle)
{
dlclose(handle);
}
void* ApplePlatform::GetProcAddress(void* handle, const char* symbol)
{
return dlsym(handle, symbol);
}
Array<ApplePlatform::StackFrame> ApplePlatform::GetStackFrames(int32 skipCount, int32 maxDepth, void* context)
{
Array<StackFrame> result;
#if CRASH_LOG_ENABLE
void* callstack[120];
skipCount = Math::Min<int32>(skipCount, ARRAY_COUNT(callstack));
int32 maxCount = Math::Min<int32>(ARRAY_COUNT(callstack), skipCount + maxDepth);
int32 count = backtrace(callstack, maxCount);
int32 useCount = count - skipCount;
if (useCount > 0)
{
char** names = backtrace_symbols(callstack + skipCount, useCount);
result.Resize(useCount);
for (int32 i = 0; i < useCount; i++)
{
char* name = names[i];
StackFrame& frame = result[i];
frame.ProgramCounter = callstack[skipCount + i];
frame.ModuleName[0] = 0;
frame.FileName[0] = 0;
frame.LineNumber = 0;
int32 nameLen = Math::Min<int32>(StringUtils::Length(name), ARRAY_COUNT(frame.FunctionName) - 1);
Platform::MemoryCopy(frame.FunctionName, name, nameLen);
frame.FunctionName[nameLen] = 0;
}
free(names);
}
#endif
return result;
}
#endif

View File

@@ -0,0 +1,107 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_MAC || PLATFORM_IOS
#include "../Unix/UnixPlatform.h"
/// <summary>
/// The Apple platform implementation and application management utilities.
/// </summary>
class FLAXENGINE_API ApplePlatform : public UnixPlatform
{
public:
static float ScreenScale;
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)
{
return __atomic_load_n(dst, __ATOMIC_RELAXED);
}
FORCE_INLINE static int64 AtomicRead(int64 volatile* dst)
{
return __atomic_load_n(dst, __ATOMIC_RELAXED);
}
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 Tick();
static void BeforeExit();
static void Exit();
static void SetHighDpiAwarenessEnabled(bool enable);
static String GetUserLocaleName();
static bool GetHasFocus();
static void CreateGuid(Guid& result);
static bool CanOpenUrl(const StringView& url);
static void OpenUrl(const StringView& url);
static Rectangle GetMonitorBounds(const Float2& screenPos);
static Float2 GetDesktopSize();
static Rectangle GetVirtualDesktopBounds();
static String GetMainDirectory();
static String GetExecutableFilePath();
static String GetWorkingDirectory();
static bool SetWorkingDirectory(const String& path);
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);
static Array<StackFrame, HeapAllocation> GetStackFrames(int32 skipCount = 0, int32 maxDepth = 60, void* context = nullptr);
};
#endif

View File

@@ -2,25 +2,25 @@
#pragma once
#if PLATFORM_MAC
#if PLATFORM_MAC || PLATFORM_IOS
#include "../Unix/UnixThread.h"
#include <signal.h>
/// <summary>
/// Thread object for Mac platform.
/// Thread object for Apple platform.
/// </summary>
class FLAXENGINE_API MacThread : public UnixThread
class FLAXENGINE_API AppleThread : public UnixThread
{
public:
/// <summary>
/// Initializes a new instance of the <see cref="MacThread"/> class.
/// Initializes a new instance of the <see cref="AppleThread"/> class.
/// </summary>
/// <param name="runnable">The runnable.</param>
/// <param name="name">The thread name.</param>
/// <param name="priority">The thread priority.</param>
MacThread(IRunnable* runnable, const String& name, ThreadPriority priority)
AppleThread(IRunnable* runnable, const String& name, ThreadPriority priority)
: UnixThread(runnable, name, priority)
{
}
@@ -35,9 +35,9 @@ public:
/// <param name="priority">Tells the thread whether it needs to adjust its priority or not. Defaults to normal priority</param>
/// <param name="stackSize">The size of the stack to create. 0 means use the current thread's stack size</param>
/// <returns>Pointer to the new thread or null if cannot create it</returns>
static MacThread* Create(IRunnable* runnable, const String& name, ThreadPriority priority = ThreadPriority::Normal, uint32 stackSize = 0)
static AppleThread* Create(IRunnable* runnable, const String& name, ThreadPriority priority = ThreadPriority::Normal, uint32 stackSize = 0)
{
return (MacThread*)Setup(New<MacThread>(runnable, name, priority), stackSize);
return (AppleThread*)Setup(New<AppleThread>(runnable, name, priority), stackSize);
}
protected:

View File

@@ -2,15 +2,22 @@
#pragma once
#if PLATFORM_MAC || PLATFORM_IOS
#include "Engine/Core/Types/String.h"
#include <CoreFoundation/CoreFoundation.h>
class FLAXENGINE_API MacUtils
// Apple platform utilities.
class AppleUtils
{
public:
static String ToString(CFStringRef str);
static CFStringRef ToString(const StringView& str);
#if PLATFORM_MAC
static Float2 PosToCoca(const Float2& pos);
static Float2 CocaToPos(const Float2& pos);
static Float2 GetScreensOrigin();
#endif
};
#endif

View File

@@ -520,6 +520,16 @@ void PlatformBase::CreateGuid(Guid& result)
result = Guid(dateThingHigh, randomThing | (sequentialThing << 16), cyclesThing, dateThingLow);
}
Rectangle PlatformBase::GetMonitorBounds(const Float2& screenPos)
{
return Rectangle(Float2::Zero, Platform::GetDesktopSize());
}
Rectangle PlatformBase::GetVirtualDesktopBounds()
{
return Rectangle(Float2::Zero, Platform::GetDesktopSize());
}
Float2 PlatformBase::GetVirtualDesktopSize()
{
return Platform::GetVirtualDesktopBounds().Size;
@@ -616,6 +626,8 @@ const Char* ToString(PlatformType type)
return TEXT("PlayStation 5");
case PlatformType::Mac:
return TEXT("Mac");
case PlatformType::iOS:
return TEXT("iOS");
default:
return TEXT("");
}

View File

@@ -665,7 +665,7 @@ public:
/// </summary>
/// <param name="screenPos">The screen position (in pixels).</param>
/// <returns>The monitor bounds.</returns>
API_FUNCTION() static Rectangle GetMonitorBounds(const Float2& screenPos) = delete;
API_FUNCTION() static Rectangle GetMonitorBounds(const Float2& screenPos);
/// <summary>
/// Gets size of the primary desktop.
@@ -677,7 +677,7 @@ public:
/// Gets virtual bounds of the desktop made of all the monitors outputs attached.
/// </summary>
/// <returns>Whole desktop size.</returns>
API_PROPERTY() static Rectangle GetVirtualDesktopBounds() = delete;
API_PROPERTY() static Rectangle GetVirtualDesktopBounds();
/// <summary>
/// Gets virtual size of the desktop made of all the monitors outputs attached.

View File

@@ -4,7 +4,7 @@
#if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT
#include "Win32/Win32ConditionVariable.h"
#elif PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC
#elif PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC || PLATFORM_IOS
#include "Unix/UnixConditionVariable.h"
#elif PLATFORM_SWITCH
#include "Platforms/Switch/Engine/Platform/SwitchConditionVariable.h"

View File

@@ -4,7 +4,7 @@
#if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT
#include "Win32/Win32CriticalSection.h"
#elif PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC
#elif PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC || PLATFORM_IOS
#include "Unix/UnixCriticalSection.h"
#elif PLATFORM_SWITCH
#include "Platforms/Switch/Engine/Platform/SwitchCriticalSection.h"

View File

@@ -58,6 +58,11 @@ API_ENUM() enum class PlatformType
/// Running on Mac.
/// </summary>
Mac = 10,
/// <summary>
/// Running on iPhone.
/// </summary>
iOS = 11,
};
/// <summary>
@@ -152,6 +157,8 @@ API_ENUM() enum class ArchitectureType
#include "Platforms/Switch/Engine/Platform/SwitchDefines.h"
#elif PLATFORM_MAC
#include "Mac/MacDefines.h"
#elif PLATFORM_IOS
#include "iOS/iOSDefines.h"
#else
#error Missing Defines implementation!
#endif

View File

@@ -4,7 +4,7 @@
#if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT
#include "Win32/Win32File.h"
#elif PLATFORM_LINUX || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC
#elif PLATFORM_LINUX || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC || PLATFORM_IOS
#include "Unix/UnixFile.h"
#elif PLATFORM_ANDROID
#include "Android/AndroidFile.h"

View File

@@ -22,6 +22,8 @@
#include "Platforms/Switch/Engine/Platform/SwitchFileSystem.h"
#elif PLATFORM_MAC
#include "Mac/MacFileSystem.h"
#elif PLATFORM_IOS
#include "Apple/AppleFileSystem.h"
#else
#error Missing File System implementation!
#endif

View File

@@ -555,21 +555,11 @@ struct GetMonitorBoundsData
}
};
Rectangle GDKPlatform::GetMonitorBounds(const Float2& screenPos)
{
return Rectangle(Float2::Zero, GetDesktopSize());
}
Float2 GDKPlatform::GetDesktopSize()
{
return Float2(1920, 1080);
}
Rectangle GDKPlatform::GetVirtualDesktopBounds()
{
return Rectangle(Float2::Zero, GetDesktopSize());
}
void GDKPlatform::GetEnvironmentVariables(Dictionary<String, String>& result)
{
const LPWCH environmentStr = GetEnvironmentStringsW();

View File

@@ -68,9 +68,7 @@ public:
static bool GetHasFocus();
static bool CanOpenUrl(const StringView& url);
static void OpenUrl(const StringView& url);
static Rectangle GetMonitorBounds(const Float2& screenPos);
static Float2 GetDesktopSize();
static Rectangle GetVirtualDesktopBounds();
static void GetEnvironmentVariables(Dictionary<String, String, HeapAllocation>& result);
static bool GetEnvironmentVariable(const String& name, String& value);
static bool SetEnvironmentVariable(const String& name, const String& value);

View File

@@ -3,8 +3,9 @@
#if PLATFORM_MAC
#include "MacFileSystem.h"
#include "MacUtils.h"
#include "Engine/Platform/Apple/AppleUtils.h"
#include "Engine/Platform/File.h"
#include "Engine/Platform/Platform.h"
#include "Engine/Core/Types/String.h"
#include "Engine/Core/Types/StringView.h"
#include "Engine/Core/Types/TimeSpan.h"
@@ -22,13 +23,11 @@
#include <fcntl.h>
#include <AppKit/AppKit.h>
const DateTime UnixEpoch(1970, 1, 1);
void InitMacDialog(NSSavePanel* dialog, const StringView& initialDirectory, const StringView& filter, const StringView& title)
{
if (initialDirectory.HasChars())
{
[dialog setDirectoryURL:[NSURL fileURLWithPath:(NSString*)MacUtils::ToString(initialDirectory) isDirectory:YES]];
[dialog setDirectoryURL:[NSURL fileURLWithPath:(NSString*)AppleUtils::ToString(initialDirectory) isDirectory:YES]];
}
if (filter.HasChars())
{
@@ -41,13 +40,13 @@ void InitMacDialog(NSSavePanel* dialog, const StringView& initialDirectory, cons
String extension = entries[i];
if (extension.StartsWith(TEXT("*.")))
extension = extension.Substring(2);
[fileTypes addObject:(NSString*)MacUtils::ToString(extension)];
[fileTypes addObject:(NSString*)AppleUtils::ToString(extension)];
}
[dialog setAllowedFileTypes:fileTypes];*/
}
if (title.HasChars())
{
[dialog setMessage:(NSString*)MacUtils::ToString(title)];
[dialog setMessage:(NSString*)AppleUtils::ToString(title)];
}
}
@@ -69,12 +68,12 @@ bool MacFileSystem::ShowOpenFileDialog(Window* parentWindow, const StringView& i
{
const NSArray* urls = [dialog URLs];
for (int32 i = 0; i < [urls count]; i++)
filenames.Add(MacUtils::ToString((CFStringRef)[[urls objectAtIndex:i] path]));
filenames.Add(AppleUtils::ToString((CFStringRef)[[urls objectAtIndex:i] path]));
}
else
{
const NSURL* url = [dialog URL];
filenames.Add(MacUtils::ToString((CFStringRef)[url path]));
filenames.Add(AppleUtils::ToString((CFStringRef)[url path]));
}
result = false;
}
@@ -97,7 +96,7 @@ bool MacFileSystem::ShowSaveFileDialog(Window* parentWindow, const StringView& i
if ([dialog runModal] == NSModalResponseOK)
{
const NSURL* url = [dialog URL];
filenames.Add(MacUtils::ToString((CFStringRef)[url path]));
filenames.Add(AppleUtils::ToString((CFStringRef)[url path]));
result = false;
}
@@ -122,7 +121,7 @@ bool MacFileSystem::ShowBrowseFolderDialog(Window* parentWindow, const StringVie
if ([dialog runModal] == NSModalResponseOK)
{
const NSURL* url = [dialog URL];
path = MacUtils::ToString((CFStringRef)[url path]);
path = AppleUtils::ToString((CFStringRef)[url path]);
result = false;
}
@@ -136,510 +135,4 @@ bool MacFileSystem::ShowFileExplorer(const StringView& path)
return Platform::StartProcess(TEXT("open"), String::Format(TEXT("\"{0}\""), path), StringView::Empty) != 0;
}
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;
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)
{
String home;
Platform::GetEnvironmentVariable(TEXT("HOME"), home);
switch (type)
{
case SpecialFolder::Desktop:
result = home / TEXT("/Desktop");
break;
case SpecialFolder::Documents:
result = home / TEXT("/Documents");
break;
case SpecialFolder::Pictures:
result = home / TEXT("/Pictures");
break;
case SpecialFolder::AppData:
case SpecialFolder::LocalAppData:
result = home / TEXT("/Library/Caches");
break;
case SpecialFolder::ProgramData:
result = home / TEXT("/Library/Application Support");
break;
case SpecialFolder::Temporary:
Platform::GetEnvironmentVariable(TEXT("TMPDIR"), result);
break;
}
}
#endif

View File

@@ -4,53 +4,20 @@
#if PLATFORM_MAC
#include "Engine/Platform/Base/FileSystemBase.h"
#include "../Apple/AppleFileSystem.h"
/// <summary>
/// Mac platform implementation of filesystem service.
/// </summary>
class FLAXENGINE_API MacFileSystem : public FileSystemBase
class FLAXENGINE_API MacFileSystem : public AppleFileSystem
{
public:
// [FileSystemBase]
// [AppleFileSystem]
static bool ShowOpenFileDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& filter, bool multiSelect, const StringView& title, Array<String, HeapAllocation>& filenames);
static bool ShowSaveFileDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& filter, bool multiSelect, const StringView& title, Array<String, HeapAllocation>& filenames);
static bool ShowBrowseFolderDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& title, String& path);
static bool ShowFileExplorer(const StringView& path);
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

@@ -4,7 +4,7 @@
#include "MacPlatform.h"
#include "MacWindow.h"
#include "MacUtils.h"
#include "Engine/Platform/Apple/AppleUtils.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Types/Guid.h"
#include "Engine/Core/Types/String.h"
@@ -17,8 +17,8 @@
#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/StringUtils.h"
#include "Engine/Platform/WindowsManager.h"
#include "Engine/Platform/Clipboard.h"
#include "Engine/Platform/IGuiData.h"
@@ -48,117 +48,16 @@
#include <execinfo.h>
#endif
CPUInfo MacCpu;
Guid DeviceId;
String UserLocale, ComputerName;
double SecondsPerCycle;
String ComputerName;
NSAutoreleasePool* AutoreleasePool = nullptr;
float MacPlatform::ScreenScale = 1.0f;
String MacUtils::ToString(CFStringRef str)
{
if (!str)
return String::Empty;
String result;
const int32 length = CFStringGetLength(str);
if (length > 0)
{
CFRange range = CFRangeMake(0, length);
result.ReserveSpace(length);
CFStringGetBytes(str, range, kCFStringEncodingUTF16LE, '?', false, (uint8*)result.Get(), length * sizeof(Char), nullptr);
}
return result;
}
CFStringRef MacUtils::ToString(const StringView& str)
{
return CFStringCreateWithBytes(nullptr, (const UInt8*)str.GetText(), str.Length() * sizeof(Char), kCFStringEncodingUTF16LE, false);
}
Float2 MacUtils::PosToCoca(const Float2& pos)
{
// MacOS uses y-coordinate starting at the bottom of the screen
Float2 result = pos;// / MacPlatform::ScreenScale;
result.Y *= -1;
result += GetScreensOrigin();
return result;
}
Float2 MacUtils::CocaToPos(const Float2& pos)
{
// MacOS uses y-coordinate starting at the bottom of the screen
Float2 result = pos;// * MacPlatform::ScreenScale;
result -= GetScreensOrigin();
result.Y *= -1;
return result;// * MacPlatform::ScreenScale;
}
Float2 MacUtils::GetScreensOrigin()
{
Float2 result = Float2::Zero;
NSArray* screenArray = [NSScreen screens];
for (NSUInteger i = 0; i < [screenArray count]; i++)
{
NSRect rect = [[screenArray objectAtIndex:i] frame];
Float2 pos(rect.origin.x, rect.origin.y + rect.size.height);
pos *= MacPlatform::ScreenScale;
if (pos.X < result.X)
result.X = pos.X;
if (pos.Y > result.Y)
result.Y = pos.Y;
}
return result;
}
void MacClipboard::Clear()
{
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
[pasteboard clearContents];
}
void MacClipboard::SetText(const StringView& text)
{
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
[pasteboard clearContents];
[pasteboard writeObjects:[NSArray arrayWithObject:(NSString*)MacUtils::ToString(text)]];
}
void MacClipboard::SetRawData(const Span<byte>& data)
{
}
void MacClipboard::SetFiles(const Array<String>& files)
{
}
String MacClipboard::GetText()
{
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
NSArray* classes = [NSArray arrayWithObject:[NSString class]];
NSDictionary* options = [NSDictionary dictionary];
if (![pasteboard canReadObjectForClasses:classes options:options])
return String::Empty;
NSArray* objects = [pasteboard readObjectsForClasses:classes options:options];
return MacUtils::ToString((CFStringRef)[objects objectAtIndex:0]);
}
Array<byte> MacClipboard::GetRawData()
{
return Array<byte>();
}
Array<String> MacClipboard::GetFiles()
{
return Array<String>();
}
DialogResult MessageBox::Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon)
{
if (CommandLine::Options.Headless)
return DialogResult::None;
CFStringRef textRef = MacUtils::ToString(text);
CFStringRef captionRef = MacUtils::ToString(caption);
CFStringRef textRef = AppleUtils::ToString(text);
CFStringRef captionRef = AppleUtils::ToString(caption);
CFOptionFlags flags = 0;
switch (buttons)
{
@@ -189,6 +88,83 @@ DialogResult MessageBox::Show(Window* parent, const StringView& text, const Stri
return DialogResult::OK;
}
Float2 AppleUtils::PosToCoca(const Float2& pos)
{
// MacOS uses y-coordinate starting at the bottom of the screen
Float2 result = pos;// / ApplePlatform::ScreenScale;
result.Y *= -1;
result += GetScreensOrigin();
return result;
}
Float2 AppleUtils::CocaToPos(const Float2& pos)
{
// MacOS uses y-coordinate starting at the bottom of the screen
Float2 result = pos;// * ApplePlatform::ScreenScale;
result -= GetScreensOrigin();
result.Y *= -1;
return result;// * ApplePlatform::ScreenScale;
}
Float2 AppleUtils::GetScreensOrigin()
{
Float2 result = Float2::Zero;
NSArray* screenArray = [NSScreen screens];
for (NSUInteger i = 0; i < [screenArray count]; i++)
{
NSRect rect = [[screenArray objectAtIndex:i] frame];
Float2 pos(rect.origin.x, rect.origin.y + rect.size.height);
pos *= ApplePlatform::ScreenScale;
if (pos.X < result.X)
result.X = pos.X;
if (pos.Y > result.Y)
result.Y = pos.Y;
}
return result;
}
void MacClipboard::Clear()
{
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
[pasteboard clearContents];
}
void MacClipboard::SetText(const StringView& text)
{
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
[pasteboard clearContents];
[pasteboard writeObjects:[NSArray arrayWithObject:(NSString*)AppleUtils::ToString(text)]];
}
void MacClipboard::SetRawData(const Span<byte>& data)
{
}
void MacClipboard::SetFiles(const Array<String>& files)
{
}
String MacClipboard::GetText()
{
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
NSArray* classes = [NSArray arrayWithObject:[NSString class]];
NSDictionary* options = [NSDictionary dictionary];
if (![pasteboard canReadObjectForClasses:classes options:options])
return String::Empty;
NSArray* objects = [pasteboard readObjectsForClasses:classes options:options];
return AppleUtils::ToString((CFStringRef)[objects objectAtIndex:0]);
}
Array<byte> MacClipboard::GetRawData()
{
return Array<byte>();
}
Array<String> MacClipboard::GetFiles()
{
return Array<String>();
}
class MacKeyboard : public Keyboard
{
public:
@@ -217,215 +193,28 @@ public:
}
};
typedef uint16_t offset_t;
#define align_mem_up(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
bool MacPlatform::Is64BitPlatform()
{
return PLATFORM_64BITS;
}
CPUInfo MacPlatform::GetCPUInfo()
{
return MacCpu;
}
int32 MacPlatform::GetCacheLineSize()
{
return MacCpu.CacheLineSize;
}
MemoryStats MacPlatform::GetMemoryStats()
{
MemoryStats result;
int64 value64;
size_t value64Size = sizeof(value64);
if (sysctlbyname("hw.memsize", &value64, &value64Size, nullptr, 0) != 0)
value64 = 1024 * 1024;
result.TotalPhysicalMemory = value64;
int id[] = { CTL_HW, HW_MEMSIZE };
if (sysctl(id, 2, &value64, &value64Size, nullptr, 0) != 0)
value64Size = 1024;
result.UsedPhysicalMemory = value64Size;
xsw_usage swapusage;
size_t swapusageSize = sizeof(swapusage);
result.TotalVirtualMemory = result.TotalPhysicalMemory;
result.UsedVirtualMemory = result.UsedPhysicalMemory;
if (sysctlbyname("vm.swapusage", &swapusage, &swapusageSize, nullptr, 0) == 0)
{
result.TotalVirtualMemory += swapusage.xsu_total;
result.UsedVirtualMemory += swapusage.xsu_used;
}
return result;
}
ProcessMemoryStats MacPlatform::GetProcessMemoryStats()
{
ProcessMemoryStats result;
result.UsedPhysicalMemory = 1024;
result.UsedVirtualMemory = 1024;
return result;
}
uint64 MacPlatform::GetCurrentThreadID()
{
return (uint64)pthread_mach_thread_np(pthread_self());
}
void MacPlatform::SetThreadPriority(ThreadPriority priority)
{
// TODO: impl this
}
void MacPlatform::SetThreadAffinityMask(uint64 affinityMask)
{
// TODO: impl this
}
void MacPlatform::Sleep(int32 milliseconds)
{
usleep(milliseconds * 1000);
}
double MacPlatform::GetTimeSeconds()
{
return SecondsPerCycle * mach_absolute_time();
}
uint64 MacPlatform::GetTimeCycles()
{
return mach_absolute_time();
}
uint64 MacPlatform::GetClockFrequency()
{
return (uint64)(1.0 / SecondsPerCycle);
}
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 (UnixPlatform::Init())
if (ApplePlatform::Init())
return true;
// Init timing
{
mach_timebase_info_data_t info;
mach_timebase_info(&info);
SecondsPerCycle = 1e-9 * (double)info.numer / (double)info.denom;
}
// Get CPU info
int32 value32;
int64 value64;
size_t value32Size = sizeof(value32), value64Size = sizeof(value64);
if (sysctlbyname("hw.packages", &value32, &value32Size, nullptr, 0) != 0)
value32 = 1;
MacCpu.ProcessorPackageCount = value32;
if (sysctlbyname("hw.physicalcpu", &value32, &value32Size, nullptr, 0) != 0)
value32 = 1;
MacCpu.ProcessorCoreCount = value32;
if (sysctlbyname("hw.logicalcpu", &value32, &value32Size, nullptr, 0) != 0)
value32 = 1;
MacCpu.LogicalProcessorCount = value32;
if (sysctlbyname("hw.l1icachesize", &value32, &value32Size, nullptr, 0) != 0)
value32 = 0;
MacCpu.L1CacheSize = value32;
if (sysctlbyname("hw.l2cachesize", &value32, &value32Size, nullptr, 0) != 0)
value32 = 0;
MacCpu.L2CacheSize = value32;
if (sysctlbyname("hw.l3cachesize", &value32, &value32Size, nullptr, 0) != 0)
value32 = 0;
MacCpu.L3CacheSize = value32;
if (sysctlbyname("hw.pagesize", &value32, &value32Size, nullptr, 0) != 0)
value32 = vm_page_size;
MacCpu.PageSize = value32;
if (sysctlbyname("hw.cpufrequency_max", &value64, &value64Size, nullptr, 0) != 0)
value64 = GetClockFrequency();
MacCpu.ClockSpeed = value64;
if (sysctlbyname("hw.cachelinesize", &value32, &value32Size, nullptr, 0) != 0)
value32 = PLATFORM_CACHE_LINE_SIZE;
MacCpu.CacheLineSize = value32;
// Get device id
{
io_registry_entry_t ioRegistryRoot = IORegistryEntryFromPath(kIOMasterPortDefault, "IOService:/");
CFStringRef deviceUuid = (CFStringRef)IORegistryEntryCreateCFProperty(ioRegistryRoot, CFSTR(kIOPlatformUUIDKey), kCFAllocatorDefault, 0);
IOObjectRelease(ioRegistryRoot);
String uuidStr = MacUtils::ToString(deviceUuid);
String uuidStr = AppleUtils::ToString(deviceUuid);
Guid::Parse(uuidStr, DeviceId);
CFRelease(deviceUuid);
}
// Get locale
{
CFLocaleRef locale = CFLocaleCopyCurrent();
CFStringRef localeLang = (CFStringRef)CFLocaleGetValue(locale, kCFLocaleLanguageCode);
CFStringRef localeCountry = (CFStringRef)CFLocaleGetValue(locale, kCFLocaleCountryCode);
UserLocale = MacUtils::ToString(localeLang);
String localeCountryStr = MacUtils::ToString(localeCountry);
if (localeCountryStr.HasChars())
UserLocale += TEXT("-") + localeCountryStr;
CFRelease(locale);
CFRelease(localeLang);
CFRelease(localeCountry);
}
// Get computer name
{
CFStringRef computerName = SCDynamicStoreCopyComputerName(nullptr, nullptr);
ComputerName = MacUtils::ToString(computerName);
ComputerName = AppleUtils::ToString(computerName);
CFRelease(computerName);
}
// Init user
{
String username;
GetEnvironmentVariable(TEXT("USER"), username);
OnPlatformUserAdd(New<User>(username));
}
// Find the maximum scale of the display to handle high-dpi displays scaling factor
{
NSArray* screenArray = [NSScreen screens];
@@ -442,10 +231,12 @@ bool MacPlatform::Init()
// Init application
[NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
AutoreleasePool = [[NSAutoreleasePool alloc] init];
// Init main menu
NSMenu* mainMenu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
[NSApp setMainMenu:mainMenu];
// TODO: expose main menu for app (eg. to be used by Game or Editor on macOS-only)
AutoreleasePool = [[NSAutoreleasePool alloc] init];
Input::Mouse = New<MacMouse>();
Input::Keyboard = New<MacKeyboard>();
@@ -455,7 +246,7 @@ bool MacPlatform::Init()
void MacPlatform::LogInfo()
{
UnixPlatform::LogInfo();
ApplePlatform::LogInfo();
char str[250];
size_t strSize = sizeof(str);
@@ -488,24 +279,6 @@ void MacPlatform::Tick()
AutoreleasePool = [[NSAutoreleasePool alloc] init];
}
void MacPlatform::BeforeExit()
{
}
void MacPlatform::Exit()
{
}
void MacPlatform::SetHighDpiAwarenessEnabled(bool enable)
{
// Disable resolution scaling in low dpi mode
if (!enable)
{
CustomDpiScale /= ScreenScale;
ScreenScale = 1.0f;
}
}
int32 MacPlatform::GetDpi()
{
CGDirectDisplayID mainDisplay = CGMainDisplayID();
@@ -515,9 +288,9 @@ int32 MacPlatform::GetDpi()
return Math::Max(dpi, 72.0f);
}
String MacPlatform::GetUserLocaleName()
Guid MacPlatform::GetUniqueDeviceId()
{
return UserLocale;
return DeviceId;
}
String MacPlatform::GetComputerName()
@@ -525,40 +298,6 @@ 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();
}
void MacPlatform::CreateGuid(Guid& result)
{
uuid_t uuid;
uuid_generate(uuid);
auto ptr = (uint32*)&uuid;
result.A = ptr[0];
result.B = ptr[1];
result.C = ptr[2];
result.D = ptr[3];
}
bool MacPlatform::CanOpenUrl(const StringView& url)
{
return false;
}
void MacPlatform::OpenUrl(const StringView& url)
{
}
Float2 MacPlatform::GetMousePosition()
{
CGEventRef event = CGEventCreate(nullptr);
@@ -584,7 +323,7 @@ Float2 MacPlatform::GetDesktopSize()
Rectangle GetDisplayBounds(CGDirectDisplayID display)
{
CGRect rect = CGDisplayBounds(display);
float screnScale = MacPlatform::ScreenScale;
float screnScale = ApplePlatform::ScreenScale;
return Rectangle(rect.origin.x * screnScale, rect.origin.y * screnScale, rect.size.width * screnScale, rect.size.height * screnScale);
}
@@ -625,54 +364,11 @@ String MacPlatform::GetMainDirectory()
return path;
}
String MacPlatform::GetExecutableFilePath()
{
char buf[PATH_MAX];
uint32 size = PATH_MAX;
String result;
if (_NSGetExecutablePath(buf, &size) == 0)
result.SetUTF8(buf, StringUtils::Length(buf));
return result;
}
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;
}
int32 MacProcess(const StringView& cmdLine, const StringView& workingDir, const Dictionary<String, String>& environment, bool waitForEnd, bool logOutput)
{
LOG(Info, "Command: {0}", cmdLine);
@@ -731,7 +427,7 @@ int32 MacPlatform::StartProcess(const StringView& filename, const StringView& ar
// Special case if filename points to the app package (use actual executable)
String exePath = filename;
{
NSString* processPath = (NSString*)MacUtils::ToString(filename);
NSString* processPath = (NSString*)AppleUtils::ToString(filename);
if (![[NSFileManager defaultManager] fileExistsAtPath: processPath])
{
NSString* appName = [[processPath lastPathComponent] stringByDeletingPathExtension];
@@ -746,7 +442,7 @@ int32 MacPlatform::StartProcess(const StringView& filename, const StringView& ar
{
processPath = [bundle executablePath];
if (processPath != nil)
exePath = MacUtils::ToString((CFStringRef)processPath);
exePath = AppleUtils::ToString((CFStringRef)processPath);
}
}
}
@@ -766,57 +462,4 @@ int32 MacPlatform::RunProcess(const StringView& cmdLine, const StringView& worki
return MacProcess(cmdLine, workingDir, environment, true, true);
}
void* MacPlatform::LoadLibrary(const Char* filename)
{
const StringAsANSI<> filenameANSI(filename);
void* result = dlopen(filenameANSI.Get(), RTLD_LAZY | RTLD_LOCAL);
if (!result)
{
LOG(Error, "Failed to load {0} because {1}", filename, String(dlerror()));
}
return result;
}
void MacPlatform::FreeLibrary(void* handle)
{
dlclose(handle);
}
void* MacPlatform::GetProcAddress(void* handle, const char* symbol)
{
return dlsym(handle, symbol);
}
Array<MacPlatform::StackFrame> MacPlatform::GetStackFrames(int32 skipCount, int32 maxDepth, void* context)
{
Array<StackFrame> result;
#if CRASH_LOG_ENABLE
void* callstack[120];
skipCount = Math::Min<int32>(skipCount, ARRAY_COUNT(callstack));
int32 maxCount = Math::Min<int32>(ARRAY_COUNT(callstack), skipCount + maxDepth);
int32 count = backtrace(callstack, maxCount);
int32 useCount = count - skipCount;
if (useCount > 0)
{
char** names = backtrace_symbols(callstack + skipCount, useCount);
result.Resize(useCount);
for (int32 i = 0; i < useCount; i++)
{
char* name = names[i];
StackFrame& frame = result[i];
frame.ProgramCounter = callstack[skipCount + i];
frame.ModuleName[0] = 0;
frame.FileName[0] = 0;
frame.LineNumber = 0;
int32 nameLen = Math::Min<int32>(StringUtils::Length(name), ARRAY_COUNT(frame.FunctionName) - 1);
Platform::MemoryCopy(frame.FunctionName, name, nameLen);
frame.FunctionName[nameLen] = 0;
}
free(names);
}
#endif
return result;
}
#endif

View File

@@ -4,115 +4,33 @@
#if PLATFORM_MAC
#include "../Unix/UnixPlatform.h"
#include "../Apple/ApplePlatform.h"
/// <summary>
/// The Mac platform implementation and application management utilities.
/// </summary>
class FLAXENGINE_API MacPlatform : public UnixPlatform
class FLAXENGINE_API MacPlatform : public ApplePlatform
{
public:
static float ScreenScale;
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)
{
return __atomic_load_n(dst, __ATOMIC_RELAXED);
}
FORCE_INLINE static int64 AtomicRead(int64 volatile* dst)
{
return __atomic_load_n(dst, __ATOMIC_RELAXED);
}
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);
// [ApplePlatform]
static bool Init();
static void LogInfo();
static void BeforeRun();
static void Tick();
static void BeforeExit();
static void Exit();
static void SetHighDpiAwarenessEnabled(bool enable);
static int32 GetDpi();
static String GetUserLocaleName();
static Guid GetUniqueDeviceId();
static String GetComputerName();
static bool GetHasFocus();
static void CreateGuid(Guid& result);
static bool CanOpenUrl(const StringView& url);
static void OpenUrl(const StringView& url);
static Float2 GetMousePosition();
static void SetMousePosition(const Float2& pos);
static Rectangle GetMonitorBounds(const Float2& screenPos);
static Float2 GetDesktopSize();
static Rectangle GetVirtualDesktopBounds();
static Rectangle GetMonitorBounds(const Float2& screenPos);
static Float2 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 int32 StartProcess(const StringView& filename, const StringView& args, const StringView& workingDir, bool hiddenWindow = false, bool waitForEnd = false);
static int32 RunProcess(const StringView& cmdLine, const StringView& workingDir, bool hiddenWindow = true);
static int32 RunProcess(const StringView& cmdLine, const StringView& workingDir, const Dictionary<String, String, HeapAllocation>& environment, bool hiddenWindow = true);
static void* LoadLibrary(const Char* filename);
static void FreeLibrary(void* handle);
static void* GetProcAddress(void* handle, const char* symbol);
static Array<StackFrame, HeapAllocation> GetStackFrames(int32 skipCount = 0, int32 maxDepth = 60, void* context = nullptr);
};
#endif

View File

@@ -3,7 +3,7 @@
#if PLATFORM_MAC
#include "../Window.h"
#include "MacUtils.h"
#include "Engine/Platform/Apple/AppleUtils.h"
#include "Engine/Platform/IGuiData.h"
#include "Engine/Core/Log.h"
#include "Engine/Input/Input.h"
@@ -208,7 +208,7 @@ void GetDragDropData(const MacWindow* window, id<NSDraggingInfo> sender, Float2&
if ([[pasteboard types] containsObject:NSPasteboardTypeString])
{
dropData.CurrentType = IGuiData::Type::Text;
dropData.AsText = MacUtils::ToString((CFStringRef)[pasteboard stringForType:NSPasteboardTypeString]);
dropData.AsText = AppleUtils::ToString((CFStringRef)[pasteboard stringForType:NSPasteboardTypeString]);
}
else
{
@@ -218,7 +218,7 @@ void GetDragDropData(const MacWindow* window, id<NSDraggingInfo> sender, Float2&
{
NSString* url = [[files objectAtIndex:i] path];
NSString* file = [NSURL URLWithString:url].path;
dropData.AsFiles.Add(MacUtils::ToString((CFStringRef)file));
dropData.AsFiles.Add(AppleUtils::ToString((CFStringRef)file));
}
}
}
@@ -572,7 +572,7 @@ MacWindow::MacWindow(const CreateWindowSettings& settings)
: WindowBase(settings)
{
_clientSize = Float2(settings.Size.X, settings.Size.Y);
Float2 pos = MacUtils::PosToCoca(settings.Position);
Float2 pos = AppleUtils::PosToCoca(settings.Position);
NSRect frame = NSMakeRect(pos.X, pos.Y - settings.Size.Y, settings.Size.X, settings.Size.Y);
NSUInteger styleMask = NSWindowStyleMaskClosable;
if (settings.IsRegularWindow)
@@ -606,7 +606,7 @@ MacWindow::MacWindow(const CreateWindowSettings& settings)
MacViewImpl* view = [[MacViewImpl alloc] init];
view.wantsLayer = YES;
[view setWindow:this];
window.title = (__bridge NSString*)MacUtils::ToString(settings.Title);
window.title = (__bridge NSString*)AppleUtils::ToString(settings.Title);
[window setWindow:this];
[window setReleasedWhenClosed:NO];
[window setMinSize:NSMakeSize(settings.MinimumSize.X, settings.MinimumSize.Y)];
@@ -779,7 +779,7 @@ void MacWindow::SetClientBounds(const Rectangle& clientArea)
//newRect.origin.x = oldRect.origin.x;
//newRect.origin.y = NSMaxY(oldRect) - newRect.size.height;
Float2 pos = MacUtils::PosToCoca(clientArea.Location);
Float2 pos = AppleUtils::PosToCoca(clientArea.Location);
Float2 titleSize = GetWindowTitleSize(this);
newRect.origin.x = pos.X + titleSize.X;
newRect.origin.y = pos.Y - newRect.size.height + titleSize.Y;
@@ -792,7 +792,7 @@ void MacWindow::SetPosition(const Float2& position)
NSWindow* window = (NSWindow*)_window;
if (!window)
return;
Float2 pos = MacUtils::PosToCoca(position) / MacPlatform::ScreenScale;
Float2 pos = AppleUtils::PosToCoca(position) / MacPlatform::ScreenScale;
NSRect rect = [window frame];
[window setFrameOrigin:NSMakePoint(pos.X, pos.Y - rect.size.height)];
}
@@ -803,7 +803,7 @@ Float2 MacWindow::GetPosition() const
if (!window)
return Float2::Zero;
NSRect rect = [window frame];
return MacUtils::CocaToPos(Float2(rect.origin.x, rect.origin.y + rect.size.height) * MacPlatform::ScreenScale);
return AppleUtils::CocaToPos(Float2(rect.origin.x, rect.origin.y + rect.size.height) * MacPlatform::ScreenScale);
}
Float2 MacWindow::GetSize() const
@@ -868,7 +868,7 @@ void MacWindow::SetTitle(const StringView& title)
NSWindow* window = (NSWindow*)_window;
if (!window)
return;
[window setTitle:(__bridge NSString*)MacUtils::ToString(_title)];
[window setTitle:(__bridge NSString*)AppleUtils::ToString(_title)];
}
DragDropEffect MacWindow::DoDragDrop(const StringView& data)

View File

@@ -4,7 +4,7 @@
#if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT
#include "Win32/Win32Network.h"
#elif PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_MAC
#elif PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_MAC || PLATFORM_IOS
#include "Unix/UnixNetwork.h"
#elif PLATFORM_PS4
#include "Platforms/PS4/Engine/Platform/PS4Network.h"
@@ -12,8 +12,6 @@
#include "Platforms/PS5/Engine/Platform/PS5Network.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

@@ -79,8 +79,14 @@ public class Platform : EngineModule
break;
case TargetPlatform.Mac:
options.SourcePaths.Add(Path.Combine(FolderPath, "Unix"));
options.SourcePaths.Add(Path.Combine(FolderPath, "Apple"));
options.SourcePaths.Add(Path.Combine(FolderPath, "Mac"));
break;
case TargetPlatform.iOS:
options.SourcePaths.Add(Path.Combine(FolderPath, "Unix"));
options.SourcePaths.Add(Path.Combine(FolderPath, "Apple"));
options.SourcePaths.Add(Path.Combine(FolderPath, "iOS"));
break;
default: throw new InvalidPlatformException(options.Platform.Target);
}
if (options.Target.IsEditor)
@@ -92,6 +98,7 @@ public class Platform : EngineModule
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"));
options.SourceFiles.Add(Path.Combine(FolderPath, "iOS", "iOSPlatformSettings.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

@@ -28,6 +28,8 @@
#include "Platforms/Switch/Engine/Platform/SwitchPlatform.h"
#elif PLATFORM_MAC
#include "Mac/MacPlatform.h"
#elif PLATFORM_IOS
#include "iOS/iOSPlatform.h"
#else
#error Missing Platform implementation!
#endif

View File

@@ -14,8 +14,8 @@
#include "Android/AndroidThread.h"
#elif PLATFORM_SWITCH
#include "Platforms/Switch/Engine/Platform/SwitchThread.h"
#elif PLATFORM_MAC
#include "Mac/MacThread.h"
#elif PLATFORM_MAC || PLATFORM_IOS
#include "Apple/AppleThread.h"
#else
#error Missing Thread implementation!
#endif

View File

@@ -243,8 +243,8 @@ class UnixFile;
typedef UnixFile File;
class MacPlatform;
typedef MacPlatform Platform;
class MacThread;
typedef MacThread Thread;
class AppleThread;
typedef AppleThread Thread;
class MacWindow;
typedef MacWindow Window;
class UnixNetwork;
@@ -252,6 +252,31 @@ typedef UnixNetwork Network;
class UserBase;
typedef UserBase User;
#elif PLATFORM_IOS
class ClipboardBase;
typedef ClipboardBase Clipboard;
class UnixCriticalSection;
typedef UnixCriticalSection CriticalSection;
class UnixConditionVariable;
typedef UnixConditionVariable ConditionVariable;
class AppleFileSystem;
typedef AppleFileSystem FileSystem;
class FileSystemWatcherBase;
typedef FileSystemWatcherBase FileSystemWatcher;
class UnixFile;
typedef UnixFile File;
class iOSPlatform;
typedef iOSPlatform Platform;
class AppleThread;
typedef AppleThread Thread;
class iOSWindow;
typedef iOSWindow Window;
class UnixNetwork;
typedef UnixNetwork Network;
class UserBase;
typedef UserBase User;
#else
#error Missing Types implementation!

View File

@@ -186,18 +186,6 @@ Float2 UWPPlatform::GetDesktopSize()
return result;
}
Rectangle UWPPlatform::GetMonitorBounds(const Float2& screenPos)
{
// TODO: do it in a proper way
return Rectangle(Float2::Zero, GetDesktopSize());
}
Rectangle UWPPlatform::GetVirtualDesktopBounds()
{
// TODO: do it in a proper way
return Rectangle(Float2::Zero, GetDesktopSize());
}
Window* UWPPlatform::CreateWindow(const CreateWindowSettings& settings)
{
// Settings with provided UWPWindowImpl are only valid

View File

@@ -39,9 +39,7 @@ public:
static void OpenUrl(const StringView& url);
static Float2 GetMousePosition();
static void SetMousePosition(const Float2& pos);
static Rectangle GetMonitorBounds(const Float2& screenPos);
static Float2 GetDesktopSize();
static Rectangle GetVirtualDesktopBounds();
static Window* CreateWindow(const CreateWindowSettings& settings);
static void* LoadLibrary(const Char* filename);
};

View File

@@ -22,6 +22,8 @@
#include "Platforms/PS5/Engine/Platform/PS5Window.h"
#elif PLATFORM_MAC
#include "Mac/MacWindow.h"
#elif PLATFORM_IOS
#include "iOS/iOSWindow.h"
#else
#error Missing Window implementation!
#endif

View File

@@ -0,0 +1,17 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_IOS
#include "../Unix/UnixDefines.h"
// Platform description
#define PLATFORM_TYPE PlatformType::iOS
#define PLATFORM_64BITS 1
#define PLATFORM_ARCH_ARM64 1
#define PLATFORM_ARCH ArchitectureType::ARM64
#define PLATFORM_CACHE_LINE_SIZE 128
#define PLATFORM_DEBUG_BREAK __builtin_trap()
#endif

View File

@@ -0,0 +1,91 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#if PLATFORM_IOS
#include "iOSPlatform.h"
#include "iOSWindow.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Types/String.h"
#include "Engine/Platform/StringUtils.h"
#include "Engine/Platform/MessageBox.h"
#include <UIKit/UIKit.h>
#include <sys/utsname.h>
int32 Dpi = 96;
Guid DeviceId;
DialogResult MessageBox::Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon)
{
// TODO: implement message box popup on iOS
return DialogResult::OK;
}
bool iOSPlatform::Init()
{
if (ApplePlatform::Init())
return true;
ScreenScale = [[UIScreen mainScreen] scale];
CustomDpiScale *= ScreenScale;
Dpi = 72; // TODO: calculate screen dpi (probably hardcoded map for iPhone model)
return false;
}
void iOSPlatform::LogInfo()
{
ApplePlatform::LogInfo();
struct utsname systemInfo;
uname(&systemInfo);
NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
LOG(Info, "{3}, iOS {0}.{1}.{2}", version.majorVersion, version.minorVersion, version.patchVersion, String(systemInfo.machine));
}
void iOSPlatform::Tick()
{
// Process system events
while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0001, true) == kCFRunLoopRunHandledSource)
{
}
}
int32 iOSPlatform::GetDpi()
{
return Dpi;
}
Guid iOSPlatform::GetUniqueDeviceId()
{
return Guid::Empty; // TODO: use MAC address of the iPhone to generate device id (at least within network connection state)
}
String iOSPlatform::GetComputerName()
{
return TEXT("iPhone");
}
Float2 iOSPlatform::GetDesktopSize()
{
CGRect frame = [[UIScreen mainScreen] bounds];
float scale = [[UIScreen mainScreen] scale];
return Float2((float)frame.size.width * scale, (float)frame.size.height * scale);
}
String iOSPlatform::GetMainDirectory()
{
String path = StringUtils::GetDirectoryName(GetExecutableFilePath());
if (path.EndsWith(TEXT("/Contents/iOS")))
{
// If running from executable in a package, go up to the Contents
path = StringUtils::GetDirectoryName(path);
}
return path;
}
Window* iOSPlatform::CreateWindow(const CreateWindowSettings& settings)
{
return New<iOSWindow>(settings);
}
#endif

View File

@@ -0,0 +1,28 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_IOS
#include "../Apple/ApplePlatform.h"
/// <summary>
/// The iOS platform implementation and application management utilities.
/// </summary>
class FLAXENGINE_API iOSPlatform : public ApplePlatform
{
public:
// [ApplePlatform]
static bool Init();
static void LogInfo();
static void Tick();
static int32 GetDpi();
static Guid GetUniqueDeviceId();
static String GetComputerName();
static Float2 GetDesktopSize();
static String GetMainDirectory();
static Window* CreateWindow(const CreateWindowSettings& settings);
};
#endif

View File

@@ -0,0 +1,50 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_IOS || USE_EDITOR
#include "Engine/Core/Config/PlatformSettingsBase.h"
#include "Engine/Core/Types/String.h"
#include "Engine/Scripting/SoftObjectReference.h"
class Texture;
/// <summary>
/// iOS platform settings.
/// </summary>
API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API iOSPlatformSettings : public SettingsBase
{
DECLARE_SCRIPTING_TYPE_MINIMAL(iOSPlatformSettings);
/// <summary>
/// The app identifier (reversed DNS, eg. com.company.product). Custom tokens: ${PROJECT_NAME}, ${COMPANY_NAME}.
/// </summary>
API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"General\")")
String AppIdentifier = TEXT("com.${COMPANY_NAME}.${PROJECT_NAME}");
/// <summary>
/// Custom icon texture to use for the application (overrides the default one).
/// </summary>
API_FIELD(Attributes="EditorOrder(1000), EditorDisplay(\"Other\")")
SoftObjectReference<Texture> OverrideIcon;
public:
/// <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 iOSPlatformSettings* Get();
// [SettingsBase]
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override
{
DESERIALIZE(AppIdentifier);
DESERIALIZE(OverrideIcon);
}
};
#if PLATFORM_IOS
typedef iOSPlatformSettings PlatformSettings;
#endif
#endif

View File

@@ -0,0 +1,212 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#if PLATFORM_IOS
#include "../Window.h"
#include "Engine/Platform/Apple/AppleUtils.h"
#include "Engine/Core/Log.h"
#include "Engine/Input/Input.h"
#include "Engine/Graphics/RenderTask.h"
#include <UIKit/UIKit.h>
#include <QuartzCore/CAMetalLayer.h>
@interface iOSUIWindow : UIWindow
{
}
@end
@implementation iOSUIWindow
@end
@interface iOSUIViewController : UIViewController
@end
@interface iOSUIView : UIView
@property iOSWindow* window;
@end
@implementation iOSUIView
+(Class) layerClass { return [CAMetalLayer class]; }
- (void)setFrame:(CGRect)frame
{
[super setFrame:frame];
if (!_window)
return;
float scale = [[UIScreen mainScreen] scale];
_window->CheckForResize((float)frame.size.width * scale, (float)frame.size.height * scale);
}
@end
@implementation iOSUIViewController
{
}
- (BOOL)prefersHomeIndicatorAutoHidden
{
return YES;
}
- (BOOL)prefersStatusBarHidden
{
return YES;
}
-(UIStatusBarAnimation)preferredStatusBarUpdateAnimation
{
return UIStatusBarAnimationSlide;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
@end
iOSWindow::iOSWindow(const CreateWindowSettings& settings)
: WindowBase(settings)
{
// Fullscreen by default
CGRect frame = [[UIScreen mainScreen] bounds];
float scale = [[UIScreen mainScreen] scale];
_clientSize = Float2((float)frame.size.width * scale, (float)frame.size.height * scale);
// Setup view
_view = [[iOSUIView alloc] initWithFrame:frame];
iOSUIView* v = (iOSUIView*)_view;
[v resignFirstResponder];
[v setNeedsDisplay];
[v setHidden:NO];
[v setOpaque:YES];
[v setAutoResizeDrawable:YES];
v.backgroundColor = [UIColor clearColor];
v.window = this;
// Setp view controller
_viewController = [[iOSUIViewController alloc] init];
iOSUIViewController* vc = (iOSUIViewController*)_viewController;
[vc setView:v];
[vc setNeedsUpdateOfHomeIndicatorAutoHidden];
[vc setNeedsStatusBarAppearanceUpdate];
// Setup window
_window = [[iOSUIWindow alloc] initWithFrame:frame];
iOSUIWindow* w = (iOSUIWindow*)_window;
[w setRootViewController:vc];
[w setContentMode:UIViewContentModeScaleToFill];
[w makeKeyAndVisible];
[w setBounds:frame];
w.backgroundColor = [UIColor clearColor];
}
iOSWindow::~iOSWindow()
{
[(iOSUIWindow*)_window release];
[(iOSUIView*)_view release];
[(iOSUIViewController*)_viewController release];
_window = nullptr;
_view = nullptr;
_layer = nullptr;
}
void iOSWindow::CheckForResize(float width, float height)
{
const Float2 clientSize(width, height);
if (clientSize != _clientSize)
{
_clientSize = clientSize;
OnResize(width, height);
}
}
void* iOSWindow::GetNativePtr() const
{
return _window;
}
void iOSWindow::Show()
{
if (!_visible)
{
InitSwapChain();
if (_showAfterFirstPaint)
{
if (RenderTask)
RenderTask->Enabled = true;
return;
}
// Show
_focused = true;
// Base
WindowBase::Show();
}
}
bool iOSWindow::IsClosed() const
{
return _window != nullptr;
}
bool iOSWindow::IsForegroundWindow() const
{
return Platform::GetHasFocus() && IsFocused();
}
void iOSWindow::BringToFront(bool force)
{
Focus();
}
void iOSWindow::SetIsFullscreen(bool isFullscreen)
{
}
void iOSWindow::SetClientBounds(const Rectangle& clientArea)
{
}
void iOSWindow::SetPosition(const Float2& position)
{
}
Float2 iOSWindow::GetPosition() const
{
return Float2::Zero;
}
Float2 iOSWindow::GetSize() const
{
return _clientSize;
}
Float2 iOSWindow::GetClientSize() const
{
return _clientSize;
}
Float2 iOSWindow::ScreenToClient(const Float2& screenPos) const
{
return screenPos;
}
Float2 iOSWindow::ClientToScreen(const Float2& clientPos) const
{
return clientPos;
}
void iOSWindow::SetTitle(const StringView& title)
{
_title = title;
}
#endif

View File

@@ -0,0 +1,49 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_IOS
#include "Engine/Platform/Base/WindowBase.h"
#include "Engine/Platform/Platform.h"
/// <summary>
/// Implementation of the window class for iOS platform.
/// </summary>
class FLAXENGINE_API iOSWindow : public WindowBase
{
private:
Float2 _clientSize;
void* _window;
void* _layer;
void* _view;
void* _viewController;
public:
iOSWindow(const CreateWindowSettings& settings);
~iOSWindow();
void CheckForResize(float width, float height);
public:
// [Window]
void* GetNativePtr() const override;
void Show() override;
bool IsClosed() const override;
bool IsForegroundWindow() const override;
void BringToFront(bool force = false) override;
void SetClientBounds(const Rectangle& clientArea) override;
void SetPosition(const Float2& position) override;
Float2 GetPosition() const override;
Float2 GetSize() const override;
Float2 GetClientSize() const override;
Float2 ScreenToClient(const Float2& screenPos) const override;
Float2 ClientToScreen(const Float2& clientPos) const override;
void SetIsFullscreen(bool isFullscreen) override;
void SetTitle(const StringView& title) override;
};
#endif