Feature: add Witness to watch file change
This commit is contained in:
parent
f57b340671
commit
02879e17f5
|
@ -27,6 +27,10 @@
|
|||
496BDEE021196F1E00C5207F /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 496BDEDF21196F1E00C5207F /* Logger.swift */; };
|
||||
49722FE4211ED56C00650A41 /* parseINI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49722FE1211ED56B00650A41 /* parseINI.swift */; };
|
||||
49722FE8211F28CC00650A41 /* initConfig.ini in Resources */ = {isa = PBXBuildFile; fileRef = 49722FE7211F28CC00650A41 /* initConfig.ini */; };
|
||||
49722FEF211F338B00650A41 /* FileEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49722FEA211F338B00650A41 /* FileEvent.swift */; };
|
||||
49722FF0211F338B00650A41 /* EventStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49722FEB211F338B00650A41 /* EventStream.swift */; };
|
||||
49722FF1211F338B00650A41 /* Witness.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49722FEC211F338B00650A41 /* Witness.swift */; };
|
||||
49722FF2211F338B00650A41 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 49722FEE211F338B00650A41 /* Info.plist */; };
|
||||
497F0DF320DE2FE50077AD41 /* Icon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 497F0DF220DE2FE50077AD41 /* Icon.icns */; };
|
||||
4989F98020D01C8F0001E564 /* clash.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4989F97E20D01C8F0001E564 /* clash.a */; };
|
||||
4989F98420D02D200001E564 /* Country.mmdb in Resources */ = {isa = PBXBuildFile; fileRef = 4989F98320D02D200001E564 /* Country.mmdb */; };
|
||||
|
@ -95,6 +99,11 @@
|
|||
496BDEDF21196F1E00C5207F /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
|
||||
49722FE1211ED56B00650A41 /* parseINI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = parseINI.swift; sourceTree = "<group>"; };
|
||||
49722FE7211F28CC00650A41 /* initConfig.ini */ = {isa = PBXFileReference; lastKnownFileType = text; path = initConfig.ini; sourceTree = "<group>"; };
|
||||
49722FEA211F338B00650A41 /* FileEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileEvent.swift; sourceTree = "<group>"; };
|
||||
49722FEB211F338B00650A41 /* EventStream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventStream.swift; sourceTree = "<group>"; };
|
||||
49722FEC211F338B00650A41 /* Witness.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Witness.swift; sourceTree = "<group>"; };
|
||||
49722FED211F338B00650A41 /* Witness.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Witness.h; sourceTree = "<group>"; };
|
||||
49722FEE211F338B00650A41 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
497F0DF220DE2FE50077AD41 /* Icon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = Icon.icns; sourceTree = "<group>"; };
|
||||
4989F97E20D01C8F0001E564 /* clash.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = clash.a; sourceTree = "<group>"; };
|
||||
4989F97F20D01C8F0001E564 /* clash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = clash.h; sourceTree = "<group>"; };
|
||||
|
@ -180,6 +189,7 @@
|
|||
49722FDD211ED2A900650A41 /* Vendor */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
49722FE9211F338B00650A41 /* Witness */,
|
||||
49722FDE211ED2CF00650A41 /* INIParser */,
|
||||
);
|
||||
path = Vendor;
|
||||
|
@ -193,6 +203,18 @@
|
|||
path = INIParser;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
49722FE9211F338B00650A41 /* Witness */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
49722FEA211F338B00650A41 /* FileEvent.swift */,
|
||||
49722FEB211F338B00650A41 /* EventStream.swift */,
|
||||
49722FEC211F338B00650A41 /* Witness.swift */,
|
||||
49722FED211F338B00650A41 /* Witness.h */,
|
||||
49722FEE211F338B00650A41 /* Info.plist */,
|
||||
);
|
||||
path = Witness;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4989F98520D0AA300001E564 /* ViewControllers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -460,6 +482,7 @@
|
|||
4952C3CC2116E952004A4FA8 /* PreferencesWindowController.xib in Resources */,
|
||||
4989F98420D02D200001E564 /* Country.mmdb in Resources */,
|
||||
497F0DF320DE2FE50077AD41 /* Icon.icns in Resources */,
|
||||
49722FF2211F338B00650A41 /* Info.plist in Resources */,
|
||||
492C4865210EC9E4004554A0 /* ProxyConfig in Resources */,
|
||||
4989F98E20D0AE990001E564 /* sampleConfig.ini in Resources */,
|
||||
49CF3B6320CED9CF001EBF94 /* check_proxy_helper.sh in Resources */,
|
||||
|
@ -555,10 +578,13 @@
|
|||
49CF3B2120CD7463001EBF94 /* AppDelegate.swift in Sources */,
|
||||
4952C3CB2116E952004A4FA8 /* PreferencesWindowController.swift in Sources */,
|
||||
496BDEE021196F1E00C5207F /* Logger.swift in Sources */,
|
||||
49722FEF211F338B00650A41 /* FileEvent.swift in Sources */,
|
||||
4913C82321157D0200F6B87C /* Notification.swift in Sources */,
|
||||
492C4871210EF62E004554A0 /* ClashConfig.swift in Sources */,
|
||||
492C4869210EE6B9004554A0 /* ApiRequest.swift in Sources */,
|
||||
49CF3B6520CEE06C001EBF94 /* ConfigManager.swift in Sources */,
|
||||
49722FF1211F338B00650A41 /* Witness.swift in Sources */,
|
||||
49722FF0211F338B00650A41 /* EventStream.swift in Sources */,
|
||||
4952C3BF2115C7CA004A4FA8 /* ProxyMenuItemFactory.swift in Sources */,
|
||||
4966E9E32118153A00A391FB /* NSUserNotificationCenter+Extension.swift in Sources */,
|
||||
49722FE4211ED56C00650A41 /* parseINI.swift in Sources */,
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
//
|
||||
// EventStream.swift
|
||||
// Witness
|
||||
//
|
||||
// Created by Niels de Hoog on 23/09/15.
|
||||
// Copyright © 2015 Invisible Pixel. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
* The type of event stream to be used. For more information, please refer to the File System Events Programming Guide: https://developer.apple.com/library/mac/documentation/Darwin/Conceptual/FSEvents_ProgGuide/UsingtheFSEventsFramework/UsingtheFSEventsFramework.html#//apple_ref/doc/uid/TP40005289-CH4-SW6
|
||||
*/
|
||||
|
||||
public enum StreamType {
|
||||
case hostBased // default
|
||||
case diskBased
|
||||
}
|
||||
|
||||
class EventStream {
|
||||
let paths: [String]
|
||||
|
||||
// use explicitly unwrapped optional so we can pass self as context to stream
|
||||
private var stream: FSEventStreamRef!
|
||||
private let changeHandler: FileEventHandler
|
||||
|
||||
init(paths: [String], type: StreamType = .hostBased, flags: EventStreamCreateFlags, latency: TimeInterval, deviceToWatch: dev_t = 0, changeHandler: @escaping FileEventHandler) {
|
||||
self.paths = paths
|
||||
self.changeHandler = changeHandler
|
||||
|
||||
func callBack(stream: ConstFSEventStreamRef, clientCallbackInfo: UnsafeMutableRawPointer?, numEvents: Int, eventPaths: UnsafeMutableRawPointer, eventFlags: UnsafePointer<FSEventStreamEventFlags>, eventIDs: UnsafePointer<FSEventStreamEventId>) {
|
||||
|
||||
let eventStream = unsafeBitCast(clientCallbackInfo, to: EventStream.self)
|
||||
let paths = unsafeBitCast(eventPaths, to: NSArray.self)
|
||||
|
||||
var events = [FileEvent]()
|
||||
for i in 0..<Int(numEvents) {
|
||||
let event = FileEvent(path: paths[i] as! String, flags: FileEventFlags(rawValue: eventFlags[i]))
|
||||
events.append(event)
|
||||
}
|
||||
|
||||
eventStream.changeHandler(events)
|
||||
}
|
||||
|
||||
var context = FSEventStreamContext()
|
||||
context.info = unsafeBitCast(self, to: UnsafeMutableRawPointer.self)
|
||||
|
||||
let combinedFlags = flags.union(.UseCFTypes)
|
||||
|
||||
switch type {
|
||||
case .hostBased:
|
||||
stream = FSEventStreamCreate(nil, callBack, &context, paths as CFArray, FSEventStreamEventId(kFSEventStreamEventIdSinceNow), latency, combinedFlags.rawValue)
|
||||
case .diskBased:
|
||||
stream = FSEventStreamCreateRelativeToDevice(nil, callBack, &context, deviceToWatch, paths as CFArray, FSEventStreamEventId(kFSEventStreamEventIdSinceNow), latency, combinedFlags.rawValue)
|
||||
}
|
||||
|
||||
FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), CFRunLoopMode.defaultMode.rawValue)
|
||||
FSEventStreamStart(stream)
|
||||
}
|
||||
|
||||
func flush() {
|
||||
FSEventStreamFlushSync(stream)
|
||||
}
|
||||
|
||||
func flushAsync() {
|
||||
FSEventStreamFlushAsync(stream)
|
||||
}
|
||||
|
||||
deinit {
|
||||
// stop stream
|
||||
FSEventStreamStop(stream)
|
||||
|
||||
// unschedule from all run loops
|
||||
FSEventStreamInvalidate(stream)
|
||||
|
||||
// release
|
||||
FSEventStreamRelease(stream)
|
||||
}
|
||||
}
|
||||
|
||||
public struct EventStreamCreateFlags: OptionSet {
|
||||
public let rawValue: FSEventStreamCreateFlags
|
||||
public init(rawValue: FSEventStreamCreateFlags) { self.rawValue = rawValue }
|
||||
init(_ value: Int) { self.rawValue = FSEventStreamCreateFlags(value) }
|
||||
|
||||
public static let None = EventStreamCreateFlags(kFSEventStreamCreateFlagNone)
|
||||
|
||||
// setting the UseCFTypes flag has no consequences, because Witness will always enable it
|
||||
public static let UseCFTypes = EventStreamCreateFlags(kFSEventStreamCreateFlagUseCFTypes)
|
||||
public static let NoDefer = EventStreamCreateFlags(kFSEventStreamCreateFlagNoDefer)
|
||||
public static let WatchRoot = EventStreamCreateFlags(kFSEventStreamCreateFlagWatchRoot)
|
||||
public static let IgnoreSelf = EventStreamCreateFlags(kFSEventStreamCreateFlagIgnoreSelf)
|
||||
public static let FileEvents = EventStreamCreateFlags(kFSEventStreamCreateFlagFileEvents)
|
||||
public static let MarkSelf = EventStreamCreateFlags(kFSEventStreamCreateFlagMarkSelf)
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
//
|
||||
// FileEvent.swift
|
||||
// Witness
|
||||
//
|
||||
// Created by Niels de Hoog on 24/09/15.
|
||||
// Copyright © 2015 Invisible Pixel. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct FileEvent {
|
||||
public let path: String
|
||||
public let flags: FileEventFlags
|
||||
}
|
||||
|
||||
public struct FileEventFlags: OptionSet {
|
||||
public let rawValue: FSEventStreamEventFlags
|
||||
public init(rawValue: FSEventStreamEventFlags) { self.rawValue = rawValue }
|
||||
init(_ value: Int) { self.rawValue = FSEventStreamEventFlags(value) }
|
||||
|
||||
public static let None = FileEventFlags(kFSEventStreamEventFlagNone)
|
||||
|
||||
public static let MustScanSubDirs = FileEventFlags(kFSEventStreamEventFlagMustScanSubDirs)
|
||||
|
||||
public static let UserDropped = FileEventFlags(kFSEventStreamEventFlagUserDropped)
|
||||
public static let KernelDropped = FileEventFlags(kFSEventStreamEventFlagKernelDropped)
|
||||
|
||||
public static let EventIdsWrapped = FileEventFlags(kFSEventStreamEventFlagEventIdsWrapped)
|
||||
|
||||
public static let HistoryDone = FileEventFlags(kFSEventStreamEventFlagHistoryDone)
|
||||
|
||||
public static let RootChanged = FileEventFlags(kFSEventStreamEventFlagRootChanged)
|
||||
|
||||
public static let Mount = FileEventFlags(kFSEventStreamEventFlagMount)
|
||||
public static let Unmount = FileEventFlags(kFSEventStreamEventFlagUnmount)
|
||||
|
||||
public static let ItemCreated = FileEventFlags(kFSEventStreamEventFlagItemCreated)
|
||||
public static let ItemRemoved = FileEventFlags(kFSEventStreamEventFlagItemRemoved)
|
||||
public static let ItemInodeMetaMod = FileEventFlags(kFSEventStreamEventFlagItemInodeMetaMod)
|
||||
public static let ItemRenamed = FileEventFlags(kFSEventStreamEventFlagItemRenamed)
|
||||
public static let ItemModified = FileEventFlags(kFSEventStreamEventFlagItemModified)
|
||||
public static let ItemFinderInfoMod = FileEventFlags(kFSEventStreamEventFlagItemFinderInfoMod)
|
||||
public static let ItemChangeOwner = FileEventFlags(kFSEventStreamEventFlagItemChangeOwner)
|
||||
public static let ItemXattrMod = FileEventFlags(kFSEventStreamEventFlagItemXattrMod)
|
||||
public static let ItemIsFile = FileEventFlags(kFSEventStreamEventFlagItemIsFile)
|
||||
public static let ItemIsDir = FileEventFlags(kFSEventStreamEventFlagItemIsDir)
|
||||
public static let ItemIsSymlink = FileEventFlags(kFSEventStreamEventFlagItemIsSymlink)
|
||||
public static let ItemIsHardLink = FileEventFlags(kFSEventStreamEventFlagItemIsHardlink)
|
||||
public static let ItemIsLastHardLink = FileEventFlags(kFSEventStreamEventFlagItemIsLastHardlink)
|
||||
|
||||
public static let OwnEvent = FileEventFlags(kFSEventStreamEventFlagOwnEvent)
|
||||
}
|
||||
|
||||
extension FileEventFlags: CustomStringConvertible {
|
||||
public var description: String {
|
||||
var strings = [String]()
|
||||
|
||||
if self == .None {
|
||||
strings.append("None")
|
||||
}
|
||||
if (self.contains(.MustScanSubDirs)) {
|
||||
strings.append("Must Scan Subdirectories")
|
||||
}
|
||||
if (self.contains(.UserDropped)) {
|
||||
strings.append("User Dropped")
|
||||
}
|
||||
if (self.contains(.KernelDropped)) {
|
||||
strings.append("Kernel Dropped")
|
||||
}
|
||||
if (self.contains(.EventIdsWrapped)) {
|
||||
strings.append("Event IDs wrapped")
|
||||
}
|
||||
if (self.contains(.HistoryDone)) {
|
||||
strings.append("History Done")
|
||||
}
|
||||
if (self.contains(.RootChanged)) {
|
||||
strings.append("Root Changed")
|
||||
}
|
||||
if (self.contains(.Mount)) {
|
||||
strings.append("Mount")
|
||||
}
|
||||
if (self.contains(.Unmount)) {
|
||||
strings.append("Unmount")
|
||||
}
|
||||
if (self.contains(.ItemCreated)) {
|
||||
strings.append("Item created")
|
||||
}
|
||||
if (self.contains(.ItemRemoved)) {
|
||||
strings.append("Item Removed")
|
||||
}
|
||||
if (self.contains(.ItemInodeMetaMod)) {
|
||||
strings.append("Item Inode Meta Modification")
|
||||
}
|
||||
if (self.contains(.ItemRenamed)) {
|
||||
strings.append("Item Renamed")
|
||||
}
|
||||
if (self.contains(.ItemModified)) {
|
||||
strings.append("Item Modified")
|
||||
}
|
||||
if (self.contains(.ItemFinderInfoMod)) {
|
||||
strings.append("Item Finder Info Modification")
|
||||
}
|
||||
if (self.contains(.ItemChangeOwner)) {
|
||||
strings.append("Item Change Owner")
|
||||
}
|
||||
if (self.contains(.ItemXattrMod)) {
|
||||
strings.append("Item Xattr Modification")
|
||||
}
|
||||
if (self.contains(.ItemIsFile)) {
|
||||
strings.append("Item Is File")
|
||||
}
|
||||
if (self.contains(.ItemIsDir)) {
|
||||
strings.append("Item Is Directory")
|
||||
}
|
||||
if (self.contains(.ItemIsSymlink)) {
|
||||
strings.append("Item Is Symbolic Link")
|
||||
}
|
||||
if (self.contains(.ItemIsHardLink)) {
|
||||
strings.append("Item Is Hard Link")
|
||||
}
|
||||
if (self.contains(.ItemIsLastHardLink)) {
|
||||
strings.append("Item Is Last Hard Link")
|
||||
}
|
||||
if (self.contains(.OwnEvent)) {
|
||||
strings.append("Own event")
|
||||
}
|
||||
|
||||
return strings.joined(separator: ",")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2015 Invisible Pixel. All rights reserved.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// Witness.h
|
||||
// Witness
|
||||
//
|
||||
// Created by Niels de Hoog on 23/09/15.
|
||||
// Copyright © 2015 Invisible Pixel. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <CoreServices/CoreServices.h>
|
||||
|
||||
//! Project version number for Witness.
|
||||
FOUNDATION_EXPORT double WitnessVersionNumber;
|
||||
|
||||
//! Project version string for Witness.
|
||||
FOUNDATION_EXPORT const unsigned char WitnessVersionString[];
|
||||
|
||||
// In this header, you should import all the public headers of your framework using statements like #import <Witness/PublicHeader.h>
|
||||
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// Witness.swift
|
||||
// Witness
|
||||
//
|
||||
// Created by Niels de Hoog on 23/09/15.
|
||||
// Copyright © 2015 Invisible Pixel. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public typealias FileEventHandler = (_ events: [FileEvent]) -> ()
|
||||
|
||||
public struct Witness {
|
||||
private let stream: EventStream
|
||||
var paths: [String] {
|
||||
return stream.paths
|
||||
}
|
||||
|
||||
public init(paths: [String], flags: EventStreamCreateFlags = .None, latency: TimeInterval = 1.0, changeHandler: @escaping FileEventHandler) {
|
||||
self.stream = EventStream(paths: paths, flags: flags, latency: latency, changeHandler: changeHandler)
|
||||
}
|
||||
|
||||
public init(paths: [String], streamType: StreamType, flags: EventStreamCreateFlags = .None, latency: TimeInterval = 1.0, deviceToWatch: dev_t, changeHandler: @escaping FileEventHandler) {
|
||||
self.stream = EventStream(paths: paths, type: streamType, flags: flags, latency: latency, deviceToWatch: deviceToWatch, changeHandler: changeHandler)
|
||||
}
|
||||
|
||||
public func flush() {
|
||||
self.stream.flush()
|
||||
}
|
||||
|
||||
public func flushAsync() {
|
||||
self.stream.flushAsync()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue