diff options
author | Mattias Andrée <maandree@operamail.com> | 2014-03-27 17:23:28 +0100 |
---|---|---|
committer | Mattias Andrée <maandree@operamail.com> | 2014-03-27 17:23:28 +0100 |
commit | 0e012fbed672e723bb4471671bcd2c546ff4a9a6 (patch) | |
tree | 3ba72cbabca760480f68b1c464ee5b5f2e83b81a | |
parent | m doc (diff) | |
download | blueshift-0e012fbed672e723bb4471671bcd2c546ff4a9a6.tar.gz blueshift-0e012fbed672e723bb4471671bcd2c546ff4a9a6.tar.bz2 blueshift-0e012fbed672e723bb4471671bcd2c546ff4a9a6.tar.xz |
m + doc
Signed-off-by: Mattias Andrée <maandree@operamail.com>
-rw-r--r-- | src/adhoc.py | 97 | ||||
-rw-r--r-- | src/aux.py | 30 |
2 files changed, 82 insertions, 45 deletions
diff --git a/src/adhoc.py b/src/adhoc.py index b90418e..8d3a57f 100644 --- a/src/adhoc.py +++ b/src/adhoc.py @@ -15,6 +15,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +# This module contains the implementions of the policies for ad-hoc mode. + import sys import time import signal @@ -26,63 +28,81 @@ if len(parser.files) > 0: print('%s: warning: configuration script arguments are not supported in ad-hoc mode' % sys.argv[0]) ## Determine whether we should run in continuous mode -continuous = any(map(lambda a : (a is not None) and (len(a) == 2), settings)) -continuous = continuous or (location is not None) +continuous = any(map(lambda a : (a is not None) and (len(a) == 2), settings)) or (location is not None) ## Select default settings when not specified d = lambda a, default : [default, default] if a is None else (a * 2 if len(a) == 1 else a) -gammas = d(gammas, "1:1:1") -rgb_brightnesses = d(rgb_brightnesses, "1") -cie_brightnesses = d(cie_brightnesses, "1") +# Set gamma and brightness to 1 (unmodified) if not specified +gammas = d(gammas, '1:1:1') +rgb_brightnesses = d(rgb_brightnesses, '1') +cie_brightnesses = d(cie_brightnesses, '1') if (rgb_temperatures is None) and (cie_temperatures is None): + # If temperature is not specified, set the temperature + # to 3700 K during the day, and 6500 K (neutral) during + # the night. Do not use CIE xyY, hence set cie_temperatures + # to 6500 K (neutral). rgb_temperatures = ['3700', '6500'] cie_temperatures = ['6500', '6500'] else: + # If cie_temperatures is specified but not rgb_temperatures, + # then set rgb_temperatures to 6500 K. But if rgb_temperatures + # is indeed specified but only with one value, duplicate that + # value so that it is used both during day and night. if rgb_temperatures is None: rgb_temperatures = ['6500', '6500'] elif len(rgb_temperatures) == 1: rgb_temperatures *= 2 + # And vice versa. if cie_temperatures is None: cie_temperatures = ['6500', '6500'] elif len(cie_temperatures) == 1: cie_temperatures *= 2 ## Parse string arrays into floating point matrices +# Pack all settings into an unregulare matrix settings = [gammas, rgb_brightnesses, cie_brightnesses, rgb_temperatures, cie_temperatures, [location]] +# Parse into a vector-matrix s = lambda f, v : f(v) if v is not None else None settings = [s(lambda c : [s(lambda x : [float(y) for y in x.split(':')], x) for x in c], c) for c in settings] +# Unpack settings [gammas, rgb_brightnesses, cie_brightnesses, rgb_temperatures, cie_temperatures, location] = settings location = None if location is None else location[0] ## Select method for calculating to what degree the adjustments should be applied +# Assume it is day if not running in continuous mode alpha = lambda : 1 if continuous: if location is not None: + # If we have a geographical location, determine to + # what degree it is day from the Sun's elecation alpha = lambda : sun(*location) else: - def alpha_(): + # If we do not have a location, determine to what + # degree it is day from local time, + def alpha(): + ''' + This algorithm is very crude. + It places 100 % day at 12:00 and 100 % night at + 22:00, and only at those exact time points. + + @return :float [0, 1] floating point of the degree to which it is day + ''' now = datetime.datetime.now() hh, mm = now.hour, now.minute + now.second / 60 if 12 <= hh <= 22: return 1 - (hh - 12) / (22 - 12) - mm / 60 - if hh <= 12: - hh += 22 - 12 - return (hh - 22) / 14 + m / 60 - alpha = alpha_ + return (hh + (10 if hh <= 12 else 0) - 22) / 14 + mm / 60 ## Set monitor control -def reduce(f, items): - ''' - https://en.wikipedia.org/wiki/Fold_(higher-order_function) - ''' - if len(items) < 2: - return items - rc = items[0] - for i in range(1, len(items)): - rc = f(rc, items[i]) - return rc -output = reduce(lambda x, y : x + y, [a.split(',') for a in output]) -monitor_controller = lambda : (drm if ttymode else randr)(*output) +output_ = [] +for o in output: + output_ += o.split(',') +# Use selected CRTC:s (all if none are selected) +# in the first screen or graphics card. +monitor_controller = lambda : (drm if ttymode else randr)(*output_) + +# Interpolation between day and night and between pure and adjusted +interpol_ = lambda d, p, a, r : d * r + (p[0] * a + p[1] * (1 - a)) * (1 - r) def apply(dayness, pureness): ''' @@ -91,36 +111,59 @@ def apply(dayness, pureness): @param dayness:float The visibility of the sun @param pureness:float Transitioning progress, 1 for at clean state, 0 for at adjusted state ''' + # Clean up adjustments from last run start_over() - interpol_ = lambda d, p, a, r : d * r + (p[0] * a + p[1] * (1 - a)) * (1 - r) + # Interpolation between day and night and between pure and adjusted interpol = lambda d, p : [interpol_(d, [p[0][i], p[1][i]], dayness, pureness) for i in range(len(p[0]))] + # Apply temperature adjustment temperature_algorithm = lambda t : clip_whitepoint(divide_by_maximum(cmf_10deg(t))) rgb_temperature(*interpol(6500, rgb_temperatures), algorithm = temperature_algorithm) cie_temperature(*interpol(6500, cie_temperatures), algorithm = temperature_algorithm) + # Apply white point brightness adjustment rgb_brightness(*interpol(1, rgb_brightnesses)) cie_brightness(*interpol(1, cie_brightnesses)) + # Clip values, specifically those under zero to + # avoid complex numbers, and apply gamma correction clip() gamma(*interpol(1, gammas)) + # Clip values to avoid unwanted effects on oversaturation clip() + # Apply settings to all selected monitors monitor_controller() if continuous and not doreset: ## Continuous mode - def periodically(year, month, day, hour, minute, second, weekday, fade): + def periodically(_year, _month, _day, _hour, _minute, _second, _weekday, fade): + ''' + Invoked periodically + + @param fade:float? Blueshift can use this function to fade into a state when it start + or exits. `fade` can either be negative, zero or positive or `None`, + but the magnitude of value cannot exceed 1. When Blueshift starts, + this function will be invoked multiple with the time parameters + of the time it is invoked and each time `fade` will increase towards + 1, starting at 0, when the value is 1, the settings should be applied + to 100 %. After this this function will be invoked once again with + `fade` being `None`. When Blueshift exits the same behaviour is used + except, `fade` decrease towards -1 but start slightly below 0, when + -1 is reached all settings should be normal. Then Blueshift will NOT + invoke this function with `fade` being `None`, instead it will by + itself revert all settings and quit. + ''' apply(alpha(), 0 if fade is None else 1 - abs(fade)) else: ## One shot mode if not panicgate: + ## Fade in to settings signal.signal(signal.SIGTERM, signal_SIGTERM) trans = 0 - while running: + while running and (trans < 1): try: apply(alpha(), trans if doreset else 1 - trans) trans += 0.05 time.sleep(0.1) except KeyboardInterrupt: signal_SIGTERM(0, None) - if trans >= 1: - break + ## Apply or revert settings and exit apply(alpha(), 1 if doreset else 0) @@ -15,6 +15,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +# This module contains auxiliary function. + from curve import * @@ -22,8 +24,8 @@ def translate_to_integers(): ''' Translate the curves from float to integer - @param :(r:list<int>, g:list<int>, b:list<int>) The red curve, the green curve and, - the blue curve mapped to integers + @return :(r:list<int>, g:list<int>, b:list<int>) The red curve, the green curve and, + the blue curve mapped to integers ''' R_curve, G_curve, B_curve = [0] * i_size, [0] * i_size, [0] * i_size for i_curve, o_curve in ((r_curve, R_curve), (g_curve, G_curve), (b_curve, B_curve)): @@ -43,10 +45,8 @@ def ramps_to_function(r, g, b): @param b:list<int> The blue colour curves as [0, 65535] integers @return :()→void Function to invoke to apply the curves that the parameters [r, g and b] represents ''' - r = [y / 65535 for y in r] - g = [y / 65535 for y in g] - b = [y / 65535 for y in b] - return functionise((r, g, b)) + fp = lambda c : [y / 65535 for y in c] + return functionise((fp(r), fp(g), fp(b))) def linearly_interpolate_ramp(r, g, b): # TODO test, demo and document this @@ -59,21 +59,15 @@ def linearly_interpolate_ramp(r, g, b): # TODO test, demo and document this @return :(r:list<float>, g:list<float>, b:list<float>) The input parameters extended to sizes of `o_size`, or their original size, whatever is larger. ''' - R, G, B = None, None, None - R = r[:] if len(r) >= o_size else ([None] * o_size) - G = g[:] if len(g) >= o_size else ([None] * o_size) - B = b[:] if len(b) >= o_size else ([None] * o_size) + C = lambda c : c[:] if len(c) >= o_size else ([None] * o_size) + R, G, B = C(r), C(g), C(b) for small, large in curves(R, G, B): - if len(large) > len(small): - small_ = len(small) - 1 - large_ = len(large) - 1 + small_, large_ = len(small) - 1, len(large) - 1 + if large_ > small_: for i in range(len(large)): j = i * small_ / large_ - j, w = int(j), j % 1 - k = j + 1 - if k > small_: - k = small_ - large[i] = small[j] * (w - 1) + small[k] * w + j, w, k = int(j), j % 1, min(int(j) + 1, small_) + large[i] = small[j] * (1 - w) + small[k] * w return (R, G, B) |