Skip to content

Instantly share code, notes, and snippets.

@anongit
Forked from anonymous/appnap.swift
Last active March 4, 2018 10:55
Show Gist options
  • Save anongit/a831e7de78e2fc3873b47c49a25e6b77 to your computer and use it in GitHub Desktop.
Save anongit/a831e7de78e2fc3873b47c49a25e6b77 to your computer and use it in GitHub Desktop.
/*
Execute script using the interpreter:
$ swift appnap.swift "Skype"
or compile it first then run:
$ swiftc -O appnap.swift
$ ./appnap "(?i)skype" # (?i) — ignore case
*/
import AppKit
func runUntilKeyboardInterrupt(_ callback: @escaping () -> Void) {
// See: https://stackoverflow.com/a/45714258
// Make sure the signal does not terminate the application.
signal(SIGINT, SIG_IGN)
let source = DispatchSource.makeSignalSource(signal: SIGINT)
source.setEventHandler {
callback()
exit(0)
}
source.resume()
RunLoop.current.run(
mode: RunLoopMode.defaultRunLoopMode,
before: NSDate.distantFuture
)
}
func main(_ args: [String]) {
if args.count != 2 {
print("Usage: \(args[0]) <pattern>")
return
}
guard let pattern = try? NSRegularExpression(pattern: "^.*\(args[1]).*$") else {
print("Invalid regex pattern")
return
}
// What to match against the pattern
let appToString = { (app: NSRunningApplication) in
app.executableURL?.path ?? app.bundleIdentifier ?? "[]"
}
let matchesPattern = { (app: NSRunningApplication) -> Bool in
let x = appToString(app)
return pattern.numberOfMatches(in: x, range: NSRange(x.startIndex..., in: x)) > 0
}
let workspace = NSWorkspace.shared
var active: NSRunningApplication?
var suspended = Set<pid_t>()
let resume = { (pid: pid_t) in
kill(pid, SIGCONT)
suspended.remove(pid)
}
let suspend = { (pid: pid_t) in
kill(pid, SIGSTOP)
suspended.insert(pid)
}
if let app = workspace.frontmostApplication, matchesPattern(app) {
active = app
}
workspace.runningApplications
.filter { $0 != active && matchesPattern($0) }
.forEach { (app) in
print("suspending \(appToString(app))")
suspend(app.processIdentifier)
}
// The observervation is only happening while token exists,
// so the token variable is actually used.
let token = workspace.observe(\.frontmostApplication) { workspace, _ in
if let previous = active {
print("suspending \(appToString(previous))")
suspend(previous.processIdentifier)
active = nil
}
guard let app = workspace.frontmostApplication else {
return
}
if matchesPattern(app) {
print("resuming \(appToString(app))")
resume(app.processIdentifier)
active = app
}
}
runUntilKeyboardInterrupt {
suspended.forEach(resume)
}
}
main(CommandLine.arguments)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment