From 003727a0fe03304af0ac0f315cfb38250994d02b Mon Sep 17 00:00:00 2001 From: Charlie Qiu Date: Sat, 11 Mar 2017 00:14:56 +0800 Subject: [PATCH] Implement configurable shortcuts for toggle running and switch proxy mode. - By MASShortcut. - A new shortcuts preferences window. --- ShadowsocksX-NG.xcodeproj/project.pbxproj | 16 ++ ShadowsocksX-NG/AppDelegate.swift | 141 +++++++----------- ShadowsocksX-NG/Base.lproj/MainMenu.xib | 10 +- .../ShadowsocksX-NG-Bridging-Header.h | 4 +- ShadowsocksX-NG/ShortcutsController.h | 15 ++ ShadowsocksX-NG/ShortcutsController.m | 32 ++++ .../ShortcutsPreferencesWindowController.h | 22 +++ .../ShortcutsPreferencesWindowController.m | 26 ++++ .../ShortcutsPreferencesWindowController.xib | 75 ++++++++++ 9 files changed, 251 insertions(+), 90 deletions(-) create mode 100644 ShadowsocksX-NG/ShortcutsController.h create mode 100644 ShadowsocksX-NG/ShortcutsController.m create mode 100644 ShadowsocksX-NG/ShortcutsPreferencesWindowController.h create mode 100644 ShadowsocksX-NG/ShortcutsPreferencesWindowController.m create mode 100644 ShadowsocksX-NG/ShortcutsPreferencesWindowController.xib diff --git a/ShadowsocksX-NG.xcodeproj/project.pbxproj b/ShadowsocksX-NG.xcodeproj/project.pbxproj index dd00469..7c71c31 100755 --- a/ShadowsocksX-NG.xcodeproj/project.pbxproj +++ b/ShadowsocksX-NG.xcodeproj/project.pbxproj @@ -36,6 +36,9 @@ 9B3FFF4C1D09D8F70019A709 /* install_helper.sh in Resources */ = {isa = PBXBuildFile; fileRef = 9B3FFF4B1D09D8F70019A709 /* install_helper.sh */; }; 9B3FFF4F1D09D9D50019A709 /* ProxyConfHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B3FFF4E1D09D9D50019A709 /* ProxyConfHelper.m */; }; 9B3FFF541D09E2D10019A709 /* proxy_conf_helper in Resources */ = {isa = PBXBuildFile; fileRef = 9B3FFF441D09CD3B0019A709 /* proxy_conf_helper */; }; + 9B5831EE1E72EBF9009D5B7D /* ShortcutsPreferencesWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9B5831EC1E72EBF9009D5B7D /* ShortcutsPreferencesWindowController.xib */; }; + 9B5831F31E72FA63009D5B7D /* ShortcutsPreferencesWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B5831F21E72FA63009D5B7D /* ShortcutsPreferencesWindowController.m */; }; + 9B5831F61E7302F8009D5B7D /* ShortcutsController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B5831F51E7302F8009D5B7D /* ShortcutsController.m */; }; 9B6BF9521E27B23F0061B9A7 /* LaunchHelper.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9B6BF9501E27B1F20061B9A7 /* LaunchHelper.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 9B6BF9541E27B2570061B9A7 /* ServiceManagement.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B6BF9531E27B2570061B9A7 /* ServiceManagement.framework */; }; 9B9CBCA61E25E1DB00FC61AA /* KcptunProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B9CBCA51E25E1DB00FC61AA /* KcptunProfile.swift */; }; @@ -171,6 +174,11 @@ 9B3FFF4E1D09D9D50019A709 /* ProxyConfHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ProxyConfHelper.m; sourceTree = ""; }; 9B3FFF501D09DAEA0019A709 /* proxy_conf_helper_version.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = proxy_conf_helper_version.h; sourceTree = ""; }; 9B3FFF511D09DBA20019A709 /* ShadowsocksX-NG-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ShadowsocksX-NG-Bridging-Header.h"; sourceTree = ""; }; + 9B5831EC1E72EBF9009D5B7D /* ShortcutsPreferencesWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ShortcutsPreferencesWindowController.xib; sourceTree = ""; }; + 9B5831F11E72FA63009D5B7D /* ShortcutsPreferencesWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShortcutsPreferencesWindowController.h; sourceTree = ""; }; + 9B5831F21E72FA63009D5B7D /* ShortcutsPreferencesWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ShortcutsPreferencesWindowController.m; sourceTree = ""; }; + 9B5831F41E7302F8009D5B7D /* ShortcutsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShortcutsController.h; sourceTree = ""; }; + 9B5831F51E7302F8009D5B7D /* ShortcutsController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ShortcutsController.m; sourceTree = ""; }; 9B6BF94B1E27B1F10061B9A7 /* LaunchHelper.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = LaunchHelper.xcodeproj; path = LaunchHelper/LaunchHelper.xcodeproj; sourceTree = ""; }; 9B6BF9531E27B2570061B9A7 /* ServiceManagement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ServiceManagement.framework; path = System/Library/Frameworks/ServiceManagement.framework; sourceTree = SDKROOT; }; 9B9CBCA51E25E1DB00FC61AA /* KcptunProfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KcptunProfile.swift; sourceTree = ""; }; @@ -341,6 +349,8 @@ 9B3FFF501D09DAEA0019A709 /* proxy_conf_helper_version.h */, 9BA04B211D23D5A5005AAD7F /* ProxyConfTool.h */, 9BA04B221D23D5A5005AAD7F /* ProxyConfTool.m */, + 9B5831F41E7302F8009D5B7D /* ShortcutsController.h */, + 9B5831F51E7302F8009D5B7D /* ShortcutsController.m */, ); path = "ShadowsocksX-NG"; sourceTree = ""; @@ -371,6 +381,9 @@ C8E42A701D4F2CAF0074C7EA /* UserRulesController.xib */, C6E28E911DA79380004F8330 /* HTTPPreferencesWindowController.swift */, C6E28E971DA79705004F8330 /* HTTPPreferencesWindowController.xib */, + 9B5831EC1E72EBF9009D5B7D /* ShortcutsPreferencesWindowController.xib */, + 9B5831F11E72FA63009D5B7D /* ShortcutsPreferencesWindowController.h */, + 9B5831F21E72FA63009D5B7D /* ShortcutsPreferencesWindowController.m */, ); name = UI; sourceTree = ""; @@ -569,6 +582,7 @@ 9B07EFAC1D048E880052D9DF /* menu_icon@2x.png in Resources */, 9B07EFA71D048BBB0052D9DF /* ss-local in Resources */, 9B07EFAF1D048E880052D9DF /* menu_icon_disabled@2x.png in Resources */, + 9B5831EE1E72EBF9009D5B7D /* ShortcutsPreferencesWindowController.xib in Resources */, 9B07EFAE1D048E880052D9DF /* menu_icon_disabled.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -712,6 +726,8 @@ files = ( 9B3FFF171D072FDE0019A709 /* LaunchAtLoginController.m in Sources */, 9B3FFF4F1D09D9D50019A709 /* ProxyConfHelper.m in Sources */, + 9B5831F61E7302F8009D5B7D /* ShortcutsController.m in Sources */, + 9B5831F31E72FA63009D5B7D /* ShortcutsPreferencesWindowController.m in Sources */, 9BB706A71D1B982300551F0E /* SWBApplication.m in Sources */, 9B3FFF1E1D0732660019A709 /* Utils.m in Sources */, 9B3FFF321D08CEE40019A709 /* SWBQRCodeWindowController.m in Sources */, diff --git a/ShadowsocksX-NG/AppDelegate.swift b/ShadowsocksX-NG/AppDelegate.swift index 3cb987d..35bcd59 100755 --- a/ShadowsocksX-NG/AppDelegate.swift +++ b/ShadowsocksX-NG/AppDelegate.swift @@ -18,6 +18,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele var proxyPreferencesWinCtrl: ProxyPreferencesController! var editUserRulesWinCtrl: UserRulesController! var httpPreferencesWinCtrl : HTTPPreferencesWindowController! + var shortcutsPreferencesWinCtrl: ShortcutsPreferencesWindowController! let keyCodeP = kVK_ANSI_P let keyCodeS = kVK_ANSI_S @@ -145,6 +146,45 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele self.updateCopyHttpProxyExportMenu() } ) + notifyCenter.addObserver(forName: NSNotification.Name(rawValue: "NOTIFY_TOGGLE_RUNNING"), object: nil, queue: nil + , using: { + (note) in + var isOn = UserDefaults.standard.bool(forKey: "ShadowsocksOn") + isOn = !isOn + if isOn { + self.isNameTextField.stringValue = "Shadowsocks: On".localized + } + else { + self.isNameTextField.stringValue = "Shadowsocks: Off".localized + } + + UserDefaults.standard.set(isOn, forKey: "ShadowsocksOn") + + self.updateMainMenu() + self.applyConfig() + self.fadeInHud() + } + ) + notifyCenter.addObserver(forName: NSNotification.Name(rawValue: "NOTIFY_SWITCH_PROXY_MODE"), object: nil, queue: nil + , using: { + (note) in + + switch Globals.proxyType { + case .pac: + Globals.proxyType = .global + UserDefaults.standard.setValue("global", forKey: "ShadowsocksRunningMode") + self.isNameTextField.stringValue = "Global Mode".localized + case .global: + Globals.proxyType = .pac + UserDefaults.standard.setValue("auto", forKey: "ShadowsocksRunningMode") + self.isNameTextField.stringValue = "Auto Mode By PAC".localized + } + + self.updateRunningModeMenu() + self.applyConfig() + self.fadeInHud() + } + ) notifyCenter.addObserver(forName: NSNotification.Name(rawValue: "NOTIFY_FOUND_SS_URL"), object: nil, queue: nil) { (note: Notification) in @@ -209,7 +249,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele applyConfig() // Register global hotkey - registerHotkey() + ShortcutsController.bindShortcuts() } func applicationWillTerminate(_ aNotification: Notification) { @@ -245,92 +285,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele } } - // MARK: - Hotkey Methods - func registerHotkey() -> Void { - registerEventHotKey(keyCode: UInt32(keyCodeP)) // to toggle PAC and Global Mode -// registerEventHotKey(keyCode: UInt32(keyCodeS)) // to toggle SS on or off - registerEventHandler() - } - - func registerEventHotKey(keyCode: UInt32) { - var gMyHotKeyID = EventHotKeyID() - gMyHotKeyID.signature = OSType(fourCharCodeFrom(string: "sxng")) - gMyHotKeyID.id = keyCode - - // Register hotkey. - RegisterEventHotKey(UInt32(keyCode), - UInt32(modifierKeys), - gMyHotKeyID, - GetApplicationEventTarget(), - 0, - &hotKeyRef) - } - - func registerEventHandler() { - var eventType = EventTypeSpec() - eventType.eventClass = OSType(kEventClassKeyboard) - eventType.eventKind = OSType(kEventHotKeyPressed) - - // Void pointer to `self`: - let context = Unmanaged.passUnretained(self).toOpaque() - - // Install handler. - InstallEventHandler(GetApplicationEventTarget(), {(nextHanlder, theEvent, userContext) -> OSStatus in - // Extract pointer to `self` from void pointer: - let mySelf = Unmanaged.fromOpaque(userContext!).takeUnretainedValue() - - var hotKeyId = EventHotKeyID() - GetEventParameter(theEvent, EventParamName(kEventParamDirectObject), EventParamType(typeEventHotKeyID), nil, MemoryLayout.size, nil, &hotKeyId) - - if hotKeyId.id == UInt32(mySelf.keyCodeP) { - // P key pressed - switch Globals.proxyType { - case .pac: - Globals.proxyType = .global - UserDefaults.standard.setValue("global", forKey: "ShadowsocksRunningMode") - mySelf.isNameTextField.stringValue = "Global Mode" - mySelf.updateRunningModeMenu() - mySelf.applyConfig() - case .global: - Globals.proxyType = .pac - UserDefaults.standard.setValue("auto", forKey: "ShadowsocksRunningMode") - mySelf.isNameTextField.stringValue = "Auto Mode" - mySelf.updateRunningModeMenu() - mySelf.applyConfig() - } - } - else if hotKeyId.id == UInt32(mySelf.keyCodeS) { - // S key pressed - var isOn = UserDefaults.standard.bool(forKey: "ShadowsocksOn") - isOn = !isOn - if isOn { - mySelf.isNameTextField.stringValue = "Shadowsocks: On".localized - } - else { - mySelf.isNameTextField.stringValue = "Shadowsocks: Off".localized - } - - UserDefaults.standard.set(isOn, forKey: "ShadowsocksOn") - mySelf.updateMainMenu() - mySelf.applyConfig() - } - - mySelf.fadeInHud() - - return noErr - }, 1, &eventType, context, nil) - } - - - func fourCharCodeFrom(string: String) -> FourCharCode { - assert(string.characters.count == 4, "String length must be 4") - var result: FourCharCode = 0 - for char in string.utf16 { - result = (result << 8) + FourCharCode(char) - } - return result - } - // MARK: - UI Methods @IBAction func toggleRunning(_ sender: NSMenuItem) { let defaults = UserDefaults.standard @@ -477,6 +431,19 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele proxyPreferencesWinCtrl.window?.makeKeyAndOrderFront(self) } + @IBAction func editShortcutsPreferences(_ sender: NSMenuItem) { + if shortcutsPreferencesWinCtrl != nil { + shortcutsPreferencesWinCtrl.close() + } + + shortcutsPreferencesWinCtrl = ShortcutsPreferencesWindowController( + windowNibName: "ShortcutsPreferencesWindowController") + + shortcutsPreferencesWinCtrl.showWindow(self) + NSApp.activate(ignoringOtherApps: true) + shortcutsPreferencesWinCtrl.window?.makeKeyAndOrderFront(self) + } + @IBAction func selectServer(_ sender: NSMenuItem) { let index = sender.tag - kProfileMenuItemIndexBase let spMgr = ServerProfileManager.instance diff --git a/ShadowsocksX-NG/Base.lproj/MainMenu.xib b/ShadowsocksX-NG/Base.lproj/MainMenu.xib index 0223bdc..12ab899 100755 --- a/ShadowsocksX-NG/Base.lproj/MainMenu.xib +++ b/ShadowsocksX-NG/Base.lproj/MainMenu.xib @@ -152,12 +152,18 @@ - + + + + + + + @@ -219,7 +225,7 @@ - + diff --git a/ShadowsocksX-NG/ShadowsocksX-NG-Bridging-Header.h b/ShadowsocksX-NG/ShadowsocksX-NG-Bridging-Header.h index c4ff6cc..222e9be 100644 --- a/ShadowsocksX-NG/ShadowsocksX-NG-Bridging-Header.h +++ b/ShadowsocksX-NG/ShadowsocksX-NG-Bridging-Header.h @@ -6,6 +6,8 @@ #import "LaunchAtLoginController.h" #import "SWBQRCodeWindowController.h" +#import "ShortcutsPreferencesWindowController.h" +#import "ShortcutsController.h" #import "Utils.h" #import "ProxyConfHelper.h" -#import "ProxyConfTool.h" \ No newline at end of file +#import "ProxyConfTool.h" diff --git a/ShadowsocksX-NG/ShortcutsController.h b/ShadowsocksX-NG/ShortcutsController.h new file mode 100644 index 0000000..e14bfde --- /dev/null +++ b/ShadowsocksX-NG/ShortcutsController.h @@ -0,0 +1,15 @@ +// +// ShortcutsController.h +// ShadowsocksX-NG +// +// Created by 邱宇舟 on 2017/3/10. +// Copyright © 2017年 qiuyuzhou. All rights reserved. +// + +#import + +@interface ShortcutsController : NSObject + ++ (void)bindShortcuts; + +@end diff --git a/ShadowsocksX-NG/ShortcutsController.m b/ShadowsocksX-NG/ShortcutsController.m new file mode 100644 index 0000000..a8dd302 --- /dev/null +++ b/ShadowsocksX-NG/ShortcutsController.m @@ -0,0 +1,32 @@ +// +// ShortcutsController.m +// ShadowsocksX-NG +// +// Created by 邱宇舟 on 2017/3/10. +// Copyright © 2017年 qiuyuzhou. All rights reserved. +// + +#import "ShortcutsController.h" + +#import + +#import "ShortcutsPreferencesWindowController.h" + + +@implementation ShortcutsController + ++ (void)bindShortcuts { + MASShortcutBinder* binder = [MASShortcutBinder sharedBinder]; + [binder + bindShortcutWithDefaultsKey:kGlobalShortcutToggleRunning + toAction:^{ + [[NSNotificationCenter defaultCenter] postNotificationName: @"NOTIFY_TOGGLE_RUNNING" object: nil]; + }]; + [binder + bindShortcutWithDefaultsKey:kGlobalShortcutSwitchProxyMode + toAction:^{ + [[NSNotificationCenter defaultCenter] postNotificationName: @"NOTIFY_SWITCH_PROXY_MODE" object: nil]; + }]; +} + +@end diff --git a/ShadowsocksX-NG/ShortcutsPreferencesWindowController.h b/ShadowsocksX-NG/ShortcutsPreferencesWindowController.h new file mode 100644 index 0000000..f29a38b --- /dev/null +++ b/ShadowsocksX-NG/ShortcutsPreferencesWindowController.h @@ -0,0 +1,22 @@ +// +// ShortcutsPreferencesWindowController.h +// ShadowsocksX-NG +// +// Created by 邱宇舟 on 2017/3/10. +// Copyright © 2017年 qiuyuzhou. All rights reserved. +// + +#import +#import + + +static NSString *const kGlobalShortcutToggleRunning = @"ToggleRunning"; +static NSString *const kGlobalShortcutSwitchProxyMode= @"SwitchProxyMode"; + + +@interface ShortcutsPreferencesWindowController : NSWindowController + +@property(nonatomic, weak) IBOutlet MASShortcutView* toggleRunningShortcutCtrl; +@property(nonatomic, weak) IBOutlet MASShortcutView* switchModeShortcutCtrl; + +@end diff --git a/ShadowsocksX-NG/ShortcutsPreferencesWindowController.m b/ShadowsocksX-NG/ShortcutsPreferencesWindowController.m new file mode 100644 index 0000000..092c3e8 --- /dev/null +++ b/ShadowsocksX-NG/ShortcutsPreferencesWindowController.m @@ -0,0 +1,26 @@ +// +// ShortcutsPreferencesWindowController.m +// ShadowsocksX-NG +// +// Created by 邱宇舟 on 2017/3/10. +// Copyright © 2017年 qiuyuzhou. All rights reserved. +// + +#import "ShortcutsPreferencesWindowController.h" + + +@interface ShortcutsPreferencesWindowController () + +@end + +@implementation ShortcutsPreferencesWindowController + +- (void)windowDidLoad { + [super windowDidLoad]; + + // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. + self.toggleRunningShortcutCtrl.associatedUserDefaultsKey = kGlobalShortcutToggleRunning; + self.switchModeShortcutCtrl.associatedUserDefaultsKey = kGlobalShortcutSwitchProxyMode; +} + +@end diff --git a/ShadowsocksX-NG/ShortcutsPreferencesWindowController.xib b/ShadowsocksX-NG/ShortcutsPreferencesWindowController.xib new file mode 100644 index 0000000..5d265a6 --- /dev/null +++ b/ShadowsocksX-NG/ShortcutsPreferencesWindowController.xib @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +