From fc49478a082a300940878e5de72fa2055ca7b3b0 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Fri, 21 Feb 2014 14:50:00 +0100 Subject: add vidmode support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- .gitignore | 1 + DEPENDENCIES | 8 ++- Makefile | 25 +++++--- TODO | 1 - info/blueshift.texinfo | 19 +++--- src/blueshift_vidmode.pyx | 74 +++++++++++++++++++++++ src/blueshift_vidmode_c.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++ src/monitor.py | 35 ++++++++++- 8 files changed, 292 insertions(+), 22 deletions(-) create mode 100644 src/blueshift_vidmode.pyx create mode 100644 src/blueshift_vidmode_c.c diff --git a/.gitignore b/.gitignore index a978561..ec0c588 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ obj/ *.zip __pycache__/ /src/blueshift_randr.c +/src/blueshift_vidmode.c *.info *.pdf *.dvi diff --git a/DEPENDENCIES b/DEPENDENCIES index 8c7a6ee..0b94abc 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -1,7 +1,9 @@ RUNTIME DEPENDENCIES: python3 - libxcb + libxcb (opt-out, for randr) + libx11 (opt-out, for vidmode) + libxxf86vm (opt-out, for vidmode) argparser-python (https://github.com/maandree/argparser) @@ -10,7 +12,9 @@ MAKE DEPENDENCIES: cython c99 (a C 99 compliant compiler; provided by gcc) python3 - libxcb + libxcb (opt-out, for randr) + libx11 (opt-out, for vidmode) + libxxf86vm (opt-out, for vidmode) make coreutils sed diff --git a/Makefile b/Makefile index 8e2c9d1..2e87f52 100644 --- a/Makefile +++ b/Makefile @@ -19,16 +19,21 @@ SHEBANG ?= /usr/bin/python3 COMMAND ?= blueshift PKGNAME ?= blueshift +SERVER_BINDINGS ?= randr vidmode + PKGCONFIG ?= pkg-config OPTIMISE ?= -Og -g WARN = -Wall -Wextra -pedantic -LIBS = xcb-randr python3 +LIBS_randr = xcb-randr +LIBS_vidmode = x11 xxf86vm +LIBS = python3 $(foreach B,$(SERVER_BINDINGS),$(LIBS_$(B))) STD = c99 FLAGS = $$($(PKGCONFIG) --cflags --libs $(LIBS)) -std=$(STD) $(WARN) $(OPTIMISE) -fPIC DATAFILES = 2deg 10deg redshift redshift_old PYFILES = __main__.py colour.py curve.py monitor.py solar.py icc.py +CBINDINGS = $(foreach B,$(SERVER_BINDINGS),blueshift_$(B).so) EXAMPLES = comprehensive sleepmode @@ -54,7 +59,7 @@ dvi: blueshift.dvi ps: blueshift.ps .PHONY: command -command: bin/blueshift_randr.so bin/blueshift +command: $(foreach C,$(CBINDINGS),bin/$(C)) bin/blueshift .PHONY: shell shell: bash zsh fish @@ -84,7 +89,7 @@ obj/%.py: src/%.py sed -i '/^LIBDIR *= /s#^.*$$#LIBDIR = '\''$(LIBDIR)'\''#' $@ -bin/blueshift_randr.so: obj/blueshift_randr.o obj/blueshift_randr_c.o +bin/%.so: obj/%.o obj/%_c.o @mkdir -p bin $(CC) $(FLAGS) -shared -o $@ $^ @@ -96,10 +101,10 @@ obj/%.o: obj/%.c @mkdir -p obj $(CC) $(FLAGS) -c -o $@ $< -obj/blueshift_randr.c: src/blueshift_randr.pyx +obj/%.c: src/%.pyx @mkdir -p obj - if ! cython -3 -v $<; then src/blueshift_randr.c ; false ; fi - mv src/blueshift_randr.c $@ + if ! cython -3 -v $<; then src/$*.c ; false ; fi + mv src/$*.c $@ %.info: info/%.texinfo @@ -142,11 +147,11 @@ install-all: install-base install-doc install-shell install-base: install-command install-license .PHONY: install-command -install-command: bin/blueshift_randr.so bin/blueshift $(foreach D,$(DATAFILES),res/$(D)) +install-command: $(foreach C,$(CBINDINGS),bin/$(C)) bin/blueshift $(foreach D,$(DATAFILES),res/$(D)) install -dm755 -- "$(DESTDIR)$(BINDIR)" install -m755 bin/blueshift -- "$(DESTDIR)$(BINDIR)/$(COMMAND)" install -dm755 -- "$(DESTDIR)$(LIBDIR)" - install -m755 bin/blueshift_randr.so -- "$(DESTDIR)$(LIBDIR)/blueshift_randr.so" + install -m755 $(foreach C,$(CBINDINGS),bin/$(C)) -- "$(DESTDIR)$(LIBDIR)" install -dm755 -- "$(DESTDIR)$(DATADIR)/$(PKGNAME)" install -m644 -- $(foreach D,$(DATAFILES),res/$(D)) "$(DESTDIR)$(DATADIR)/$(PKGNAME)" @@ -205,7 +210,7 @@ install-fish: blueshift.fish .PHONY: uninstall uninstall: -rm -- "$(DESTDIR)$(BINDIR)/$(COMMAND)" - -rm -- "$(DESTDIR)$(LIBDIR)/blueshift_randr.so" + -rm -- $(foreach C,$(CBINDINGS),"$(DESTDIR)$(LIBDIR)/$(C)") -rm -- "$(DESTDIR)$(LICENSEDIR)/$(PKGNAME)/COPYING" -rm -- "$(DESTDIR)$(LICENSEDIR)/$(PKGNAME)/LICENSE" -rmdir -- "$(DESTDIR)$(LICENSEDIR)/$(PKGNAME)" @@ -231,5 +236,5 @@ uninstall: .PHONY: all clean: - -rm -r bin obj src/blueshift_randr.c blueshift.{ba,z,fi}sh + -rm -r bin obj src/blueshift_randr.c src/blueshift_vidmode.c blueshift.{ba,z,fi}sh diff --git a/TODO b/TODO index 03d0137..5c0e511 100644 --- a/TODO +++ b/TODO @@ -5,5 +5,4 @@ Medium priority: Low priority: Tray icon with support for temporary enable/disable and configuration reload - VidMode support diff --git a/info/blueshift.texinfo b/info/blueshift.texinfo index 902b1e6..a112a93 100644 --- a/info/blueshift.texinfo +++ b/info/blueshift.texinfo @@ -546,17 +546,20 @@ the three colour components, not tuples. Input and output is one colour instance. To apply a colour curve to the display -server, invoke the @code{randr} function; +server, invoke the @code{randr} function, or +@code{vidmode}@footnote{@code{vidmode} has +the same API as @code{randr}, but it only +supports using the zeroth CRTC}; @code{print_curves} can be used to print the curves to stdout instead (for debugging). These functions apply the curves to all -monitors in the default screen (screen 0), put -you can also use select monitors by specifying -each monitor in as separate arguments. The -monitors are indexed from zero. The screen by -can be selected by adding the argument -@code{screen = X}, where @code{X} is the -index of the screen. +monitors in the default screen (screen 0), +put you can also use select monitors by +specifying each monitor in as separate +arguments. The monitors are indexed from +zero. The screen by can be selected by +adding the argument @code{screen = X}, +where @code{X} is the index of the screen. If you want to write your own curve flushing fucntion @code{translate_to_integers} can be diff --git a/src/blueshift_vidmode.pyx b/src/blueshift_vidmode.pyx new file mode 100644 index 0000000..7d29655 --- /dev/null +++ b/src/blueshift_vidmode.pyx @@ -0,0 +1,74 @@ +# -*- python -*- + +# Copyright © 2014 Mattias Andrée (maandree@member.fsf.org) +# +# 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 +# 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +cimport cython +from libc.stdlib cimport malloc, free + + +cdef extern int blueshift_vidmode_open(int use_screen) +cdef extern int blueshift_vidmode_apply(unsigned long long int use_crtcs, + unsigned short int* r_curve, + unsigned short int* g_curve, + unsigned short int* b_curve) +cdef extern void blueshift_vidmode_close() + + +def vidmode_open(int use_screen): + ''' + Start stage of colour curve control + + @param use_screen The screen to use + @return Zero on success + ''' + return blueshift_vidmode_open(use_screen) + + +def vidmode_apply(unsigned long long use_crtcs, r_curve, g_curve, b_curve): + ''' + Apply stage of colour curve control + + @param use_crtcs Mask of CRTC:s to use + @param r_curve:list The red colour curve + @param g_curve:list The green colour curve + @param b_curve:list The blue colour curve + @return Zero on success + ''' + cdef unsigned short int* r + cdef unsigned short int* g + cdef unsigned short int* b + r = malloc(256 * 2) + g = malloc(256 * 2) + b = malloc(256 * 2) + if (r is NULL) or (g is NULL) or (b is NULL): + raise MemoryError() + for i in range(256): + r[i] = r_curve[i] & 0xFFFF + g[i] = g_curve[i] & 0xFFFF + b[i] = b_curve[i] & 0xFFFF + rc = blueshift_vidmode_apply(use_crtcs, r, g, b) + free(r) + free(g) + free(b) + return rc + + +def vidmode_close(): + ''' + Resource freeing stage of colour curve control + ''' + blueshift_vidmode_close() + diff --git a/src/blueshift_vidmode_c.c b/src/blueshift_vidmode_c.c new file mode 100644 index 0000000..b160e7e --- /dev/null +++ b/src/blueshift_vidmode_c.c @@ -0,0 +1,151 @@ +/** + * Copyright © 2014 Mattias Andrée (maandree@member.fsf.org) + * + * 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 + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include +#include +#include + +#include +#include + + + +/** + * The X server display + */ +static Display* display; + +/** + * The X screen + */ +static int screen; + +/** + * Size of colour curves on the X-axis + */ +static int curve_size; + + + +/** + * Start stage of colour curve control + * + * @param use_screen The screen to use + * @return Zero on success + */ +int blueshift_vidmode_open(int use_screen) +{ + int _major, _minor; + uint16_t* r_gamma; + uint16_t* g_gamma; + uint16_t* b_gamma; + + + /* Get X display */ + + if ((display = XOpenDisplay(NULL)) == NULL) + { + fprintf(stderr, "Cannot open X display\n"); + return 1; + } + + + /* Check for VidMode extension */ + + if (XF86VidModeQueryVersion(display, &_major, &_minor) == 0) + { + fprintf(stderr, "VidMode version query failed\n"); + XCloseDisplay(display); + return 1; + } + + + /* Get curve X-axis size */ + + screen = use_screen; + if (XF86VidModeGetGammaRampSize(display, screen, &curve_size) == 0) + { + fprintf(stderr, "VidMode gamma size query failed\n"); + XCloseDisplay(display); + return 1; + } + + if (curve_size < 1) + { + fprintf(stderr, "VidMode gamma size query failed\n"); + XCloseDisplay(display); + return 1; + } + + + /* Acquire curve control */ + + r_gamma = malloc(3 * curve_size * sizeof(uint16_t)); + if (r_gamma == NULL) + { + fprintf(stderr, "Out of memory\n"); + return 1; + } + g_gamma = r_gamma + curve_size; + b_gamma = g_gamma + curve_size; + if (XF86VidModeGetGammaRamp(display, screen, curve_size, r_gamma, g_gamma, b_gamma) == 0) + { + fprintf(stderr, "VidMode gamma query failed\n"); + free(r_gamma); + XCloseDisplay(display); + return 1; + } + free(r_gamma); + + return 0; +} + + +/** + * Apply stage of colour curve control + * + * @param use_crtcs Mask of CRTC:s to use + * @param r_curve The red colour curve + * @param g_curve The green colour curve + * @param b_curve The blue colour curve + * @return Zero on success + */ +int blueshift_vidmode_apply(uint64_t use_crtcs, uint16_t* r_curve, uint16_t* g_curve, uint16_t* b_curve) +{ + (void) use_crtcs; + + /* Apply curves */ + + if (XF86VidModeSetGammaRamp(display, screen, curve_size, r_curve, g_curve, b_curve) == 0) + { + fprintf(stderr, "VidMode gamma control failed\n"); + return 1; + } + + return 0; +} + + +/** + * Resource freeing stage of colour curve control + */ +void blueshift_vidmode_close(void) +{ + /* Free remaining resources */ + + XCloseDisplay(display); +} + diff --git a/src/monitor.py b/src/monitor.py index 87a2290..ecc827b 100644 --- a/src/monitor.py +++ b/src/monitor.py @@ -24,6 +24,7 @@ LIBDIR = 'bin' sys.path.append(LIBDIR) randr_opened = None +vidmode_opened = None def translate_to_integers(): @@ -46,11 +47,15 @@ def close_c_bindings(): ''' Close all C bindings and let them free resources and close connections ''' - global randr_opened + global randr_opened, vidmode_opened if randr_opened is not None: from blueshift_randr import randr_close randr_opened = None randr_close() + if vidmode_opened is not None: + from blueshift_vidmode import vidmode_close + vidmode_opened = None + vidmode_close() def randr(*crtcs, screen = 0): @@ -81,6 +86,34 @@ def randr(*crtcs, screen = 0): pass # Happens on exit by TERM signal +def vidmode(*crtcs, screen = 0): + ''' + Applies colour curves using the X11 extension vidmode + + @param *crtcs The CRT controllers to use, all are used if none are specified + @param screen The screen that the monitors belong to + ''' + from blueshift_vidmode import vidmode_open, vidmode_apply, vidmode_close + global vidmode_opened + crtcs = sum([1 << i for i in list(crtcs)]) + if crtcs == 0: + crtcs = (1 << 64) - 1 + + (R_curve, G_curve, B_curve) = translate_to_integers() + if (vidmode_opened is None) or not (vidmode_opened == screen): + if vidmode_opened is not None: + vidmode_close() + if vidmode_open(screen) == 0: + vidmode_opened = screen + else: + sys.exit(1) + try: + if not vidmode_apply(crtcs, R_curve, G_curve, B_curve) == 0: + sys.exit(1) + except OverflowError: + pass # Happens on exit by TERM signal + + def print_curves(*crtcs, screen = 0): ''' Prints the curves to stdout -- cgit v1.2.3-70-g09d2