فهرست منبع

WIP: macOS port
- Folder/file watcher now functional
- Drag and drop functionality now functional
- Fixed splash screen
- Deactivating non-floating windows no longer hides them

Marko Pintera 7 سال پیش
والد
کامیت
e77542da58

+ 72 - 21
Source/BansheeCore/Private/MacOS/BsMacOSFolderMonitor.cpp

@@ -3,11 +3,13 @@
 #include "Platform/BsFolderMonitor.h"
 #include "FileSystem/BsFileSystem.h"
 #include "Error/BsException.h"
+#include "Utility/BsTimer.h"
 
 #include <CoreServices/CoreServices.h>
 
 namespace bs
 {
+	static constexpr UINT32 WRITE_STEADY_WAIT = 2000;
 	CFStringRef FolderMonitorMode = CFSTR("BSFolderMonitor");
 
 	enum class FileActionType
@@ -18,6 +20,13 @@ namespace bs
 		Renamed
 	};
 
+	struct CreatedFileInfo
+	{
+		Path path;
+		UINT64 lastSize;
+		Timer timer;
+	};
+
 	struct FileAction
 	{
 		static FileAction* createAdded(const WString& fileName)
@@ -138,7 +147,7 @@ namespace bs
 		FolderChangeBits filter;
 		FSEventStreamRef streamRef;
 		bool hasStarted;
-		UnorderedSet<FSEventStreamEventId> renameEvents;
+		Vector<CreatedFileInfo> createdFiles;
 	};
 
 	FolderMonitor::FolderWatchInfo::FolderWatchInfo(
@@ -182,7 +191,7 @@ namespace bs
 
 		if (streamRef)
 		{
-			FSEventStreamScheduleWithRunLoop(streamRef, CFRunLoopGetMain(), FolderMonitorMode);
+			FSEventStreamScheduleWithRunLoop(streamRef, CFRunLoopGetCurrent(), FolderMonitorMode);
 			if(FSEventStreamStart(streamRef))
 				hasStarted = true;
 		}
@@ -219,6 +228,11 @@ namespace bs
 			auto pathEntry = (CFStringRef)CFArrayGetValueAtIndex(paths, i);
 			Path path = CFStringGetCStringPtr(pathEntry, kCFStringEncodingUTF8);
 
+			// Ignore folder meta-data (.DS_Store)
+			String filename = path.getFilename(false);
+			if(filename == ".DS_Store")
+				continue;
+
 			CFIndex pathLength = CFStringGetLength(pathEntry);
 			if(pathLength == 0)
 				continue;
@@ -238,23 +252,21 @@ namespace bs
 			bool wasCreated = (flags & kFSEventStreamEventFlagItemCreated) != 0;
 			bool wasModified = (flags & kFSEventStreamEventFlagItemModified) != 0;
 			bool wasRemoved = (flags & kFSEventStreamEventFlagItemRemoved) != 0;
+			bool ownerChange = (flags & kFSEventStreamEventFlagItemChangeOwner) != 0;
+
+			// Ignore owner change as they just result in duplicate events
+			if(ownerChange)
+				continue;
 
 			// Rename events get translated to create/remove events
 			bool wasRenamed = (flags & kFSEventStreamEventFlagItemRenamed) != 0;
 
 			if(wasRenamed)
 			{
-				auto iterFind = watcher->renameEvents.find(eventIds[i]);
-				if (iterFind == watcher->renameEvents.end())
-				{
-					wasRemoved = true;
-					watcher->renameEvents.insert(eventIds[i]);
-				}
+				if(FileSystem::exists(path))
+					folderData->fileActions.push_back(FileAction::createAdded(path.toWString()));
 				else
-				{
-					wasCreated = true;
-					watcher->renameEvents.erase(eventIds[i]);
-				}
+					folderData->fileActions.push_back(FileAction::createRemoved(path.toWString()));
 			}
 
 			// File/folder was added
@@ -268,7 +280,14 @@ namespace bs
 				else
 				{
 					if (watcher->filter.isSet(FolderChangeBit::FileName))
-						folderData->fileActions.push_back(FileAction::createAdded(path.toWString()));
+					{
+						// We delay all file creation events until the file is done writing
+						watcher->createdFiles.push_back(CreatedFileInfo());
+						CreatedFileInfo& createdFileInfo = watcher->createdFiles.back();
+						createdFileInfo.path = path;
+						createdFileInfo.lastSize = FileSystem::getFileSize(path);
+						createdFileInfo.timer.reset();
+					}
 				}
 			}
 
@@ -290,12 +309,20 @@ namespace bs
 			// File was modified
 			if(wasModified && watcher->filter.isSet(FolderChangeBit::FileWrite))
 			{
-				folderData->fileActions.push_back(FileAction::createModified(path.toWString()));
+				// Don't send out modified event if file was created
+				auto iterFind = std::find_if(watcher->createdFiles.begin(), watcher->createdFiles.end(),
+					 [&path](const CreatedFileInfo& info)
+					 {
+						return info.path == path;
+					 }
+				);
+
+				if(iterFind == watcher->createdFiles.end())
+					folderData->fileActions.push_back(FileAction::createModified(path.toWString()));
 			}
 		}
 	}
 
-
 	class FolderMonitor::FileNotifyInfo
 	{
 	};
