From 1425a1c178d07a68e62c13cdfaf2ec3dd1ffb0f1 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Tue, 11 Mar 2014 03:20:10 +0100 Subject: add threaded example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- Makefile | 2 +- examples/threaded | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 examples/threaded diff --git a/Makefile b/Makefile index 89d4432..5600dff 100644 --- a/Makefile +++ b/Makefile @@ -66,7 +66,7 @@ PYFILES = __main__.py colour.py curve.py monitor.py solar.py icc.py adhoc.py CBINDINGS = $(foreach B,$(SERVER_BINDINGS),blueshift_$(B).so) # Configuration script example files EXAMPLES = comprehensive sleepmode crtc-detection crtc-searching logarithmic \ - xmobar xpybar stored-settings current-settings xmonad + xmobar xpybar stored-settings current-settings xmonad threaded # Build rules diff --git a/examples/threaded b/examples/threaded new file mode 100644 index 0000000..962563a --- /dev/null +++ b/examples/threaded @@ -0,0 +1,200 @@ +# -*- python -*- + +# This example demonstrates how you can make a +# multithreaded configurations script + +import threading + + +# Geographical coodinates. +# (KTH building D computer laboratories in this example.) +latitude, longitude = 59.3472, 18.0728 + +# Adjust settings by solar elevation. +get_dayness = lambda : sun(latitude, longitude) + +# Colour temperature at high day and high night, respectively. +temperature_day, temperature_night = [6500], [3700] + +# Gamma of the monitors. +gamma_red = [1.16, 1.10] +gamma_green = [1.15, 1.16] +gamma_blue = [1.11, 1.10] + + +# Make colour curves thread local +class threadlocal: + def __init__(self, obj): + self.default = obj + self.tmap = {} + + def __getitem__(self, i): + t = threading.current_thread() + if t not in self.tmap: + self.tmap[t] = self.default[:] + return self.tmap[t][i] + + def __len__(self): + t = threading.current_thread() + if t not in self.tmap: + self.tmap[t] = self.default[:] + return len(self.tmap[t]) + + def __setitem__(self, i, x): + t = threading.current_thread() + if t not in self.tmap: + self.tmap[t] = self.default[:] + self.tmap[t][i] = x + + def __delitem__(self, i): + t = threading.current_thread() + if t not in self.tmap: + self.tmap[t] = self.default[:] + del self.tmap[t][x] + +r_curve = threadlocal([i / (i_size - 1) for i in range(i_size)]) +g_curve = threadlocal([i / (i_size - 1) for i in range(i_size)]) +b_curve = threadlocal([i / (i_size - 1) for i in range(i_size)]) + +## cmf_10deg uses non-thread safe cache, run once in advance so it is not done by the threads +cmf_10deg(0) + + +# Thread synchronisation barrier +barrier = threading.Barrier(len(gamma_red) + 1) + +# Help functions for colour interpolation. +interpol, purify = None, None + +# Parameters in `periodically` +fade_ = None + +def adjust(m): + ''' + Adjust monitor colours + + @param m:int The CRTC index + ''' + while True: + # Wait for start cue. + barrier.wait() + + # Calculate temperature. + temperature_ = interpol(temperature_day, temperature_night) + if fade_ is not None: + temperature_ = purify(temperature_, 6500) + + # Remove settings from last run. + start_over() + + # Apply colour temperature using raw CIE 1964 10 degree CMF data with interpolation. + temperature(temperature_, lambda t : divide_by_maximum(cmf_10deg(t))) + + # Clip colour curves to fit [0, 1] to avoid errors by complex numbers. + clip() + + # Apply gamma correction to monitor. + gamma(gamma_red[m], gamma_green[m], gamma_blue[m]) + + # Flush settings to monitor. + randr(m) + + # Signal thread completion. + barrier.wait() + +# Create threads. +for m in range(len(gamma_red)): + thread = threading.Thread(target = adjust, args = (m,)) + thread.setDaemon(True) + thread.start() + +last_dayness = None +def periodically(year, month, day, hour, minute, second, weekday, fade): + ''' + :(int, int, int, int, int, int, int, float?)?→void Place holder for periodically invoked function + + Invoked periodically + + If you want to control at what to invoke this function next time + you can set the value of the global variable `wait_period` to the + number of seconds to wait before invoking this function again. + The value does not need to be an integer. + + @param year:int The year + @param month:int The month, 1 = January, 12 = December + @param day:int The day, minimum value is 1, probable maximum value is 31 (*) + @param hour:int The hour, minimum value is 0, maximum value is 23 + @param minute:int The minute, minimum value is 0, maximum value is 59 + @param second:int The second, minimum value is 0, probable maximum value is 60 (**) + @param weekday:int The weekday, 1 = Monday, 7 = Sunday + @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. + + (*) Can be exceeded if the calendar system is changed, like in 1712-(02)Feb-30 + (**) See https://en.wikipedia.org/wiki/Leap_second + ''' + global last_dayness, wait_period, interpol, purify, fade_ + + dayness = get_dayness() + # Do not do unnecessary work. + if fade is None: + if dayness == last_dayness: + return + last_dayness = dayness + + # Pass parameters to threads. + fade_ = fade + + # Help functions for colour interpolation. + interpol = lambda _day, _night : _day[m % len(_day)] * dayness + _night[m % len(_night)] * (1 - dayness) + purify = lambda current, pure : current * abs(fade) + pure * (1 - abs(fade)) + + # Signal all threads to start. + barrier.wait() + + # Wait for all threads. + barrier.wait() + + +def reset(): + ''' + Invoked to reset the displays + ''' + for m in range(len(gamma_red)): + # Remove settings from last run. + start_over() + + # Apply gamma correction to monitor. + gamma(gamma_red[m], gamma_green[m], gamma_blue[m]) + + # Flush settings to monitor. + randr(m) + + +# Set transition time, 0 on high day and 5 seconds on high night. +fadein_time = 5 * (1 - get_dayness()) +# Do 10 changes per second. +fadein_steps = fadein_time * 10 + +# Transition on exit in the same way, calculated on exit. +old_signal_SIGTERM = signal_SIGTERM +if 'SIGTERM' not in conf_storage: + conf_storage['SIGTERM'] = old_signal_SIGTERM +else: + old_signal_SIGTERM = conf_storage['SIGTERM'] +def signal_SIGTERM(signum, frame): + global fadeout_time, fadeout_steps + fadeout_time = 5 * (1 - get_dayness()) + fadeout_steps = fadeout_time * 10 + old_signal_SIGTERM(signum, frame) + -- cgit v1.2.3-70-g09d2