diff --git a/V2RayX.xcodeproj/project.pbxproj b/V2RayX.xcodeproj/project.pbxproj index 1a0b2f1..135f581 100644 --- a/V2RayX.xcodeproj/project.pbxproj +++ b/V2RayX.xcodeproj/project.pbxproj @@ -7,6 +7,12 @@ objects = { /* Begin PBXBuildFile section */ + 631B81132134EFC100ED6268 /* MASShortcut.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 631B81122134EFC100ED6268 /* MASShortcut.framework */; }; + 631B81142134F0FC00ED6268 /* MASShortcut.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 631B81122134EFC100ED6268 /* MASShortcut.framework */; }; + 631B81152134F0FC00ED6268 /* MASShortcut.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 631B81122134EFC100ED6268 /* MASShortcut.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 631B811A2134F40700ED6268 /* ShortcutsController.m in Sources */ = {isa = PBXBuildFile; fileRef = 631B81192134F40700ED6268 /* ShortcutsController.m */; }; + 631B811E21351CED00ED6268 /* ToastWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 631B811C21351CED00ED6268 /* ToastWindowController.m */; }; + 631B811F21351CED00ED6268 /* ToastWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 631B811D21351CED00ED6268 /* ToastWindowController.xib */; }; 9504C07D1C662C3000352520 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9504C07C1C662C3000352520 /* AppDelegate.m */; }; 9504C0801C662C3000352520 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 9504C07F1C662C3000352520 /* main.m */; }; 9504C0821C662C3000352520 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9504C0811C662C3000352520 /* Assets.xcassets */; }; @@ -56,6 +62,17 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + 631B81162134F0FC00ED6268 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 631B81152134F0FC00ED6268 /* MASShortcut.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; 9504C0C41C662DB400352520 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -77,6 +94,12 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 631B81122134EFC100ED6268 /* MASShortcut.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = MASShortcut.framework; sourceTree = ""; }; + 631B81182134F40700ED6268 /* ShortcutsController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShortcutsController.h; sourceTree = ""; }; + 631B81192134F40700ED6268 /* ShortcutsController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ShortcutsController.m; sourceTree = ""; }; + 631B811B21351CED00ED6268 /* ToastWindowController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ToastWindowController.h; sourceTree = ""; }; + 631B811C21351CED00ED6268 /* ToastWindowController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ToastWindowController.m; sourceTree = ""; }; + 631B811D21351CED00ED6268 /* ToastWindowController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ToastWindowController.xib; sourceTree = ""; }; 9504C0781C662C3000352520 /* V2RayX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = V2RayX.app; sourceTree = BUILT_PRODUCTS_DIR; }; 9504C07B1C662C3000352520 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 9504C07C1C662C3000352520 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -144,7 +167,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 631B81132134EFC100ED6268 /* MASShortcut.framework in Frameworks */, 95A143CC1C87FCDC008756B9 /* Cocoa.framework in Frameworks */, + 631B81142134F0FC00ED6268 /* MASShortcut.framework in Frameworks */, 9504C0C11C662D9400352520 /* libz.tbd in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -204,6 +229,11 @@ 9504C0D21C66511B00352520 /* ServerProfile.h */, 9504C0D31C66511B00352520 /* ServerProfile.m */, 9504C0861C662C3000352520 /* Info.plist */, + 631B81182134F40700ED6268 /* ShortcutsController.h */, + 631B81192134F40700ED6268 /* ShortcutsController.m */, + 631B811B21351CED00ED6268 /* ToastWindowController.h */, + 631B811C21351CED00ED6268 /* ToastWindowController.m */, + 631B811D21351CED00ED6268 /* ToastWindowController.xib */, 9504C07E1C662C3000352520 /* Supporting Files */, ); path = V2RayX; @@ -296,6 +326,7 @@ 95FCB2451C688BB20081B6FD /* Frameworks */ = { isa = PBXGroup; children = ( + 631B81122134EFC100ED6268 /* MASShortcut.framework */, 95A143CA1C87FC6E008756B9 /* Cocoa.framework */, 9504C0C01C662D9400352520 /* libz.tbd */, ); @@ -334,6 +365,7 @@ 9DB94D77209D273A0001E9DD /* Run Script */, 953B60541DB3E31D00D40654 /* Run Script */, 9504C0761C662C3000352520 /* Resources */, + 631B81162134F0FC00ED6268 /* Embed Frameworks */, ); buildRules = ( ); @@ -426,6 +458,7 @@ buildActionMask = 2147483647; files = ( 9D5EAC151FDCD70300C14B46 /* geoip.dat in Resources */, + 631B811F21351CED00ED6268 /* ToastWindowController.xib in Resources */, 9D5EAC161FDCD70300C14B46 /* geosite.dat in Resources */, 9D3CEE6F209E070F0077CCFE /* customizedConfigWindow.xib in Resources */, 9D5EAC171FDCD70300C14B46 /* v2ctl in Resources */, @@ -486,6 +519,7 @@ 9577E1BE1C6B065E00093BD3 /* GCDWebServerStreamedResponse.m in Sources */, 9504C0801C662C3000352520 /* main.m in Sources */, 9577E1BB1C6B065E00093BD3 /* GCDWebServerDataResponse.m in Sources */, + 631B811A2134F40700ED6268 /* ShortcutsController.m in Sources */, 9577E1BA1C6B065E00093BD3 /* GCDWebServerURLEncodedFormRequest.m in Sources */, 9577E1B71C6B065E00093BD3 /* GCDWebServerDataRequest.m in Sources */, 9577E1BC1C6B065E00093BD3 /* GCDWebServerErrorResponse.m in Sources */, @@ -497,6 +531,7 @@ 9577E1B61C6B065E00093BD3 /* GCDWebServerResponse.m in Sources */, 9577E1B21C6B065E00093BD3 /* GCDWebServer.m in Sources */, 9504C0D41C66511B00352520 /* ServerProfile.m in Sources */, + 631B811E21351CED00ED6268 /* ToastWindowController.m in Sources */, 9577E1B41C6B065E00093BD3 /* GCDWebServerFunctions.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -643,6 +678,11 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/V2RayX", + "$(PROJECT_DIR)/V2RayX/Dependencies", + ); INFOPLIST_FILE = V2RayX/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = cenmrev.V2RayX; @@ -655,6 +695,11 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/V2RayX", + "$(PROJECT_DIR)/V2RayX/Dependencies", + ); INFOPLIST_FILE = V2RayX/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = cenmrev.V2RayX; diff --git a/V2RayX/AppDelegate.h b/V2RayX/AppDelegate.h index 326cc13..77e6cac 100644 --- a/V2RayX/AppDelegate.h +++ b/V2RayX/AppDelegate.h @@ -27,6 +27,7 @@ int runCommandLine(NSString* launchPath, NSArray* arguments); @interface AppDelegate : NSObject { BOOL proxyState; ProxyMode proxyMode; + ProxyMode lastProxyMode; NSInteger localPort; NSInteger httpPort; BOOL udpSupport; diff --git a/V2RayX/AppDelegate.m b/V2RayX/AppDelegate.m index c96709b..7ee3295 100644 --- a/V2RayX/AppDelegate.m +++ b/V2RayX/AppDelegate.m @@ -11,6 +11,8 @@ #import "ConfigWindowController.h" #import #import "ServerProfile.h" +#import "ShortcutsController.h" +#import "ToastWindowController.h" #define kUseAllServer -10 @@ -23,6 +25,7 @@ @interface AppDelegate () { FSEventStreamRef fsEventStream; NSData* v2rayJSONconfig; + ToastWindowController *tosat; } @end @@ -117,6 +120,10 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { // resume the service when mac wakes up [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(configurationDidChange) name:NSWorkspaceDidWakeNotification object:NULL]; + + // Register global hotkey + [ShortcutsController bindShortcuts]; + [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(hotkeyChangeProxyMode) name:@"NOTIFY_SWITCH_PROXY_MODE_SHORTCUT" object:nil]; } - (void) writeDefaultSettings { @@ -216,6 +223,7 @@ - (IBAction)chooseV2rayRules:(id)sender { [self backupSystemProxy]; } proxyMode = rules; + lastProxyMode = proxyMode; [self configurationDidChange]; } @@ -224,6 +232,7 @@ - (IBAction)choosePacMode:(id)sender { [self backupSystemProxy]; } proxyMode = pac; + lastProxyMode = proxyMode; [self configurationDidChange]; } @@ -231,6 +240,7 @@ - (IBAction)chooseGlobalMode:(id)sender { if(proxyState == true && proxyMode == manual) { [self backupSystemProxy]; } + lastProxyMode = proxyMode; proxyMode = global; [self configurationDidChange]; } @@ -240,9 +250,45 @@ - (IBAction)chooseManualMode:(id)sender { [self restoreSystemProxy]; } proxyMode = manual; + lastProxyMode = proxyMode; [self configurationDidChange]; } +- (void)hotkeyChangeProxyMode { + if (proxyMode == global) { + switch (lastProxyMode) { + case rules: + [self chooseV2rayRules:nil]; + [self makeToast:@"V2rayRules Mode"]; + break; + case pac: + [self choosePacMode:nil]; + [self makeToast:@"Pac Mode"]; + break; + case manual: + [self chooseManualMode:nil]; + [self makeToast:@"Manual Mode"]; + break; + default: + break; + } + } else { + [self chooseGlobalMode:nil]; + [self makeToast:@"Global Mode"]; + } +} + +- (void)makeToast:(NSString *)message { + if (tosat) { + [tosat close]; + } + tosat = [[ToastWindowController alloc] initWithWindowNibName:@"ToastWindowController"]; + tosat.message = message; + [tosat showWindow:self]; + [tosat fadeInHud]; + +} + - (IBAction)showConfigWindow:(id)sender { if (configWindowController) { [configWindowController close]; diff --git a/V2RayX/ConfigWindow.xib b/V2RayX/ConfigWindow.xib index 1c48ba9..8f119bf 100644 --- a/V2RayX/ConfigWindow.xib +++ b/V2RayX/ConfigWindow.xib @@ -29,7 +29,7 @@ - + @@ -557,6 +557,31 @@ Gw + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/V2RayX/Info.plist b/V2RayX/Info.plist index 226fb36..9f027ed 100644 --- a/V2RayX/Info.plist +++ b/V2RayX/Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 296 + 330 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) LSUIElement diff --git a/V2RayX/MASShortcut.framework/Headers b/V2RayX/MASShortcut.framework/Headers new file mode 120000 index 0000000..a177d2a --- /dev/null +++ b/V2RayX/MASShortcut.framework/Headers @@ -0,0 +1 @@ +Versions/Current/Headers \ No newline at end of file diff --git a/V2RayX/MASShortcut.framework/MASShortcut b/V2RayX/MASShortcut.framework/MASShortcut new file mode 120000 index 0000000..b75eafb --- /dev/null +++ b/V2RayX/MASShortcut.framework/MASShortcut @@ -0,0 +1 @@ +Versions/Current/MASShortcut \ No newline at end of file diff --git a/V2RayX/MASShortcut.framework/Modules b/V2RayX/MASShortcut.framework/Modules new file mode 120000 index 0000000..5736f31 --- /dev/null +++ b/V2RayX/MASShortcut.framework/Modules @@ -0,0 +1 @@ +Versions/Current/Modules \ No newline at end of file diff --git a/V2RayX/MASShortcut.framework/Resources b/V2RayX/MASShortcut.framework/Resources new file mode 120000 index 0000000..953ee36 --- /dev/null +++ b/V2RayX/MASShortcut.framework/Resources @@ -0,0 +1 @@ +Versions/Current/Resources \ No newline at end of file diff --git a/V2RayX/MASShortcut.framework/Versions/A/Headers/MASDictionaryTransformer.h b/V2RayX/MASShortcut.framework/Versions/A/Headers/MASDictionaryTransformer.h new file mode 100755 index 0000000..6e53fd8 --- /dev/null +++ b/V2RayX/MASShortcut.framework/Versions/A/Headers/MASDictionaryTransformer.h @@ -0,0 +1,19 @@ +extern NSString *const MASDictionaryTransformerName; + +/** + Converts shortcuts for storage in user defaults. + + User defaults can’t stored custom types directly, they have to + be serialized to `NSData` or some other supported type like an + `NSDictionary`. In Cocoa Bindings, the conversion can be done + using value transformers like this one. + + There’s a built-in transformer (`NSKeyedUnarchiveFromDataTransformerName`) + that converts any `NSCoding` types to `NSData`, but with shortcuts + it makes sense to use a dictionary instead – the defaults look better + when inspected with the `defaults` command-line utility and the + format is compatible with an older sortcut library called Shortcut + Recorder. +*/ +@interface MASDictionaryTransformer : NSValueTransformer +@end diff --git a/V2RayX/MASShortcut.framework/Versions/A/Headers/MASKeyCodes.h b/V2RayX/MASShortcut.framework/Versions/A/Headers/MASKeyCodes.h new file mode 100755 index 0000000..4c000b1 --- /dev/null +++ b/V2RayX/MASShortcut.framework/Versions/A/Headers/MASKeyCodes.h @@ -0,0 +1,44 @@ +#import +#import +#import "MASKeyMasks.h" + +// These glyphs are missed in Carbon.h +typedef NS_ENUM(unsigned short, kMASShortcutGlyph) { + kMASShortcutGlyphEject = 0x23CF, + kMASShortcutGlyphClear = 0x2715, + kMASShortcutGlyphDeleteLeft = 0x232B, + kMASShortcutGlyphDeleteRight = 0x2326, + kMASShortcutGlyphLeftArrow = 0x2190, + kMASShortcutGlyphRightArrow = 0x2192, + kMASShortcutGlyphUpArrow = 0x2191, + kMASShortcutGlyphDownArrow = 0x2193, + kMASShortcutGlyphEscape = 0x238B, + kMASShortcutGlyphHelp = 0x003F, + kMASShortcutGlyphPageDown = 0x21DF, + kMASShortcutGlyphPageUp = 0x21DE, + kMASShortcutGlyphTabRight = 0x21E5, + kMASShortcutGlyphReturn = 0x2305, + kMASShortcutGlyphReturnR2L = 0x21A9, + kMASShortcutGlyphPadClear = 0x2327, + kMASShortcutGlyphNorthwestArrow = 0x2196, + kMASShortcutGlyphSoutheastArrow = 0x2198, +}; + +NS_INLINE NSString* NSStringFromMASKeyCode(unsigned short ch) +{ + return [NSString stringWithFormat:@"%C", ch]; +} + +NS_INLINE NSUInteger MASPickCocoaModifiers(NSUInteger flags) +{ + return (flags & (NSEventModifierFlagControl | NSEventModifierFlagShift | NSEventModifierFlagOption | NSEventModifierFlagCommand)); +} + +NS_INLINE UInt32 MASCarbonModifiersFromCocoaModifiers(NSUInteger cocoaFlags) +{ + return + (cocoaFlags & NSEventModifierFlagCommand ? cmdKey : 0) + | (cocoaFlags & NSEventModifierFlagOption ? optionKey : 0) + | (cocoaFlags & NSEventModifierFlagControl ? controlKey : 0) + | (cocoaFlags & NSEventModifierFlagShift ? shiftKey : 0); +} diff --git a/V2RayX/MASShortcut.framework/Versions/A/Headers/MASKeyMasks.h b/V2RayX/MASShortcut.framework/Versions/A/Headers/MASKeyMasks.h new file mode 100755 index 0000000..5813c1d --- /dev/null +++ b/V2RayX/MASShortcut.framework/Versions/A/Headers/MASKeyMasks.h @@ -0,0 +1,18 @@ +#import + +// https://github.com/shpakovski/MASShortcut/issues/99 +// +// Long story short: NSControlKeyMask and friends were replaced with NSEventModifierFlagControl +// and similar in macOS Sierra. The project builds fine & clean, but including MASShortcut in +// a project with deployment target set to 10.12 results in several deprecation warnings because +// of the control masks. Simply replacing the old symbols with the new ones isn’t an option, +// since it breaks the build on older SDKs – in Travis, for example. +// +// It should be safe to remove this whole thing once the 10.12 SDK is ubiquitous. + +#if __MAC_OS_X_VERSION_MAX_ALLOWED < 101200 +#define NSEventModifierFlagCommand NSCommandKeyMask +#define NSEventModifierFlagControl NSControlKeyMask +#define NSEventModifierFlagOption NSAlternateKeyMask +#define NSEventModifierFlagShift NSShiftKeyMask +#endif diff --git a/V2RayX/MASShortcut.framework/Versions/A/Headers/MASShortcut.h b/V2RayX/MASShortcut.framework/Versions/A/Headers/MASShortcut.h new file mode 100755 index 0000000..8f420e4 --- /dev/null +++ b/V2RayX/MASShortcut.framework/Versions/A/Headers/MASShortcut.h @@ -0,0 +1,81 @@ +#import "MASKeyCodes.h" + +/** + A model class to hold a key combination. + + This class just represents a combination of keys. It does not care if + the combination is valid or can be used as a hotkey, it doesn’t watch + the input system for the shortcut appearance, nor it does access user + defaults. +*/ +@interface MASShortcut : NSObject + +/** + The virtual key code for the keyboard key. + + Hardware independent, same as in `NSEvent`. See `Events.h` in the HIToolbox + framework for a complete list, or Command-click this symbol: `kVK_ANSI_A`. +*/ +@property (nonatomic, readonly) NSUInteger keyCode; + +/** + Cocoa keyboard modifier flags. + + Same as in `NSEvent`: `NSCommandKeyMask`, `NSAlternateKeyMask`, etc. +*/ +@property (nonatomic, readonly) NSUInteger modifierFlags; + +/** + Same as `keyCode`, just a different type. +*/ +@property (nonatomic, readonly) UInt32 carbonKeyCode; + +/** + Carbon modifier flags. + + A bit sum of `cmdKey`, `optionKey`, etc. +*/ +@property (nonatomic, readonly) UInt32 carbonFlags; + +/** + A string representing the “key” part of a shortcut, like the `5` in `⌘5`. + + @warning The value may change depending on the active keyboard layout. + For example for the `^2` keyboard shortcut (`kVK_ANSI_2+NSControlKeyMask` + to be precise) the `keyCodeString` is `2` on the US keyboard, but `ě` when + the Czech keyboard layout is active. See the spec for details. +*/ +@property (nonatomic, readonly) NSString *keyCodeString; + +/** + A key-code string used in key equivalent matching. + + For precise meaning of “key equivalents” see the `keyEquivalent` + property of `NSMenuItem`. Here the string is used to support shortcut + validation (“is the shortcut already taken in this menu?”) and + for display in `NSMenu`. + + The value of this property may differ from `keyCodeString`. For example + the Russian keyboard has a `Г` (Ge) Cyrillic character in place of the + latin `U` key. This means you can create a `^Г` shortcut, but in menus + that’s always displayed as `^U`. So the `keyCodeString` returns `Г` + and `keyCodeStringForKeyEquivalent` returns `U`. +*/ +@property (nonatomic, readonly) NSString *keyCodeStringForKeyEquivalent; + +/** + A string representing the shortcut modifiers, like the `⌘` in `⌘5`. +*/ +@property (nonatomic, readonly) NSString *modifierFlagsString; + +- (instancetype)initWithKeyCode:(NSUInteger)code modifierFlags:(NSUInteger)flags; ++ (instancetype)shortcutWithKeyCode:(NSUInteger)code modifierFlags:(NSUInteger)flags; + +/** + Creates a new shortcut from an `NSEvent` object. + + This is just a convenience initializer that reads the key code and modifiers from an `NSEvent`. +*/ ++ (instancetype)shortcutWithEvent:(NSEvent *)anEvent; + +@end diff --git a/V2RayX/MASShortcut.framework/Versions/A/Headers/MASShortcutBinder.h b/V2RayX/MASShortcut.framework/Versions/A/Headers/MASShortcutBinder.h new file mode 100755 index 0000000..e7406de --- /dev/null +++ b/V2RayX/MASShortcut.framework/Versions/A/Headers/MASShortcutBinder.h @@ -0,0 +1,67 @@ +#import "MASShortcutMonitor.h" + +/** + Binds actions to user defaults keys. + + If you store shortcuts in user defaults (for example by binding + a `MASShortcutView` to user defaults), you can use this class to + connect an action directly to a user defaults key. If the shortcut + stored under the key changes, the action will get automatically + updated to the new one. + + This class is mostly a wrapper around a `MASShortcutMonitor`. It + watches the changes in user defaults and updates the shortcut monitor + accordingly with the new shortcuts. +*/ +@interface MASShortcutBinder : NSObject + +/** + A convenience shared instance. + + You may use it so that you don’t have to manage an instance by hand, + but it’s perfectly fine to allocate and use a separate instance instead. +*/ ++ (instancetype) sharedBinder; + +/** + The underlying shortcut monitor. +*/ +@property(strong) MASShortcutMonitor *shortcutMonitor; + +/** + Binding options customizing the access to user defaults. + + As an example, you can use `NSValueTransformerNameBindingOption` to customize + the storage format used for the shortcuts. By default the shortcuts are converted + from `NSData` (`NSKeyedUnarchiveFromDataTransformerName`). Note that if the + binder is to work with `MASShortcutView`, both object have to use the same storage + format. +*/ +@property(copy) NSDictionary *bindingOptions; + +/** + Binds given action to a shortcut stored under the given defaults key. + + In other words, no matter what shortcut you store under the given key, + pressing it will always trigger the given action. +*/ +- (void) bindShortcutWithDefaultsKey: (NSString*) defaultsKeyName toAction: (dispatch_block_t) action; + +/** + Disconnect the binding between user defaults and action. + + In other words, the shortcut stored under the given key will no longer trigger an action. +*/ +- (void) breakBindingWithDefaultsKey: (NSString*) defaultsKeyName; + +/** + Register default shortcuts in user defaults. + + This is a convenience frontent to `[NSUserDefaults registerDefaults]`. + The dictionary should contain a map of user defaults’ keys to appropriate + keyboard shortcuts. The shortcuts will be transformed according to + `bindingOptions` and registered using `registerDefaults`. +*/ +- (void) registerDefaultShortcuts: (NSDictionary*) defaultShortcuts; + +@end diff --git a/V2RayX/MASShortcut.framework/Versions/A/Headers/MASShortcutMonitor.h b/V2RayX/MASShortcut.framework/Versions/A/Headers/MASShortcutMonitor.h new file mode 100755 index 0000000..dc3d458 --- /dev/null +++ b/V2RayX/MASShortcut.framework/Versions/A/Headers/MASShortcutMonitor.h @@ -0,0 +1,27 @@ +#import "MASShortcut.h" + +/** + Executes action when a shortcut is pressed. + + There can only be one instance of this class, otherwise things + will probably not work. (There’s a Carbon event handler inside + and there can only be one Carbon event handler of a given type.) +*/ +@interface MASShortcutMonitor : NSObject + +- (instancetype) init __unavailable; ++ (instancetype) sharedMonitor; + +/** + Register a shortcut along with an action. + + Attempting to insert an already registered shortcut probably won’t work. + It may burn your house or cut your fingers. You have been warned. +*/ +- (BOOL) registerShortcut: (MASShortcut*) shortcut withAction: (dispatch_block_t) action; +- (BOOL) isShortcutRegistered: (MASShortcut*) shortcut; + +- (void) unregisterShortcut: (MASShortcut*) shortcut; +- (void) unregisterAllShortcuts; + +@end diff --git a/V2RayX/MASShortcut.framework/Versions/A/Headers/MASShortcutValidator.h b/V2RayX/MASShortcut.framework/Versions/A/Headers/MASShortcutValidator.h new file mode 100755 index 0000000..95fe828 --- /dev/null +++ b/V2RayX/MASShortcut.framework/Versions/A/Headers/MASShortcutValidator.h @@ -0,0 +1,29 @@ +#import "MASShortcut.h" + +/** + This class is used by the recording control to tell which shortcuts are acceptable. + + There are two kinds of shortcuts that are not considered acceptable: shortcuts that + are too simple (like single letter keys) and shortcuts that are already used by the + operating system. +*/ +@interface MASShortcutValidator : NSObject + +/** + Set to `YES` if you want to accept Option-something shortcuts. + + `NO` by default, since Option-something shortcuts are often used by system, + for example Option-G will type the © sign. This also applies to Option-Shift + shortcuts – in other words, shortcut recorder will not accept shortcuts like + Option-Shift-K by default. (Again, since Option-Shift-K inserts the Apple + logo sign by default.) +*/ +@property(assign) BOOL allowAnyShortcutWithOptionModifier; + ++ (instancetype) sharedValidator; + +- (BOOL) isShortcutValid: (MASShortcut*) shortcut; +- (BOOL) isShortcut: (MASShortcut*) shortcut alreadyTakenInMenu: (NSMenu*) menu explanation: (NSString**) explanation; +- (BOOL) isShortcutAlreadyTakenBySystem: (MASShortcut*) shortcut explanation: (NSString**) explanation; + +@end diff --git a/V2RayX/MASShortcut.framework/Versions/A/Headers/MASShortcutView+Bindings.h b/V2RayX/MASShortcut.framework/Versions/A/Headers/MASShortcutView+Bindings.h new file mode 100755 index 0000000..01b2246 --- /dev/null +++ b/V2RayX/MASShortcut.framework/Versions/A/Headers/MASShortcutView+Bindings.h @@ -0,0 +1,25 @@ +#import "MASShortcutView.h" + +/** + A simplified interface to bind the recorder value to user defaults. + + You can bind the `shortcutValue` to user defaults using the standard + `bind:toObject:withKeyPath:options:` call, but since that’s a lot to type + and read, here’s a simpler option. + + Setting the `associatedUserDefaultsKey` binds the view’s shortcut value + to the given user defaults key. You can supply a value transformer to convert + values between user defaults and `MASShortcut`. If you don’t supply + a transformer, the `NSUnarchiveFromDataTransformerName` will be used + automatically. + + Set `associatedUserDefaultsKey` to `nil` to disconnect the binding. +*/ +@interface MASShortcutView (Bindings) + +@property(copy) NSString *associatedUserDefaultsKey; + +- (void) setAssociatedUserDefaultsKey: (NSString*) newKey withTransformer: (NSValueTransformer*) transformer; +- (void) setAssociatedUserDefaultsKey: (NSString*) newKey withTransformerName: (NSString*) transformerName; + +@end diff --git a/V2RayX/MASShortcut.framework/Versions/A/Headers/MASShortcutView.h b/V2RayX/MASShortcut.framework/Versions/A/Headers/MASShortcutView.h new file mode 100755 index 0000000..75d4eb3 --- /dev/null +++ b/V2RayX/MASShortcut.framework/Versions/A/Headers/MASShortcutView.h @@ -0,0 +1,26 @@ +@class MASShortcut, MASShortcutValidator; + +extern NSString *const MASShortcutBinding; + +typedef NS_ENUM(NSInteger, MASShortcutViewStyle) { + MASShortcutViewStyleDefault = 0, // Height = 19 px + MASShortcutViewStyleTexturedRect, // Height = 25 px + MASShortcutViewStyleRounded, // Height = 43 px + MASShortcutViewStyleFlat +}; + +@interface MASShortcutView : NSView + +@property (nonatomic, strong) MASShortcut *shortcutValue; +@property (nonatomic, strong) MASShortcutValidator *shortcutValidator; +@property (nonatomic, getter = isRecording) BOOL recording; +@property (nonatomic, getter = isEnabled) BOOL enabled; +@property (nonatomic, copy) void (^shortcutValueChange)(MASShortcutView *sender); +@property (nonatomic, assign) MASShortcutViewStyle style; + +/// Returns custom class for drawing control. ++ (Class)shortcutCellClass; + +- (void)setAcceptsFirstResponder:(BOOL)value; + +@end diff --git a/V2RayX/MASShortcut.framework/Versions/A/Headers/Shortcut.h b/V2RayX/MASShortcut.framework/Versions/A/Headers/Shortcut.h new file mode 100755 index 0000000..5a8de8e --- /dev/null +++ b/V2RayX/MASShortcut.framework/Versions/A/Headers/Shortcut.h @@ -0,0 +1,8 @@ +#import "MASKeyMasks.h" +#import "MASShortcut.h" +#import "MASShortcutValidator.h" +#import "MASShortcutMonitor.h" +#import "MASShortcutBinder.h" +#import "MASDictionaryTransformer.h" +#import "MASShortcutView.h" +#import "MASShortcutView+Bindings.h" diff --git a/V2RayX/MASShortcut.framework/Versions/A/MASShortcut b/V2RayX/MASShortcut.framework/Versions/A/MASShortcut new file mode 100755 index 0000000..9d28d85 Binary files /dev/null and b/V2RayX/MASShortcut.framework/Versions/A/MASShortcut differ diff --git a/V2RayX/MASShortcut.framework/Versions/A/Modules/module.modulemap b/V2RayX/MASShortcut.framework/Versions/A/Modules/module.modulemap new file mode 100644 index 0000000..e6307a1 --- /dev/null +++ b/V2RayX/MASShortcut.framework/Versions/A/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module MASShortcut { + umbrella header "Shortcut.h" + + export * + module * { export * } +} diff --git a/V2RayX/MASShortcut.framework/Versions/A/Resources/Info.plist b/V2RayX/MASShortcut.framework/Versions/A/Resources/Info.plist new file mode 100644 index 0000000..a816cfa --- /dev/null +++ b/V2RayX/MASShortcut.framework/Versions/A/Resources/Info.plist @@ -0,0 +1,44 @@ + + + + + BuildMachineOSBuild + 17G65 + CFBundleDevelopmentRegion + English + CFBundleExecutable + MASShortcut + CFBundleIdentifier + com.github.shpakovski.MASShortcut + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + MASShortcut + CFBundlePackageType + FMWK + CFBundleShortVersionString + 2.3.6 + CFBundleSupportedPlatforms + + MacOSX + + CFBundleVersion + 2.3.6 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 9F2000 + DTPlatformVersion + GM + DTSDKBuild + 17E189 + DTSDKName + macosx10.13 + DTXcode + 0941 + DTXcodeBuild + 9F2000 + NSHumanReadableCopyright + Copyright © Vadim Shpakovski. All rights reserved. + + diff --git a/V2RayX/MASShortcut.framework/Versions/A/Resources/cs.lproj/Localizable.strings b/V2RayX/MASShortcut.framework/Versions/A/Resources/cs.lproj/Localizable.strings new file mode 100644 index 0000000..bd25e51 Binary files /dev/null and b/V2RayX/MASShortcut.framework/Versions/A/Resources/cs.lproj/Localizable.strings differ diff --git a/V2RayX/MASShortcut.framework/Versions/A/Resources/de.lproj/Localizable.strings b/V2RayX/MASShortcut.framework/Versions/A/Resources/de.lproj/Localizable.strings new file mode 100644 index 0000000..ed230e0 Binary files /dev/null and b/V2RayX/MASShortcut.framework/Versions/A/Resources/de.lproj/Localizable.strings differ diff --git a/V2RayX/MASShortcut.framework/Versions/A/Resources/en.lproj/Localizable.strings b/V2RayX/MASShortcut.framework/Versions/A/Resources/en.lproj/Localizable.strings new file mode 100644 index 0000000..84341f0 Binary files /dev/null and b/V2RayX/MASShortcut.framework/Versions/A/Resources/en.lproj/Localizable.strings differ diff --git a/V2RayX/MASShortcut.framework/Versions/A/Resources/es.lproj/Localizable.strings b/V2RayX/MASShortcut.framework/Versions/A/Resources/es.lproj/Localizable.strings new file mode 100644 index 0000000..5b604e1 Binary files /dev/null and b/V2RayX/MASShortcut.framework/Versions/A/Resources/es.lproj/Localizable.strings differ diff --git a/V2RayX/MASShortcut.framework/Versions/A/Resources/fr.lproj/Localizable.strings b/V2RayX/MASShortcut.framework/Versions/A/Resources/fr.lproj/Localizable.strings new file mode 100644 index 0000000..cab002e Binary files /dev/null and b/V2RayX/MASShortcut.framework/Versions/A/Resources/fr.lproj/Localizable.strings differ diff --git a/V2RayX/MASShortcut.framework/Versions/A/Resources/it.lproj/Localizable.strings b/V2RayX/MASShortcut.framework/Versions/A/Resources/it.lproj/Localizable.strings new file mode 100644 index 0000000..74b6528 Binary files /dev/null and b/V2RayX/MASShortcut.framework/Versions/A/Resources/it.lproj/Localizable.strings differ diff --git a/V2RayX/MASShortcut.framework/Versions/A/Resources/ja.lproj/Localizable.strings b/V2RayX/MASShortcut.framework/Versions/A/Resources/ja.lproj/Localizable.strings new file mode 100644 index 0000000..eaef0b7 Binary files /dev/null and b/V2RayX/MASShortcut.framework/Versions/A/Resources/ja.lproj/Localizable.strings differ diff --git a/V2RayX/MASShortcut.framework/Versions/A/Resources/ko.lproj/Localizable.strings b/V2RayX/MASShortcut.framework/Versions/A/Resources/ko.lproj/Localizable.strings new file mode 100644 index 0000000..897047d Binary files /dev/null and b/V2RayX/MASShortcut.framework/Versions/A/Resources/ko.lproj/Localizable.strings differ diff --git a/V2RayX/MASShortcut.framework/Versions/A/Resources/nl.lproj/Localizable.strings b/V2RayX/MASShortcut.framework/Versions/A/Resources/nl.lproj/Localizable.strings new file mode 100644 index 0000000..183efeb Binary files /dev/null and b/V2RayX/MASShortcut.framework/Versions/A/Resources/nl.lproj/Localizable.strings differ diff --git a/V2RayX/MASShortcut.framework/Versions/A/Resources/pl.lproj/Localizable.strings b/V2RayX/MASShortcut.framework/Versions/A/Resources/pl.lproj/Localizable.strings new file mode 100644 index 0000000..f4226e1 Binary files /dev/null and b/V2RayX/MASShortcut.framework/Versions/A/Resources/pl.lproj/Localizable.strings differ diff --git a/V2RayX/MASShortcut.framework/Versions/A/Resources/pt.lproj/Localizable.strings b/V2RayX/MASShortcut.framework/Versions/A/Resources/pt.lproj/Localizable.strings new file mode 100644 index 0000000..dfa1bcc Binary files /dev/null and b/V2RayX/MASShortcut.framework/Versions/A/Resources/pt.lproj/Localizable.strings differ diff --git a/V2RayX/MASShortcut.framework/Versions/A/Resources/ru.lproj/Localizable.strings b/V2RayX/MASShortcut.framework/Versions/A/Resources/ru.lproj/Localizable.strings new file mode 100644 index 0000000..8869dd3 Binary files /dev/null and b/V2RayX/MASShortcut.framework/Versions/A/Resources/ru.lproj/Localizable.strings differ diff --git a/V2RayX/MASShortcut.framework/Versions/A/Resources/sv.lproj/Localizable.strings b/V2RayX/MASShortcut.framework/Versions/A/Resources/sv.lproj/Localizable.strings new file mode 100644 index 0000000..7f23a55 Binary files /dev/null and b/V2RayX/MASShortcut.framework/Versions/A/Resources/sv.lproj/Localizable.strings differ diff --git a/V2RayX/MASShortcut.framework/Versions/A/Resources/zh-Hans.lproj/Localizable.strings b/V2RayX/MASShortcut.framework/Versions/A/Resources/zh-Hans.lproj/Localizable.strings new file mode 100644 index 0000000..bf1c66b Binary files /dev/null and b/V2RayX/MASShortcut.framework/Versions/A/Resources/zh-Hans.lproj/Localizable.strings differ diff --git a/V2RayX/MASShortcut.framework/Versions/A/Resources/zh-Hant.lproj/Localizable.strings b/V2RayX/MASShortcut.framework/Versions/A/Resources/zh-Hant.lproj/Localizable.strings new file mode 100644 index 0000000..2bdb714 Binary files /dev/null and b/V2RayX/MASShortcut.framework/Versions/A/Resources/zh-Hant.lproj/Localizable.strings differ diff --git a/V2RayX/MASShortcut.framework/Versions/Current b/V2RayX/MASShortcut.framework/Versions/Current new file mode 120000 index 0000000..8c7e5a6 --- /dev/null +++ b/V2RayX/MASShortcut.framework/Versions/Current @@ -0,0 +1 @@ +A \ No newline at end of file diff --git a/V2RayX/ShortcutsController.h b/V2RayX/ShortcutsController.h new file mode 100644 index 0000000..4c024d7 --- /dev/null +++ b/V2RayX/ShortcutsController.h @@ -0,0 +1,15 @@ +// +// ShortcutsController.h +// V2RayX +// +// Created by Jarvis on 2018/8/28. +// Copyright © 2018年 Project V2Ray. All rights reserved. +// + +#import + +@interface ShortcutsController : NSObject + ++ (void)bindShortcuts; + +@end diff --git a/V2RayX/ShortcutsController.m b/V2RayX/ShortcutsController.m new file mode 100644 index 0000000..e4f4974 --- /dev/null +++ b/V2RayX/ShortcutsController.m @@ -0,0 +1,23 @@ +// +// ShortcutsController.m +// V2RayX +// +// Created by Jarvis on 2018/8/28. +// Copyright © 2018年 Project V2Ray. All rights reserved. +// + +#import "ShortcutsController.h" +#import + +@implementation ShortcutsController + ++ (void)bindShortcuts { + MASShortcutBinder* binder = [MASShortcutBinder sharedBinder]; + [binder + bindShortcutWithDefaultsKey: @"SwitchProxyMode" + toAction:^{ + [[NSNotificationCenter defaultCenter] postNotificationName: @"NOTIFY_SWITCH_PROXY_MODE_SHORTCUT" object: nil]; + }]; +} + +@end diff --git a/V2RayX/ToastWindowController.h b/V2RayX/ToastWindowController.h new file mode 100644 index 0000000..945d754 --- /dev/null +++ b/V2RayX/ToastWindowController.h @@ -0,0 +1,16 @@ +// +// ToastWindowController.h +// V2RayX +// +// Created by Jarvis on 2018/8/28. +// Copyright © 2018年 Project V2Ray. All rights reserved. +// + +#import + +@interface ToastWindowController : NSWindowController + +@property (strong) NSString *message; + +- (void)fadeInHud; +@end diff --git a/V2RayX/ToastWindowController.m b/V2RayX/ToastWindowController.m new file mode 100644 index 0000000..8d955e7 --- /dev/null +++ b/V2RayX/ToastWindowController.m @@ -0,0 +1,112 @@ +// +// ToastWindowController.m +// V2RayX +// +// Created by Jarvis on 2018/8/28. +// Copyright © 2018年 Project V2Ray. All rights reserved. +// + +#import "ToastWindowController.h" +#import + +@interface ToastWindowController () +@property (weak) IBOutlet NSView *panelView; +@property (weak) IBOutlet NSTextField *titleTextField; +@property (strong) NSTimer *timerToFadeOut; +@property (assign) BOOL fadingOut; +@end + +@implementation ToastWindowController + +- (void)windowDidLoad { + [super windowDidLoad]; + + self.shouldCascadeWindows = NO; + + if (self.window) { + NSWindow *win = self.window; + [win setOpaque:NO]; + [win setBackgroundColor:[NSColor clearColor]]; + [win setStyleMask:NSWindowStyleMaskBorderless]; + [win setHidesOnDeactivate:NO]; + [win setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces]; + [win setLevel:NSFloatingWindowLevel]; + [win orderFrontRegardless]; + } + CALayer *viewLayer = [[CALayer alloc] init]; + [viewLayer setBackgroundColor: CGColorCreateGenericRGB(0.05, 0.05, 0.05, 0.75)]; + [viewLayer setCornerRadius:18.0]; + self.panelView.wantsLayer = YES; + self.panelView.layer = viewLayer; + self.panelView.layer.opaque = 0.0; + self.titleTextField.stringValue = self.message; + + [self setupHud]; +} + +- (void)setupHud { + [self.titleTextField sizeToFit]; + CGFloat kHudHorizontalMargin = 30; + CGFloat kHudHeight = 90.0; + + CGRect labelFrame = self.titleTextField.frame; + CGRect hubWindowFrame = self.window.frame; + hubWindowFrame.size.width = labelFrame.size.width + kHudHorizontalMargin * 2; + hubWindowFrame.size.height = kHudHeight; + + NSRect screenRect = NSScreen.screens[0].visibleFrame; + hubWindowFrame.origin.x = (screenRect.size.width - hubWindowFrame.size.width) * 0.5; + hubWindowFrame.origin.y = (screenRect.size.height - hubWindowFrame.size.height) * 0.5; + [self.window setFrame:hubWindowFrame display:YES]; + + NSRect viewFrame = hubWindowFrame; + viewFrame.origin.x = 0; + viewFrame.origin.y = 0; + self.panelView.frame = viewFrame; + + labelFrame.origin.x = kHudHorizontalMargin; + labelFrame.origin.y = (hubWindowFrame.size.height - labelFrame.size.height) * 0.5; + self.titleTextField.frame = labelFrame; +} + +- (void)fadeInHud { + if (_timerToFadeOut) { + [_timerToFadeOut invalidate]; + _timerToFadeOut = nil; + } + + _fadingOut = NO; + + [CATransaction begin]; + [CATransaction setAnimationDuration:0.35]; + [CATransaction setCompletionBlock:^{ + [self didFadeIn]; + }]; + self.panelView.layer.opacity = 1.0; + [CATransaction commit]; +} + +- (void)didFadeIn { + _timerToFadeOut = [NSTimer scheduledTimerWithTimeInterval:0.35 target:self selector:@selector(fadeOutHud) userInfo:nil repeats:NO]; +} + +- (void)fadeOutHud { + _fadingOut = YES; + + [CATransaction begin]; + [CATransaction setAnimationDuration:0.35]; + [CATransaction setCompletionBlock:^{ + [self didFadeOut]; + }]; + self.panelView.layer.opacity = 0.0; + [CATransaction commit]; +} + +- (void)didFadeOut { + if (_fadingOut) { + [self.window orderOut:self]; + } + _fadingOut = NO; +} + +@end diff --git a/V2RayX/ToastWindowController.xib b/V2RayX/ToastWindowController.xib new file mode 100644 index 0000000..02a2032 --- /dev/null +++ b/V2RayX/ToastWindowController.xib @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +