Skip to content

Commit

Permalink
now supports multiple apps for napping
Browse files Browse the repository at this point in the history
  • Loading branch information
omikun committed Mar 5, 2018
1 parent 2368389 commit f8b1589
Showing 1 changed file with 126 additions and 56 deletions.
182 changes: 126 additions & 56 deletions RumpMyNap.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import subprocess
import os
import signal
import logging
import rumps
import logging
from threading import Thread
Expand All @@ -18,13 +19,27 @@
sys.exit(1)

SUSPENDED = set() #set of PIDs that has been suspended
DONT_SUSPEND_NAME = ('Terminal', 'Activity Monitor') #set of apps to never suspend/resume
DONT_SUSPEND_NAME = ('iTerm2', 'Terminal', 'Activity Monitor') #set of apps to never suspend/resume

sucky_app_names = set()
last_sucky_app_names = set()
settings_updated = [False]
# if user changes one or more settings in 1 tick:
# in next tick
# unsuspend everything not selected, suspend everything selected (except active app)
nap_this_app = None
menuStates = {}
launchedApps = None

launchedApps = NSWorkspace.sharedWorkspace().launchedApplications()
appNames = [ app['NSApplicationName'] for app in launchedApps ]
print(appNames)
desiredApp = None
teststring = "hello world"
def init_logger():
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)
return logger

def name_of(app):
if app is None:
Expand All @@ -35,50 +50,58 @@ def name_of(app):
app_name = app_name.encode("utf8", "ignore")
return app_name

menuStates = {}
def update_state(adding, appName):
settings_updated[0] = True
print("setting updated!!!!!")
if adding:
sucky_app_names.add(appName)
else:
sucky_app_names.discard(appName)


def clearOtherStates(appName):
'only used for keeping only 1 app monitored at a time'
for k, v in menuStates.items():
if k == appName:
continue
v.state = False
rumpsClass = \

def init_bar(launchedApps):
rumpsClass = \
'''class AwesomeStatusBarApp(rumps.App):
'''
menuItemString =\
menuItemString =\
''' @rumps.clicked(%s)
def onoff%d(self, sender):
global desiredApp
sender.state = not sender.state
appName = %s
print(\'clicked on \',appName)
update_state(sender.state, appName)
if sender.state:
desiredApp = appName
menuStates[appName] = sender
clearOtherStates(appName)
print(desiredApp)
# clearOtherStates(appName)
'''
quit_menu =\
''' @rumps.clicked('my quit')
quit_menu =\
''' @rumps.clicked('SAFE QUIT')
def myquit(self, sender):
print(\'my own quiting\')
for pid in SUSPENDED:
os.kill(int(pid), signal.SIGCONT)
print(\'Quiting with cleanup\')
clean_exit()
rumps.quit_application()
'''
for i, launchedApp in enumerate(launchedApps):
if name_of(launchedApp) in DONT_SUSPEND_NAME:
continue
#appStr = 'launchedApp[%d]' % i
appNameStr = '\'%s\'' % name_of(launchedApp)
rumpsClass += menuItemString % (appNameStr, i, appNameStr)

rumpsClass += quit_menu
#print(rumpsClass)
exec(rumpsClass)
for i, launchedApp in enumerate(launchedApps):
if name_of(launchedApp) in DONT_SUSPEND_NAME:
continue
#appStr = 'launchedApp[%d]' % i
appNameStr = '\'%s\'' % name_of(launchedApp)
rumpsClass += menuItemString % (appNameStr, i, appNameStr)

rumpsClass += quit_menu
return rumpsClass

def start_bar():
AwesomeStatusBarApp("RMN").run()

def clean_exit():
for pid in SUSPENDED:
os.kill(int(pid), signal.SIGCONT)

Expand All @@ -94,36 +117,83 @@ def get_pids(app):
pass
return pids

def otherThread():
def suspend(prev_app):
if name_of(prev_app) in DONT_SUSPEND_NAME:
print(name_of(prev_app) + ' not suspended, in dont suspend list')
return
pids = get_pids(prev_app)
logger.debug('Suspending %s (%s)', pids, name_of(prev_app))
for pid in pids:
SUSPENDED.add(pid)
os.kill(int(pid), signal.SIGSTOP)


def resume(app):
'Resume apps that have been suspended and arent on the do not suspend list'
if name_of(app) in DONT_SUSPEND_NAME:
print(name_of(app) + ' not resumed, in dont suspend list')
return
pids = get_pids(app)
for pid in pids:
if pid in SUSPENDED:
break
else:
return
# only resume pids that are suspended
logger.debug('Resuming %s (%s)', pids, name_of(app))
for pid in pids:
SUSPENDED.discard(pid)
os.kill(int(pid), signal.SIGCONT)
for pid in pids:
os.kill(int(pid), signal.SIGCONT)

def on_update_settings(launchedApps, cur_app):
global last_sucky_app_names
settings_updated[0] = False
# resume all apps
# suspend all sucky_app_names
print("updating settings:")
new_sucky = sucky_app_names - last_sucky_app_names
not_sucky = last_sucky_app_names - sucky_app_names
print(sucky_app_names)
print(last_sucky_app_names)
last_sucky_app_names = set(sucky_app_names)
for l_app in launchedApps:
if l_app == cur_app:
print(name_of(l_app), "is current app, skipping")
continue
if name_of(l_app) in new_sucky:
suspend(l_app)
if name_of(l_app) in not_sucky:
resume(l_app)

def my_app_nap():
prev_app = None
while True:
app = NSWorkspace.sharedWorkspace().activeApplication()
if prev_app != app:
print(app['NSApplicationName'])
print('checking if %s is %s' % (app['NSApplicationName'], desiredApp))
if app['NSApplicationName'] == desiredApp:
print("Continuing ", app['NSApplicationName'])
pids = get_pids(app)
for pid in pids:
os.kill(int(pid), signal.SIGCONT)
for pid in pids:
os.kill(int(pid), signal.SIGCONT)
if prev_app and prev_app['NSApplicationName'] == desiredApp:
print("Stopping ", prev_app['NSApplicationName'])
pids = get_pids(prev_app)
for pid in pids:
SUSPENDED.add(pid)
os.kill(int(pid), signal.SIGSTOP)
prev_app = app
time.sleep(1)
cur_app = NSWorkspace.sharedWorkspace().activeApplication()
if settings_updated[0]:
print("settings update detected in my_app_nap()")
on_update_settings(launchedApps, cur_app)
if prev_app != cur_app:
if cur_app['NSApplicationName'] in sucky_app_names:
resume(cur_app)
if prev_app and prev_app['NSApplicationName'] in sucky_app_names:
suspend(prev_app)
prev_app = cur_app
time.sleep(0.5)

if __name__ == '__main__':
thread = Thread(target=otherThread)
thread.start()
start_bar()
thread.join()
print('\nResuming all suspended apps')
for pid in SUSPENDED:
os.kill(int(pid), signal.SIGCONT)

try:
# TODO precaution: resume all launched apps in case last shutdown left some apps hanging
logger = init_logger()
launchedApps = NSWorkspace.sharedWorkspace().launchedApplications()
exec(init_bar(launchedApps))
thread = Thread(target=my_app_nap)
thread.start()
start_bar()
thread.join()
except KeyboardInterrupt:
print('\nResuming all suspended apps')
for pid in SUSPENDED:
os.kill(int(pid), signal.SIGCONT)

0 comments on commit f8b1589

Please sign in to comment.