Skip to content

Instantly share code, notes, and snippets.

@hiway
Last active February 12, 2018 14:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hiway/f55a5d24abb59d970dcc64ad738302a7 to your computer and use it in GitHub Desktop.
Save hiway/f55a5d24abb59d970dcc64ad738302a7 to your computer and use it in GitHub Desktop.
Automatically suspend selcted apps when not in use.

MacOS App Auto-Suspend

This is a quick hack to test whether a Python script can manage suspending and resuming other applications on MacOS. This version is a draft and has been tested for a few hours on MacOS El Capitan, your mileage may vary.

The script should be run with system python, if you have installed another copy with brew or another manager, use /usr/bin/python instead of python in the following:

Installation:

Download the autosuspend.py file, and run it with:

$ python autosuspend.py

Edit the file (configuration is near the bottom) to your preferences.

You can hit ctrl+c to quit the script and stop suspending apps.

#!/usr/bin/python
from collections import defaultdict
from AppKit import NSWorkspace
from time import sleep
import os
import logging
logger = logging.getLogger()
handler = logging.StreamHandler()
formatter = logging.Formatter(
'%(asctime)s %(name)-8s %(levelname)-8s %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.INFO)
state = defaultdict()
def get_active_app_name():
# https://stackoverflow.com/a/25214024
# return NSWorkspace.sharedWorkspace().frontmostApplication().localizedName()
return NSWorkspace.sharedWorkspace().activeApplication()['NSApplicationName']
def suspend(app, app_bin):
logger.info('SUSPEND: {}'.format(app))
if isinstance(app_bin, str):
app_bin = (app_bin, )
for a_bin in app_bin:
cmd = 'killall -STOP {}'.format(a_bin)
logger.debug('CMD: {!r}'.format(cmd))
os.system(cmd)
state[app] = False
def resume(app, app_bin):
logger.info('RESUME: {}'.format(app))
if isinstance(app_bin, str):
app_bin = (app_bin, )
for a_bin in app_bin:
cmd = 'killall -CONT {}'.format(a_bin)
logger.debug('CMD: {!r}'.format(cmd))
os.system(cmd)
state[app] = True
def monitor(suspend_apps):
try:
while True:
sleep(0.2)
active = get_active_app_name()
if active in suspend_apps:
if active not in state:
resume(active, suspend_apps[active])
state[active] = True
if not state[active]:
resume(active, suspend_apps[active])
all_except_active = {k:v for k, v in state.items() if v and k!= active}
if any(all_except_active.values()):
[suspend(k, suspend_apps[k]) for k, v in all_except_active.items()]
except (Exception, BaseException):
print('')
logger.info('Resuming all apps before exiting.')
[resume(k, suspend_apps[k]) for k, v in state.items() if not v]
if __name__ == '__main__':
# Configuration format:
# App name as it appears in `Activity Monitor` utility.
# App binary-name as it appears in "ps aux | egrep -i [APP-NAME] "
# If an app (example Firefox) has multiple worker processes
# with different binary names, you can pass a list of all binaries to suspend.
config = {
# AppName: binary-name
'Firefox': ['firefox', 'plugin-container'],
'Preview': 'Preview',
'PyCharm': 'pycharm',
'TagSpaces': 'TagSpaces',
}
monitor(config)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment