Repo // Deprecating Zonble's FSEventStreamHelper.

This commit is contained in:
ShikiSuen 2022-09-08 12:38:04 +08:00
parent 70de63dd35
commit f5bec584a5
10 changed files with 95 additions and 125 deletions

View File

@ -18,7 +18,6 @@ $ Contributors and volunteers of the upstream repo, having no responsibility in
- McBopomofo for macOS 2.x architect.
- Voltaire candidate window MK2 (massively modified as MK3 in vChewing by Shiki Suen).
- Notifier window and Tooltip UI.
- FSEventStreamHelper.
- App-style installer (only preserved for developer purposes).
- mgrPrefs (userdefaults manager).
- apiUpdate.

View File

@ -82,10 +82,8 @@
## 應用授權
威注音專案僅用到小麥注音的下述程式組件MIT License
威注音專案目前僅用到小麥注音的下述程式組件MIT License
- 狀態管理引擎 & NSStringUtils & FSEventStreamHelper (by Zonble Yang),基於狀態設計模式:
- ctlInputMethod 輸入法主控制器內則採用策略設計模式來處理各種狀態。
- 半衰記憶模組的 C++ 原版作者是 Mengjuei Hsieh且由 Shiki Suen 用 Swift 與 C# 分別重寫、繼續開發。
- 僅供研發人員調試方便而使用的 App 版安裝程式 (by Zonble Yang),不對公眾使用。
- Voltaire MK2 選字窗、飄雲通知視窗 (by Zonble Yang),有大幅度修改。

View File

@ -82,10 +82,8 @@
## 应用授权
威注音专案仅用到小麦注音的下述程式组件MIT License
威注音专案目前仅用到小麦注音的下述程式组件MIT License
- 状态管理引擎 & NSStringUtils & FSEventStreamHelper (by Zonble Yang),基于状态设计模式:
- ctlInputMethod 输入法主控制器内则采用策略设计模式来处理各种状态。
- 半衰记忆模组的 C++ 原版作者是 Mengjuei Hsieh且由 Shiki Suen 用 Swift 与 C# 分别重写、继续开发。
- 仅供研发人员调试方便而使用的 App 版安装程式 (by Zonble Yang),不对公众使用。
- Voltaire MK2 选字窗、飘云通知视窗 (by Zonble Yang),有大幅度修改。

View File

@ -0,0 +1,58 @@
// (c) 2018 Daniel Galasko
// Ref: https://medium.com/over-engineering/monitoring-a-folder-for-changes-in-ios-dc3f8614f902
import Foundation
class FolderMonitor {
// MARK: Properties
/// A file descriptor for the monitored directory.
private var monitoredFolderFileDescriptor: CInt = -1
/// A dispatch queue used for sending file changes in the directory.
private let folderMonitorQueue = DispatchQueue(label: "FolderMonitorQueue", attributes: .concurrent)
/// A dispatch source to monitor a file descriptor created from the directory.
private var folderMonitorSource: DispatchSourceFileSystemObject?
/// URL for the directory being monitored.
let url: URL
var folderDidChange: (() -> Void)?
// MARK: Initializers
init(url: URL) {
self.url = url
}
// MARK: Monitoring
/// Listen for changes to the directory (if we are not already).
func startMonitoring() {
guard folderMonitorSource == nil, monitoredFolderFileDescriptor == -1 else {
return
}
// Open the directory referenced by URL for monitoring only.
monitoredFolderFileDescriptor = open(url.path, O_EVTONLY)
// Define a dispatch source monitoring the directory for additions, deletions, and renamings.
folderMonitorSource = DispatchSource.makeFileSystemObjectSource(
fileDescriptor: monitoredFolderFileDescriptor, eventMask: .write, queue: folderMonitorQueue
)
// Define the block to call when a file change is detected.
folderMonitorSource?.setEventHandler { [weak self] in
self?.folderDidChange?()
}
// Define a cancel handler to ensure the directory is closed when the source is cancelled.
folderMonitorSource?.setCancelHandler { [weak self] in
guard let strongSelf = self else { return }
close(strongSelf.monitoredFolderFileDescriptor)
strongSelf.monitoredFolderFileDescriptor = -1
strongSelf.folderMonitorSource = nil
}
// Start monitoring the directory via the source.
folderMonitorSource?.resume()
}
/// Stop listening for changes to the directory, if the source has been created.
func stopMonitoring() {
folderMonitorSource?.cancel()
}
}

