diff --git a/README.md b/README.md index 831f594..7681085 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,34 @@ Memmon remembers what your Mac forgets – A simple deamon that restores your window positions on external monitors. + +## Install + +1. You will need macOS 10.10 or newer. +2. Grant Memmon the Accessibility privilege. Go to "System Preference" > "Security & Privacy" > "Accessibility" and add Memmon to that list. Otherwise, you can't move other application windows around and the app has no purpose. +3. Thats it. The app runs in your status bar. + + +### Status Icon + +You can hide the status icon either via `defaults` or the same-titled menu entry. If you do so, the only way to quit the app is by killing the process (with Activity.app or `killall Memmon`). + +Memmon has exactly one app-setting, the status icon. You can manipulate the display of the icon, or hide the icon completely: + +```sh +# disable status icon completely +defaults write de.relikd.Memmon icon -int 0 +# Use window-dots-icon +defaults write de.relikd.Memmon icon -int 1 +# Use monitor-with-windows icon (default) +defaults write de.relikd.Memmon icon -int 2 +# re-enable status icon and use default icon +defaults delete de.relikd.Memmon icon +``` + +![status icons](img/status_icons.png) + + ## FAQ ### Why‽ @@ -23,33 +51,11 @@ Yes, for example [Mjolnir](https://github.com/mjolnirapp/mjolnir) or [Hammerspoo ### What is it good for? -First off, Memmon is just 130 lines of code – no dependencies. You can audit it in 5 minutes. Or just build it from scratch if you like (just run `make`). +First off, Memmon is just 140 lines of code – no dependencies. You can audit it in 5 minutes and build it from scratch – just run `make`. Secondly, it does one thing and one thing only: Save and restore window positions whenever your monitor setup changes. -## Install - -1. You will need macOS 10.10 or newer. -2. Grant Memmon the Accessibility privilege. Go to "System Preference" > "Security & Privacy" > "Accessibility" and add Memmon to that list. Otherwise, you can't move other application windows around and the app has no purpose. -3. Thats it. The app runs in your status bar. - ### Develop You can either run the `main.swift` file directly with `swift main.swift`, via Terminal `./main.swift` (`chmod 755 main.swift`), or create a new Xcode project. Select the Command-Line template and after creation replace the existing `main.swift` with the bundled one. - -To build the app run `make`. - - -### Hide Status Icon - -You can hide the status icon either via the same-titled menu entry. If you do so, the only way to quit the app is by killing the process (with Activity.app or `killall Memmon`). - -If you like to hide the icon directly on launch, use this app-setting: - -```sh -# disable status icon completely -defaults write de.relikd.Memmon invisible -bool True -# re-enable status icon -defaults delete de.relikd.Memmon invisible -``` diff --git a/img/status_icons.png b/img/status_icons.png new file mode 100644 index 0000000..2e0f350 Binary files /dev/null and b/img/status_icons.png differ diff --git a/src/main.swift b/src/main.swift index bcb0999..1f798d1 100755 --- a/src/main.swift +++ b/src/main.swift @@ -11,12 +11,16 @@ class AppDelegate: NSObject, NSApplicationDelegate { private var state: [Int: WinConf] = [:] // [screencount: [pid: [windows]]] func applicationDidFinishLaunching(_ aNotification: Notification) { - if UserDefaults.standard.bool(forKey: "invisible") == true { - return - } + UserDefaults.standard.register(defaults: ["icon": 2]) + let icon = UserDefaults.standard.integer(forKey: "icon") + if icon == 0 { return } self.statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) if let button = self.statusItem.button { - button.image = NSImage.statusIconMonitor + switch icon { + case 1: button.image = NSImage.statusIconDots + case 2: button.image = NSImage.statusIconMonitor + default: button.image = NSImage.statusIconMonitor + } } self.statusItem.menu = NSMenu(title: "") self.statusItem.menu!.addItem(withTitle: "Memmon (v1.1)", action: nil, keyEquivalent: "") @@ -134,6 +138,24 @@ class AppDelegate: NSObject, NSApplicationDelegate { } extension NSImage { + static var statusIconDots: NSImage { + let img = NSImage.init(size: .init(width: 20, height: 20), flipped: true) { + let ctx = NSGraphicsContext.current!.cgContext + let w = $0.width + let h = $0.height + let sw = 0.025 * w // stroke width + ctx.stroke(CGRect(x: 0.0 * w, y: 0.15 * h, width: 1.0 * w, height: 0.7 * h).insetBy(dx: sw / 2, dy: sw / 2), width: sw) + ctx.fill(CGRect(x: 0, y: 0.55 * h, width: w, height: sw)) + let circle = CGRect(x: 0, y: 0.25 * h, width: 0.2 * w, height: 0.2 * w) + ctx.fillEllipse(in: circle.offsetBy(dx: 0.12 * w, dy: 0)) + ctx.fillEllipse(in: circle.offsetBy(dx: 0.4 * w, dy: 0)) + ctx.fillEllipse(in: circle.offsetBy(dx: 0.68 * w, dy: 0)) + return true + } + img.isTemplate = true + return img + } + static var statusIconMonitor: NSImage { let img = NSImage.init(size: .init(width: 21, height: 14), flipped: true) { let ctx = NSGraphicsContext.current!.cgContext