summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMattias Andrée <m@maandree.se>2024-10-05 23:38:34 +0200
committerMattias Andrée <m@maandree.se>2024-10-05 23:38:34 +0200
commite2bf827ecc9e54b321c2febfe3664ec0428bc8fc (patch)
tree5f0a6fc7aed31fd59a9d5e62e9248edd3f0cbabe /src
parentUpdate e-mail (diff)
downloadblueshift-e2bf827ecc9e54b321c2febfe3664ec0428bc8fc.tar.gz
blueshift-e2bf827ecc9e54b321c2febfe3664ec0428bc8fc.tar.bz2
blueshift-e2bf827ecc9e54b321c2febfe3664ec0428bc8fc.tar.xz
Update e-mail
Signed-off-by: Mattias Andrée <m@maandree.se>
Diffstat (limited to 'src')
-rwxr-xr-xsrc/__main__.py9
-rw-r--r--src/adhoc.py2
-rw-r--r--src/aux.py2
-rw-r--r--src/backlight.py4
-rw-r--r--src/blackbody.py2
-rw-r--r--src/blueshift_iccprofile.c2
-rw-r--r--src/colour.py2
-rw-r--r--src/completion2
-rw-r--r--src/curve.py37
-rw-r--r--src/icc.py2
-rw-r--r--src/interpolation.py122
-rw-r--r--src/libgammaman.py119
-rw-r--r--src/monitor.py236
-rw-r--r--src/output.py877
-rw-r--r--src/solar.py2
-rw-r--r--src/weather.py4
16 files changed, 1024 insertions, 400 deletions
diff --git a/src/__main__.py b/src/__main__.py
index 850992c..b76927f 100755
--- a/src/__main__.py
+++ b/src/__main__.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
copyright = '''
-Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (maandree@kth.se)
+Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (m@maandree.se)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
@@ -38,7 +38,7 @@ PROGRAM_NAME = 'blueshift'
:str The name of the program
'''
-PROGRAM_VERSION = '1.90.1'
+PROGRAM_VERSION = '1.91'
'''
:str The version of the program
'''
@@ -439,7 +439,7 @@ def continuous_run():
with_fadein = lambda : (fadein_steps > 0) and (fadein_time is not None) and not panicgate
with_fadeout = lambda : (fadeout_steps > 0) and (fadeout_time is not None)
- try: # TODO why does it look like there is a small jump at the beginning?
+ try:
## Run until we get a signal to exit
# When the program start we are fading in,
# than we run in normal periodical state.
@@ -741,6 +741,3 @@ else:
if periodically is not None:
continuous_run()
-## Close C bindings to free resource and close connections
-close_c_bindings()
-
diff --git a/src/adhoc.py b/src/adhoc.py
index 517aca8..cea65be 100644
--- a/src/adhoc.py
+++ b/src/adhoc.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (maandree@kth.se)
+# Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (m@maandree.se)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/src/aux.py b/src/aux.py
index 71c0cce..bc0f361 100644
--- a/src/aux.py
+++ b/src/aux.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (maandree@kth.se)
+# Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (m@maandree.se)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/src/backlight.py b/src/backlight.py
index cde0ef5..f4a30d6 100644
--- a/src/backlight.py
+++ b/src/backlight.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (maandree@kth.se)
+# Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (m@maandree.se)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -94,6 +94,6 @@ class Backlight:
file.write(('%i\n' % (value + self.__minimum)).encode('utf-8'))
file.flush()
else:
- cmd = ['adjbacklight', self.__controller, '--set', str(value + self.__minimum)]
+ cmd = ['adjbacklight', '-s', str(value + self.__minimum), self.__controller]
Popen(cmd, stdout = sys.stdout, stderr = sys.stderr).wait()
diff --git a/src/blackbody.py b/src/blackbody.py
index 923fef3..6f9b687 100644
--- a/src/blackbody.py
+++ b/src/blackbody.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (maandree@kth.se)
+# Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (m@maandree.se)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
diff --git a/src/blueshift_iccprofile.c b/src/blueshift_iccprofile.c
index aed4fd4..806f47b 100644
--- a/src/blueshift_iccprofile.c
+++ b/src/blueshift_iccprofile.c
@@ -1,5 +1,5 @@
/**
- * Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (maandree@kth.se)
+ * Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (m@maandree.se)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/src/colour.py b/src/colour.py
index 737fa39..4adca8d 100644
--- a/src/colour.py
+++ b/src/colour.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (maandree@kth.se)
+# Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (m@maandree.se)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
diff --git a/src/completion b/src/completion
index c67a48d..05af4c4 100644
--- a/src/completion
+++ b/src/completion
@@ -1,4 +1,4 @@
-; Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (maandree@kth.se)
+; Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (m@maandree.se)
;
; This program is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
diff --git a/src/curve.py b/src/curve.py
index d1f35cd..e1fcef0 100644
--- a/src/curve.py
+++ b/src/curve.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (maandree@kth.se)
+# Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (m@maandree.se)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
@@ -15,8 +15,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-# This module contains the colour curve definitions and functions for
-# manipulating the colour curves
+# This module is deprecated!
import math
@@ -252,9 +251,9 @@ def gamma(r, g = ..., b = ...):
'''
Apply gamma correction on the colour curves
- @param r:float The gamma parameter for the red curve
- @param g:float|... The gamma parameter for the green curve, defaults to `r` if `...`
- @param b:float|... The gamma parameter for the blue curve, defaults to `g` if `...`
+ @param r:float The gamma parameter for the red colour curve
+ @param g:float|... The gamma parameter for the green colour curve, defaults to `r` if `...`
+ @param b:float|... The gamma parameter for the blue colour curve, defaults to `g` if `...`
'''
# Handle overloading
if g is ...: g = r
@@ -270,9 +269,9 @@ def negative(r = True, g = ..., b = ...):
'''
Reverse the colour curves (negative image with gamma preservation)
- @param r:bool Whether to invert the red curve
- @param g:bool|... Whether to invert the green curve, defaults to `r` if `...`
- @param b:bool|... Whether to invert the blue curve, defaults to `g` if `...`
+ @param r:bool Whether to invert the red colour curve
+ @param g:bool|... Whether to invert the green colour curve, defaults to `r` if `...`
+ @param b:bool|... Whether to invert the blue colour curve, defaults to `g` if `...`
'''
# Handle overloading
if g is ...: g = r
@@ -288,9 +287,9 @@ def rgb_invert(r = True, g = ..., b = ...):
'''
Invert the colour curves (negative image with gamma invertion), using sRGB
- @param r:bool Whether to invert the red curve
- @param g:bool|... Whether to invert the green curve, defaults to `r` if `...`
- @param b:bool|... Whether to invert the blue curve, defaults to `g` if `...`
+ @param r:bool Whether to invert the red colour curve
+ @param g:bool|... Whether to invert the green colour curve, defaults to `r` if `...`
+ @param b:bool|... Whether to invert the blue colour curve, defaults to `g` if `...`
'''
# Handle overloading
if g is ...: g = r
@@ -306,9 +305,9 @@ def cie_invert(r = True, g = ..., b = ...):
'''
Invert the colour curves (negative image with gamma invertion), using CIE xyY
- @param r:bool Whether to invert the red curve
- @param g:bool|... Whether to invert the green curve, defaults to `r` if `...`
- @param b:bool|... Whether to invert the blue curve, defaults to `g` if `...`
+ @param r:bool Whether to invert the red colour curve
+ @param g:bool|... Whether to invert the green colour curve, defaults to `r` if `...`
+ @param b:bool|... Whether to invert the blue colour curve, defaults to `g` if `...`
'''
# Handle overloading
if g is ...: g = r
@@ -336,9 +335,9 @@ def sigmoid(r, g = ..., b = ...):
only way to adjust the black point on many LCD
monitors.
- @param r:float? The sigmoid parameter for the red curve
- @param g:float|...? The sigmoid parameter for the green curve, defaults to `r` if `...`
- @param b:float|...? The sigmoid parameter for the blue curve, defaults to `g` if `...`
+ @param r:float? The sigmoid parameter for the red colour curve
+ @param g:float|...? The sigmoid parameter for the green colour curve, defaults to `r` if `...`
+ @param b:float|...? The sigmoid parameter for the blue colour curve, defaults to `g` if `...`
'''
# Handle overloading
if g is ...: g = r
@@ -546,7 +545,7 @@ def start_over():
def clip(r = True, g = ..., b = ...):
'''
- Clip all values below the actual minimum and above actual maximums
+ Clip all values below the actual minimum and above the actual maximum
@param r:bool Whether to clip the red colour curve
@param g:bool|... Whether to clip the green colour curve, defaults to `r` if `...`
diff --git a/src/icc.py b/src/icc.py
index de8a991..eb20888 100644
--- a/src/icc.py
+++ b/src/icc.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (maandree@kth.se)
+# Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (m@maandree.se)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
diff --git a/src/interpolation.py b/src/interpolation.py
index d67c3bd..e3f6444 100644
--- a/src/interpolation.py
+++ b/src/interpolation.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (maandree@kth.se)
+# Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (m@maandree.se)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,34 +20,67 @@
from aux import *
from curve import *
+# TODO doc: size parameter has been added
-def linearly_interpolate_ramp(r, g, b):
+
+def __interpolate(r, g, b, size, interpolate, decimate = None): ## TODO document
+ if decimate is None:
+ decimate = lambda orig, out : __decimate(orig, out, interpolate)
+ if size is None:
+ size = (max(o_size, len(r)), max(o_size, len(g)), max(o_size, len(b)))
+ elif isinstance(size, int):
+ size = (size, size, size)
+ if len(r) == size[0]:
+ r = r[:]
+ elif len(r) > size[0]:
+ r = interpolate(r, [None] * size[0])
+ else:
+ r = decimate(r, [None] * size[0])
+ if len(g) == size[1]:
+ g = g[:]
+ elif len(g) > size[1]:
+ g = interpolate(g, [None] * size[1])
+ else:
+ g = decimate(g, [None] * size[1])
+ if len(b) == size[0]:
+ b = b[:]
+ elif len(b) > size[0]:
+ b = interpolate(b, [None] * size[2])
+ else:
+ b = decimate(b, [None] * size[2])
+ return (r, g, b)
+
+
+def __decimate(orig, out, interpolate): ## TODO document
+ pass # TODO
+
+
+def linearly_interpolate_ramp(r, g, b, size = None):
'''
Linearly interpolate ramps to the size of the output axes
@param r:list<float> The red colour curves
@param g:list<float> The green colour curves
@param b:list<float> The blue colour curves
- @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.
+ @param size:int|(r:int, g:int, b:int)? Either the size of all output ramps, the size
+ if the output ramps individually, or `None` for
+ whichever is larger of`o_size` and the size of
+ the input ramps
+ @return :(r:list<float>, g:list<float>, b:list<float>) The input ramps extended to the choosen 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 ((r, R), (g, G), (b, B)):
- small_, large_ = len(small) - 1, len(large) - 1
- # Only interpolate if scaling up
- if large_ > small_:
- for i in range(len(large)):
- # Scaling
- j = i * small_ / large_
- # Floor, weight, ceiling
- j, w, k = int(j), j % 1, min(int(j) + 1, small_)
- # Interpolation
- large[i] = small[j] * (1 - w) + small[k] * w
- return (R, G, B)
+ def interpolate(orig, out):
+ orig_, out_ = len(orig) - 1, len(out) - 1
+ for i in range(len(out)):
+ # Scaling
+ j = i * orig_ / out_
+ # Floor, weight, ceiling
+ j, w, k = int(j), j % 1, min(int(j) + 1, orig_)
+ # Interpolation
+ out[i] = orig[j] * (1 - w) + orig[k] * w
+ return __interpolate(r, g, b, size, interpolate)
-def cubicly_interpolate_ramp(r, g, b, tension = 0):
+def cubicly_interpolate_ramp(r, g, b, tension = 0, size = None):
'''
Interpolate ramps to the size of the output axes using cubic Hermite spline
@@ -55,11 +88,18 @@ def cubicly_interpolate_ramp(r, g, b, tension = 0):
@param g:list<float> The green colour curves
@param b:list<float> The blue colour curves
@param tension:float A [0, 1] value of the tension
- @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.
+ @param size:int|(r:int, g:int, b:int)? Either the size of all output ramps, the size
+ if the output ramps individually, or `None` for
+ whichever is larger of`o_size` and the size of
+ the input ramps
+ @return :(r:list<float>, g:list<float>, b:list<float>) The input ramps extended to the choosen size
'''
- C = lambda c : c[:] if len(c) >= o_size else ([None] * o_size)
- R, G, B = C(r), C(g), C(b)
+ if size is None:
+ size = (max(o_size, len(r)), max(o_size, len(g)), max(o_size, len(b)))
+ elif isinstance(size, int):
+ size = (size, size, size)
+ C = lambda c, i : c[:] if len(c) >= size[i] else ([None] * size[i])
+ R, G, B = C(r, 0), C(g, 1), C(b, 2)
# Basis functions
#h00 = lambda t : (1 + 2 * t) * (1 - t) ** 2
h10 = lambda t : t * (1 - t) ** 2
@@ -101,7 +141,7 @@ def cubicly_interpolate_ramp(r, g, b, tension = 0):
return (R, G, B)
-def monotonicly_cubicly_interpolate_ramp(r, g, b, tension = 0):
+def monotonicly_cubicly_interpolate_ramp(r, g, b, tension = 0, size = None):
'''
Interpolate ramps to the size of the output axes using
monotone cubic Hermite spline and the Fritsch–Carlson method
@@ -113,11 +153,18 @@ def monotonicly_cubicly_interpolate_ramp(r, g, b, tension = 0):
@param g:list<float> The green colour curves
@param b:list<float> The blue colour curves
@param tension:float A [0, 1] value of the tension
- @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.
+ @param size:int|(r:int, g:int, b:int)? Either the size of all output ramps, the size
+ if the output ramps individually, or `None` for
+ whichever is larger of`o_size` and the size of
+ the input ramps
+ @return :(r:list<float>, g:list<float>, b:list<float>) The input ramps extended to the choosen size
'''
- C = lambda c : c[:] if len(c) >= o_size else ([None] * o_size)
- R, G, B = C(r), C(g), C(b)
+ if size is None:
+ size = (max(o_size, len(r)), max(o_size, len(g)), max(o_size, len(b)))
+ elif isinstance(size, int):
+ size = (size, size, size)
+ C = lambda c, i : c[:] if len(c) >= size[i] else ([None] * size[i])
+ R, G, B = C(r, 0), C(g, 1), C(b, 2)
# Basis functions
#h00 = lambda t : (1 + 2 * t) * (1 - t) ** 2
h10 = lambda t : t * (1 - t) ** 2
@@ -185,7 +232,7 @@ def monotonicly_cubicly_interpolate_ramp(r, g, b, tension = 0):
return (R, G, B)
-def polynomially_interpolate_ramp(r, g, b): # TODO Speedup, demo and document this
+def polynomially_interpolate_ramp(r, g, b, size = None): # TODO Speedup, demo and document this
'''
Polynomially interpolate ramps to the size of the output axes.
@@ -197,11 +244,18 @@ def polynomially_interpolate_ramp(r, g, b): # TODO Speedup, demo and document th
@param r:list<float> The red colour curves
@param g:list<float> The green colour curves
@param b:list<float> The blue colour curves
- @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.
+ @param size:int|(r:int, g:int, b:int)? Either the size of all output ramps, the size
+ if the output ramps individually, or `None` for
+ whichever is larger of`o_size` and the size of
+ the input ramps
+ @return :(r:list<float>, g:list<float>, b:list<float>) The input ramps extended to the choosen size
'''
- C = lambda c : c[:] if len(c) >= o_size else ([None] * o_size)
- R, G, B, linear = C(r), C(g), C(b), [None]
+ if size is None:
+ size = (max(o_size, len(r)), max(o_size, len(g)), max(o_size, len(b)))
+ elif isinstance(size, int):
+ size = (size, size, size)
+ C = lambda c, i : c[:] if len(c) >= size[i] else ([None] * size[i])
+ R, G, B, linear = C(r, 0), C(g, 1), C(b, 2), [None]
for ci, (small, large) in enumerate(((r, R), (g, G), (b, B))):
small_, large_ = len(small) - 1, len(large) - 1
# Only interpolate if scaling up
@@ -277,7 +331,7 @@ def eliminate_halos(r, g, b, R, G, B):
large[X1 : X2 + 1] = linear[ci][X1 : X2 + 1]
-def interpolate_function(function, interpolator):
+def interpolate_function(function, interpolator): ## TODO size=
'''
Interpolate a function that applies adjustments from a lookup table
diff --git a/src/libgammaman.py b/src/libgammaman.py
deleted file mode 100644
index ceb6573..0000000
--- a/src/libgammaman.py
+++ /dev/null
@@ -1,119 +0,0 @@
-#!/usr/bin/env python3
-
-# Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (maandree@kth.se)
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# 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 is responsible for keeping track of resources for
-# the monitor module.
-
-import libgamma
-
-
-cache = {}
-'''
-Resource cache
-'''
-
-
-def get_method(name):
- '''
- Translate an adjustment method name into an ID
-
- @param name:str? The adjustment method's name
- @return :int The adjustment method's ID
- '''
- method = { 'randr' : libgamma.LIBGAMMA_METHOD_X_RANDR
- , 'vidmode' : libgamma.LIBGAMMA_METHOD_X_VIDMODE
- , 'drm' : libgamma.LIBGAMMA_METHOD_LINUX_DRM
- , 'w32gdi' : libgamma.LIBGAMMA_METHOD_W32_GDI
- , 'quartz' : libgamma.LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
- , 'dummy' : libgamma.LIBGAMMA_METHOD_DUMMY
- , None : None
- }
- method = method[name] if name in method else None
- if name is None:
- method = libgamma.list_methods(0)[0]
- elif method is None:
- raise Exception('Invalid method: %s' % name)
- elif not libgamma.is_method_available(method):
- raise Exception('Invalid method: %s' % name)
- return method
-
-
-def get_display(display, method):
- '''
- Get a display
-
- @param display:str? The display ID
- @param method:int The adjustment method
- @return :libgamma.Display Display object
- '''
- if display is None:
- display = libgamma.method_default_site(method)
- if method not in cache:
- cache[method] = {}
- cache_displays = cache[method]
- if display not in cache_displays:
- site = libgamma.Site(method, display)
- cache_displays[display] = site
- site.cache_screens = {}
- return cache_displays[display]
-
-
-def get_screen(screen, display, method):
- '''
- Get a screen
-
- @param screen:int The screen index
- @param display:str? The display ID
- @param method:int The adjustment method
- @return :libgamma.Screen Screen object
- '''
- display = get_display(display, method)
- cache_screens = display.cache_screens
- if screen not in cache_screens:
- partition = libgamma.Partition(display, screen)
- cache_screens[screen] = partition
- partition.cache_crtcs = {}
- return cache_screens[screen]
-
-
-def get_crtc(crtc, screen, display, method):
- '''
- Get a CRTC
-
- @param crtc:int The CRTC index
- @param screen:int The screen index
- @param display:str? The display ID
- @param method:int The adjustment method
- @return :libgamma.CRTC CRTC object
- '''
- screen = get_screen(screen, display, method)
- cache_crtcs = screen.cache_crtcs
- if crtc not in cache_crtcs:
- monitor = libgamma.CRTC(screen, crtc)
- cache_crtcs[crtc] = monitor
- (monitor.info, _) = monitor.information(~0)
- return cache_crtcs[crtc]
-
-
-def close():
- '''
- Release all resources
- '''
- global cache
- del cache
- cache = {}
-
diff --git a/src/monitor.py b/src/monitor.py
index 5c8d8fc..91472f4 100644
--- a/src/monitor.py
+++ b/src/monitor.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (maandree@kth.se)
+# Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (m@maandree.se)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -15,25 +15,17 @@
# 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 is responsible for access to the monitors.
+# This module is deprecated!
-import os
import sys
-from subprocess import Popen, PIPE
from aux import *
from curve import *
+from output import EDID
-import libgammaman
-import libgamma
-
-def close_c_bindings():
- '''
- Close all C bindings and let them free resources and close connections
- '''
- libgammaman.close()
+cached_displays = {}
def get_gamma(crtc = 0, screen = 0, display = None, *, method = None):
@@ -46,14 +38,16 @@ def get_gamma(crtc = 0, screen = 0, display = None, *, method = None):
@param method:str? The adjustment method
@return :()→void Function to invoke to apply the curves that was used when this function was invoked
'''
- # Get CRTC objet
- method = libgammaman.get_method(method)
- crtc = libgammaman.get_crtc(crtc, screen, display, method)
- # Create gamma ramps
- ramps = libgamma.GammaRamps(i_size, i_size, i_size)
- # Get gamma
- crtc.get_gamma(ramps)
+ import output
+ if not get_gamma.warned:
+ get_gamma.warned = True
+ print('get_gamma() is deprecated', file = sys.stderr)
+ if (method, display) not in cached_displays:
+ cached_displays[(method, display)] = output.get_outputs(method = method, display = display)
+ crtc = cached_displays[(method, display)].screens[screen].crtcs[crtc]
+ ramps = crtc.get_gamma()
return ramps_to_function(ramps.red, ramps.green, ramps.blue)
+get_gamma.warned = False
def set_gamma(*crtcs, screen = 0, display = None, method = None):
@@ -65,19 +59,22 @@ def set_gamma(*crtcs, screen = 0, display = None, method = None):
@param display:str? The display to which to connect, `None` for current display
@param method:str? The adjustment method
'''
- for crtc in (0,) if len(crtcs) == 0 else crtcs:
- # Convert curves to [0, 0xFFFF] integer lists
- (R_curve, G_curve, B_curve) = translate_to_integers()
- # Get CRTC objet
- method = libgammaman.get_method(method)
- crtc = libgammaman.get_crtc(crtc, screen, display, method)
- # Create gamma ramps
- ramps = libgamma.GammaRamps(i_size, i_size, i_size)
- # Set gamma
- ramps.red[:] = R_curve
- ramps.green[:] = G_curve
- ramps.blue[:] = B_curve
- crtc.set_gamma(ramps)
+ import output
+ if not set_gamma.warned:
+ set_gamma.warned = True
+ print('set_gamma() is deprecated', file = sys.stderr)
+ if (method, display) not in cached_displays:
+ cached_displays[(method, display)] = output.get_outputs(method = method, display = display)
+ screen = cached_displays[(method, display)].screens[screen]
+ ramps = output.Ramps(None, depth = -1, size = i_size)
+ ramps.red[:] = r_curve
+ ramps.green[:] = g_curve
+ ramps.blue[:] = b_curve
+ for crtc in range(len(screen.crtcs)) if len(crtcs) == 0 else crtcs:
+ crtc = screen.crtcs[crtc]
+ size = (crtc.red_gamma_size, crtc.green_gamma_size, crtc.blue_gamma_size)
+ crtc.set_gamma(ramps.copy(depth = crtc.gamma_depth, size = size))
+set_gamma.warned = False
def randr_get(crtc = 0, screen = 0, display = None):
@@ -89,7 +86,11 @@ def randr_get(crtc = 0, screen = 0, display = None):
@param display:str? The display to which to connect, `None` for current display
@return :()→void Function to invoke to apply the curves that was used when this function was invoked
'''
+ if not randr_get.warned:
+ randr_get.warned = True
+ print('randr_get() is deprecated', file = sys.stderr)
return get_gamma(crtc, screen, display, method = 'randr')
+randr_get.warned = False
def vidmode_get(crtc = 0, screen = 0, display = None):
'''
@@ -100,7 +101,11 @@ def vidmode_get(crtc = 0, screen = 0, display = None):
@param display:str? The display to which to connect, `None` for current display
@return :()→void Function to invoke to apply the curves that was used when this function was invoked
'''
+ if not vidmode_get.warned:
+ vidmode_get.warned = True
+ print('vidmode_get() is deprecated', file = sys.stderr)
return get_gamma(crtc, screen, display, method = 'vidmode')
+vidmode_get.warned = False
def drm_get(crtc = 0, screen = 0, display = None):
'''
@@ -111,7 +116,11 @@ def drm_get(crtc = 0, screen = 0, display = None):
@param display:str? Dummy parameter for compatibility with `randr_get` and `vidmode_get`
@return :()→void Function to invoke to apply the curves that was used when this function was invoked
'''
+ if not drm_get.warned:
+ drm_get.warned = True
+ print('drm_get() is deprecated', file = sys.stderr)
return get_gamma(crtc, screen, display, method = 'drm')
+drm_get.warned = False
def w32gdi_get(crtc = 0, screen = 0, display = None):
'''
@@ -122,7 +131,11 @@ def w32gdi_get(crtc = 0, screen = 0, display = None):
@param display:str? Dummy parameter for compatibility with `randr_get` and `vidmode_get`
@return :()→void Function to invoke to apply the curves that was used when this function was invoked
'''
+ if not w32gdi_get.warned:
+ w32gdi_get.warned = True
+ print('w32gdi_get() is deprecated', file = sys.stderr)
return get_gamma(crtc, screen, display, method = 'w32gdi')
+w32gdi_get.warned = False
def quartz_get(crtc = 0, screen = 0, display = None):
'''
@@ -133,7 +146,11 @@ def quartz_get(crtc = 0, screen = 0, display = None):
@param display:str? Dummy parameter for compatibility with `randr_get` and `vidmode_get`
@return :()→void Function to invoke to apply the curves that was used when this function was invoked
'''
+ if not quartz_get.warned:
+ quartz_get.warned = True
+ print('quartz_get() is deprecated', file = sys.stderr)
return get_gamma(crtc, screen, display, method = 'quartz')
+quartz_get.warned = False
def randr(*crtcs, screen = 0, display = None):
@@ -144,7 +161,11 @@ def randr(*crtcs, screen = 0, display = None):
@param screen:int The screen to which the monitors belong
@param display:str? The display to which to connect, `None` for current display
'''
+ if not randr.warned:
+ randr.warned = True
+ print('randr() is deprecated', file = sys.stderr)
set_gamma(*crtcs, screen = screen, display = display, method = 'randr')
+randr.warned = False
def vidmode(*crtcs, screen = 0, display = None):
'''
@@ -154,7 +175,11 @@ def vidmode(*crtcs, screen = 0, display = None):
@param screen:int The screen to which the monitors belong
@param display:str? The display to which to connect, `None` for current display
'''
+ if not vidmode.warned:
+ vidmode.warned = True
+ print('vidmode() is deprecated', file = sys.stderr)
set_gamma(*crtcs, screen = screen, display = display, method = 'vidmode')
+vidmode.warned = False
def drm(*crtcs, screen = 0, display = None):
'''
@@ -165,7 +190,11 @@ def drm(*crtcs, screen = 0, display = None):
named `screen` for compatibility with `randr` and `vidmode`
@param display:str? Dummy parameter for compatibility with `randr` and `vidmode`
'''
+ if not drm.warned:
+ drm.warned = True
+ print('drm() is deprecated', file = sys.stderr)
set_gamma(*crtcs, screen = screen, display = display, method = 'drm')
+drm.warned = False
def w32gdi(*crtcs, screen = 0, display = None):
'''
@@ -175,7 +204,11 @@ def w32gdi(*crtcs, screen = 0, display = None):
@param screen:int Dummy parameter for compatibility with `randr`, `vidmode` and `drm`
@param display:str? Dummy parameter for compatibility with `randr` and `vidmode`
'''
+ if not w32gdi.warned:
+ w32gdi.warned = True
+ print('w32gdi() is deprecated', file = sys.stderr)
set_gamma(*crtcs, screen = screen, display = display, method = 'w32gdi')
+w32gdi.warned = False
def quartz(*crtcs, screen = 0, display = None):
'''
@@ -185,7 +218,11 @@ def quartz(*crtcs, screen = 0, display = None):
@param screen:int Dummy parameter for compatibility with `randr`, `vidmode` and `drm`
@param display:str? Dummy parameter for compatibility with `randr` and `vidmode`
'''
+ if not quartz.warned:
+ quartz.warned = True
+ print('quartz() is deprecated', file = sys.stderr)
set_gamma(*crtcs, screen = screen, display = display, method = 'quartz')
+quartz.warned = False
def print_curves(*crtcs, screen = 0, display = None, compact = False):
@@ -197,6 +234,9 @@ def print_curves(*crtcs, screen = 0, display = None, compact = False):
@param display:str? Dummy parameter
@param compact:bool Whether to print in compact form
'''
+ if not print_curves.warned:
+ print_curves.warned = True
+ print('print_curves() is deprecated', file = sys.stderr)
# Convert curves to [0, 0xFFFF] integer lists
(R_curve, G_curve, B_curve) = translate_to_integers()
if compact:
@@ -226,6 +266,7 @@ def print_curves(*crtcs, screen = 0, display = None, compact = False):
print(G_curve)
# Print the blue colour curve
print(B_curve)
+print_curves.warned = False
@@ -459,55 +500,6 @@ class Output:
return '[Name: %s, Connected: %s, Width: %s, Height: %s, CRTC: %s, Screen: %s, EDID: %s]' % rc
-class EDID:
- '''
- Data structure for parsed EDID:s
-
- @var widthmm:int? The physical width of the monitor, measured in millimetres, `None` if not defined
- @var heightmm:int? The physical height of the monitor, measured in millimetres, `None` if not defined or not connected
- @var gamma:float? The monitor's estimated gamma characteristics, `None` if not specified
- @var gamma_correction:float? Gamma correction to use unless you know more exact values, calculated from `gamma`
- '''
- def __init__(self, edid):
- '''
- Constructor, parses an EDID
-
- @param edid:str The edid to parse, in hexadecimal representation
- @exception :Exception Raised when the EDID is not of a support format
- '''
- # Check the length of the EDID
- if not len(edid) == 256:
- raise Exception('EDID version not support, length mismatch')
- # Check the EDID:s magic number
- if not edid[:16].upper() == '00FFFFFFFFFFFF00':
- raise Exception('EDID version not support, magic number mismatch')
- # Convert to raw byte representation
- edid = [int(edid[i * 2 : i * 2 + 2], 16) for i in range(128)]
- # Check EDID structure version and revision
- if not edid[18] == 1:
- raise Exception('EDID version not support, version mismatch, only 1.3 supported')
- if not edid[19] == 3:
- raise Exception('EDID version not support, revision mismatch, only 1.3 supported')
- # Get the maximum displayable image dimension
- self.widthmm = edid[21] * 10
- self.heightmm = edid[22] * 10
- if (self.widthmm == 0) or (self.heightmm == 0):
- # If either is zero, it is undefined, e.g. a projector
- self.widthmm = None
- self.heightmm = None
- # Get the gamma characteristics
- if (edid[23] == 255):
- # Not specified if FFh
- self.gamma = None
- self.gamma_correction = None
- else:
- self.gamma = (edid[23] + 100) / 100
- self.gamma_correction = self.gamma / 2.2
- # Check the EDID checksum
- if not sum(edid) % 256 == 0:
- raise Exception('Incorrect EDID checksum')
-
-
def list_screens(method = None, display = None):
'''
Retrieve informantion about all screens, outputs and CRTC:s
@@ -518,43 +510,29 @@ def list_screens(method = None, display = None):
@param display:str? The display to use, `None` for the current one
@return :Screens An instance of a datastructure with the relevant information
'''
- method = libgammaman.get_method(method)
- display_ = libgammaman.get_display(display, method)
- screen_n = display_.partitions_available
+ import output
+ if not list_screens.warned:
+ list_screens.warned = True
+ print('list_screens() is deprecated', file = sys.stderr)
+ if (method, display) not in cached_displays:
+ cached_displays[(method, display)] = output.get_outputs(method = method, display = display)
rc = Screens()
- rc.screens = [None] * screen_n
-
- for screen_i in range(screen_n):
- screen = libgammaman.get_screen(screen_i, display, method)
- # And add it to the table
- rc.screens[screen_i] = s = Screen()
- # Store the number of CRTC:s
- s.crtc_count = crtc_n = screen.crtcs_available
- # Prepare the current screens output table when we know how many outputs it has
- s.outputs = [None] * crtc_n
- for crtc_i in range(crtc_n):
- crtc = libgamma.CRTC(screen, crtc_i)
- (info, _) = crtc.information(~0)
- s.outputs[crtc_i] = o = Output()
- # Store the screen index for the output so that it is easy
- # to look it up when iterating over outputs
- o.screen = screen_i
- # Store the name of the output
- o.name = info.connector_name if info.connector_name_error == 0 else None
- # Store the connection status of the output's connector
- o.connected = info.active if info.active_error == 0 else None
- # Store the physical dimensions of the monitor
- o.widthmm, o.heightmm = info.width_mm, info.height_mm
- # But if it is zero or less it is unknown
- if (o.widthmm <= 0) or (o.heightmm <= 0):
- o.widthmm, o.heightmm = None, None
- # Store the CRTC index of the output
- o.crtc = crtc_i
- # Store the output's extended dislay identification data
- o.edid = None if info.edid is None or not info.edid_error == 0 else libgamma.behex_edid(info.edid)
-
+ rc.screens = cached_displays[(method, display)].screens
+ for screen_i, screen in enumerate(rc.screens):
+ screen.crtc_count = len(screen.crtcs)
+ screen.outputs = [None] * screen.crtc_count
+ for crtc_i in range(screen.crtc_count):
+ screen.outputs[crtc_i] = output = Output()
+ output.screen = screen_i
+ output.crtc = crtc_i
+ crtc = screen.crtcs[crtc_i]
+ output.name = crtc.connector_name
+ output.connected = crtc.active
+ output.widthmm = crtc.width_mm
+ output.heightmm = crtc.height_mm
+ output.edid = crtc.edid
return rc
-
+list_screens.warned = False
def list_screens_randr(display = None):
@@ -564,7 +542,11 @@ def list_screens_randr(display = None):
@param display:str? The display to use, `None` for the current one
@return :Screens An instance of a datastructure with the relevant information
'''
+ if not list_screens_randr.warned:
+ list_screens_randr.warned = True
+ print('list_screens_randr() is deprecated', file = sys.stderr)
return list_screens('randr', display)
+list_screens_randr.warned = False
def list_screens_drm():
'''
@@ -572,7 +554,11 @@ def list_screens_drm():
@return :Screens An instance of a datastructure with the relevant information
'''
+ if not list_screens_drm.warned:
+ list_screens_drm.warned = True
+ print('list_screens_drm() is deprecated', file = sys.stderr)
return list_screens('drm', None)
+list_screens_drm.warned = False
def list_screens_quartz():
'''
@@ -580,7 +566,11 @@ def list_screens_quartz():
@return :Screens An instance of a datastructure with the relevant information
'''
+ if not list_screens_quartz.warned:
+ list_screens_quartz.warned = True
+ print('list_screens_quartz() is deprecated', file = sys.stderr)
return list_screens('quartz', None)
+list_screens_quartz.warned = False
def list_screens_w32gdi():
'''
@@ -588,13 +578,23 @@ def list_screens_w32gdi():
@return :Screens An instance of a datastructure with the relevant information
'''
+ if not list_screens_w32gdi.warned:
+ list_screens_w32gdi.warned = True
+ print('list_screens_w32gdi() is deprecated', file = sys.stderr)
return list_screens('w32gdi', None)
+list_screens_w32gdi.warned = False
def quartz_restore():
'''
Restore all CRTC:s' gamma ramps the settings in ColorSync
'''
- method = libgammaman.get_method('quartz')
- libgammaman.get_display(None, method).restore()
+ import output
+ if not quartz_restore.warned:
+ quartz_restore.warned = True
+ print('quartz_restore() is deprecated', file = sys.stderr)
+ if ('quartz', None) not in cached_displays:
+ cached_displays[('quartz', None)] = output.get_outputs(method = 'quartz')
+ cached_displays[('quartz', None)].restore()
+quartz_restore.warned = False
diff --git a/src/output.py b/src/output.py
index d0de14a..5df4e7c 100644
--- a/src/output.py
+++ b/src/output.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (maandree@kth.se)
+# Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (m@maandree.se)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -17,6 +17,13 @@
# This module is responsible for access to the monitors.
+import math
+
+from colour import *
+from blackbody import *
+
+
+
class Tristate:
'''
Ternary values
@@ -106,7 +113,7 @@ class EDID:
'''
Constructor
- @edid edid:str The EDID in upper case hexadecimal representation
+ @param edid:str The EDID in upper case hexadecimal representation
'''
self.manufacturer_id = None
self.manufacturer_product_code = None
@@ -143,11 +150,14 @@ class EDID:
self.green_chroma = None
self.blue_chroma = None
self.white_chroma = None
+ ## FOR LEGACY {
+ self.__gamma_correction = ...
+ ## }
if edid[:len('00FFFFFFFFFFFF00')] == '00FFFFFFFFFFFF00' or len(edid) % 2 == 1:
return
edid = [int(edid[i * 2 : i * 2 + 2], 16) for i in range(len(edid) // 2)]
- if sum(edid) % 256 != 0 or len(edid[:128]) < 128:
+ if len(edid) < 128 or sum(edid[:128]) % 256 != 0:
return
self.manufacturer_id = [(edid[8] >> 2) & 0x0F, (edid[9] >> 4) | (edid[8] & 1) << 4, edid[9] & 0x0F]
@@ -175,8 +185,8 @@ class EDID:
self.width_mm = edid[21] * 10
self.height_mm = edid[22] * 10
if edid[21] == 0 or edid[22] == 0:
- self.width_mm = self.height_mm = 0
- self.display_gamma = edid[23] / 100 + 1 if edid[23] else None
+ self.width_mm = self.height_mm = None
+ self.display_gamma = None if edid[23] == 255 else edid[23] / 100 + 1
self.dpms_standby_supported = (edid[24] & 128) == 128
self.dpms_suspend_supported = (edid[24] & 64) == 64
self.dpms_active_off_supported = (edid[24] & 32) == 32
@@ -211,6 +221,69 @@ class EDID:
self.white_chroma = (wx / 1024, wy / 1024)
# There are also mode lines and maybe extensions, but yeah...
+
+ ## FOR LEGACY {
+ @property
+ def widthmm(self):
+ if not EDID.warned_widthmm:
+ EDID.warned_widthmm = True
+ print('EDID.widthmm is deprecated, use EDID.width_mm instead', file = sys.stderr)
+ return self.width_mm
+ @widthmm.setter
+ def widthmm(self, value):
+ if not EDID.warned_widthmm:
+ EDID.warned_widthmm = True
+ print('EDID.widthmm is deprecated, use EDID.width_mm instead', file = sys.stderr)
+ self.width_mm = value
+ @property
+ def heightmm(self):
+ if not EDID.warned_heightmm:
+ EDID.warned_heightmm = True
+ print('EDID.heightmm is deprecated, use EDID.height_mm instead', file = sys.stderr)
+ return self.height_mm
+ @heightmm.setter
+ def heightmm(self, value):
+ if not EDID.warned_heightmm:
+ EDID.warned_heightmm = True
+ print('EDID.heightmm is deprecated, use EDID.height_mm instead', file = sys.stderr)
+ self.height_mm = value
+ @property
+ def gamma(self):
+ if not EDID.warned_gamma:
+ EDID.warned_gamma = True
+ print('EDID.gamma is deprecated, use EDID.display_gamma instead', file = sys.stderr)
+ return self.display_gamma
+ @gamma.setter
+ def gamma(self, value):
+ if not EDID.warned_gamma:
+ EDID.warned_gamma = True
+ print('EDID.gamma is deprecated, use EDID.display_gamma instead', file = sys.stderr)
+ self.display_gamma = value
+ @property
+ def gamma_correction(self):
+ if not EDID.warned_gamma_correction:
+ EDID.warned_gamma_correction = True
+ print('EDID.gamma_correction is deprecated', file = sys.stderr)
+ if self.__gamma_correction is ...:
+ if self.display_gamma is None:
+ self.__gamma_correction = None
+ else:
+ self.__gamma_correction = self.display_gamma / 2.2
+ return self.__gamma_correction
+ @gamma_correction.setter
+ def gamma_correction(self, value):
+ if not EDID.warned_gamma_correction:
+ EDID.warned_gamma_correction = True
+ print('EDID.gamma_correction is deprecated', file = sys.stderr)
+ self.__gamma_correction = value
+
+EDID.warned_widthmm = False
+EDID.warned_heightmm = False
+EDID.warned_gamma = False
+EDID.warned_gamma_correction = False
+## }
+
+
class MultiCRTC:
'''
A group of CRTC:s organised for efficient gamma ramp adjustments
@@ -231,6 +304,7 @@ class MultiCRTC:
for crtc in crtcs:
self.add(crtc)
+
def add(self, crtc):
'''
Add a CRTC
@@ -271,6 +345,7 @@ class MultiCRTC:
subsubfound.append(crtc)
+
def make_ramps(self, depth = -2):
'''
Create a gamma-ramp trio where each ramp is as large as the
@@ -291,6 +366,7 @@ class MultiCRTC:
size[2] = max(size[2], crtc.blue_gamma_size)
return Ramps(None, depth = depth, size = size)
+
def set_gamma(self, ramps, priority = None, rule = None, lifespan = 1):
'''
Set the gamma ramps on all CRTC:s in the group
@@ -319,7 +395,7 @@ class MultiCRTC:
refsize = (ref.red_gamma_size, ref.green_gamma_size, ref.blue_gamma_size)
if refsize == (len(ramps.red), len(ramps.green), len(ramps.blue)):
ramps_size = ramps
- else
+ else:
ramps_size = Ramps.copy(ramps, refcrtc.depth, refsize)
for sublayer in layer:
ref = sublayer[0][0]
@@ -333,6 +409,7 @@ class MultiCRTC:
for crtc in subsublayer:
ramps_backend = crtc.set_gamma(ramps_backend, priority, rule, lifespan)
+
class CRTC:
'''
A CRTC
@@ -340,6 +417,7 @@ class CRTC:
@function restore:(self)?→void Restore the CLUT:s to the (configured) system
defaults, `None` if not supported
+ @variable screen:Screen The screen
@variable edid:str? The EDID in upper case hexadecimal representation
@variable red_gamma_size:int? The number of stops in the red gamma ramp
@variable green_gamma_size:int? The number of stops in the green gamma ramp
@@ -477,6 +555,7 @@ class CRTC:
self.blue_chroma = None
self.white_chroma = None
+
def make_ramps(self, depth = None):
'''
Create gamma ramps with the same size as the CRTC expects
@@ -491,6 +570,7 @@ class CRTC:
'''
return Ramps(self, depth = depth)
+
@property
def edid_data(self):
'''
@@ -502,12 +582,16 @@ class CRTC:
self.__edid_data = None if self.edid is None else EDID(self.edid)
return self.__edid_data
+
class Screen:
'''
A screen or graphics card
- @function restore:(self)?→void Restore the CLUT:s to the (configured) system
- defaults, `None` if not supported
+ @function restore:(self)?→void Restore the CLUT:s to the (configured) system defaults,
+ `None` if not supported
+
+ @variable display:Display The display
+ @variable crtcs:list<LibgammaCRTC> The CRTC:s in the screen
'''
def __len__(self):
'''
@@ -517,6 +601,7 @@ class Screen:
'''
return len(self.crtcs)
+
def __getitem__(self, indices):
'''
Get CRTC:s in the screen
@@ -526,6 +611,7 @@ class Screen:
'''
return self.crtcs[indices]
+
def __iter__(self):
'''
Iterator of the screen's CRTC:s
@@ -535,12 +621,17 @@ class Screen:
for value in self[:]:
yield value
+
class Display:
'''
A display
- @function restore:(self)?→void Restore the CLUT:s to the (configured) system
- defaults, `None` if not supported
+ @function restore:(self)?→void Restore the CLUT:s to the (configured) system defaults,
+ `None` if not supported
+
+ @variable screens:list<LibgammaScreen> The screens in the display
+ @variable crtcs:list<LibgammaCRTC> The CRTC:s in the display
+ @variable cooperative:bool Whether the adjustment method supports cooperative gamma
'''
def __len__(self):
'''
@@ -550,6 +641,7 @@ class Display:
'''
return len(self.crtcs)
+
def __getitem__(self, indices):
'''
Get screens in the display
@@ -559,6 +651,7 @@ class Display:
'''
return self.crtcs[indices]
+
def __iter__(self):
'''
Iterator of the display's screens
@@ -568,7 +661,8 @@ class Display:
for value in self[:]:
yield value
-class Ramps: ## TODO adjustments
+
+class Ramps:
'''
Gamma ramps
@@ -580,6 +674,7 @@ class Ramps: ## TODO adjustments
32-bit integers, 64 for unsigned 64-bit integers,
-1 for single-precision floating-point values, and
-2 for double-precision floating-point values
+ @variable maximum:float The largest stop value
'''
def __init__(self, crtc, depth = None, size = None):
'''
@@ -589,32 +684,34 @@ class Ramps: ## TODO adjustments
only be `None` if neither `depth` nor
`size` is `None`
@param depth:int? The gamma depth, 8 for unsigned 8-bit integers,
- 16 for unsigned 16-bit integers, 32 for unsigned
- 32-bit integers, 64 for unsigned 64-bit integers,
- -1 for single-precision floating-point values,
- -2 for double-precision floating-point values, and
- `None` for the gamma depth the CRTC expects
+ 16 for unsigned 16-bit integers, 32 for unsigned
+ 32-bit integers, 64 for unsigned 64-bit integers,
+ -1 for single-precision floating-point values,
+ -2 for double-precision floating-point values, and
+ `None` for the gamma depth the CRTC expects
@param size:int|(red:int, green:int, blue:int)?
- The size of the ramps, either an integer of the size that
- is applied to all three channels, three integers with
- the size of each channel, or `None` for the sizes the
- CRTC expects
+ The size of the ramps, either an integer of the size that
+ is applied to all three channels, three integers with
+ the size of each channel, or `None` for the sizes the
+ CRTC expects
'''
if depth is None:
depth = crtc.depth
if size is not None and isinstance(size, int):
size = (size, size, size)
self.depth = depth
- def make_ramp(depth, size):
- if depth > 0:
- m = 1 << (depth - 1)
- return [int(x * m / (size - 1) + 0.5) for x in range(size)]
- else:
+ self.maximum = 1 if depth < 0 else (1 << depth) - 1
+ if depth > 0:
+ def make_ramp(depth, size):
+ return [int(x * self.maximum / (size - 1) + 0.5) for x in range(size)]
+ else:
+ def make_ramp(depth, size):
return [x / (size - 1) for x in range(size)]
self.red = make_ramp(self.depth, crtc.red_gamma_size if size is None else size[0])
self.green = make_ramp(self.depth, crtc.green_gamma_size if size is None else size[1])
self.blue = make_ramp(self.depth, crtc.blue_gamma_size if size is None else size[2])
+
def copy(self, depth = None, size = None, interpolation = None):
'''
Create a copy, optionally with a new depth or size
@@ -645,19 +742,601 @@ class Ramps: ## TODO adjustments
pass
elif interpolation is None:
import interpolation as interpol
- ramps = interpol.linearly_interpolate_ramp(*ramps)
+ ramps = interpol.linearly_interpolate_ramp(*ramps, size = size)
else:
- ramps = interpolation(*ramps)
+ ramps = interpolation(*ramps, size = size)
r.red[:] = ramps[0]
r.green[:] = ramps[1]
r.blue[:] = ramps[2]
- old_max = 1 << (self.depth - 1) if self.depth > 0 else 1
- new_max = 1 << (depth - 1) if depth > 0 else 1
- if new_max != old_max:
+ if r.maximum != self.maximum:
for ramp in (r.red, r.green, r.blue):
for i in range(len(ramp)):
- ramp[i] = ramp[i] * new_max / old_max
+ ramp[i] = ramp[i] * r.maximum / self.maximum
return r
+
+
+ def __str__(self, compact = False):
+ '''
+ Create a string of the ramps that is useful for debugging
+
+ @param compact:bool Whether to apply run-length compression when suitable
+ @return :str A printable string
+ '''
+ if not compact:
+ return '%s\n%s\n%s' % (repr(red), repr(green), repr(blue))
+ rgb = ([], [], [])
+ for r, w in zip((self.red, self.green, self.blue), rgb):
+ last, count = None, 0
+ for value in r:
+ if self.depth > 0:
+ value = int(value + 0.5)
+ if value == last:
+ count += 1
+ else:
+ if last is not None:
+ if count > 1:
+ w.append(repr(last))
+ else:
+ w.append('%s {%i}' % (repr(last), count))
+ last = value
+ count = 1
+ if last is not None:
+ if count > 1:
+ w.append(repr(last))
+ else:
+ w.append('%s {%i}' % (repr(last), count))
+ return '[%s]\n[%s]\n[%s]' % (', '.join(rgb[0]), ', '.join(rgb[1]), ', '.join(rgb[2]))
+
+
+ def __bool(self, r, g, b):
+ if g is ...: g = r
+ if b is ...: b = g
+ ret = []
+ if r: ret.append(self.red)
+ if g: ret.append(self.green)
+ if b: ret.append(self.blue)
+ return ret
+
+
+ def __datum(self, r, g, b):
+ if g is ...: g = r
+ if b is ...: b = g
+ ret = []
+ if r is not None: ret.append((self.red, r))
+ if g is not None: ret.append((self.green, g))
+ if b is not None: ret.append((self.blue, b))
+ return ret
+
+
+ def temperature(self, temperature, algorithm):
+ '''
+ Change colour temperature according to the CIE illuminant series D using CIE sRBG
+
+ @param temperature:float|str The blackbody temperature in kelvins, or a name
+ @param algorithm:(float)→(float, float, float) Algorithm for calculating a white point, for example `cmf_10deg`
+ '''
+ self.rgb_temperature(temperature, algorithm)
+
+
+ def rgb_temperature(self, temperature, algorithm):
+ '''
+ Change colour temperature according to the CIE illuminant series D using CIE sRBG
+
+ @param temperature:float|str The blackbody temperature in kelvins, or a name
+ @param algorithm:(float)→(float, float, float) Algorithm for calculating a white point, for example `cmf_10deg`
+ '''
+ # Resolve colour temperature name
+ temperature = kelvins(temperature)
+ # Do nothing if the temperature is neutral
+ if temperature == 6500: return
+ # Otherwise manipulate the colour curves
+ self.rgb_brightness(*(algorithm(temperature)))
+
+
+ def cie_temperature(self, temperature, algorithm):
+ '''
+ Change colour temperature according to the CIE illuminant series D using CIE xyY
+
+ @param temperature:float|str The blackbody temperature in kelvins, or a name
+ @param algorithm:(float)→(float, float, float) Algorithm for calculating a white point, for example `cmf_10deg`
+ '''
+ # Resolve colour temperature name
+ temperature = kelvins(temperature)
+ # Do nothing if the temperature is neutral
+ if temperature == 6500: return
+ # Otherwise manipulate the colour curves
+ self.cie_brightness(*(algorithm(temperature)))
+
+
+ def rgb_contrast(self, r, g = ..., b = ...):
+ '''
+ Apply contrast correction on the colour curves using sRGB
+
+ In this context, contrast is a measure of difference between the whitepoint and blackpoint,
+ if the difference is 0 than they are both grey
+
+ @param r:float? The contrast parameter for the red curve
+ @param g:float|...? The contrast parameter for the green curve, defaults to `r` if `...`
+ @param b:float|...? The contrast parameter for the blue curve, defaults to `g` if `...`
+ '''
+ half = self.maximum / 2
+ for (curve, level) in self.__value(r, g, b):
+ if not level == 1.0:
+ curve[:] = [(y - half) * level + half for y in curve]
+
+
+ def cie_contrast(self, r, g = ..., b = ...):
+ '''
+ Apply contrast correction on the colour curves using CIE xyY
+
+ In this context, contrast is a measure of difference between the whitepoint and blackpoint,
+ if the difference is 0 than they are both grey
+
+ @param r:float? The contrast parameter for the red curve
+ @param g:float|...? The contrast parameter for the green curve, defaults to `r` if `...`
+ @param b:float|...? The contrast parameter for the blue curve, defaults to `g` if `...`
+ '''
+ # Handle overloading
+ if g is ...: g = r
+ if b is ...: b = g
+ # Check if we can reduce the overhead, we can if the adjustments are identical
+ same = r == g == b
+ # Check we need to do any adjustment
+ if (not same) or (not r == 1.0):
+ if same:
+ if r is None:
+ return
+ # Manipulate all curves in one step if their adjustments are identical
+ for i in range(i_size):
+ # Convert to CIE xyY
+ (x, y, Y) = srgb_to_ciexyy(self.red[i] / self.maximum,
+ self.green[i] / self.maximum,
+ self.blue[i] / self.maximum)
+ # Manipulate illumination and convert back to sRGB
+ (r_, g_, b_) = ciexyy_to_srgb(x, y, (Y - 0.5) * r + 0.5)
+ if r: self.red[i] = r_ * self.maximum
+ if g: self.green[i] = g_ * self.maximum
+ if b: self.blue[i] = b_ * self.maximum
+ else:
+ # Manipulate all curves individually if their adjustments are not identical
+ for i in range(i_size):
+ # Convert to CIE xyY
+ (x, y, Y) = srgb_to_ciexyy(self.red[i] / self.maximum,
+ self.green[i] / self.maximum,
+ self.blue[i] / self.maximum)
+ # Manipulate illumination and convert back to sRGB
+ if r: self.red[i] = ciexyy_to_srgb(x, y, (Y - 0.5) * r + 0.5)[0] * self.maximum
+ if g: self.green[i] = ciexyy_to_srgb(x, y, (Y - 0.5) * g + 0.5)[1] * self.maximum
+ if b: self.blue[i] = ciexyy_to_srgb(x, y, (Y - 0.5) * b + 0.5)[2] * self.maximum
+
+
+ def rgb_brightness(self, r, g = ..., b = ...):
+ '''
+ Apply brightness correction on the colour curves using sRGB
+
+ In this context, brightness is a measure of the whiteness of the whitepoint
+
+ @param r:float? The brightness parameter for the red curve
+ @param g:float|...? The brightness parameter for the green curve, defaults to `r` if `...`
+ @param b:float|...? The brightness parameter for the blue curve, defaults to `g` if `...`
+ '''
+ for (curve, level) in curves(r, g, b):
+ if not level == 1.0:
+ curve[:] = [y * level for y in curve]
+
+
+ def cie_brightness(self, r, g = ..., b = ...):
+ '''
+ Apply brightness correction on the colour curves using CIE xyY
+
+ In this context, brightness is a measure of the whiteness of the whitepoint
+
+ @param r:float? The brightness parameter for the red curve
+ @param g:float|...? The brightness parameter for the green curve, defaults to `r` if `...`
+ @param b:float|...? The brightness parameter for the blue curve, defaults to `g` if `...`
+ '''
+ # Handle overloading
+ if g is ...: g = r
+ if b is ...: b = g
+ # Check if we can reduce the overhead, we can if the adjustments are identical
+ same = r == g == b
+ # Check we need to do any adjustment
+ if (not same) or (not r == 1.0):
+ if same:
+ if r is None:
+ return
+ # Manipulate all curves in one step if their adjustments are identical
+ for i in range(i_size):
+ # Convert to CIE xyY
+ (x, y, Y) = srgb_to_ciexyy(self.red[i] / self.maximum,
+ self.green[i] / self.maximum,
+ self.blue[i] / self.maximum)
+ (r_, g_, b_) = ciexyy_to_srgb(x, y, Y * r)
+ if r: self.red[i] = r_ * self.maximum
+ if g: self.green[i] = g_ * self.maximum
+ if b: self.blue[i] = b_ * self.maximum
+ else:
+ # Manipulate all curves individually if their adjustments are not identical
+ for i in range(i_size):
+ # Convert to CIE xyY
+ (x, y, Y) = srgb_to_ciexyy(self.red[i] / self.maximum,
+ self.green[i] / self.maximum,
+ self.blue[i] / self.maximum)
+ # Manipulate illumination and convert back to sRGB
+ if r: self.red[i] = ciexyy_to_srgb(x, y, Y * r)[0] * self.maximum
+ if g: self.green[i] = ciexyy_to_srgb(x, y, Y * g)[1] * self.maximum
+ if b: self.blue[i] = ciexyy_to_srgb(x, y, Y * b)[2] * self.maximum
+
+
+ def linearise(self, r = True, g = ..., b = ...):
+ '''
+ Convert the curves from formatted in standard RGB to linear RGB
+
+ @param r:bool Whether to convert the red colour curve
+ @param g:bool|... Whether to convert the green colour curve, defaults to `r` if `...`
+ @param b:bool|... Whether to convert the blue colour curve, defaults to `g` if `...`
+ '''
+ # Handle overloading
+ if g is ...: g = r
+ if b is ...: b = g
+ # Convert colour space
+ if not r and not g and not b:
+ return
+ for i in range(i_size):
+ (r_, g_, b_) = standard_to_linear(self.red[i] / self.maximum,
+ self.green[i] / self.maximum,
+ self.blue[i] / self.maximum)
+ if r: self.red[i] = r_ * self.maximum
+ if g: self.green[i] = g_ * self.maximum
+ if b: self.blue[i] = b_ * self.maximum
+
+
+ def standardise(self, r = True, g = ..., b = ...):
+ '''
+ Convert the curves from formatted in linear RGB to standard RGB
+
+ @param r:bool Whether to convert the red colour curve
+ @param g:bool|... Whether to convert the green colour curve, defaults to `r` if `...`
+ @param b:bool|... Whether to convert the blue colour curve, defaults to `g` if `...`
+ '''
+ # Handle overloading
+ if g is ...: g = r
+ if b is ...: b = g
+ # Convert colour space
+ if not r and not g and not b:
+ return
+ for i in range(i_size):
+ (r_, g_, b_) = linear_to_standard(self.red[i] / self.maximum,
+ self.green[i] / self.maximum,
+ self.blue[i] / self.maximum)
+ if r: self.red[i] = r_ * self.maximum
+ if g: self.green[i] = g_ * self.maximum
+ if b: self.blue[i] = b_ * self.maximum
+
+
+ def gamma(self, r, g = ..., b = ...):
+ '''
+ Apply gamma correction on the colour curves
+
+ @param r:float? The gamma parameter for the red colour curve
+ @param g:float|...? The gamma parameter for the green colour curve, defaults to `r` if `...`
+ @param b:float|...? The gamma parameter for the blue colour curve, defaults to `g` if `...`
+ '''
+ for (curve, level) in self.__value(r, g, b):
+ if not level == 1.0:
+ curve[:] = [(y / self.maximum) ** (1 / level) * self.maximum for y in curve]
+
+
+ def negative(self, r = True, g = ..., b = ...):
+ '''
+ Reverse the colour curves (negative image with gamma preservation)
+
+ @param r:bool Whether to invert the red colour curve
+ @param g:bool|... Whether to invert the green colour curve, defaults to `r` if `...`
+ @param b:bool|... Whether to invert the blue colour curve, defaults to `g` if `...`
+ '''
+ for curve in self.__bool(r, g, b):
+ curve[:] = reversed(curve)
+
+
+ def rgb_invert(self, r = True, g = ..., b = ...):
+ '''
+ Invert the colour curves (negative image with gamma invertion), using sRGB
+
+ @param r:bool Whether to invert the red colour curve
+ @param g:bool|... Whether to invert the green colour curve, defaults to `r` if `...`
+ @param b:bool|... Whether to invert the blue colour curve, defaults to `g` if `...`
+ '''
+ for curve in self.__bool(r, g, b):
+ curve[:] = [self.maximum - y for y in curve]
+
+
+ def cie_invert(self, r = True, g = ..., b = ...):
+ '''
+ Invert the colour curves (negative image with gamma invertion), using CIE xyY
+
+ @param r:bool Whether to invert the red colour curve
+ @param g:bool|... Whether to invert the green colour curve, defaults to `r` if `...`
+ @param b:bool|... Whether to invert the blue colour curve, defaults to `g` if `...`
+ '''
+ # Handle overloading
+ if g is ...: g = r
+ if b is ...: b = g
+ # Manipulate the colour curves if any curve should be manipulated
+ if r or g or b:
+ for i in range(i_size):
+ # Convert to CIE xyY
+ (x, y, Y) = srgb_to_ciexyy(self.red[i] / self.maximum,
+ self.green[i] / self.maximum,
+ self.blue[i] / self.maximum)
+ # Invert illumination and convert to back sRGB
+ (r_, g_, b_) = ciexyy_to_srgb(x, y, 1 - Y)
+ # Apply the new values on the selected channels
+ if r: self.red[i] = r_ * self.maximum
+ if g: self.green[i] = g_ * self.maximum
+ if b: self.blue[i] = b_ * self.maximum
+
+
+ def sigmoid(self, r, g = ..., b = ...):
+ '''
+ Apply S-curve correction on the colour curves.
+ This is intended for fine tuning LCD monitors,
+ 4.5 is good value start start testing at.
+ You would probably like to use rgb_limits before
+ this to adjust the black point as that is the
+ only way to adjust the black point on many LCD
+ monitors.
+
+ @param r:float? The sigmoid parameter for the red colour curve
+ @param g:float|...? The sigmoid parameter for the green colour curve, defaults to `r` if `...`
+ @param b:float|...? The sigmoid parameter for the blue colour curve, defaults to `g` if `...`
+ '''
+ for (curve, level) in self.__value(r, g, b):
+ for i in range(i_size):
+ try:
+ curve[i] = (0.5 - math.log(self.maximum / curve[i] - 1) / level) * self.maximum
+ except:
+ # Corner cases:
+ # curve[i] = 0 → 0 -- Division by zero
+ # curve[i] = self.maximum → self.maximum -- Logarithm of zero
+ pass
+
+
+ def rgb_limits(self, r_min, r_max, g_min = ..., g_max = ..., b_min = ..., b_max = ...):
+ '''
+ Changes the black point and the white point, using sRGB
+
+ @param r_min:float The red component value of the black point
+ @param r_max:float The red component value of the white point
+ @param g_min:float|... The green component value of the black point, defaults to `r_min`
+ @param g_max:float|... The green component value of the white point, defaults to `r_max`
+ @param b_min:float|... The blue component value of the black point, defaults to `g_min`
+ @param b_max:float|... The blue component value of the white point, defaults to `g_max`
+ '''
+ # Handle overloading
+ if g_min is ...: g_min = r_min
+ if g_max is ...: g_max = r_max
+ if b_min is ...: b_min = g_min
+ if b_max is ...: b_max = g_max
+ # Manipulate the colour curves
+ for (curve, (level_min, level_max)) in self.__values((r_min, r_max), (g_min, g_max), (b_min, b_max)):
+ # But not if the adjustments are neutral
+ if (level_min != 0) or (level_max != self.maximum):
+ curve[:] = [y * (level_max - level_min) + level_min for y in curve]
+
+
+ def cie_limits(self, r_min, r_max, g_min = ..., g_max = ..., b_min = ..., b_max = ...):
+ '''
+ Changes the black point and the white point, using CIE xyY
+
+ @param r_min:float The red component value of the black point
+ @param r_max:float The red component value of the white point
+ @param g_min:float|... The green component value of the black point, defaults to `r_min`
+ @param g_max:float|... The green component value of the white point, defaults to `r_max`
+ @param b_min:float|... The blue component value of the black point, defaults to `g_min`
+ @param b_max:float|... The blue component value of the white point, defaults to `g_max`
+ '''
+ # Handle overloading
+ if g_min is ...: g_min = r_min
+ if g_max is ...: g_max = r_max
+ if b_min is ...: b_min = g_min
+ if b_max is ...: b_max = g_max
+ # Check if we can reduce the overhead, we can if the adjustments are identical
+ same = (r_min == g_min == b_min) and (r_max == g_max == b_max)
+ # Check we need to do any adjustment
+ if (not same) or (not r_min == 0) or (not r_max == self.maximum):
+ if same:
+ # Manipulate all curves in one step if their adjustments are identical
+ for i in range(i_size):
+ # Convert to CIE xyY
+ (x, y, Y) = srgb_to_ciexyy(self.red[i] / self.maximum,
+ self.green[i] / self.maximum,
+ self.blue[i] / self.maximum)
+ # Manipulate illumination
+ Y = Y * (r_max - r_min) + r_min
+ # Convert back to sRGB
+ (r_, g_, b_) = ciexyy_to_srgb(x, y, Y)
+ self.red[i] = r_ * self.maximum
+ self.green[i] = g_ * self.maximum
+ self.blue[i] = b_ * self.maximum
+ else:
+ # Manipulate all curves individually if their adjustments are not identical
+ for i in range(i_size):
+ # Convert to CIE xyY
+ (x, y, Y) = srgb_to_ciexyy(self.red[i] / self.maximum,
+ self.green[i] / self.maximum,
+ self.blue[i] / self.maximum)
+ # Manipulate illumination and convert back to sRGB
+ self.red[i] = ciexyy_to_srgb(x, y, Y * (r_max - r_min) + r_min)[0] * self.maximum
+ self.green[i] = ciexyy_to_srgb(x, y, Y * (g_max - g_min) + g_min)[1] * self.maximum
+ self.blue[i] = ciexyy_to_srgb(x, y, Y * (b_max - b_min) + b_min)[2] * self.maximum
+
+
+ def manipulate(self, r, g = ..., b = ...):
+ '''
+ Manipulate the colour curves using a (lambda) function
+
+ @param r:(float)?→float Function to manipulate the red colour curve
+ @param g:(float)?→float|... Function to manipulate the green colour curve, defaults to `r` if `...`
+ @param b:(float)?→float|... Function to manipulate the blue colour curve, defaults to `g` if `...`
+
+ `None` means that nothing is done for that subpixel
+
+ The lambda functions thats a colour value and maps it to a new colour value.
+ For example, if the red value 0.5 is already mapped to 0.25, then if the function
+ maps 0.25 to 0.5, the red value 0.5 will revert back to being mapped to 0.5.
+ '''
+ for (curve, f) in self.__values(r, g, b):
+ curve[:] = [f(y) for y in curve]
+
+
+ def cie_manipulate(self, r, g = ..., b = ...):
+ '''
+ Manipulate the colour curves using a (lambda) function on the CIE xyY colour space
+
+ @param r:(float)?→float Function to manipulate the red colour curve
+ @param g:(float)?→float|... Function to manipulate the green colour curve, defaults to `r` if `...`
+ @param b:(float)?→float|... Function to manipulate the blue colour curve, defaults to `g` if `...`
+
+ `None` means that nothing is done for that subpixel
+
+ The lambda functions thats a colour value and maps it to a new illumination value.
+ For example, if the value 0.5 is already mapped to 0.25, then if the function
+ maps 0.25 to 0.5, the value 0.5 will revert back to being mapped to 0.5.
+ '''
+ # Handle overloading
+ if g is ...: g = r
+ if b is ...: b = g
+ # Check if we can reduce the overhead, we can if the adjustments are identical
+ same = (r is g) and (g is b)
+ if same:
+ if r is None:
+ return
+ # Manipulate all curves in one step if their adjustments are identical
+ for i in range(i_size):
+ # Convert to CIE xyY
+ (x, y, Y) = srgb_to_ciexyy(self.red[i] / self.maximum,
+ self.green[i] / self.maximum,
+ self.blue[i] / self.maximum)
+ # Manipulate and convert by to sRGB
+ (r_, g_, b_) = ciexyy_to_srgb(x, y, r(Y))
+ self.red[i] = r_ * self.maximum
+ self.green[i] = g_ * self.maximum
+ self.blue[i] = b_ * self.maximum
+ elif any(f is not None for f in (r, g, b)):
+ # Manipulate all curves individually if their adjustments are not identical
+ # if we are given a function for any curve
+ for i in range(i_size):
+ # Convert to CIE xyY
+ (x, y, Y) = srgb_to_ciexyy(self.red[i] / self.maximum,
+ self.green[i] / self.maximum,
+ self.blue[i] / self.maximum)
+ # Manipulate and convert by to sRGB for selected channels individually
+ if r is not None: self.red[i] = ciexyy_to_srgb(x, y, r(Y))[0] * self.maximum
+ if g is not None: self.green[i] = ciexyy_to_srgb(x, y, g(Y))[1] * self.maximum
+ if b is not None: self.blue[i] = ciexyy_to_srgb(x, y, b(Y))[2] * self.maximum
+
+
+ def lower_resolution(self, rx_colours = None, ry_colours = None, gx_colours = ..., gy_colours = ..., bx_colours = ..., by_colours = ...):
+ '''
+ Emulates low colour resolution
+
+ @param rx_colours:int? The number of colours to emulate on the red encoding axis
+ @param ry_colours:int? The number of colours to emulate on the red output axis
+ @param gx_colours:int|...? The number of colours to emulate on the green encoding axis, `rx_colours` if `...`
+ @param gy_colours:int|...? The number of colours to emulate on the green output axis, `ry_colours` if `...`
+ @param bx_colours:int|...? The number of colours to emulate on the blue encoding axis, `gx_colours` if `...`
+ @param by_colours:int|...? The number of colours to emulate on the blue output axis, `gy_colours` if `...`
+
+ Where `None` is used the default value will be used, for *x_colours:es that is `i_size`,
+ and for *y_colours:es that is `o_size`
+ '''
+ # Handle overloading
+ if gx_colours is ...: gx_colours = rx_colours
+ if gy_colours is ...: gy_colours = ry_colours
+ if bx_colours is ...: bx_colours = gx_colours
+ if by_colours is ...: by_colours = gy_colours
+ # Select default values where default is requested
+ if rx_colours is None: rx_colours = i_size
+ if ry_colours is None: ry_colours = o_size
+ if gx_colours is None: gx_colours = i_size
+ if gy_colours is None: gy_colours = o_size
+ if bx_colours is None: bx_colours = i_size
+ if by_colours is None: by_colours = o_size
+ # Combine pair X and Y parameters for each channel
+ r_colours = (rx_colours, ry_colours)
+ g_colours = (gx_colours, gy_colours)
+ b_colours = (bx_colours, by_colours)
+ # Manipulate colour curves
+ for i_curve, (x_colours, y_colours) in self.__values(r_colours, g_colours, b_colours):
+ # But not if adjustment is neutral
+ if (x_colours == i_size) and (y_colours == o_size):
+ continue
+ o_curve = [0] * i_size
+ x_, y_, i_ = x_colours - 1, y_colours - 1, i_size - 1
+ for i in range(i_size):
+ # Scale encoding
+ x = int(i * x_colours / i_size)
+ x = int(x * i_ / x_)
+ # Scale output
+ y = int(i_curve[x] / self.maximum * y_ + 0.5)
+ o_curve[i] = y / y_ * self.maximum
+ i_curve[:] = o_curve
+
+
+ def start_over(self, r = True, g = ..., b = ...):
+ '''
+ Reverts the curves to identity mappings
+
+ @param r:bool Whether to reset the red colour curve
+ @param g:bool|... Whether to reset the green colour curve, defaults to `r` if `...`
+ @param b:bool|... Whether to reset the blue colour curve, defaults to `g` if `...`
+ '''
+ if self.depth > 0:
+ def make_ramp(size):
+ return [int(x * self.maximum / (size - 1) + 0.5) for x in range(size)]
+ else:
+ def make_ramp(size):
+ return [x / (size - 1) for x in range(size)]
+ for curve in self.__bool(r, g, b):
+ curve[:] = make_ramp(len(curve))
+
+
+ def clip_below(self, r = True, g = ..., b = ...):
+ '''
+ Clip all values below the actual minimum
+
+ @param r:bool Whether to clip the red colour curve
+ @param g:bool|... Whether to clip the green colour curve, defaults to `r` if `...`
+ @param b:bool|... Whether to clip the blue colour curve, defaults to `g` if `...`
+ '''
+ for curve in self.__bool(r, g, b):
+ curve[:] = [max(0, y) for y in curve]
+
+
+ def clip_above(self, r = True, g = ..., b = ...):
+ '''
+ Clip all values above the actual maximum
+
+ @param r:bool Whether to clip the red colour curve
+ @param g:bool|... Whether to clip the green colour curve, defaults to `r` if `...`
+ @param b:bool|... Whether to clip the blue colour curve, defaults to `g` if `...`
+ '''
+ for curve in self.__bool(r, g, b):
+ curve[:] = [min(y, self.maximum) for y in curve]
+
+
+ def clip(self, r = True, g = ..., b = ...):
+ '''
+ Clip all values below the actual minimum and above the actual maximum
+
+ @param r:bool Whether to clip the red colour curve
+ @param g:bool|... Whether to clip the green colour curve, defaults to `r` if `...`
+ @param b:bool|... Whether to clip the blue colour curve, defaults to `g` if `...`
+ '''
+ for curve in self.__bool(r, g, b):
+ curve[:] = [min(max(0, y), self.maximum) for y in curve]
+
class LibgammaCRTC(CRTC):
'''
@@ -674,39 +1353,40 @@ class LibgammaCRTC(CRTC):
'''
import libgamma
CRTC.__init__(self)
- self.crtc = libgamma.CRTC(screen, crtc)
+ self.crtc = libgamma.CRTC(screen.screen, crtc)
+ self.screen = screen
if screen.display.caps.crtc_restore:
self.restore = self.crtc.restore
else:
self.restore = None
- info = libgamma.information(~0)[0]
+ info = self.crtc.information(~0)[0]
connector_types = {
- libgamma.LIBGAMMA_CONNECTOR_TYPE_9PinDIN = '9PinDIN',
- libgamma.LIBGAMMA_CONNECTOR_TYPE_Component = 'Component',
- libgamma.LIBGAMMA_CONNECTOR_TYPE_Composite = 'Composite',
- libgamma.LIBGAMMA_CONNECTOR_TYPE_DSI = 'DSI',
- libgamma.LIBGAMMA_CONNECTOR_TYPE_DVI = 'DVI',
- libgamma.LIBGAMMA_CONNECTOR_TYPE_DVIA = 'DVIA',
- libgamma.LIBGAMMA_CONNECTOR_TYPE_DVID = 'DVID',
- libgamma.LIBGAMMA_CONNECTOR_TYPE_DVII = 'DVII',
- libgamma.LIBGAMMA_CONNECTOR_TYPE_DisplayPort = 'DisplayPort',
- libgamma.LIBGAMMA_CONNECTOR_TYPE_HDMI = 'HDMI',
- libgamma.LIBGAMMA_CONNECTOR_TYPE_HDMIA = 'HDMIA',
- libgamma.LIBGAMMA_CONNECTOR_TYPE_HDMIB = 'HDMIB',
- libgamma.LIBGAMMA_CONNECTOR_TYPE_LFP = 'LFP',
- libgamma.LIBGAMMA_CONNECTOR_TYPE_LVDS = 'LVDS',
- libgamma.LIBGAMMA_CONNECTOR_TYPE_SVIDEO = 'SVIDEO',
- libgamma.LIBGAMMA_CONNECTOR_TYPE_TV = 'TV',
- libgamma.LIBGAMMA_CONNECTOR_TYPE_VGA = 'VGA',
- libgamma.LIBGAMMA_CONNECTOR_TYPE_VIRTUAL = 'VIRTUAL',
- libgamma.LIBGAMMA_CONNECTOR_TYPE_eDP = 'eDP'
+ libgamma.LIBGAMMA_CONNECTOR_TYPE_9PinDIN : '9PinDIN',
+ libgamma.LIBGAMMA_CONNECTOR_TYPE_Component : 'Component',
+ libgamma.LIBGAMMA_CONNECTOR_TYPE_Composite : 'Composite',
+ libgamma.LIBGAMMA_CONNECTOR_TYPE_DSI : 'DSI',
+ libgamma.LIBGAMMA_CONNECTOR_TYPE_DVI : 'DVI',
+ libgamma.LIBGAMMA_CONNECTOR_TYPE_DVIA : 'DVIA',
+ libgamma.LIBGAMMA_CONNECTOR_TYPE_DVID : 'DVID',
+ libgamma.LIBGAMMA_CONNECTOR_TYPE_DVII : 'DVII',
+ libgamma.LIBGAMMA_CONNECTOR_TYPE_DisplayPort : 'DisplayPort',
+ libgamma.LIBGAMMA_CONNECTOR_TYPE_HDMI : 'HDMI',
+ libgamma.LIBGAMMA_CONNECTOR_TYPE_HDMIA : 'HDMIA',
+ libgamma.LIBGAMMA_CONNECTOR_TYPE_HDMIB : 'HDMIB',
+ libgamma.LIBGAMMA_CONNECTOR_TYPE_LFP : 'LFP',
+ libgamma.LIBGAMMA_CONNECTOR_TYPE_LVDS : 'LVDS',
+ libgamma.LIBGAMMA_CONNECTOR_TYPE_SVIDEO : 'SVIDEO',
+ libgamma.LIBGAMMA_CONNECTOR_TYPE_TV : 'TV',
+ libgamma.LIBGAMMA_CONNECTOR_TYPE_VGA : 'VGA',
+ libgamma.LIBGAMMA_CONNECTOR_TYPE_VIRTUAL : 'VIRTUAL',
+ libgamma.LIBGAMMA_CONNECTOR_TYPE_eDP : 'eDP'
}
subpixel_orders = {
- libgamma.LIBGAMMA_SUBPIXEL_ORDER_HORIZONTAL_BGR = 'BGR',
- libgamma.LIBGAMMA_SUBPIXEL_ORDER_HORIZONTAL_RGB = 'RGB',
- libgamma.LIBGAMMA_SUBPIXEL_ORDER_NONE = 'None',
- libgamma.LIBGAMMA_SUBPIXEL_ORDER_VERTICAL_BGR = 'vBGR',
- libgamma.LIBGAMMA_SUBPIXEL_ORDER_VERTICAL_RGB = 'vRGB'
+ libgamma.LIBGAMMA_SUBPIXEL_ORDER_HORIZONTAL_BGR : 'BGR',
+ libgamma.LIBGAMMA_SUBPIXEL_ORDER_HORIZONTAL_RGB : 'RGB',
+ libgamma.LIBGAMMA_SUBPIXEL_ORDER_NONE : 'None',
+ libgamma.LIBGAMMA_SUBPIXEL_ORDER_VERTICAL_BGR : 'vBGR',
+ libgamma.LIBGAMMA_SUBPIXEL_ORDER_VERTICAL_RGB : 'vRGB'
}
self.edid = None if info.edid_error else libgamma.behex_edid_uppercase(info.edid)
self.width_mm = None if info.width_mm_error else info.width_mm
@@ -728,6 +1408,7 @@ class LibgammaCRTC(CRTC):
self.ramps = libgamma.GammaRamps(self.red_gamma_size, self.green_gamma_size,
self.blue_gamma_size, depth = self.gamma_depth)
+
@property
def backend(self):
'''
@@ -738,6 +1419,7 @@ class LibgammaCRTC(CRTC):
'''
return 'libgamma'
+
def get_gamma(self, low_priority = None, high_priority = None, coalesce = True):
'''
Get the gamma ramps on the CRTC or the table of applied adjustments
@@ -761,9 +1443,10 @@ class LibgammaCRTC(CRTC):
'''
if low_priority is not None or high_priority is not None or not coalesce:
raise Exception('Cooperative gamma is not supported')
- self.crtc.get_gamma(self.ramps):
+ self.crtc.get_gamma(self.ramps)
return Ramps.copy(self.ramps)
+
def set_gamma(self, ramps, priority = None, rule = None, lifespan = 1):
'''
Set the gamma ramps on the CRTC
@@ -785,17 +1468,17 @@ class LibgammaCRTC(CRTC):
if priority is not None or rule is not None or lifespan != 1:
raise Exception('Cooperative gamma is not supported')
if ramps is self.ramps:
- self.crtc.set_gamma(ramps):
+ self.crtc.set_gamma(ramps)
return ramps
- match = ramps.gamma == self.gamma_depth:
- match = match and len(ramps.red) == self.red_gamma_size:
- match = match and len(ramps.green) == self.green_gamma_size:
- match = match and len(ramps.blue) == self.blue_gamma_size:
+ match = ramps.depth == self.gamma_depth
+ match = match and len(ramps.red) == self.red_gamma_size
+ match = match and len(ramps.green) == self.green_gamma_size
+ match = match and len(ramps.blue) == self.blue_gamma_size
if not match:
- ramps = Ramps.copy(ramp, self.gamma_depth,
+ ramps = Ramps.copy(ramps, self.gamma_depth,
(self.red_gamma_size, self.green_gamma_size, self.blue_gamma_size))
if isinstance(ramps, libgamma.GammaRamps):
- self.crtc.set_gamma(ramps):
+ self.crtc.set_gamma(ramps)
return
for i in range(len(ramps.red)):
self.ramps.red[i] = ramps.red[i]
@@ -806,11 +1489,10 @@ class LibgammaCRTC(CRTC):
self.crtc.set_gamma(self.ramps)
return self.ramps
+
class LibgammaScreen(Screen):
'''
A screen (or graphics card) using the libgamma backend
-
- @variable crtcs:list<LibgammaCRTC> The CRTC:s in the screen
'''
def __init__(self, display, screen, crtcs = None):
'''
@@ -823,7 +1505,8 @@ class LibgammaScreen(Screen):
@param crtcs:set<int|str>? List of CRTC:s to include, `None` for all
'''
import libgamma
- self.screen = libgamma.Partition(display, screen)
+ self.screen = libgamma.Partition(display.display, screen)
+ self.display = display
if display.caps.partition_restore:
self.restore = self.screen.restore
elif display.caps.crtc_restore:
@@ -833,14 +1516,15 @@ class LibgammaScreen(Screen):
self.crtcs = []
if crtcs is not None:
crtcs = list(crtcs)
- for i in range(self.display.crtcs_available):
- crtc = LibgammaCRTC(self.screen, i)
+ for i in range(self.screen.crtcs_available):
+ crtc = LibgammaCRTC(self, i)
if (crtcs is None) or (i in crtcs) or (crtc.connector_name in crtcs):
self.crtcs.append(crtc)
elif isinstance(crtc.edid, str) and (crtc.edid.upper() in crtcs):
self.crtcs.append(crtc)
else:
del crtc
+
@property
def backend(self):
@@ -852,20 +1536,18 @@ class LibgammaScreen(Screen):
'''
return 'libgamma'
+
def __restore_all_crtcs(self):
'''
Restore the CLUT:s to the (configured) system defaults, for each CRTC
'''
- for crtc for self.crtcs:
+ for crtc in self.crtcs:
crtc.restore()
+
class LibgammaDisplay(Display):
'''
A display using the libgamma backend
-
- @variable screens:list<LibgammaScreen> The screens in the display
- @variable crtcs:list<LibgammaCRTC> The CRTC:s in the display
- @variable cooperative:bool Whether the adjustment method supports cooperative gamma
'''
def __init__(self, method = None, display = None, screens = None, crtcs = None):
'''
@@ -903,10 +1585,11 @@ class LibgammaDisplay(Display):
cs = crtcs
if isinstance(cs, dict):
cs = cs[screen] if screen in cs else []
- screen = LibgammaScreen(self.site, screen, cs)
+ screen = LibgammaScreen(self, screen, cs)
self.screens.append(screen)
self.crtcs.extend(screen.crtcs)
+
@property
def backend(self):
'''
@@ -917,6 +1600,7 @@ class LibgammaDisplay(Display):
'''
return 'libgamma'
+
@property
def lowest_priority(self):
'''
@@ -930,6 +1614,7 @@ class LibgammaDisplay(Display):
'''
return None
+
@property
def highest_priority(self):
'''
@@ -943,13 +1628,15 @@ class LibgammaDisplay(Display):
'''
return None
+
def __restore_all_partitions(self):
'''
Restore the CLUT:s to the (configured) system defaults, for each screen
'''
- for screen for self.screens:
+ for screen in self.screens:
screen.restore()
+
def get_adjustment_methods(libgamma_level = 0):
'''
Returns a list of available adjustment methods
@@ -969,18 +1656,19 @@ def get_adjustment_methods(libgamma_level = 0):
import libgamma
lgamma_meths = libgamma.list_methods(libgamma_level)
lgamma_map = {
- libgamma.LIBGAMMA_METHOD_DUMMY = 'dummy',
- libgamma.LIBGAMMA_METHOD_X_RANDR = 'randr',
- libgamma.LIBGAMMA_METHOD_X_VIDMODE = 'vidmode'
- libgamma.LIBGAMMA_METHOD_LINUX_DRM = 'drm',
- libgamma.LIBGAMMA_METHOD_W32_GDI = 'w32gdi',
- libgamma.LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS = 'quartz'
+ libgamma.LIBGAMMA_METHOD_DUMMY : 'dummy',
+ libgamma.LIBGAMMA_METHOD_X_RANDR : 'randr',
+ libgamma.LIBGAMMA_METHOD_X_VIDMODE : 'vidmode',
+ libgamma.LIBGAMMA_METHOD_LINUX_DRM : 'drm',
+ libgamma.LIBGAMMA_METHOD_W32_GDI : 'w32gdi',
+ libgamma.LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS : 'quartz'
}
ret += [lgamma_map[m] if m in lgamma_map else m for m in lgamma_meths]
except:
pass
return ret
+
def get_outputs(method = None, display = None, screens = None, crtcs = None):
'''
Get access to CRTC for editing the their gamma ramps
@@ -1003,14 +1691,19 @@ def get_outputs(method = None, display = None, screens = None, crtcs = None):
@return :Display A display
'''
if isinstance(method, str):
- lgamma_meths = {
- 'dummy' = libgamma.LIBGAMMA_METHOD_DUMMY,
- 'randr' = libgamma.LIBGAMMA_METHOD_X_RANDR,
- 'vidmode' = libgamma.LIBGAMMA_METHOD_X_VIDMODE,
- 'drm' = libgamma.LIBGAMMA_METHOD_LINUX_DRM,
- 'w32gdi' = libgamma.LIBGAMMA_METHOD_W32_GDI,
- 'quartz' = libgamma.LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
- }
- return LibgammaDisplay(lgamma_meths[method], display, screen, crtc)
+ #try:
+ import libgamma
+ lgamma_meths = {
+ 'dummy' : libgamma.LIBGAMMA_METHOD_DUMMY,
+ 'randr' : libgamma.LIBGAMMA_METHOD_X_RANDR,
+ 'vidmode' : libgamma.LIBGAMMA_METHOD_X_VIDMODE,
+ 'drm' : libgamma.LIBGAMMA_METHOD_LINUX_DRM,
+ 'w32gdi' : libgamma.LIBGAMMA_METHOD_W32_GDI,
+ 'quartz' : libgamma.LIBGAMMA_METHOD_QUARTZ_CORE_GRAPHICS
+ }
+ return LibgammaDisplay(lgamma_meths[method], display, screens, crtcs)
+ #except:
+ # pass
+ #raise Exception("Adjustment method %s is not available" % method)
else:
return LibgammaDisplay(method, display, screen, crtc)
diff --git a/src/solar.py b/src/solar.py
index 6afb56d..074bf7f 100644
--- a/src/solar.py
+++ b/src/solar.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (maandree@kth.se)
+# Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (m@maandree.se)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
diff --git a/src/weather.py b/src/weather.py
index 7bd7402..17a8638 100644
--- a/src/weather.py
+++ b/src/weather.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (maandree@kth.se)
+# Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (m@maandree.se)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -114,7 +114,7 @@ def weather(station = None, downloader = None):
visibility = None
## Get weather
weather = '' if 'weather' not in output else output['weather']
- ## Unify conjnuctions
+ ## Unify conjuctions
weather = weather.replace(',', ';').replace(' with ', ';')
## Remove undesired details
# Not important as we are not pilots, we are probably far away