Merge remote-tracking branch 'origin/master' into 1.7
This commit is contained in:
@@ -70,6 +70,46 @@ CFStringRef AppleUtils::ToString(const StringView& str)
|
||||
return CFStringCreateWithBytes(nullptr, (const UInt8*)str.GetText(), str.Length() * sizeof(Char), kCFStringEncodingUTF16LE, false);
|
||||
}
|
||||
|
||||
NSString* AppleUtils::ToNSString(const StringView& str)
|
||||
{
|
||||
NSString* ret = !str.IsEmpty() ? [[NSString alloc] initWithBytes: (const UInt8*)str.Get() length: str.Length() * sizeof(Char) encoding: NSUTF16LittleEndianStringEncoding] : nil;
|
||||
return ret ? ret : @"";
|
||||
}
|
||||
|
||||
NSString* AppleUtils::ToNSString(const char* string)
|
||||
{
|
||||
NSString* ret = string ? [NSString stringWithUTF8String: string] : nil;
|
||||
return ret ? ret : @"";
|
||||
}
|
||||
|
||||
|
||||
NSArray* AppleUtils::ParseArguments(NSString* argsString) {
|
||||
NSMutableArray *argsArray = [NSMutableArray array];
|
||||
NSScanner *scanner = [NSScanner scannerWithString:argsString];
|
||||
NSString *currentArg = nil;
|
||||
BOOL insideQuotes = NO;
|
||||
|
||||
while (![scanner isAtEnd]) {
|
||||
if (insideQuotes) {
|
||||
[scanner scanUpToString:@"\"" intoString:¤tArg];
|
||||
[scanner scanString:@"\"" intoString:NULL];
|
||||
insideQuotes = NO;
|
||||
} else {
|
||||
[scanner scanUpToString:@" " intoString:¤tArg];
|
||||
[scanner scanString:@" " intoString:NULL];
|
||||
}
|
||||
|
||||
if ([currentArg isEqualToString:@"\""]) {
|
||||
insideQuotes = YES;
|
||||
} else if (currentArg) {
|
||||
[argsArray addObject:currentArg];
|
||||
}
|
||||
}
|
||||
|
||||
return [argsArray copy];
|
||||
}
|
||||
|
||||
|
||||
typedef uint16_t offset_t;
|
||||
#define align_mem_up(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@ class AppleUtils
|
||||
public:
|
||||
static String ToString(CFStringRef str);
|
||||
static CFStringRef ToString(const StringView& str);
|
||||
static NSString* ToNSString(const StringView& str);
|
||||
static NSString* ToNSString(const char* string);
|
||||
static NSArray* ParseArguments(NSString* argsString);
|
||||
#if PLATFORM_MAC
|
||||
static Float2 PosToCoca(const Float2& pos);
|
||||
static Float2 CocaToPos(const Float2& pos);
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "Windows/WindowsFileSystemWatcher.h"
|
||||
#elif PLATFORM_LINUX
|
||||
#include "Linux/LinuxFileSystemWatcher.h"
|
||||
#elif PLATFORM_MAC
|
||||
#include "Mac/MacFileSystemWatcher.h"
|
||||
#else
|
||||
#include "Base/FileSystemWatcherBase.h"
|
||||
#endif
|
||||
|
||||
@@ -136,7 +136,7 @@ bool LinuxFileSystem::ShowFileExplorer(const StringView& path)
|
||||
{
|
||||
const StringAsANSI<> pathAnsi(*path, path.Length());
|
||||
char cmd[2048];
|
||||
sprintf(cmd, "nautilus %s &", pathAnsi.Get());
|
||||
sprintf(cmd, "xdg-open %s &", pathAnsi.Get());
|
||||
system(cmd);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ bool MacFileSystem::ShowBrowseFolderDialog(Window* parentWindow, const StringVie
|
||||
|
||||
bool MacFileSystem::ShowFileExplorer(const StringView& path)
|
||||
{
|
||||
return Platform::StartProcess(TEXT("open"), String::Format(TEXT("\"{0}\""), path), StringView::Empty) != 0;
|
||||
return [[NSWorkspace sharedWorkspace] selectFile: AppleUtils::ToNSString(FileSystem::ConvertRelativePathToAbsolute(path)) inFileViewerRootedAtPath: @""];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
100
Source/Engine/Platform/Mac/MacFileSystemWatcher.cpp
Normal file
100
Source/Engine/Platform/Mac/MacFileSystemWatcher.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_MAC
|
||||
#include "MacFileSystemWatcher.h"
|
||||
#include "Engine/Platform/Apple/AppleUtils.h"
|
||||
#include "Engine/Platform/CriticalSection.h"
|
||||
#include "Engine/Platform/Thread.h"
|
||||
#include "Engine/Threading/ThreadSpawner.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Core/Types/StringView.h"
|
||||
|
||||
void DirectoryWatchCallback( ConstFSEventStreamRef StreamRef, void* FileWatcherPtr, size_t EventCount, void* EventPaths, const FSEventStreamEventFlags EventFlags[], const FSEventStreamEventId EventIDs[] )
|
||||
{
|
||||
MacFileSystemWatcher* macFileSystemWatcher = (MacFileSystemWatcher*)FileWatcherPtr;
|
||||
if (macFileSystemWatcher)
|
||||
{
|
||||
CFArrayRef EventPathArray = (CFArrayRef)EventPaths;
|
||||
for( size_t EventIndex = 0; EventIndex < EventCount; ++EventIndex )
|
||||
{
|
||||
const FSEventStreamEventFlags Flags = EventFlags[EventIndex];
|
||||
if( !(Flags & kFSEventStreamEventFlagItemIsFile) && !(Flags & kFSEventStreamEventFlagItemIsDir) )
|
||||
{
|
||||
// events about symlinks don't concern us
|
||||
continue;
|
||||
}
|
||||
|
||||
auto action = FileSystemAction::Unknown;
|
||||
|
||||
const bool added = ( Flags & kFSEventStreamEventFlagItemCreated );
|
||||
const bool renamed = ( Flags & kFSEventStreamEventFlagItemRenamed );
|
||||
const bool modified = ( Flags & kFSEventStreamEventFlagItemModified );
|
||||
const bool removed = ( Flags & kFSEventStreamEventFlagItemRemoved );
|
||||
|
||||
if (added)
|
||||
{
|
||||
action = FileSystemAction::Create;
|
||||
}
|
||||
|
||||
if (renamed || modified)
|
||||
{
|
||||
action = FileSystemAction::Delete;
|
||||
}
|
||||
|
||||
if (removed)
|
||||
{
|
||||
action = FileSystemAction::Modify;
|
||||
}
|
||||
|
||||
const String resolvedPath = AppleUtils::ToString((CFStringRef)CFArrayGetValueAtIndex(EventPathArray,EventIndex));
|
||||
|
||||
macFileSystemWatcher->OnEvent(resolvedPath, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MacFileSystemWatcher::MacFileSystemWatcher(const String& directory, bool withSubDirs)
|
||||
: FileSystemWatcherBase(directory, withSubDirs)
|
||||
{
|
||||
|
||||
CFStringRef FullPathMac = AppleUtils::ToString(StringView(directory));
|
||||
CFArrayRef PathsToWatch = CFArrayCreate(NULL, (const void**)&FullPathMac, 1, NULL);
|
||||
|
||||
CFAbsoluteTime Latency = 0.2;
|
||||
|
||||
FSEventStreamContext Context;
|
||||
Context.version = 0;
|
||||
Context.info = this;
|
||||
Context.retain = NULL;
|
||||
Context.release = NULL;
|
||||
Context.copyDescription = NULL;
|
||||
|
||||
EventStream = FSEventStreamCreate( NULL,
|
||||
&DirectoryWatchCallback,
|
||||
&Context,
|
||||
PathsToWatch,
|
||||
kFSEventStreamEventIdSinceNow,
|
||||
Latency,
|
||||
kFSEventStreamCreateFlagUseCFTypes | kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents
|
||||
);
|
||||
|
||||
CFRelease(PathsToWatch);
|
||||
CFRelease(FullPathMac);
|
||||
|
||||
FSEventStreamScheduleWithRunLoop( EventStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode );
|
||||
FSEventStreamStart( EventStream );
|
||||
|
||||
IsRunning = true;
|
||||
}
|
||||
|
||||
MacFileSystemWatcher::~MacFileSystemWatcher()
|
||||
{
|
||||
if (IsRunning)
|
||||
{
|
||||
FSEventStreamStop(EventStream);
|
||||
FSEventStreamUnscheduleFromRunLoop(EventStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||
FSEventStreamInvalidate(EventStream);
|
||||
FSEventStreamRelease(EventStream);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
39
Source/Engine/Platform/Mac/MacFileSystemWatcher.h
Normal file
39
Source/Engine/Platform/Mac/MacFileSystemWatcher.h
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_MAC
|
||||
#include "Engine/Platform/Base/FileSystemWatcherBase.h"
|
||||
|
||||
#include <CoreServices/CoreServices.h>
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Mac platform implementation of the file system watching object.
|
||||
/// </summary>
|
||||
class FLAXENGINE_API MacFileSystemWatcher : public FileSystemWatcherBase
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MacFileSystemWatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="directory">The directory to watch.</param>
|
||||
/// <param name="withSubDirs">True if monitor the directory tree rooted at the specified directory or just a given directory.</param>
|
||||
MacFileSystemWatcher(const String& directory, bool withSubDirs);
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="MacFileSystemWatcher"/> class.
|
||||
/// </summary>
|
||||
~MacFileSystemWatcher();
|
||||
|
||||
public:
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
FSEventStreamRef EventStream;
|
||||
bool IsRunning;
|
||||
};
|
||||
#endif
|
||||
@@ -324,13 +324,16 @@ void MacPlatform::BeforeRun()
|
||||
void MacPlatform::Tick()
|
||||
{
|
||||
// Process system events
|
||||
while (true)
|
||||
NSEvent* event = nil;
|
||||
do
|
||||
{
|
||||
NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
|
||||
if (event == nil)
|
||||
break;
|
||||
[NSApp sendEvent:event];
|
||||
}
|
||||
event = [NSApp nextEventMatchingMask: NSEventMaskAny untilDate: nil inMode: NSDefaultRunLoopMode dequeue: YES];
|
||||
if (event)
|
||||
{
|
||||
[NSApp sendEvent:event];
|
||||
}
|
||||
|
||||
} while(event);
|
||||
|
||||
ApplePlatform::Tick();
|
||||
}
|
||||
@@ -429,13 +432,6 @@ Window* MacPlatform::CreateWindow(const CreateWindowSettings& settings)
|
||||
int32 MacPlatform::CreateProcess(CreateProcessSettings& settings)
|
||||
{
|
||||
LOG(Info, "Command: {0} {1}", settings.FileName, settings.Arguments);
|
||||
String cwd;
|
||||
if (settings.WorkingDirectory.HasChars())
|
||||
{
|
||||
LOG(Info, "Working directory: {0}", settings.WorkingDirectory);
|
||||
cwd = Platform::GetWorkingDirectory();
|
||||
Platform::SetWorkingDirectory(settings.WorkingDirectory);
|
||||
}
|
||||
const bool captureStdOut = settings.LogOutput || settings.SaveOutput;
|
||||
|
||||
// Special case if filename points to the app package (use actual executable)
|
||||
@@ -462,47 +458,81 @@ int32 MacPlatform::CreateProcess(CreateProcessSettings& settings)
|
||||
}
|
||||
}
|
||||
|
||||
// Sanatize the string if the exePath has spaces with properly espcaped spaces for popen
|
||||
exePath.Replace(TEXT(" "), TEXT("\\ "));
|
||||
|
||||
const String cmdLine = exePath + TEXT(" ") + settings.Arguments;
|
||||
const StringAsANSI<> cmdLineAnsi(*cmdLine, cmdLine.Length());
|
||||
FILE* pipe = popen(cmdLineAnsi.Get(), "r");
|
||||
if (cwd.Length() != 0)
|
||||
{
|
||||
Platform::SetWorkingDirectory(cwd);
|
||||
}
|
||||
if (!pipe)
|
||||
{
|
||||
LOG(Warning, "Failed to start process, errno={}", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// TODO: environment
|
||||
NSTask *task = [[NSTask alloc] init];
|
||||
task.launchPath = AppleUtils::ToNSString(exePath);
|
||||
task.arguments = AppleUtils::ParseArguments(AppleUtils::ToNSString(settings.Arguments));
|
||||
if (settings.WorkingDirectory.HasChars())
|
||||
task.currentDirectoryPath = AppleUtils::ToNSString(settings.WorkingDirectory);
|
||||
|
||||
int32 returnCode = 0;
|
||||
if (settings.WaitForEnd)
|
||||
{
|
||||
id<NSObject> outputObserver = nil;
|
||||
|
||||
if (captureStdOut)
|
||||
{
|
||||
char lineBuffer[1024];
|
||||
while (fgets(lineBuffer, sizeof(lineBuffer), pipe) != NULL)
|
||||
NSPipe *stdoutPipe = [NSPipe pipe];
|
||||
[task setStandardOutput:stdoutPipe];
|
||||
|
||||
outputObserver = [[NSNotificationCenter defaultCenter]
|
||||
addObserverForName: NSFileHandleDataAvailableNotification
|
||||
object: [stdoutPipe fileHandleForReading]
|
||||
queue: nil
|
||||
usingBlock:^(NSNotification* notification)
|
||||
{
|
||||
char* p = lineBuffer + strlen(lineBuffer) - 1;
|
||||
if (*p == '\n') *p = 0;
|
||||
String line(lineBuffer);
|
||||
if (settings.SaveOutput)
|
||||
settings.Output.Add(line.Get(), line.Length());
|
||||
if (settings.LogOutput)
|
||||
Log::Logger::Write(LogType::Info, line);
|
||||
NSData* data = [stdoutPipe fileHandleForReading].availableData;
|
||||
if (data.length)
|
||||
{
|
||||
String line((const char*)data.bytes, data.length);
|
||||
if (settings.SaveOutput)
|
||||
settings.Output.Add(line.Get(), line.Length());
|
||||
if (settings.LogOutput)
|
||||
{
|
||||
StringView lineView(line);
|
||||
if (line[line.Length() - 1] == '\n')
|
||||
lineView = StringView(line.Get(), line.Length() - 1);
|
||||
Log::Logger::Write(LogType::Info, lineView);
|
||||
}
|
||||
[[stdoutPipe fileHandleForReading] waitForDataInBackgroundAndNotify];
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
[[stdoutPipe fileHandleForReading] waitForDataInBackgroundAndNotify];
|
||||
}
|
||||
else
|
||||
|
||||
String exception;
|
||||
@try
|
||||
{
|
||||
while (!feof(pipe))
|
||||
{
|
||||
sleep(1);
|
||||
}
|
||||
[task launch];
|
||||
[task waitUntilExit];
|
||||
returnCode = [task terminationStatus];
|
||||
}
|
||||
@catch (NSException* e)
|
||||
{
|
||||
exception = e.reason.UTF8String;
|
||||
}
|
||||
if (!exception.IsEmpty())
|
||||
{
|
||||
LOG(Error, "Failed to run command {0} {1} with error {2}", settings.FileName, settings.Arguments, exception);
|
||||
returnCode = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
String exception;
|
||||
@try
|
||||
{
|
||||
[task launch];
|
||||
}
|
||||
@catch (NSException* e)
|
||||
{
|
||||
exception = e.reason.UTF8String;
|
||||
}
|
||||
if (!exception.IsEmpty())
|
||||
{
|
||||
LOG(Error, "Failed to run command {0} {1} with error {2}", settings.FileName, settings.Arguments, exception);
|
||||
returnCode = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -237,8 +237,8 @@ class UnixConditionVariable;
|
||||
typedef UnixConditionVariable ConditionVariable;
|
||||
class MacFileSystem;
|
||||
typedef MacFileSystem FileSystem;
|
||||
class FileSystemWatcherBase;
|
||||
typedef FileSystemWatcherBase FileSystemWatcher;
|
||||
class MacFileSystemWatcher;
|
||||
typedef MacFileSystemWatcher FileSystemWatcher;
|
||||
class UnixFile;
|
||||
typedef UnixFile File;
|
||||
class MacPlatform;
|
||||
|
||||
Reference in New Issue
Block a user