Spaces:
No application file
No application file
#----------------------------------------------------------------------------- | |
# Copyright (C) 2013 Min RK | |
# | |
# Distributed under the terms of the 2-clause BSD License. | |
#----------------------------------------------------------------------------- | |
from contextlib import contextmanager | |
import ctypes | |
import ctypes.util | |
objc = ctypes.cdll.LoadLibrary(ctypes.util.find_library('objc')) | |
_ = ctypes.cdll.LoadLibrary(ctypes.util.find_library('Foundation')) | |
void_p = ctypes.c_void_p | |
ull = ctypes.c_uint64 | |
objc.objc_getClass.restype = void_p | |
objc.sel_registerName.restype = void_p | |
objc.objc_msgSend.restype = void_p | |
objc.objc_msgSend.argtypes = [void_p, void_p] | |
msg = objc.objc_msgSend | |
def _utf8(s): | |
"""ensure utf8 bytes""" | |
if not isinstance(s, bytes): | |
s = s.encode('utf8') | |
return s | |
def n(name): | |
"""create a selector name (for methods)""" | |
return objc.sel_registerName(_utf8(name)) | |
def C(classname): | |
"""get an ObjC Class by name""" | |
ret = objc.objc_getClass(_utf8(classname)) | |
assert ret is not None, "Couldn't find Class %s" % classname | |
return ret | |
# constants from Foundation | |
NSActivityIdleDisplaySleepDisabled = (1 << 40) | |
NSActivityIdleSystemSleepDisabled = (1 << 20) | |
NSActivitySuddenTerminationDisabled = (1 << 14) | |
NSActivityAutomaticTerminationDisabled = (1 << 15) | |
NSActivityUserInitiated = (0x00FFFFFF | NSActivityIdleSystemSleepDisabled) | |
NSActivityUserInitiatedAllowingIdleSystemSleep = (NSActivityUserInitiated & ~NSActivityIdleSystemSleepDisabled) | |
NSActivityBackground = 0x000000FF | |
NSActivityLatencyCritical = 0xFF00000000 | |
def beginActivityWithOptions(options, reason=""): | |
"""Wrapper for: | |
[ [ NSProcessInfo processInfo] | |
beginActivityWithOptions: (uint64)options | |
reason: (str)reason | |
] | |
""" | |
NSProcessInfo = C('NSProcessInfo') | |
NSString = C('NSString') | |
objc.objc_msgSend.argtypes = [void_p, void_p, void_p] | |
reason = msg(NSString, n("stringWithUTF8String:"), _utf8(reason)) | |
objc.objc_msgSend.argtypes = [void_p, void_p] | |
info = msg(NSProcessInfo, n('processInfo')) | |
objc.objc_msgSend.argtypes = [void_p, void_p, ull, void_p] | |
activity = msg(info, | |
n('beginActivityWithOptions:reason:'), | |
ull(options), | |
void_p(reason) | |
) | |
return activity | |
def endActivity(activity): | |
"""end a process activity assertion""" | |
NSProcessInfo = C('NSProcessInfo') | |
objc.objc_msgSend.argtypes = [void_p, void_p] | |
info = msg(NSProcessInfo, n('processInfo')) | |
objc.objc_msgSend.argtypes = [void_p, void_p, void_p] | |
msg(info, n("endActivity:"), void_p(activity)) | |
_theactivity = None | |
def nope(): | |
"""disable App Nap by setting NSActivityUserInitiatedAllowingIdleSystemSleep""" | |
global _theactivity | |
_theactivity = beginActivityWithOptions( | |
NSActivityUserInitiatedAllowingIdleSystemSleep, | |
"Because Reasons" | |
) | |
def nap(): | |
"""end the caffeinated state started by `nope`""" | |
global _theactivity | |
if _theactivity is not None: | |
endActivity(_theactivity) | |
_theactivity = None | |
def napping_allowed(): | |
"""is napping allowed?""" | |
return _theactivity is None | |
def nope_scope( | |
options=NSActivityUserInitiatedAllowingIdleSystemSleep, | |
reason="Because Reasons" | |
): | |
"""context manager for beginActivityWithOptions. | |
Within this context, App Nap will be disabled. | |
""" | |
activity = beginActivityWithOptions(options, reason) | |
try: | |
yield | |
finally: | |
endActivity(activity) | |
__all__ = [ | |
"NSActivityIdleDisplaySleepDisabled", | |
"NSActivityIdleSystemSleepDisabled", | |
"NSActivitySuddenTerminationDisabled", | |
"NSActivityAutomaticTerminationDisabled", | |
"NSActivityUserInitiated", | |
"NSActivityUserInitiatedAllowingIdleSystemSleep", | |
"NSActivityBackground", | |
"NSActivityLatencyCritical", | |
"beginActivityWithOptions", | |
"endActivity", | |
"nope", | |
"nap", | |
"napping_allowed", | |
"nope_scope", | |
] | |