View File

@ -12,10 +12,8 @@ import Cocoa
import InputMethodKit
@objc(AppDelegate)
class AppDelegate: NSObject, NSApplicationDelegate,
FSEventStreamHelperDelegate, NSUserNotificationCenterDelegate
{
func helper(_: FSEventStreamHelper, didReceive _: [FSEventStreamHelper.Event]) {
class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDelegate {
private func reloadOnFolderChangeHappens() {
// 100ms 使使
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
if mgrPrefs.shouldAutoReloadUserDataFiles {
@ -30,9 +28,8 @@ class AppDelegate: NSObject, NSApplicationDelegate,
private var ctlPrefWindowInstance: ctlPrefWindow?
private var ctlAboutWindowInstance: ctlAboutWindow? // New About Window
private var checkTask: URLSessionTask?
public var fsStreamHelper = FSEventStreamHelper(
path: mgrLangModel.dataFolderPath(isDefaultFolder: false),
queue: DispatchQueue(label: "vChewing User Phrases")
public lazy var folderMonitor = FolderMonitor(
url: URL(fileURLWithPath: mgrLangModel.dataFolderPath(isDefaultFolder: false))
)
private var currentAlertType: String = ""
@ -59,8 +56,10 @@ class AppDelegate: NSObject, NSApplicationDelegate,
IME.initLangModels(userOnly: false)
}
fsStreamHelper.delegate = self
_ = fsStreamHelper.start()
folderMonitor.folderDidChange = { [weak self] in
self?.reloadOnFolderChangeHappens()
}
folderMonitor.startMonitoring()
mgrPrefs.fixOddPreferences()
mgrPrefs.setMissingDefaults()
@ -71,8 +70,15 @@ class AppDelegate: NSObject, NSApplicationDelegate,
}
}
func updateStreamHelperPath() {
fsStreamHelper.path = mgrPrefs.userDataFolderSpecified
func updateDirectoryMonitorPath() {
folderMonitor.stopMonitoring()
folderMonitor = FolderMonitor(
url: URL(fileURLWithPath: mgrLangModel.dataFolderPath(isDefaultFolder: false))
)
folderMonitor.folderDidChange = { [weak self] in
self?.reloadOnFolderChangeHappens()
}
folderMonitor.startMonitoring()
}
func showPreferences() {

View File

@ -1,88 +0,0 @@
// (c) 2011 and onwards The OpenVanilla Project (MIT License).
// All possible vChewing-specific modifications are of:
// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
// ... with NTL restriction stating that:
// No trademark license is granted to use the trade names, trademarks, service
// marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License.
import Cocoa
public protocol FSEventStreamHelperDelegate: AnyObject {
func helper(_ helper: FSEventStreamHelper, didReceive events: [FSEventStreamHelper.Event])
}
public class FSEventStreamHelper {
public struct Event {
var path: String
var flags: FSEventStreamEventFlags
var id: FSEventStreamEventId
}
public var path: String
public let dispatchQueue: DispatchQueue
public weak var delegate: FSEventStreamHelperDelegate?
public init(path: String, queue: DispatchQueue) {
self.path = path
dispatchQueue = queue
}
private var stream: FSEventStreamRef?
public func start() -> Bool {
if stream != nil {
return false
}
var context = FSEventStreamContext()
context.info = Unmanaged.passUnretained(self).toOpaque()
guard
let stream = FSEventStreamCreate(
nil,
{
_, clientCallBackInfo, eventCount, eventPaths, eventFlags, eventIds in
let helper = Unmanaged<FSEventStreamHelper>.fromOpaque(clientCallBackInfo!)
.takeUnretainedValue()
let pathsBase = eventPaths.assumingMemoryBound(to: UnsafePointer<CChar>.self)
let pathsPtr = UnsafeBufferPointer(start: pathsBase, count: eventCount)
let flagsPtr = UnsafeBufferPointer(start: eventFlags, count: eventCount)
let eventIDsPtr = UnsafeBufferPointer(start: eventIds, count: eventCount)
let events = (0..<eventCount).map {
FSEventStreamHelper.Event(
path: String(cString: pathsPtr[$0]),
flags: flagsPtr[$0],
id: eventIDsPtr[$0]
)
}
helper.delegate?.helper(helper, didReceive: events)
},
&context,
[path] as CFArray,
UInt64(kFSEventStreamEventIdSinceNow),
1.0,
FSEventStreamCreateFlags(kFSEventStreamCreateFlagNone)
)
else {
return false
}
FSEventStreamSetDispatchQueue(stream, dispatchQueue)
if !FSEventStreamStart(stream) {
FSEventStreamInvalidate(stream)
return false
}
self.stream = stream
return true
}
func stop() {
guard let stream = stream else {
return
}
FSEventStreamStop(stream)
FSEventStreamInvalidate(stream)
self.stream = nil
}
}

View File

@ -459,11 +459,10 @@ enum mgrLangModel {
return false
}
// We use FSEventStream to monitor possible changes of the user phrase folder, hence the
// lack of the needs of manually load data here unless FSEventStream is disabled by user.
if !mgrPrefs.shouldAutoReloadUserDataFiles {
loadUserPhrasesData()
}
// The new FolderMonitor module does NOT monitor cases that files are modified
// by the current application itself, requiring additional manual loading process here.
// if !mgrPrefs.shouldAutoReloadUserDataFiles {}
loadUserPhrasesData()
return true
}
return false

View File

@ -82,7 +82,7 @@ struct suiPrefPaneDictionary: View {
tbxUserDataPathSpecified = mgrPrefs.userDataFolderSpecified
BookmarkManager.shared.saveBookmark(for: url)
IME.initLangModels(userOnly: true)
(NSApplication.shared.delegate as! AppDelegate).updateStreamHelperPath()
(NSApplication.shared.delegate as! AppDelegate).updateDirectoryMonitorPath()
} else {
clsSFX.beep()
if !bolPreviousFolderValidity {

View File

@ -236,7 +236,7 @@ class ctlPrefWindow: NSWindowController {
mgrPrefs.userDataFolderSpecified = newPath
BookmarkManager.shared.saveBookmark(for: url)
IME.initLangModels(userOnly: true)
(NSApplication.shared.delegate as! AppDelegate).updateStreamHelperPath()
(NSApplication.shared.delegate as! AppDelegate).updateDirectoryMonitorPath()
} else {
clsSFX.beep()
if !bolPreviousFolderValidity {

View File

@ -10,6 +10,7 @@
5B09307628B6FC3B0021F8C5 /* shortcuts.html in Resources */ = {isa = PBXBuildFile; fileRef = 5B09307828B6FC3B0021F8C5 /* shortcuts.html */; };
5B0AF8B527B2C8290096FE54 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0AF8B427B2C8290096FE54 /* StringExtension.swift */; };
5B11328927B94CFB00E58451 /* AppleKeyboardConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */; };
5B16B84C28C9A89000ABA692 /* FolderMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B16B84B28C9A89000ABA692 /* FolderMonitor.swift */; };
5B175FFB28C5CDDC0078D1B4 /* IMKHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B175FFA28C5CDDC0078D1B4 /* IMKHelper.swift */; };
5B20430728BEE30900BFC6FD /* BookmarkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B20430628BEE30900BFC6FD /* BookmarkManager.swift */; };
5B21176C287539BB000443A9 /* ctlInputMethod_HandleStates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B21176B287539BB000443A9 /* ctlInputMethod_HandleStates.swift */; };
@ -34,7 +35,6 @@
5B5E535227EF261400C6AA1E /* IME.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B5E535127EF261400C6AA1E /* IME.swift */; };
5B5F8AEB28C86AAD007C11F1 /* NSAttributedTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B5F8AEA28C86AAD007C11F1 /* NSAttributedTextView.swift */; };
5B5F8AEE28C8826D007C11F1 /* ctlTooltip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B5F8AED28C8826D007C11F1 /* ctlTooltip.swift */; };
5B62A32927AE77D100A19448 /* FSEventStreamHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A32827AE77D100A19448 /* FSEventStreamHelper.swift */; };
5B62A33627AE795800A19448 /* mgrPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33527AE795800A19448 /* mgrPrefs.swift */; };
5B62A33D27AE7CC100A19448 /* ctlAboutWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */; };
5B62A34727AE7CD900A19448 /* ctlCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A34027AE7CD900A19448 /* ctlCandidate.swift */; };
@ -212,6 +212,7 @@
5B0AF8B427B2C8290096FE54 /* StringExtension.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = StringExtension.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B0C5EDF27C7D9870078037C /* dataCompiler.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = dataCompiler.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = AppleKeyboardConverter.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B16B84B28C9A89000ABA692 /* FolderMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderMonitor.swift; sourceTree = "<group>"; };
5B175FFA28C5CDDC0078D1B4 /* IMKHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IMKHelper.swift; sourceTree = "<group>"; };
5B18BA6F27C7BD8B0056EB19 /* LICENSE-CHS.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "LICENSE-CHS.txt"; sourceTree = "<group>"; };
5B18BA7027C7BD8B0056EB19 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
@ -249,7 +250,6 @@
5B5E535127EF261400C6AA1E /* IME.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = IME.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B5F8AEA28C86AAD007C11F1 /* NSAttributedTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSAttributedTextView.swift; sourceTree = "<group>"; };
5B5F8AED28C8826D007C11F1 /* ctlTooltip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlTooltip.swift; sourceTree = "<group>"; };
5B62A32827AE77D100A19448 /* FSEventStreamHelper.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = FSEventStreamHelper.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B62A33527AE795800A19448 /* mgrPrefs.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = mgrPrefs.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlAboutWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B62A34027AE7CD900A19448 /* ctlCandidate.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlCandidate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
@ -415,6 +415,14 @@
path = DataCompiler;
sourceTree = "<group>";
};
5B16B84A28C9A88300ABA692 /* FolderMonitor */ = {
isa = PBXGroup;
children = (
5B16B84B28C9A89000ABA692 /* FolderMonitor.swift */,
);
path = FolderMonitor;
sourceTree = "<group>";
};
5B18BA7527C7BF6D0056EB19 /* MiscRootFiles */ = {
isa = PBXGroup;
children = (
@ -476,6 +484,7 @@
5B62A30127AE732800A19448 /* 3rdParty */ = {
isa = PBXGroup;
children = (
5B16B84A28C9A88300ABA692 /* FolderMonitor */,
5B84579B2871AD2200C93B01 /* HotenkaChineseConverter */,
5B949BD72816DC4400D87B5D /* LineReader */,
5B5F8AEC28C86AB3007C11F1 /* NSAttributedTextView */,
@ -518,14 +527,6 @@
path = ControllerModules;
sourceTree = "<group>";
};
5B62A32127AE755D00A19448 /* FileHandlers */ = {
isa = PBXGroup;
children = (
5B62A32827AE77D100A19448 /* FSEventStreamHelper.swift */,
);
path = FileHandlers;
sourceTree = "<group>";
};
5B62A32227AE756300A19448 /* IMEModules */ = {
isa = PBXGroup;
children = (
@ -856,7 +857,6 @@
D427F76B278CA1BA004A2160 /* AppDelegate.swift */,
D47B92BF27972AC800458394 /* main.swift */,
5B62A31F27AE74E900A19448 /* ControllerModules */,
5B62A32127AE755D00A19448 /* FileHandlers */,
5B62A32227AE756300A19448 /* IMEModules */,
5B62A32427AE757300A19448 /* LangModelRelated */,
5B62A32327AE756800A19448 /* LanguageParsers */,
@ -1246,7 +1246,7 @@
5B62A34A27AE7CD900A19448 /* NotifierController.swift in Sources */,
5B11328927B94CFB00E58451 /* AppleKeyboardConverter.swift in Sources */,
5B54E743283A7D89001ECBDC /* lmCoreNS.swift in Sources */,
5B62A32927AE77D100A19448 /* FSEventStreamHelper.swift in Sources */,
5B16B84C28C9A89000ABA692 /* FolderMonitor.swift in Sources */,
5B2170E2289FACAD00BE7304 /* 8_Unigram.swift in Sources */,
5B21176C287539BB000443A9 /* ctlInputMethod_HandleStates.swift in Sources */,
5B62A33627AE795800A19448 /* mgrPrefs.swift in Sources */,