Feature: add Witness to watch file change

This commit is contained in:
yicheng 2018-08-12 00:09:57 +08:00
parent f57b340671
commit 02879e17f5
6 changed files with 334 additions and 0 deletions

View File

@ -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 */,

95
ClashX/Vendor/Witness/EventStream.swift vendored Executable file
View File

@ -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)
}

130
ClashX/Vendor/Witness/FileEvent.swift vendored Executable file
View File

@ -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: ",")
}
}

28
ClashX/Vendor/Witness/Info.plist vendored Executable file
View File

@ -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>

20
ClashX/Vendor/Witness/Witness.h vendored Executable file
View File

@ -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>

35
ClashX/Vendor/Witness/Witness.swift vendored Executable file
View File

@ -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()
}
}