├── MenuLock-v2.0.png ├── MenuLock ├── Assets.xcassets │ ├── Contents.json │ └── AppIcon.appiconset │ │ ├── icon16x16.png │ │ ├── icon32x32.png │ │ ├── icon64x64.png │ │ ├── icon1024x1024.png │ │ ├── icon128x128.png │ │ ├── icon256x256.png │ │ ├── icon512x512.png │ │ └── Contents.json ├── Podfile ├── io.fti.MenuLock.plist ├── Info.plist ├── KeyMap.swift ├── Base.lproj │ └── MainMenu.xib └── AppDelegate.swift ├── MenuLock.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata ├── xcuserdata │ └── fti.xcuserdatad │ │ └── xcschemes │ │ ├── xcschememanagement.plist │ │ └── MenuLock.xcscheme └── project.pbxproj ├── Podfile ├── MenuLock.xcworkspace └── contents.xcworkspacedata ├── .gitignore └── README.md /MenuLock-v2.0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shufflepuck/MenuLock/HEAD/MenuLock-v2.0.png -------------------------------------------------------------------------------- /MenuLock/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /MenuLock/Assets.xcassets/AppIcon.appiconset/icon16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shufflepuck/MenuLock/HEAD/MenuLock/Assets.xcassets/AppIcon.appiconset/icon16x16.png -------------------------------------------------------------------------------- /MenuLock/Assets.xcassets/AppIcon.appiconset/icon32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shufflepuck/MenuLock/HEAD/MenuLock/Assets.xcassets/AppIcon.appiconset/icon32x32.png -------------------------------------------------------------------------------- /MenuLock/Assets.xcassets/AppIcon.appiconset/icon64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shufflepuck/MenuLock/HEAD/MenuLock/Assets.xcassets/AppIcon.appiconset/icon64x64.png -------------------------------------------------------------------------------- /MenuLock/Assets.xcassets/AppIcon.appiconset/icon1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shufflepuck/MenuLock/HEAD/MenuLock/Assets.xcassets/AppIcon.appiconset/icon1024x1024.png -------------------------------------------------------------------------------- /MenuLock/Assets.xcassets/AppIcon.appiconset/icon128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shufflepuck/MenuLock/HEAD/MenuLock/Assets.xcassets/AppIcon.appiconset/icon128x128.png -------------------------------------------------------------------------------- /MenuLock/Assets.xcassets/AppIcon.appiconset/icon256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shufflepuck/MenuLock/HEAD/MenuLock/Assets.xcassets/AppIcon.appiconset/icon256x256.png -------------------------------------------------------------------------------- /MenuLock/Assets.xcassets/AppIcon.appiconset/icon512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shufflepuck/MenuLock/HEAD/MenuLock/Assets.xcassets/AppIcon.appiconset/icon512x512.png -------------------------------------------------------------------------------- /MenuLock.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MenuLock/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '8.0' 3 | # Uncomment this line if you're using Swift 4 | use_frameworks! 5 | 6 | target 'MenuLock' do 7 | pod 'MASShortcut' 8 | 9 | end 10 | 11 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '8.0' 3 | # Uncomment this line if you're using Swift 4 | #platform :osx, '10.10' 5 | use_frameworks! 6 | 7 | target 'MenuLock' do 8 | pod 'MASShortcut' 9 | #pod 'Cereal', '~> 2.0' 10 | 11 | end 12 | 13 | -------------------------------------------------------------------------------- /MenuLock.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /MenuLock/io.fti.MenuLock.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | RunAtLoad 6 | 7 | Program 8 | /Applications/MenuLock.app/Contents/MacOS/MenuLock 9 | Label 10 | io.fti.MenuLock 11 | 12 | 13 | -------------------------------------------------------------------------------- /MenuLock.xcodeproj/xcuserdata/fti.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | MenuLock.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 4768F0E01C84910E0069C875 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /MenuLock/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 2.0.2 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | LSUIElement 28 | 29 | NSHumanReadableCopyright 30 | Copyright © 2016 François Levaux. All rights reserved. 31 | NSMainNibFile 32 | MainMenu 33 | NSPrincipalClass 34 | NSApplication 35 | 36 | 37 | -------------------------------------------------------------------------------- /MenuLock/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "icon16x16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "icon32x32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "icon32x32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "icon64x64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "icon128x128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "icon256x256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "icon256x256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "icon512x512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "icon512x512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "icon1024x1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | }, 68 | "properties" : { 69 | "pre-rendered" : true 70 | } 71 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | Pods/ 47 | Podfile.lock 48 | 49 | # Carthage 50 | # 51 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 52 | # Carthage/Checkouts 53 | 54 | Carthage/Build 55 | 56 | # fastlane 57 | # 58 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 59 | # screenshots whenever they are needed. 60 | # For more information about the recommended setup visit: 61 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 62 | 63 | fastlane/report.xml 64 | fastlane/Preview.html 65 | fastlane/screenshots 66 | fastlane/test_output 67 | 68 | -------------------------------------------------------------------------------- /MenuLock/KeyMap.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyMapClass.swift 3 | // MenuLock 4 | // 5 | // Created by keyboard on 13/07/2016. 6 | // Copyright © 2016 François Levaux. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class Key: NSObject, NSCoding { 12 | let name: String 13 | let keyCode: UInt 14 | let keyMask: NSEventModifierFlags 15 | let keyEquivalent: String 16 | 17 | init(name: String, keyCode: UInt, keyMask: NSEventModifierFlags, keyEquivalent: AnyObject) { 18 | self.name = name 19 | self.keyCode = keyCode 20 | self.keyMask = keyMask 21 | if let keyString = keyEquivalent as? String { 22 | self.keyEquivalent = keyString 23 | } else if let keyInt = keyEquivalent as? Int { 24 | self.keyEquivalent = String(utf16CodeUnits: [unichar(keyInt)], count: 1) 25 | } else { 26 | abort() 27 | } 28 | 29 | } 30 | 31 | required convenience init?(coder aDecoder: NSCoder) { 32 | guard let name = aDecoder.decodeObject(forKey: "keyName") as? String, 33 | let keyCode = aDecoder.decodeObject(forKey: "keyCode") as? UInt, 34 | let keyMask = aDecoder.decodeObject(forKey: "keyMask") as? UInt, 35 | let keyEquivalent = aDecoder.decodeObject(forKey: "keyEquivalent") as? String 36 | else { return nil } 37 | 38 | let keyMaskFlags = NSEventModifierFlags(rawValue: keyMask) 39 | self.init(name: name, keyCode: keyCode, keyMask: keyMaskFlags, keyEquivalent: keyEquivalent as AnyObject) 40 | 41 | } 42 | 43 | 44 | func encode(with aCoder: NSCoder) { 45 | aCoder.encode(self.name, forKey: "keyName") 46 | aCoder.encode(self.keyCode, forKey: "keyCode") 47 | aCoder.encode(self.keyMask.rawValue, forKey: "keyMask") 48 | aCoder.encode(self.keyEquivalent, forKey: "keyEquivalent") 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /MenuLock/Base.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /MenuLock.xcodeproj/xcuserdata/fti.xcuserdatad/xcschemes/MenuLock.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MenuLock 2 | implements CMD+L for windows enthusiasts 3 | To be more precise, it puts display to sleep. 4 | 5 | To lock your computer, you need to have `System Preferences > Security & Privacy > Require the password Immediately after sleep saver begins`, and a password set. 6 | 7 | ![example](https://raw.githubusercontent.com/ftiff/MenuLock/master/MenuLock-v2.0.png) 8 | 9 | (Hi, Cédric!) 10 | 11 | # Why ? 12 | - Because Keychain Access' menubar introduce the question: "Why lock keychain?" 13 | - Because ctrl+shift+power/eject can become halt or reboot if user is not careful 14 | - Because Hot Corners are not loved by everyone (and they only start screensaver) 15 | 16 | # Some details 17 | 18 | ## Building the App 19 | 20 | ### Workspace Setup 21 | This app uses [Cocoapods](https://cocoapods.org). Please first install it (usually with `sudo gem install cocoapods`). 22 | Then follow this: 23 | 24 | - `git clone https://github.com/ftiff/MenuLock.git` 25 | - `cd MenuLock` 26 | - `pod install` 27 | - `open MenuLock.xcworkspace/` 28 | 29 | Then go to `Product > Scheme > Edit Scheme…` and make sure `Run > Executable` is set to "MenuLock.app". 30 | 31 | ### Build 32 | - Modify APP_VERSION in Build Settings (see https://ftiff.wordpress.com/2016/04/01/xcode-automatically-sign-and-build-package/) 33 | - In Terminal.app, navigate to the MenuLock directory 34 | - `xcodebuild -workspace MenuLock.xcworkspace -scheme MenuLock install` 35 | 36 | ### install 37 | - `sudo installer -target / -pkg /tmp/MenuLock-1.1.pkg` 38 | - `sudo launchctl load /Library/LaunchAgents/io.fti.MenuLock.plist` 39 | 40 | ## Key Modifiers 41 | 42 | `let keyMask: NSEventModifierFlags = [ .AlternateKeyMask, .ShiftKeyMask]` 43 | 44 | event modifier flag | Description 45 | --------------------|------------ 46 | AlphaShiftKeyMask | Caps Lock 47 | ShiftKeyMask | Shift 48 | ControlKeyMask | Control 49 | AlternateKeyMask | Alt 50 | CommandKeyMask | Command 51 | NumericPadKeyMask | Set if any key in the numeric keypad is pressed (and arrows) 52 | HelpKeyMask | Help Key (does this still exists?) 53 | FunctionKeyMask | The function keys include the F keys at the top of most keyboards (F1, F2, and so on) and the navigation keys in the center of most keyboards (Help, Forward Delete, Home, End, Page Up, Page Down, and the arrow keys). 54 | 55 | ## Key Code 56 | 57 | `let keyCode = UInt(0x25)` 58 | 59 | These are defined in `/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h`. Be careful these work for US Keyboards. I'd love to learn a better way to do this. 60 | 61 | Key Code | Hex Equivalent 62 | ---------|--------------- 63 | A |0x00 64 | S |0x01 65 | D |0x02 66 | F |0x03 67 | H |0x04 68 | G |0x05 69 | Z |0x06 70 | X |0x07 71 | C |0x08 72 | V |0x09 73 | B |0x0B 74 | Q |0x0C 75 | W |0x0D 76 | E |0x0E 77 | R |0x0F 78 | Y |0x10 79 | T |0x11 80 | 1 |0x12 81 | 2 |0x13 82 | 3 |0x14 83 | 4 |0x15 84 | 6 |0x16 85 | 5 |0x17 86 | Equal |0x18 87 | 9 |0x19 88 | 7 |0x1A 89 | Minus |0x1B 90 | 8 |0x1C 91 | 0 |0x1D 92 | RightBracket |0x1E 93 | O |0x1F 94 | U |0x20 95 | LeftBracket |0x21 96 | I |0x22 97 | P |0x23 98 | L |0x25 99 | J |0x26 100 | Quote |0x27 101 | K |0x28 102 | Semicolon |0x29 103 | Backslash |0x2A 104 | Comma |0x2B 105 | Slash |0x2C 106 | N |0x2D 107 | M |0x2E 108 | Period |0x2F 109 | Grave |0x32 110 | KeypadDecimal |0x41 111 | KeypadMultiply |0x43 112 | KeypadPlus |0x45 113 | KeypadClear |0x47 114 | KeypadDivide |0x4B 115 | KeypadEnter |0x4C 116 | KeypadMinus |0x4E 117 | KeypadEquals |0x51 118 | Keypad0 |0x52 119 | Keypad1 |0x53 120 | Keypad2 |0x54 121 | Keypad3 |0x55 122 | Keypad4 |0x56 123 | Keypad5 |0x57 124 | Keypad6 |0x58 125 | Keypad7 |0x59 126 | Keypad8 |0x5B 127 | Keypad9 |0x5C 128 | Return | 0x24 129 | Tab | 0x30 130 | Space | 0x31 131 | Delete | 0x33 132 | Escape | 0x35 133 | Command | 0x37 134 | Shift | 0x38 135 | CapsLock | 0x39 136 | Option | 0x3A 137 | Control | 0x3B 138 | RightShift | 0x3C 139 | RightOption | 0x3D 140 | RightControl | 0x3E 141 | Function | 0x3F 142 | F17 | 0x40 143 | VolumeUp | 0x48 144 | VolumeDown | 0x49 145 | Mute | 0x4A 146 | F18 | 0x4F 147 | F19 | 0x50 148 | F20 | 0x5A 149 | F5 | 0x60 150 | F6 | 0x61 151 | F7 | 0x62 152 | F3 | 0x63 153 | F8 | 0x64 154 | F9 | 0x65 155 | F11 | 0x67 156 | F13 | 0x69 157 | F16 | 0x6A 158 | F14 | 0x6B 159 | F10 | 0x6D 160 | F12 | 0x6F 161 | F15 | 0x71 162 | Help | 0x72 163 | Home | 0x73 164 | PageUp | 0x74 165 | ForwardDelete | 0x75 166 | F4 | 0x76 167 | End | 0x77 168 | F2 | 0x78 169 | PageDown | 0x79 170 | F1 | 0x7A 171 | LeftArrow | 0x7B 172 | RightArrow | 0x7C 173 | DownArrow | 0x7D 174 | UpArrow | 0x7E 175 | -------------------------------------------------------------------------------- /MenuLock/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // MenuLock 4 | // 5 | // Created by François Levaux on 29.02.16. 6 | // Copyright © 2016 François Levaux. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import MASShortcut 11 | 12 | 13 | @NSApplicationMain 14 | class AppDelegate: NSObject, NSApplicationDelegate { 15 | 16 | @IBOutlet weak var statusBar: NSMenu! 17 | @IBOutlet weak var statusLockScreen: NSMenuItem! 18 | 19 | @IBOutlet weak var activeKeyMenu: NSMenu! 20 | 21 | let statusItem = NSStatusBar.system().statusItem(withLength: NSSquareStatusItemLength) 22 | var activeKey: Key = Key(name: "CMD + L", keyCode: 0x25, keyMask: [ .command ], keyEquivalent: "l" as AnyObject) 23 | 24 | 25 | // List of available Keys 26 | 27 | var keyMaps = [ 28 | Key(name: "F19", keyCode: 80, keyMask: [], keyEquivalent: NSF19FunctionKey as AnyObject), 29 | Key(name: "CMD + F12", keyCode: 0x6F, keyMask: [ .command ], keyEquivalent: NSF12FunctionKey as AnyObject), 30 | Key(name: "CMD + L", keyCode: 0x25, keyMask: [ .command ], keyEquivalent: "l" as AnyObject), 31 | Key(name: "CMD + K", keyCode: 0x28, keyMask: [ .command ], keyEquivalent: "k" as AnyObject) 32 | ] 33 | 34 | @IBAction func statusItemQuit(sender: NSMenuItem) { 35 | NSApplication.shared().terminate(self) 36 | } 37 | 38 | @IBAction func statusItemLock(sender: NSMenuItem) { 39 | screenSleep() 40 | } 41 | 42 | 43 | func applicationDidFinishLaunching(_ notification: Notification) { 44 | 45 | // Setup icon 46 | 47 | let icon = NSImage(named: NSImageNameLockLockedTemplate) 48 | statusItem.image = icon 49 | statusItem.menu = statusBar 50 | 51 | 52 | // Defaults 53 | 54 | setGlobalShortcut() 55 | 56 | 57 | // Setup Active Key Menu 58 | 59 | for key in keyMaps { 60 | let menuItem = NSMenuItem() 61 | menuItem.title = key.name 62 | if activeKey.name == key.name { 63 | menuItem.state = NSOnState 64 | } 65 | menuItem.representedObject = key 66 | menuItem.action = #selector(setActiveKey(sender:)) 67 | activeKeyMenu.addItem(menuItem) 68 | } 69 | 70 | } 71 | 72 | 73 | func setGlobalShortcut() { 74 | 75 | if let key: Key = readPrefs() { 76 | activeKey = key 77 | } else { 78 | NSLog("Cannot get activeKey from UserDefaults. Setting current key.") 79 | savePrefs(key: activeKey) 80 | } 81 | 82 | // Set Menu Item Key Equivalent 83 | statusLockScreen.keyEquivalent = activeKey.keyEquivalent 84 | statusLockScreen.keyEquivalentModifierMask = activeKey.keyMask 85 | 86 | // Setup Global Shortcut 87 | let shortcut = MASShortcut(keyCode: activeKey.keyCode, modifierFlags: activeKey.keyMask.rawValue) 88 | MASShortcutMonitor.shared().register(shortcut, withAction: screenSleep) 89 | 90 | 91 | } 92 | 93 | 94 | // Set which key should trigger the sleep 95 | 96 | func setActiveKey(sender: NSMenuItem) { 97 | 98 | for menuItem in activeKeyMenu.items { 99 | menuItem.state = NSOffState 100 | } 101 | sender.state = NSOnState 102 | if let key: Key = (sender.representedObject as? Key) { 103 | savePrefs(key: key) 104 | 105 | //activeKey = key 106 | } else { 107 | NSLog("cannot see representedObject") 108 | } 109 | setGlobalShortcut() 110 | } 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | // Sleep Screen methods 119 | 120 | func screenSleep() -> Void { 121 | let registry: io_registry_entry_t = IORegistryEntryFromPath(kIOMasterPortDefault, "IOService:/IOResources/IODisplayWrangler") 122 | let _ = IORegistryEntrySetCFProperty(registry, "IORequestIdle" as CFString!, true as CFTypeRef!) 123 | IOObjectRelease(registry) 124 | } 125 | 126 | func lockScreenImmediate() -> Void { 127 | // Note: Private -- Do not use! 128 | // http://stackoverflow.com/questions/34669958/swift-how-to-call-a-c-function-loaded-from-a-dylib 129 | 130 | let libHandle = dlopen("/System/Library/PrivateFrameworks/login.framework/Versions/Current/login", RTLD_LAZY) 131 | let sym = dlsym(libHandle, "SACLockScreenImmediate") 132 | typealias myFunction = @convention(c) (Void) -> Void 133 | let SACLockScreenImmediate = unsafeBitCast(sym, to: myFunction.self) 134 | SACLockScreenImmediate() 135 | } 136 | 137 | 138 | 139 | // Preferences 140 | 141 | func savePrefs(key: Key) { 142 | let prefs = UserDefaults() 143 | let data = NSKeyedArchiver.archivedData(withRootObject: key) 144 | prefs.set(data, forKey: "currentKey") 145 | prefs.synchronize() 146 | } 147 | 148 | func readPrefs() -> Key? { 149 | let prefs = UserDefaults() 150 | if let object = prefs.object(forKey: "currentKey") { 151 | if let data = object as? Data { 152 | return NSKeyedUnarchiver.unarchiveObject(with: data as Data) as? Key 153 | } else { 154 | NSLog("cannot convert to Data") 155 | return nil 156 | } 157 | } else { 158 | return nil 159 | } 160 | } 161 | 162 | } 163 | 164 | -------------------------------------------------------------------------------- /MenuLock.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4768F0E51C84910E0069C875 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4768F0E41C84910E0069C875 /* AppDelegate.swift */; }; 11 | 4768F0E71C84910E0069C875 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4768F0E61C84910E0069C875 /* Assets.xcassets */; }; 12 | 4768F0EA1C84910E0069C875 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4768F0E81C84910E0069C875 /* MainMenu.xib */; }; 13 | 877996C61282AD19DA70A5C6 /* Pods_MenuLock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE9A6DE3DBC86D67F62CD9B8 /* Pods_MenuLock.framework */; }; 14 | D666B0361D362BE9005296DC /* KeyMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = D666B0351D362BE9005296DC /* KeyMap.swift */; }; 15 | /* End PBXBuildFile section */ 16 | 17 | /* Begin PBXFileReference section */ 18 | 0E392E529B6CA297CE7ABE66 /* Pods-MenuLock.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MenuLock.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MenuLock/Pods-MenuLock.debug.xcconfig"; sourceTree = ""; }; 19 | 4768F0E11C84910E0069C875 /* MenuLock.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MenuLock.app; sourceTree = BUILT_PRODUCTS_DIR; }; 20 | 4768F0E41C84910E0069C875 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 21 | 4768F0E61C84910E0069C875 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 22 | 4768F0E91C84910E0069C875 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 23 | 4768F0EB1C84910E0069C875 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 24 | A7628FF9B155D1AC48E9C276 /* Pods-MenuLock.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MenuLock.release.xcconfig"; path = "Pods/Target Support Files/Pods-MenuLock/Pods-MenuLock.release.xcconfig"; sourceTree = ""; }; 25 | D666B0351D362BE9005296DC /* KeyMap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyMap.swift; sourceTree = ""; }; 26 | DE9A6DE3DBC86D67F62CD9B8 /* Pods_MenuLock.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MenuLock.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | /* End PBXFileReference section */ 28 | 29 | /* Begin PBXFrameworksBuildPhase section */ 30 | 4768F0DE1C84910E0069C875 /* Frameworks */ = { 31 | isa = PBXFrameworksBuildPhase; 32 | buildActionMask = 2147483647; 33 | files = ( 34 | 877996C61282AD19DA70A5C6 /* Pods_MenuLock.framework in Frameworks */, 35 | ); 36 | runOnlyForDeploymentPostprocessing = 0; 37 | }; 38 | /* End PBXFrameworksBuildPhase section */ 39 | 40 | /* Begin PBXGroup section */ 41 | 4768F0D81C84910E0069C875 = { 42 | isa = PBXGroup; 43 | children = ( 44 | 4768F0E31C84910E0069C875 /* MenuLock */, 45 | 4768F0E21C84910E0069C875 /* Products */, 46 | 9724658849CEC9A1240B8971 /* Pods */, 47 | 5F60155205FA337FA7E20B39 /* Frameworks */, 48 | ); 49 | sourceTree = ""; 50 | }; 51 | 4768F0E21C84910E0069C875 /* Products */ = { 52 | isa = PBXGroup; 53 | children = ( 54 | 4768F0E11C84910E0069C875 /* MenuLock.app */, 55 | ); 56 | name = Products; 57 | sourceTree = ""; 58 | }; 59 | 4768F0E31C84910E0069C875 /* MenuLock */ = { 60 | isa = PBXGroup; 61 | children = ( 62 | 4768F0E41C84910E0069C875 /* AppDelegate.swift */, 63 | D666B0351D362BE9005296DC /* KeyMap.swift */, 64 | 4768F0E61C84910E0069C875 /* Assets.xcassets */, 65 | 4768F0E81C84910E0069C875 /* MainMenu.xib */, 66 | 4768F0EB1C84910E0069C875 /* Info.plist */, 67 | ); 68 | path = MenuLock; 69 | sourceTree = ""; 70 | }; 71 | 5F60155205FA337FA7E20B39 /* Frameworks */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | DE9A6DE3DBC86D67F62CD9B8 /* Pods_MenuLock.framework */, 75 | ); 76 | name = Frameworks; 77 | sourceTree = ""; 78 | }; 79 | 9724658849CEC9A1240B8971 /* Pods */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | 0E392E529B6CA297CE7ABE66 /* Pods-MenuLock.debug.xcconfig */, 83 | A7628FF9B155D1AC48E9C276 /* Pods-MenuLock.release.xcconfig */, 84 | ); 85 | name = Pods; 86 | sourceTree = ""; 87 | }; 88 | /* End PBXGroup section */ 89 | 90 | /* Begin PBXNativeTarget section */ 91 | 4768F0E01C84910E0069C875 /* MenuLock */ = { 92 | isa = PBXNativeTarget; 93 | buildConfigurationList = 4768F0EE1C84910E0069C875 /* Build configuration list for PBXNativeTarget "MenuLock" */; 94 | buildPhases = ( 95 | 4490B3C84A19D0D9FE98B2CA /* [CP] Check Pods Manifest.lock */, 96 | 4768F0DD1C84910E0069C875 /* Sources */, 97 | 4768F0DE1C84910E0069C875 /* Frameworks */, 98 | 4768F0DF1C84910E0069C875 /* Resources */, 99 | A2053F2E26D1B73BCC26FA4A /* [CP] Embed Pods Frameworks */, 100 | D0D3B6E27959104FD9F63B29 /* [CP] Copy Pods Resources */, 101 | ); 102 | buildRules = ( 103 | ); 104 | dependencies = ( 105 | ); 106 | name = MenuLock; 107 | productName = MenuLock; 108 | productReference = 4768F0E11C84910E0069C875 /* MenuLock.app */; 109 | productType = "com.apple.product-type.application"; 110 | }; 111 | /* End PBXNativeTarget section */ 112 | 113 | /* Begin PBXProject section */ 114 | 4768F0D91C84910E0069C875 /* Project object */ = { 115 | isa = PBXProject; 116 | attributes = { 117 | LastSwiftUpdateCheck = 0720; 118 | LastUpgradeCheck = 0810; 119 | ORGANIZATIONNAME = "François Levaux"; 120 | TargetAttributes = { 121 | 4768F0E01C84910E0069C875 = { 122 | CreatedOnToolsVersion = 7.2.1; 123 | DevelopmentTeam = EM3ER8T33A; 124 | DevelopmentTeamName = "Francois TIFFREAU"; 125 | LastSwiftMigration = 0800; 126 | ProvisioningStyle = Automatic; 127 | SystemCapabilities = { 128 | com.apple.Sandbox = { 129 | enabled = 0; 130 | }; 131 | }; 132 | }; 133 | }; 134 | }; 135 | buildConfigurationList = 4768F0DC1C84910E0069C875 /* Build configuration list for PBXProject "MenuLock" */; 136 | compatibilityVersion = "Xcode 3.2"; 137 | developmentRegion = English; 138 | hasScannedForEncodings = 0; 139 | knownRegions = ( 140 | en, 141 | Base, 142 | ); 143 | mainGroup = 4768F0D81C84910E0069C875; 144 | productRefGroup = 4768F0E21C84910E0069C875 /* Products */; 145 | projectDirPath = ""; 146 | projectRoot = ""; 147 | targets = ( 148 | 4768F0E01C84910E0069C875 /* MenuLock */, 149 | ); 150 | }; 151 | /* End PBXProject section */ 152 | 153 | /* Begin PBXResourcesBuildPhase section */ 154 | 4768F0DF1C84910E0069C875 /* Resources */ = { 155 | isa = PBXResourcesBuildPhase; 156 | buildActionMask = 2147483647; 157 | files = ( 158 | 4768F0E71C84910E0069C875 /* Assets.xcassets in Resources */, 159 | 4768F0EA1C84910E0069C875 /* MainMenu.xib in Resources */, 160 | ); 161 | runOnlyForDeploymentPostprocessing = 0; 162 | }; 163 | /* End PBXResourcesBuildPhase section */ 164 | 165 | /* Begin PBXShellScriptBuildPhase section */ 166 | 4490B3C84A19D0D9FE98B2CA /* [CP] Check Pods Manifest.lock */ = { 167 | isa = PBXShellScriptBuildPhase; 168 | buildActionMask = 2147483647; 169 | files = ( 170 | ); 171 | inputPaths = ( 172 | ); 173 | name = "[CP] Check Pods Manifest.lock"; 174 | outputPaths = ( 175 | ); 176 | runOnlyForDeploymentPostprocessing = 0; 177 | shellPath = /bin/sh; 178 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; 179 | showEnvVarsInLog = 0; 180 | }; 181 | A2053F2E26D1B73BCC26FA4A /* [CP] Embed Pods Frameworks */ = { 182 | isa = PBXShellScriptBuildPhase; 183 | buildActionMask = 2147483647; 184 | files = ( 185 | ); 186 | inputPaths = ( 187 | ); 188 | name = "[CP] Embed Pods Frameworks"; 189 | outputPaths = ( 190 | ); 191 | runOnlyForDeploymentPostprocessing = 0; 192 | shellPath = /bin/sh; 193 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-MenuLock/Pods-MenuLock-frameworks.sh\"\n"; 194 | showEnvVarsInLog = 0; 195 | }; 196 | D0D3B6E27959104FD9F63B29 /* [CP] Copy Pods Resources */ = { 197 | isa = PBXShellScriptBuildPhase; 198 | buildActionMask = 2147483647; 199 | files = ( 200 | ); 201 | inputPaths = ( 202 | ); 203 | name = "[CP] Copy Pods Resources"; 204 | outputPaths = ( 205 | ); 206 | runOnlyForDeploymentPostprocessing = 0; 207 | shellPath = /bin/sh; 208 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-MenuLock/Pods-MenuLock-resources.sh\"\n"; 209 | showEnvVarsInLog = 0; 210 | }; 211 | /* End PBXShellScriptBuildPhase section */ 212 | 213 | /* Begin PBXSourcesBuildPhase section */ 214 | 4768F0DD1C84910E0069C875 /* Sources */ = { 215 | isa = PBXSourcesBuildPhase; 216 | buildActionMask = 2147483647; 217 | files = ( 218 | 4768F0E51C84910E0069C875 /* AppDelegate.swift in Sources */, 219 | D666B0361D362BE9005296DC /* KeyMap.swift in Sources */, 220 | ); 221 | runOnlyForDeploymentPostprocessing = 0; 222 | }; 223 | /* End PBXSourcesBuildPhase section */ 224 | 225 | /* Begin PBXVariantGroup section */ 226 | 4768F0E81C84910E0069C875 /* MainMenu.xib */ = { 227 | isa = PBXVariantGroup; 228 | children = ( 229 | 4768F0E91C84910E0069C875 /* Base */, 230 | ); 231 | name = MainMenu.xib; 232 | sourceTree = ""; 233 | }; 234 | /* End PBXVariantGroup section */ 235 | 236 | /* Begin XCBuildConfiguration section */ 237 | 4768F0EC1C84910E0069C875 /* Debug */ = { 238 | isa = XCBuildConfiguration; 239 | buildSettings = { 240 | ALWAYS_SEARCH_USER_PATHS = NO; 241 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 242 | CLANG_CXX_LIBRARY = "libc++"; 243 | CLANG_ENABLE_MODULES = YES; 244 | CLANG_ENABLE_OBJC_ARC = YES; 245 | CLANG_WARN_BOOL_CONVERSION = YES; 246 | CLANG_WARN_CONSTANT_CONVERSION = YES; 247 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 248 | CLANG_WARN_EMPTY_BODY = YES; 249 | CLANG_WARN_ENUM_CONVERSION = YES; 250 | CLANG_WARN_INFINITE_RECURSION = YES; 251 | CLANG_WARN_INT_CONVERSION = YES; 252 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 253 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 254 | CLANG_WARN_UNREACHABLE_CODE = YES; 255 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 256 | CODE_SIGN_IDENTITY = "-"; 257 | COPY_PHASE_STRIP = NO; 258 | DEBUG_INFORMATION_FORMAT = dwarf; 259 | ENABLE_STRICT_OBJC_MSGSEND = YES; 260 | ENABLE_TESTABILITY = YES; 261 | GCC_C_LANGUAGE_STANDARD = gnu99; 262 | GCC_DYNAMIC_NO_PIC = NO; 263 | GCC_NO_COMMON_BLOCKS = YES; 264 | GCC_OPTIMIZATION_LEVEL = 0; 265 | GCC_PREPROCESSOR_DEFINITIONS = ( 266 | "DEBUG=1", 267 | "$(inherited)", 268 | ); 269 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 270 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 271 | GCC_WARN_UNDECLARED_SELECTOR = YES; 272 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 273 | GCC_WARN_UNUSED_FUNCTION = YES; 274 | GCC_WARN_UNUSED_VARIABLE = YES; 275 | MACOSX_DEPLOYMENT_TARGET = 10.11; 276 | MTL_ENABLE_DEBUG_INFO = YES; 277 | ONLY_ACTIVE_ARCH = YES; 278 | SDKROOT = macosx; 279 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 280 | }; 281 | name = Debug; 282 | }; 283 | 4768F0ED1C84910E0069C875 /* Release */ = { 284 | isa = XCBuildConfiguration; 285 | buildSettings = { 286 | ALWAYS_SEARCH_USER_PATHS = NO; 287 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 288 | CLANG_CXX_LIBRARY = "libc++"; 289 | CLANG_ENABLE_MODULES = YES; 290 | CLANG_ENABLE_OBJC_ARC = YES; 291 | CLANG_WARN_BOOL_CONVERSION = YES; 292 | CLANG_WARN_CONSTANT_CONVERSION = YES; 293 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 294 | CLANG_WARN_EMPTY_BODY = YES; 295 | CLANG_WARN_ENUM_CONVERSION = YES; 296 | CLANG_WARN_INFINITE_RECURSION = YES; 297 | CLANG_WARN_INT_CONVERSION = YES; 298 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 299 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 300 | CLANG_WARN_UNREACHABLE_CODE = YES; 301 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 302 | CODE_SIGN_IDENTITY = "-"; 303 | COPY_PHASE_STRIP = NO; 304 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 305 | ENABLE_NS_ASSERTIONS = NO; 306 | ENABLE_STRICT_OBJC_MSGSEND = YES; 307 | GCC_C_LANGUAGE_STANDARD = gnu99; 308 | GCC_NO_COMMON_BLOCKS = YES; 309 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 310 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 311 | GCC_WARN_UNDECLARED_SELECTOR = YES; 312 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 313 | GCC_WARN_UNUSED_FUNCTION = YES; 314 | GCC_WARN_UNUSED_VARIABLE = YES; 315 | MACOSX_DEPLOYMENT_TARGET = 10.11; 316 | MTL_ENABLE_DEBUG_INFO = NO; 317 | SDKROOT = macosx; 318 | }; 319 | name = Release; 320 | }; 321 | 4768F0EF1C84910E0069C875 /* Debug */ = { 322 | isa = XCBuildConfiguration; 323 | baseConfigurationReference = 0E392E529B6CA297CE7ABE66 /* Pods-MenuLock.debug.xcconfig */; 324 | buildSettings = { 325 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 326 | CODE_SIGN_IDENTITY = "Mac Developer"; 327 | COMBINE_HIDPI_IMAGES = YES; 328 | DEVELOPMENT_TEAM = EM3ER8T33A; 329 | INFOPLIST_FILE = MenuLock/Info.plist; 330 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 331 | MACOSX_DEPLOYMENT_TARGET = 10.9; 332 | PRODUCT_BUNDLE_IDENTIFIER = io.fti.MenuLock; 333 | PRODUCT_NAME = "$(TARGET_NAME)"; 334 | PROVISIONING_PROFILE = ""; 335 | PROVISIONING_PROFILE_SPECIFIER = ""; 336 | SWIFT_VERSION = 3.0; 337 | }; 338 | name = Debug; 339 | }; 340 | 4768F0F01C84910E0069C875 /* Release */ = { 341 | isa = XCBuildConfiguration; 342 | baseConfigurationReference = A7628FF9B155D1AC48E9C276 /* Pods-MenuLock.release.xcconfig */; 343 | buildSettings = { 344 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 345 | CODE_SIGN_IDENTITY = "Mac Developer"; 346 | COMBINE_HIDPI_IMAGES = YES; 347 | DEVELOPMENT_TEAM = EM3ER8T33A; 348 | INFOPLIST_FILE = MenuLock/Info.plist; 349 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 350 | MACOSX_DEPLOYMENT_TARGET = 10.9; 351 | PRODUCT_BUNDLE_IDENTIFIER = io.fti.MenuLock; 352 | PRODUCT_NAME = "$(TARGET_NAME)"; 353 | PROVISIONING_PROFILE = ""; 354 | PROVISIONING_PROFILE_SPECIFIER = ""; 355 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 356 | SWIFT_VERSION = 3.0; 357 | }; 358 | name = Release; 359 | }; 360 | /* End XCBuildConfiguration section */ 361 | 362 | /* Begin XCConfigurationList section */ 363 | 4768F0DC1C84910E0069C875 /* Build configuration list for PBXProject "MenuLock" */ = { 364 | isa = XCConfigurationList; 365 | buildConfigurations = ( 366 | 4768F0EC1C84910E0069C875 /* Debug */, 367 | 4768F0ED1C84910E0069C875 /* Release */, 368 | ); 369 | defaultConfigurationIsVisible = 0; 370 | defaultConfigurationName = Release; 371 | }; 372 | 4768F0EE1C84910E0069C875 /* Build configuration list for PBXNativeTarget "MenuLock" */ = { 373 | isa = XCConfigurationList; 374 | buildConfigurations = ( 375 | 4768F0EF1C84910E0069C875 /* Debug */, 376 | 4768F0F01C84910E0069C875 /* Release */, 377 | ); 378 | defaultConfigurationIsVisible = 0; 379 | defaultConfigurationName = Release; 380 | }; 381 | /* End XCConfigurationList section */ 382 | }; 383 | rootObject = 4768F0D91C84910E0069C875 /* Project object */; 384 | } 385 | --------------------------------------------------------------------------------