-
Notifications
You must be signed in to change notification settings - Fork 25
/
Copy pathnap_my_app.py
126 lines (103 loc) · 3.49 KB
/
nap_my_app.py
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#!/usr/bin/python
from __future__ import print_function
import time
import sys
import subprocess
import os
import signal
import logging
try:
from AppKit import NSWorkspace
except ImportError:
print('AppKit module not found, script should be run using system-default Python installations')
sys.exit(1)
SUSPENSION_WHITELIST = ['Terminal', 'Activity Monitor', 'iTerm2'] # set of apps to never suspend/resume
suspended_pids = set()
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s %(levelname)s %(name)s: %(message)s', '%b %d %H:%M:%S')
stdout = logging.StreamHandler(sys.stdout)
stdout.setFormatter(formatter)
logger.addHandler(stdout)
class Application:
def __init__(self, instance):
self.instance = instance
self.name = instance['NSApplicationName'].encode('utf8', 'ignore')
self.pid = instance['NSApplicationProcessIdentifier']
def get_pids(self):
'''
Returns all process IDs for a given application by name
:return: pids {List[Int]} all process IDs association to that application
'''
pids = [self.pid]
try:
pids.extend([int(pid) for pid in subprocess.check_output(['pgrep', '-P %s' % self.pid]).split()])
except subprocess.CalledProcessError:
pass
return pids
def suspend(self):
'''
Suspend application and all processes associated with it
'''
if self.name in SUSPENSION_WHITELIST:
return
for pid in self.get_pids():
if pid not in suspended_pids:
logger.debug('Suspending %s (%s)', self.pid, self.name)
suspended_pids.add(pid)
os.kill(pid, signal.SIGSTOP)
return
def resume(self):
'''
Resume application and all processes associated with it
'''
for pid in self.get_pids():
if pid in suspended_pids:
logger.debug('Resuming %s (%s)', self.pid, self.name)
suspended_pids.discard(pid)
os.kill(pid, signal.SIGCONT)
return
def suspend_background_apps():
'''
Suspends all apps except app in focus
'''
previous_app = None
while True:
app = Application(NSWorkspace.sharedWorkspace().activeApplication())
if previous_app is None:
previous_app = app
if previous_app and app != previous_app:
previous_app.suspend()
app.resume()
previous_app = app
time.sleep(0.7)
def suspend_apps(app_names):
'''
Suspend apps of given names
:param app_names: {List[String]} list of app names
'''
previous_app = None
while True:
app = Application(NSWorkspace.sharedWorkspace().activeApplication())
if previous_app != app:
logger.debug('Currently focused on %s', app.name)
if app.name in app_names:
app.resume()
if previous_app and previous_app.name in app_names:
previous_app.suspend()
previous_app = app
time.sleep(0.7)
def main():
if len(sys.argv) > 1:
input_app_names = sys.argv[1:]
logger.info('Napping %s', ', '.join(input_app_names))
suspend_apps(input_app_names)
else:
suspend_background_apps()
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print('Resuming all suspended apps')
for pid in suspended_pids:
os.kill(pid, signal.SIGCONT)