From e2bf827ecc9e54b321c2febfe3664ec0428bc8fc Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Sat, 5 Oct 2024 23:38:34 +0200 Subject: Update e-mail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/__main__.py | 9 +- src/adhoc.py | 2 +- src/aux.py | 2 +- src/backlight.py | 4 +- src/blackbody.py | 2 +- src/blueshift_iccprofile.c | 2 +- src/colour.py | 2 +- src/completion | 2 +- src/curve.py | 37 +- src/icc.py | 2 +- src/interpolation.py | 122 +++++-- src/libgammaman.py | 119 ------ src/monitor.py | 236 ++++++------ src/output.py | 877 ++++++++++++++++++++++++++++++++++++++++----- src/solar.py | 2 +- src/weather.py | 4 +- 16 files changed, 1024 insertions(+), 400 deletions(-) delete mode 100644 src/libgammaman.py (limited to 'src') 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 . -# 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 The red colour curves @param g:list The green colour curves @param b:list The blue colour curves - @return :(r:list, g:list, b:list) 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, g:list, b:list) 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 The green colour curves @param b:list The blue colour curves @param tension:float A [0, 1] value of the tension - @return :(r:list, g:list, b:list) 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, g:list, b:list) 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 The green colour curves @param b:list The blue colour curves @param tension:float A [0, 1] value of the tension - @return :(r:list, g:list, b:list) 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, g:list, b:list) 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 The red colour curves @param g:list The green colour curves @param b:list The blue colour curves - @return :(r:list, g:list, b:list) 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, g:list, b:list) 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 . - -# 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 . -# 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 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 The screens in the display + @variable crtcs:list 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 The CRTC:s in the screen ''' def __init__(self, display, screen, crtcs = None): ''' @@ -823,7 +1505,8 @@ class LibgammaScreen(Screen): @param crtcs:set? 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 The screens in the display - @variable crtcs:list 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 -- cgit v1.2.3-70-g09d2