summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/adhoc.py97
-rw-r--r--src/aux.py30
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)
diff --git a/src/aux.py b/src/aux.py
index b7a83f8..a21ab5d 100644
--- a/src/aux.py
+++ b/src/aux.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 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)