@@ -385,8 +412,6 @@ namespace bs
 				Lock lock(m->mainMutex);
 				FolderWatchInfo* watchInfo = *findIter;
 
-				watchInfo->stopMonitor();
-
 				m->monitorsToStop.push_back(watchInfo);
 				m->monitors.erase(findIter);
 			}
@@ -400,11 +425,7 @@ namespace bs
 
 			// Remove all watches (this will also wake up the thread)
 			for (auto& watchInfo : m->monitors)
-			{
-				watchInfo->stopMonitor();
-
 				m->monitorsToStop.push_back(watchInfo);
-			}
 
 			m->monitors.clear();
 		}
@@ -449,6 +470,36 @@ namespace bs
 			if((result == kCFRunLoopRunStopped) || (result == kCFRunLoopRunFinished))
 				break;
 
+			// Check if any created files have completed writing, and handle rename events
+			{
+				Lock lock(m->mainMutex);
+
+				for(auto& monitor : m->monitors)
+				{
+					FolderMonitor::Pimpl* folderData = monitor->owner->_getPrivateData();
+
+					for(auto iter = monitor->createdFiles.begin(); iter != monitor->createdFiles.end();)
+					{
+						CreatedFileInfo& entry = *iter;
+
+						UINT64 fileSize = FileSystem::getFileSize(entry.path);
+						if(fileSize != entry.lastSize)
+						{
+							entry.lastSize = fileSize;
+							entry.timer.reset();
+						}
+
+						if(entry.timer.getMilliseconds() > WRITE_STEADY_WAIT)
+						{
+							folderData->fileActions.push_back(FileAction::createAdded(entry.path.toWString()));
+							iter = monitor->createdFiles.erase(iter);
+						}
+						else
+							++iter;
+					}
+				}
+			}
+
 			// It's possible some system registered an input source with our loop, in which case the above check will not
 			// work. Instead check if there are any monitors left.
 			// Note: In this case we may also pay a 0.1 second timeout cost, since we don't explicitly wake the run loop.

+ 2 - 1
Source/BansheeCore/Private/MacOS/BsMacOSWindow.mm

@@ -646,7 +646,8 @@ namespace bs
 		// Makes sure that floating windows hide together with main app
 		// Also, for some reason it makes makeKeyAndOrderFront work properly when multiple windows are opened at the same
 		// frame. (Without it, only the last opened window moves to front)
-		[m->window setHidesOnDeactivate:YES];
+		if(desc.floating || desc.modal)
+			[m->window setHidesOnDeactivate:YES];
 
 		if(desc.floating)
 			[m->window setLevel:NSFloatingWindowLevel];

+ 0 - 5
Source/BansheeEngine/Platform/BsSplashScreen.cpp

@@ -161,7 +161,6 @@ namespace bs
 
 	// Note: Never freed, but that's fine
 	SplashScreen::Pimpl* SplashScreen::m = bs_new<Pimpl>();
-	const UINT32 SplashScreen::SPLASH_SCREEN_DURATION_MS = 1000;
 
 	void SplashScreen::show()
 	{
@@ -203,10 +202,6 @@ namespace bs
 		if (m->window == nullptr)
 			return;
 
-		UINT64 currentTime = m->timer.getMilliseconds();
-		if (currentTime < SPLASH_SCREEN_DURATION_MS)
-			return;
-
 		bs_delete(m->window);
 		m->window = nullptr;
 	}

+ 11 - 4
Source/BansheeGLRenderAPI/MacOS/BsMacOSRenderWindow.mm

@@ -39,8 +39,7 @@ namespace bs
 		mIsChild = iter != mDesc.platformSpecific.end();
 
 		props.isFullScreen = mDesc.fullscreen && !mIsChild;
-
-		props.isHidden = mDesc.hideUntilSwap || mDesc.hidden;
+		props.isHidden = mDesc.hidden;
 
 		mWindow = bs_new<CocoaWindow>(windowDesc);
 		mWindow->_setUserData(this);
@@ -61,6 +60,9 @@ namespace bs
 
 		RenderWindow::initialize();
 
+		if(props.isHidden)
+			mWindow->hide();
+
 		{
 			ScopedSpinLock lock(getCore()->mLock);
 			getCore()->mSyncedProperties = props;
@@ -336,6 +338,11 @@ namespace bs
 			*window = mWindow;
 			return;
 		}
+		else if(name == "WINDOW_ID")
+		{
+			UINT32* windowId = (UINT32*)data;
+			*windowId = mWindow->_getWindowId();
+		}
 	}
 
 	Vector2I MacOSRenderWindow::screenToWindowPos(const Vector2I& screenPos) const
@@ -390,8 +397,8 @@ namespace bs
 	{
 		MacOSRenderWindow::MacOSRenderWindow(const RENDER_WINDOW_DESC& desc, UINT32 renderWindowId, UINT32 cocoaWindowId,
 			const SPtr<MacOSContext>& context)
-			: RenderWindow(desc, renderWindowId), mShowOnSwap(desc.hideUntilSwap), mCocoaWindowId(cocoaWindowId)
-			, mProperties(desc), mSyncedProperties(desc)
+			: RenderWindow(desc, renderWindowId), mShowOnSwap(false)
+			, mCocoaWindowId(cocoaWindowId), mProperties(desc), mSyncedProperties(desc)
 		{
 			mContext = context;
 		}