summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xsrc/__main__.py117
1 files changed, 107 insertions, 10 deletions
diff --git a/src/__main__.py b/src/__main__.py
index 40777dc..a3ba573 100755
--- a/src/__main__.py
+++ b/src/__main__.py
@@ -25,6 +25,7 @@ import threading
from argparser import *
+
PROGRAM_NAME = 'blueshift'
'''
:str The name of the program
@@ -70,6 +71,12 @@ global monitor_controller, running, continuous_run, panic, _globals_, conf_stora
global signal_SIGTERM, signal_SIGUSR1, signal_SIGUSR2, DATADIR, LIBDIR, LIBEXECDIR
+## Open all modules that the configuration
+## scripts may want to use so that they can
+## be used without knowing the name of the
+## module. After all, we may want to move
+## functions are around without scripts
+## breaking on us.
from aux import *
from icc import *
from solar import *
@@ -196,13 +203,26 @@ sleep_condition = threading.Condition()
:Condition Condition used to make interruptable sleeps
'''
+trans_delta = -1
+'''
+:int In what direction are with transitioning?
+'''
+
+
+## Combine our globals and locals for the
+## configuration script to use
+_globals_, _locals_ = globals(), dict(locals())
+for key in _locals_:
+ _globals_[key] = _locals_[key]
def reset():
'''
Invoked to reset the displays
'''
+ # Reset colour curves
start_over()
+ # and flush adjustments
monitor_controller()
@@ -211,9 +231,12 @@ def signal_SIGALRM(signum, frame):
'''
Signal handler for SIGALRM
+ This is to time out interruptable sleeps
+
@param signum The signal number, 0 if called from the program itself
@param frame Ignore, it will probably be `None`
'''
+ # Break any sleep
with sleep_condition:
sleep_condition.notify()
@@ -222,50 +245,76 @@ def signal_SIGTERM(signum, frame):
'''
Signal handler for SIGTERM
+ This is used to exit the program cleanly
+
@param signum The signal number, 0 if called from the program itself
@param frame Ignore, it will probably be `None`
'''
global trans_delta, panic, running
+ # Request that the program should exit
running = False
+ # If we are already fading into clean adjustments,
+ # probably because we have already got a request,
+ # but perhaps because we are beginning to temporarily
+ # disable the program:
if trans_delta > 0:
+ # Request that the program exit immediate,
+ # but first clear adjustments.
panic = True
+ # Request fading into clean adjustmetns
trans_delta = 1
+ # Break any sleep
with sleep_condition:
sleep_condition.notify()
-_globals_, _locals_ = globals(), dict(locals())
-for key in _locals_:
- _globals_[key] = _locals_[key]
def signal_SIGUSR1(signum, frame):
'''
Signal handler for SIGUSR1
+ This is used to reload configuration scripts
+
@param signum The signal number, 0 if called from the program itself
@param frame Ignore, it will probably be `None`
'''
code = None
+ # Open the configuration script file,
with open(config_file, 'rb') as script:
+ # and read it.
code = script.read()
+ # Decode it, assume it is in UTF-8, and append
+ # an line ending in case the the last line is
+ # not empty, which would give us an exception.
code = code.decode('utf8', 'error') + '\n'
+ # Compile the script
code = compile(code, config_file, 'exec')
+ # And run it, with it have the same
+ # globals as this module, so that it can
+ # not only use want we have defined, but
+ # also redefine it for us.
exec(code, _globals_)
-trans_delta = -1
def signal_SIGUSR2(signum, frame):
'''
Signal handler for SIGUSR2
+ This is used to temporarily disable, and
+ enable from such disabling of, the program
+
@param signum The signal number, 0 if called from the program itself
@param frame Ignore, it will probably be `None`
'''
global trans_delta, panicgate
+ # Do no longer skip fadein transitions
panicgate = False
if trans_delta == 0:
+ # Fade out if not already fading
trans_delta = 1
else:
+ # Otherwise reverse the direction of the transition
trans_delta = -trans_delta
+ # Break any sleep
with sleep_condition:
sleep_condition.notify()
@@ -274,42 +323,90 @@ def continuous_run():
'''
Invoked to run continuously if `periodically` is not `None`
'''
- global running, wait_period, fadein_time, fadeout_time, fadein_steps, fadeout_steps, trans_delta, p, sleep, panic
+ global running, wait_period, fadein_time, fadeout_time
+ global fadein_steps, fadeout_steps, trans_delta, p, sleep, panic
+
def p(t, fade = None):
+ '''
+ Refrest the adjustments
+
+ @param :datetime The current local time
+ @param :float? The transition state, see specifications for `periodically`
+ '''
try:
+ # Extract the current weekday,
wd = t.isocalendar()[2]
+ # and invoke the function used to refresh adjustments.
periodically(t.year, t.month, t.day, t.hour, t.minute, t.second, wd, fade)
except KeyboardInterrupt:
+ # Emulate `kill -TERM` on Control+c
signal_SIGTERM(0, None)
def sleep(seconds):
+ '''
+ Delay execution for a given number of seconds,
+ or until it is request that we stop sleeping.
+
+ @param seconds:float The number of seconds to sleep
+ '''
+ # Sleep only if the sleep duration is existent
if not seconds == 0:
try:
with sleep_condition:
+ # Set a sleep timer,
signal.setitimer(signal.ITIMER_REAL, seconds)
+ # and with for it or for something else to
+ # request that we stop sleeping.
sleep_condition.wait()
except KeyboardInterrupt:
+ # Emulate `kill -TERM` on Control+c
signal_SIGTERM(0, None)
except:
try:
- time.sleep(seconds) # setitimer may not be supported
+ # setitimer may not be supported,
+ # in such case, use a regular sleep
+ time.sleep(seconds)
except KeyboardInterrupt:
+ # Emulate `kill -TERM` on Control+c
signal_SIGTERM(0, None)
def now():
+ '''
+ Get the current local time
+
+ This wrapping is done because keyboard interruptions
+ affect `datetime.datetime.now`
+
+ @return :datetime The current local time (respects summer time)
+ '''
+ # Retry at any time we get a keyboard interruption
while True:
try:
+ # Get the current local time (respects summer time)
return datetime.datetime.now()
except KeyboardInterrupt:
+ # Emulate `kill -TERM` on Control+c
signal_SIGTERM(0, None)
## Catch signals
def signal_(sig, fun):
+ '''
+ Trap a signal if the signal is supported by the operating system
+
+ @param sig:int The signal
+ @param fun:(signal:int, framestack:)→void The function to run then the signal is trapped
+ '''
try:
+ # Set up the signal trapping
signal.signal(sig, fun)
except ValueError:
- pass # not supported by the operating system
+ # Not supported by the operating system
+ pass
+ # Signal for stopping the program
signal_(signal.SIGTERM, signal_SIGTERM)
+ # Signal for reloading the configuration script
signal_(signal.SIGUSR1, signal_SIGUSR1)
+ # Signal for temporarily disable/enable the program
signal_(signal.SIGUSR2, signal_SIGUSR2)
+ # Signal used for the interruptable sleeps
signal_(signal.SIGALRM, signal_SIGALRM)
## Create initial transition
@@ -378,7 +475,7 @@ parser = ArgParser('Colour temperature controller',
'Blueshift adjusts the colour temperature of your\n'
'monitor according to brightness outside to reduce\n'
'eye strain and make it easier to fall asleep when\n'
- 'going to bed. IT can also be used to increase the\n'
+ 'going to bed. It can also be used to increase the\n'
'colour temperature and make the monitor bluer,\n'
'this helps you focus on your work.',
None, True, ArgParser.standard_abbreviations())
@@ -466,7 +563,7 @@ if (config_file is None) and any([doreset, location] + settings):
import zipimport
importer = zipimport.zipimporter(sys.argv[0])
code = importer.get_data('adhoc.py')
- pathname = sys.argv[0] + '/adhoc.py'
+ pathname = sys.argv[0] + os.sep + 'adhoc.py'
code = code.decode('utf-8', 'error') + '\n'
code = compile(code, pathname, 'exec')
exec(code, g)
@@ -474,7 +571,7 @@ else:
## Load extension and configurations via blueshiftrc
if config_file is None:
for file in ('$XDG_CONFIG_HOME/%/%rc', '$HOME/.config/%/%rc', '$HOME/.%rc', '$~/.config/%/%rc', '$~/.%c', '/etc/%rc'):
- file = file.replace('%', 'blueshift')
+ file = file.replace('/', os.sep).replace('%', 'blueshift')
for arg in ('XDG_CONFIG_HOME', 'HOME'):
if '$' + arg in file:
if arg in os.environ: