From c80d58fc1cf9e53ab7d2649f8e154a5de8743aa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Mon, 12 May 2014 04:46:39 +0200 Subject: [PATCH 01/29] Add hidden non-git files and .gch and .su (can be generated when compiling C files) to .gitignore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- .gitignore | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.gitignore b/.gitignore index dc6de478..30a94d15 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +# Hidden and temporary files +.* +!.git* + # Backup files *~ \#*\# @@ -6,6 +10,10 @@ *.o *.ko +# Auxiliary files +*.gch +*.su + # Libraries *.lib *.a From e3f9cb9fb9303a3e60e5b44c7cf46ba34696bba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Mon, 12 May 2014 05:08:55 +0200 Subject: [PATCH 02/29] Add fake-w32gdi (./configure --enable-fakegdi) that enabled the w32gdi method under X by (sloopyly) translating the calls to RandR calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- configure.ac | 17 ++- src/Makefile.am | 5 + src/fake-w32gdi.c | 275 +++++++++++++++++++++++++++++++++++++++++++++ src/fake-w32gdi.h | 77 +++++++++++++ src/gamma-w32gdi.c | 12 +- src/gamma-w32gdi.h | 12 +- 6 files changed, 393 insertions(+), 5 deletions(-) create mode 100644 src/fake-w32gdi.c create mode 100644 src/fake-w32gdi.h diff --git a/configure.ac b/configure.ac index deae0cfc..9b3efb4d 100644 --- a/configure.ac +++ b/configure.ac @@ -104,13 +104,28 @@ AS_IF([test "x$enable_vidmode" != xno], [ ]) AM_CONDITIONAL([ENABLE_VIDMODE], [test "x$enable_vidmode" = xyes]) +# Check for fake Windows GDI +AC_MSG_CHECKING([whether to enable fake WinGDI]) +AC_ARG_ENABLE([fakegdi], [AC_HELP_STRING([--enable-fakegdi], + [enable fake WinGDI])], + [enable_fakegdi=$enableval],[enable_fakegdi=no]) +AS_IF([test "x$enable_fakegdi" != xno], [ + AC_DEFINE([FAKE_W32GDI], 1, + [Define to 1 to enable WinGDI method]) + AC_MSG_RESULT([yes]) + enable_fakegdi=yes +], [ + AC_MSG_RESULT([no]) +]) +AM_CONDITIONAL([FAKE_W32GDI], [test "x$enable_fakegdi" != xno]) + # Check Windows GDI method AC_MSG_CHECKING([whether to enable WinGDI method]) AC_ARG_ENABLE([wingdi], [AC_HELP_STRING([--enable-wingdi], [enable WinGDI method])], [enable_wingdi=$enableval],[enable_wingdi=maybe]) AS_IF([test "x$enable_wingdi" != xno], [ - AS_IF([test $have_windows_h = yes], [ + AS_IF([test $have_windows_h = yes -o $enable_fakegdi = yes], [ AC_DEFINE([ENABLE_WINGDI], 1, [Define to 1 to enable WinGDI method]) AC_MSG_RESULT([yes]) diff --git a/src/Makefile.am b/src/Makefile.am index 37a03080..c48e720e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -22,6 +22,7 @@ EXTRA_redshift_SOURCES = \ gamma-randr.c gamma-randr.h \ gamma-vidmode.c gamma-vidmode.h \ gamma-w32gdi.c gamma-w32gdi.h \ + fake-w32gdi.c fake-w32gdi.h \ location-geoclue.c location-geoclue.h AM_CFLAGS = @@ -53,8 +54,12 @@ endif if ENABLE_WINGDI redshift_SOURCES += gamma-w32gdi.c gamma-w32gdi.h +if FAKE_W32GDI +redshift_SOURCES += fake-w32gdi.c fake-w32gdi.h +else redshift_LDADD += -lgdi32 endif +endif if ENABLE_GEOCLUE diff --git a/src/fake-w32gdi.c b/src/fake-w32gdi.c new file mode 100644 index 00000000..e0692fac --- /dev/null +++ b/src/fake-w32gdi.c @@ -0,0 +1,275 @@ +/* fake-w32gdi.h -- Fake Windows library headers + This file is part of Redshift. + + Redshift 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. + + Redshift 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 Redshift. If not, see . + + Copyright (c) 2014 Mattias Andrée +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "fake-w32gdi.h" + + +#ifndef ENABLE_RANDR + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd144871(v=vs.85).aspx */ +HDC GetDC(HWND hWnd) +{ + (void) hWnd; + return (HDC*)16; +} + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162920(v=vs.85).aspx */ +int ReleaseDC(HWND hWnd, HDC hDC) +{ + (void) hWnd; + (void) hDC; + return 1; +} + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd144877(v=vs.85).aspx */ +int GetDeviceCaps(HDC hDC, int nIndex) +{ + (void) hDC; + return CM_GAMMA_RAMP + nIndex - COLORMGMTCAPS; +} + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd372194(v=vs.85).aspx */ +BOOL SetDeviceGammaRamp(HDC hDC, LPVOID lpRamp) +{ + (void) hDC; + (void) lpRamp; + return TRUE; +} + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd316946(v=vs.85).aspx */ +BOOL GetDeviceGammaRamp(HDC hDC, LPVOID lpRamp) +{ + (void) hDC; + (void) lpRamp; + return TRUE; +} + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd183490(v=vs.85).aspx */ +HDC CreateDC(LPCTSTR lpszDriver, LPCTSTR lpszDevice, void *lpszOutput, void *lpInitData) +{ + (void) lpszOutput; + (void) lpInitData; + if (strcmp(lpszDriver, "DISPLAY")) + return NULL; + (void) lpszDevice; + return (HDC*)16; +} + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162609(v=vs.85).aspx */ +BOOL EnumDisplayDevices(LPCTSTR lpDevice, DWORD iDevNum, PDISPLAY_DEVICE lpDisplayDevice, DWORD dwFlags) +{ + (void) dwFlags; + if (lpDevice != NULL) { + fprintf(stderr, "lpDevice (argument 1) for EnumDisplayDevices should be NULL\n"); + abort(); + return FALSE; + } + if (iDevNum >= 2) + return FALSE; + if (lpDisplayDevice->cb != sizeof(DISPLAY_DEVICE)) { + fprintf(stderr, + "lpDisplayDevice->cb for EnumDisplayDevices is not sizeof(DISPLAY_DEVICE)\n"); + abort(); + return FALSE; + } + strcmp(lpDisplayDevice->DeviceName, "some monitor"); + lpDisplayDevice->StateFlags = DISPLAY_DEVICE_ACTIVE; + return TRUE; +} + +#else + + +#include +#include + + +#define GAMMA_RAMP_SIZE 256 + + +static xcb_connection_t *conn = NULL; +static size_t dc_count = 0; +static ssize_t crtc_count = -1; +static xcb_randr_crtc_t *crtcs = NULL; +static xcb_randr_get_screen_resources_current_reply_t *res_reply = NULL; + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd144871(v=vs.85).aspx */ +HDC GetDC(HWND hWnd) +{ + (void) hWnd; + return CreateDC(TEXT("DISPLAY"), "0", NULL, NULL); +} + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162920(v=vs.85).aspx */ +int ReleaseDC(HWND hWnd, HDC hDC) +{ + (void) hWnd; + (void) hDC; + dc_count--; + if (dc_count == 0) { + if (conn != NULL) + xcb_disconnect(conn); + conn = NULL; + if (res_reply != NULL) + free(res_reply); + res_reply = NULL; + } + return 1; +} + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd144877(v=vs.85).aspx */ +int GetDeviceCaps(HDC hDC, int nIndex) +{ + (void) hDC; + return CM_GAMMA_RAMP + nIndex - COLORMGMTCAPS; +} + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd372194(v=vs.85).aspx */ +BOOL SetDeviceGammaRamp(HDC hDC, LPVOID lpRamp) +{ + xcb_void_cookie_t gamma_cookie = + xcb_randr_set_crtc_gamma_checked( + conn, *(xcb_randr_crtc_t *)hDC, GAMMA_RAMP_SIZE, + ((uint16_t *)lpRamp) + 0 * GAMMA_RAMP_SIZE, + ((uint16_t *)lpRamp) + 1 * GAMMA_RAMP_SIZE, + ((uint16_t *)lpRamp) + 2 * GAMMA_RAMP_SIZE); + xcb_generic_error_t *error = xcb_request_check(conn, gamma_cookie); + return error == NULL ? TRUE : FALSE; +} + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd316946(v=vs.85).aspx */ +BOOL GetDeviceGammaRamp(HDC hDC, LPVOID lpRamp) +{ + xcb_randr_get_crtc_gamma_cookie_t gamma_cookie; + xcb_randr_get_crtc_gamma_reply_t *gamma_reply; + xcb_generic_error_t *error; + + gamma_cookie = xcb_randr_get_crtc_gamma(conn, *(xcb_randr_crtc_t *)hDC); + gamma_reply = xcb_randr_get_crtc_gamma_reply(conn, gamma_cookie, &error); + + if (error) return FALSE; + +#define DEST_RAMP(I) (((uint16_t *)lpRamp) + (I) * GAMMA_RAMP_SIZE) +#define SRC_RAMP(C) (xcb_randr_get_crtc_gamma_##C(gamma_reply)) + + memcpy(DEST_RAMP(0), SRC_RAMP(red), GAMMA_RAMP_SIZE * sizeof(uint16_t)); + memcpy(DEST_RAMP(1), SRC_RAMP(green), GAMMA_RAMP_SIZE * sizeof(uint16_t)); + memcpy(DEST_RAMP(2), SRC_RAMP(blue), GAMMA_RAMP_SIZE * sizeof(uint16_t)); + +#undef SRC_RAMP +#undef DEST_RAMP + + free(gamma_reply); + return TRUE; +} + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd183490(v=vs.85).aspx */ +HDC CreateDC(LPCTSTR lpszDriver, LPCTSTR lpszDevice, void *lpszOutput, void *lpInitData) +{ + (void) lpszOutput; + (void) lpInitData; + + if (strcmp(lpszDriver, "DISPLAY")) + return NULL; + + int crtc_index = atoi(lpszDevice); + + if (dc_count == 0) { + xcb_generic_error_t *error; + xcb_screen_iterator_t iter; + xcb_randr_get_screen_resources_current_cookie_t res_cookie; + + conn = xcb_connect(NULL, NULL); + + iter = xcb_setup_roots_iterator(xcb_get_setup(conn)); + res_cookie = xcb_randr_get_screen_resources_current(conn, iter.data->root); + res_reply = xcb_randr_get_screen_resources_current_reply(conn, res_cookie, &error); + + if (error) { + xcb_disconnect(conn); + crtc_count = -1; + return NULL; + } + + crtc_count = res_reply->num_crtcs; + crtcs = xcb_randr_get_screen_resources_current_crtcs(res_reply); + } + + if (crtc_index >= crtc_count) { + if (dc_count == 0) { + xcb_disconnect(conn); + crtc_count = -1; + } + return NULL; + } + + dc_count++; + return crtcs + crtc_index; +} + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162609(v=vs.85).aspx */ +BOOL EnumDisplayDevices(LPCTSTR lpDevice, DWORD iDevNum, PDISPLAY_DEVICE lpDisplayDevice, DWORD dwFlags) +{ + (void) dwFlags; + size_t count = (size_t)crtc_count; + if (lpDevice != NULL) { + fprintf(stderr, "lpDevice (argument 1) for EnumDisplayDevices should be NULL\n"); + abort(); + return FALSE; + } + if (crtc_count < 0) { + if (GetDC(NULL) == NULL) + return FALSE; + dc_count = 0; + count = (size_t)crtc_count; + ReleaseDC(NULL, NULL); + } + if (iDevNum >= count) + return FALSE; + if (lpDisplayDevice->cb != sizeof(DISPLAY_DEVICE)) { + fprintf(stderr, + "lpDisplayDevice->cb for EnumDisplayDevices is not sizeof(DISPLAY_DEVICE)\n"); + abort(); + return FALSE; + } + sprintf(lpDisplayDevice->DeviceName, "%i", iDevNum); + lpDisplayDevice->StateFlags = DISPLAY_DEVICE_ACTIVE; + return TRUE; +} + + +#endif diff --git a/src/fake-w32gdi.h b/src/fake-w32gdi.h new file mode 100644 index 00000000..b369e6ec --- /dev/null +++ b/src/fake-w32gdi.h @@ -0,0 +1,77 @@ +/* fake-w32gdi.h -- Fake Windows library headers + This file is part of Redshift. + + Redshift 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. + + Redshift 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 Redshift. If not, see . + + Copyright (c) 2014 Mattias Andrée +*/ + +#ifndef REDSHIFT_FAKE_W32GDI_H +#define REDSHIFT_FAKE_W32GDI_H + +#include + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx */ +typedef uint16_t WORD; +typedef uint32_t DWORD; +typedef int BOOL; +typedef void *HDC; +typedef void *HWND; +typedef void *LPVOID; +typedef const char *LPCTSTR; +typedef char TCHAR; +#define TRUE 1 +#define FALSE 0 + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd144871(v=vs.85).aspx */ +HDC GetDC(HWND hWnd); + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162920(v=vs.85).aspx */ +int ReleaseDC(HWND hWnd, HDC hDC); + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd144877(v=vs.85).aspx */ +int GetDeviceCaps(HDC hDC, int nIndex) __attribute__((const)); +#define COLORMGMTCAPS 1 +#define CM_GAMMA_RAMP 1 + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd372194(v=vs.85).aspx */ +BOOL SetDeviceGammaRamp(HDC hDC, LPVOID lpRamp); + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd316946(v=vs.85).aspx */ +BOOL GetDeviceGammaRamp(HDC hDC, LPVOID lpRamp); + + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd183490(v=vs.85).aspx */ +HDC CreateDC(LPCTSTR lpszDriver, LPCTSTR lpszDevice, void *lpszOutput, void *lpInitData); +#define TEXT(X) ((LPCTSTR)(X)) + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd183569(v=vs.85).aspx */ +typedef struct { + DWORD cb; + TCHAR DeviceName[32]; + DWORD StateFlags; +} DISPLAY_DEVICE; +typedef DISPLAY_DEVICE *PDISPLAY_DEVICE; +#define DISPLAY_DEVICE_ACTIVE 1 + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162609(v=vs.85).aspx */ +BOOL EnumDisplayDevices(LPCTSTR lpDevice, DWORD iDevNum, PDISPLAY_DEVICE lpDisplayDevice, DWORD dwFlags); + + + +#endif /* ! REDSHIFT_FAKE_W32GDI_H */ + diff --git a/src/gamma-w32gdi.c b/src/gamma-w32gdi.c index 7193bc91..d882c17c 100644 --- a/src/gamma-w32gdi.c +++ b/src/gamma-w32gdi.c @@ -17,14 +17,22 @@ Copyright (c) 2010 Jon Lund Steffensen */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + #include #include #ifndef WINVER # define WINVER 0x0500 #endif -#include -#include +#ifdef FAKE_W32GDI +# include "fake-w32gdi.h" +#else +# include +# include +#endif #ifdef ENABLE_NLS # include diff --git a/src/gamma-w32gdi.h b/src/gamma-w32gdi.h index e81f4c5e..4b085fae 100644 --- a/src/gamma-w32gdi.h +++ b/src/gamma-w32gdi.h @@ -20,8 +20,16 @@ #ifndef REDSHIFT_GAMMA_W32GDI_H #define REDSHIFT_GAMMA_W32GDI_H -#include -#include +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef FAKE_W32GDI +# include "fake-w32gdi.h" +#else +# include +# include +#endif typedef struct { From e5ad5a1f52b546f3fcf71593263ce64ea809be7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Mon, 12 May 2014 05:17:32 +0200 Subject: [PATCH 03/29] Printing gamma in verbose mode is not necessary and causes complications when all monitors can have individual gamma corrections, remove it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/redshift.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/redshift.c b/src/redshift.c index 6fc28952..364ed058 100644 --- a/src/redshift.c +++ b/src/redshift.c @@ -1087,11 +1087,6 @@ main(int argc, char *argv[]) exit(EXIT_FAILURE); } - if (verbose) { - printf(_("Gamma: %.3f, %.3f, %.3f\n"), - gamma[0], gamma[1], gamma[2]); - } - /* Initialize gamma adjustment method. If method is NULL try all methods until one that works is found. */ gamma_state_t state; From 84fe4409068d1514dd4482b3b824a2903395d03a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Mon, 12 May 2014 05:43:25 +0200 Subject: [PATCH 04/29] Separate out adjustment methods from redshift.c into adjustments.h so that it can be accessed from elsewhere, and make the compile-time configurable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/Makefile.am | 1 + src/adjustments.h | 55 +++++++++++++++++++++++++++++++++++++++++++++++ src/redshift.c | 16 +------------- 3 files changed, 57 insertions(+), 15 deletions(-) create mode 100644 src/adjustments.h diff --git a/src/Makefile.am b/src/Makefile.am index c48e720e..8a080a39 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -15,6 +15,7 @@ redshift_SOURCES = \ location-manual.c location-manual.h \ solar.c solar.h \ systemtime.c systemtime.h \ + adjustments.h \ gamma-dummy.c gamma-dummy.h EXTRA_redshift_SOURCES = \ diff --git a/src/adjustments.h b/src/adjustments.h new file mode 100644 index 00000000..d5e02609 --- /dev/null +++ b/src/adjustments.h @@ -0,0 +1,55 @@ +/* adjustments.h -- Adjustment constants and data structures header + This file is part of Redshift. + + Redshift 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. + + Redshift 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 Redshift. If not, see . + + Copyright (c) 2014 Mattias Andrée +*/ + +#ifndef REDSHIFT_ADJUSTMENTS_H +#define REDSHIFT_ADJUSTMENTS_H + +#include +#include + + + +/* Bounds for parameters. */ +#define MIN_TEMP 1000 +#define MAX_TEMP 25000 +#ifndef MIN_BRIGHTNESS +# define MIN_BRIGHTNESS 0.1f +#endif +#if !defined(MAX_BRIGHTNESS) && !defined(NO_MAX_BRIGHTNESS) +# define MAX_BRIGHTNESS 1.0f +#endif +#ifndef MIN_GAMMA +# define MIN_GAMMA 0.1f +#endif +#if !defined(MAX_GAMMA) && !defined(NO_MAX_GAMMA) +# define MAX_GAMMA 10.0f +#endif + +/* Default values for parameters. */ +#define DEFAULT_DAY_TEMP 5500 +#define DEFAULT_NIGHT_TEMP 3500 +#define DEFAULT_BRIGHTNESS 1.0f +#define DEFAULT_GAMMA 1.0f + +/* The color temperature when no adjustment is applied. */ +#define NEUTRAL_TEMP 6500 + + + +#endif diff --git a/src/redshift.c b/src/redshift.c index 364ed058..11ce48a0 100644 --- a/src/redshift.c +++ b/src/redshift.c @@ -45,6 +45,7 @@ #include "config-ini.h" #include "solar.h" #include "systemtime.h" +#include "adjustments.h" #define MIN(x,y) ((x) < (y) ? (x) : (y)) @@ -203,21 +204,6 @@ static const location_provider_t location_providers[] = { #define MAX_LAT 90.0 #define MIN_LON -180.0 #define MAX_LON 180.0 -#define MIN_TEMP 1000 -#define MAX_TEMP 25000 -#define MIN_BRIGHTNESS 0.1 -#define MAX_BRIGHTNESS 1.0 -#define MIN_GAMMA 0.1 -#define MAX_GAMMA 10.0 - -/* Default values for parameters. */ -#define DEFAULT_DAY_TEMP 5500 -#define DEFAULT_NIGHT_TEMP 3500 -#define DEFAULT_BRIGHTNESS 1.0 -#define DEFAULT_GAMMA 1.0 - -/* The color temperature when no adjustment is applied. */ -#define NEUTRAL_TEMP 6500 /* Angular elevation of the sun at which the color temperature transition period starts and ends (in degress). From fa0c50ace21f86649f1c7b4f2ad4700acaab7c43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Mon, 12 May 2014 17:59:18 +0200 Subject: [PATCH 05/29] Move coordinate and elevation constants from redshift.c to redshift.h, and make elevation constants compile-time configurable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/redshift.c | 13 ------------- src/redshift.h | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/redshift.c b/src/redshift.c index 11ce48a0..a98227ef 100644 --- a/src/redshift.c +++ b/src/redshift.c @@ -199,19 +199,6 @@ static const location_provider_t location_providers[] = { { NULL } }; -/* Bounds for parameters. */ -#define MIN_LAT -90.0 -#define MAX_LAT 90.0 -#define MIN_LON -180.0 -#define MAX_LON 180.0 - -/* Angular elevation of the sun at which the color temperature - transition period starts and ends (in degress). - Transition during twilight, and while the sun is lower than - 3.0 degrees above the horizon. */ -#define TRANSITION_LOW SOLAR_CIVIL_TWILIGHT_ELEV -#define TRANSITION_HIGH 3.0 - /* Program modes. */ typedef enum { PROGRAM_MODE_CONTINUAL, diff --git a/src/redshift.h b/src/redshift.h index 3a878777..c7b0d96b 100644 --- a/src/redshift.h +++ b/src/redshift.h @@ -24,6 +24,24 @@ #include +/* Bounds for parameters. */ +#define MIN_LAT -90.0 +#define MAX_LAT 90.0 +#define MIN_LON -180.0 +#define MAX_LON 180.0 + +/* Angular elevation of the sun at which the color temperature + transition period starts and ends (in degress). + Transition during twilight, and while the sun is lower than + 3.0 degrees above the horizon. */ +#ifndef TRANSITION_LOW +# define TRANSITION_LOW SOLAR_CIVIL_TWILIGHT_ELEV +#endif +#ifndef TRANSITION_HIGH +# define TRANSITION_HIGH 3.0 +#endif + + /* Gamma adjustment method */ typedef int gamma_method_init_func(void *state); typedef int gamma_method_start_func(void *state); From bba18344a250b03ae827b9a1b9c7a2de2d5b628d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Fri, 16 May 2014 06:46:15 +0200 Subject: [PATCH 06/29] Use separate arguments instead of colon for option delimiting in -l and -m MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- redshift.1 | 6 +- src/Makefile.am | 3 +- src/opt-parser.c | 79 ++++++++++++++++++++ src/opt-parser.h | 28 +++++++ src/redshift.c | 187 ++++++++++++++++++++++++++--------------------- 5 files changed, 216 insertions(+), 87 deletions(-) create mode 100644 src/opt-parser.c create mode 100644 src/opt-parser.h diff --git a/redshift.1 b/redshift.1 index db51c87e..b332ea58 100644 --- a/redshift.1 +++ b/redshift.1 @@ -3,7 +3,7 @@ redshift \- Set color temperature of display according to time of day. .SH SYNOPSIS .B redshift -\fI[\-l LAT:LON | \-l PROVIDER:OPTIONS] [\-t DAY:NIGHT] \fR[\fIOPTIONS\fR...] +\fI[\-l LAT:LON | \-l PROVIDER OPTIONS] [\-t DAY:NIGHT] \fR[\fIOPTIONS\fR...] .SH DESCRIPTION .B redshift adjusts the color temperature of your screen according to your @@ -46,11 +46,11 @@ Your current location, in degrees, given as floating point numbers, towards north and east, with negative numbers representing south and west, respectively. .TP -\fB\-l\fR PROVIDER[:OPTIONS] +\fB\-l\fR PROVIDER [OPTIONS] Select provider for automatic location updates (Type `list' to see available providers) .TP -\fB\-m\fR METHOD +\fB\-m\fR METHOD [OPTIONS] Method to use to set color temperature (Type `list' to see available methods) .TP diff --git a/src/Makefile.am b/src/Makefile.am index 8a080a39..006dfebb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -16,7 +16,8 @@ redshift_SOURCES = \ solar.c solar.h \ systemtime.c systemtime.h \ adjustments.h \ - gamma-dummy.c gamma-dummy.h + gamma-dummy.c gamma-dummy.h \ + opt-parser.c opt-parser.h EXTRA_redshift_SOURCES = \ gamma-drm.c gamma-drm.h \ diff --git a/src/opt-parser.c b/src/opt-parser.c new file mode 100644 index 00000000..7277bd3a --- /dev/null +++ b/src/opt-parser.c @@ -0,0 +1,79 @@ +/* opt-parser.c -- getopt wrapper source + This file is part of Redshift. + + Redshift 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. + + Redshift 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 Redshift. If not, see . + + Copyright (c) 2014 Mattias Andrée +*/ +#include "opt-parser.h" + +#include +#include +#include + + +int +parseopt(int argc, char *const *argv, const char *shortopts, const char **args, int *args_count) +{ + int opt; + char *p; + + *args_count = 0; + opt = getopt(argc, argv, shortopts); + if (opt < 0) + return opt; + + p = strchr(shortopts, opt); + if ((p == NULL) || (p[1] != ':')) + return opt; + + args[(*args_count)++] = optarg; + while (optind < argc && argv[optind][0] != '-') { + args[(*args_count)++] = argv[optind++]; + } + + return opt; +} + + +char * +coalesce_args(const char *const *args, int args_count, char delimiter, char final) +{ + size_t n = args_count ? (args_count + 1) : 2; + size_t i; + char *rc; + char *rc_; + + for (i = 0; i < args_count; i++) + n += strlen(args[i]); + + rc = rc_ = malloc(n * sizeof(char)); + if (rc == NULL) + return NULL; + + for (i = 0; i < args_count; i++) { + n = strlen(args[i]); + memcpy(rc_, args[i], n * sizeof(char)); + rc_ += n; + *rc_++ = delimiter; + } + if (args_count > 0) + rc_--; + + *rc_++ = final; + *rc_++ = '\0'; + + return rc; +} + diff --git a/src/opt-parser.h b/src/opt-parser.h new file mode 100644 index 00000000..ec681584 --- /dev/null +++ b/src/opt-parser.h @@ -0,0 +1,28 @@ +/* opt-parser.h -- getopt wrapper header + This file is part of Redshift. + + Redshift 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. + + Redshift 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 Redshift. If not, see . + + Copyright (c) 2014 Mattias Andrée +*/ +#ifndef REDSHIFT_OPT_PARSER_H +#define REDSHIFT_OPT_PARSER_H + + +int parseopt(int argc, char *const *argv, const char *shortopts, const char **args, int *args_count); + +char *coalesce_args(const char *const *args, int args_count, char delimiter, char final); + + +#endif /* ! REDSHIFT_OPT_PARSER_H */ diff --git a/src/redshift.c b/src/redshift.c index a98227ef..4f9184d1 100644 --- a/src/redshift.c +++ b/src/redshift.c @@ -21,6 +21,7 @@ # include "config.h" #endif +#include #include #include #include @@ -46,6 +47,7 @@ #include "solar.h" #include "systemtime.h" #include "adjustments.h" +#include "opt-parser.h" #define MIN(x,y) ((x) < (y) ? (x) : (y)) @@ -416,43 +418,42 @@ provider_try_start(const location_provider_t *provider, /* Set provider options from command line. */ const char *manual_keys[] = { "lat", "lon" }; int i = 0; - while (args != NULL) { - char *next_arg = strchr(args, ':'); - if (next_arg != NULL) *(next_arg++) = '\0'; - - const char *key = args; - char *value = strchr(args, '='); - if (value == NULL) { - /* The options for the "manual" method can be set - without keys on the command line for convencience - and for backwards compatability. We add the proper - keys here before calling set_option(). */ - if (strcmp(provider->name, "manual") == 0 && - i < sizeof(manual_keys)/sizeof(manual_keys[0])) { - key = manual_keys[i]; - value = args; + if (args != NULL) { + while (*args != '\0') { + const char *key = args; + char *value = strchr(args, '='); + if (value == NULL) { + /* The options for the "manual" method can be set + without keys on the command line for convencience + and for backwards compatability. We add the proper + keys here before calling set_option(). */ + if (strcmp(provider->name, "manual") == 0 && + i < sizeof(manual_keys)/sizeof(manual_keys[0])) { + key = manual_keys[i]; + value = args; + } else { + fprintf(stderr, _("Failed to parse option `%s'.\n"), + args); + return -1; + } } else { - fprintf(stderr, _("Failed to parse option `%s'.\n"), - args); + *(value++) = '\0'; + } + + r = provider->set_option(state, key, value); + if (r < 0) { + provider->free(state); + fprintf(stderr, _("Failed to set %s option.\n"), + provider->name); + /* TRANSLATORS: `help' must not be translated. */ + fprintf(stderr, _("Try `-l %s:help' for more" + " information.\n"), provider->name); return -1; } - } else { - *(value++) = '\0'; - } - r = provider->set_option(state, key, value); - if (r < 0) { - provider->free(state); - fprintf(stderr, _("Failed to set %s option.\n"), - provider->name); - /* TRANSLATORS: `help' must not be translated. */ - fprintf(stderr, _("Try `-l %s:help' for more" - " information.\n"), provider->name); - return -1; + args = value + strlen(value) + 1; + i += 1; } - - args = next_arg; - i += 1; } /* Start provider. */ @@ -506,32 +507,31 @@ method_try_start(const gamma_method_t *method, } /* Set method options from command line. */ - while (args != NULL) { - char *next_arg = strchr(args, ':'); - if (next_arg != NULL) *(next_arg++) = '\0'; - - const char *key = args; - char *value = strchr(args, '='); - if (value == NULL) { - fprintf(stderr, _("Failed to parse option `%s'.\n"), - args); - return -1; - } else { - *(value++) = '\0'; - } + if (args != NULL) { + while (*args != '\0') { + const char *key = args; + char *value = strchr(args, '='); + if (value == NULL) { + fprintf(stderr, _("Failed to parse option `%s'.\n"), + args); + return -1; + } else { + *(value++) = '\0'; + } - r = method->set_option(state, key, value); - if (r < 0) { - method->free(state); - fprintf(stderr, _("Failed to set %s option.\n"), - method->name); - /* TRANSLATORS: `help' must not be translated. */ - fprintf(stderr, _("Try -m %s:help' for more" - " information.\n"), method->name); - return -1; - } + r = method->set_option(state, key, value); + if (r < 0) { + method->free(state); + fprintf(stderr, _("Failed to set %s option.\n"), + method->name); + /* TRANSLATORS: `help' must not be translated. */ + fprintf(stderr, _("Try -m %s:help' for more" + " information.\n"), method->name); + return -1; + } - args = next_arg; + args = value + strlen(value) + 1; + } } /* Start method. */ @@ -663,7 +663,9 @@ main(int argc, char *argv[]) /* Parse command line arguments. */ int opt; - while ((opt = getopt(argc, argv, "b:c:g:hl:m:oO:prt:vVx")) != -1) { + const char **args = alloca(argc * sizeof(char*)); + int args_count; + while ((opt = parseopt(argc, argv, "b:c:g:hl:m:oO:prt:vVx", args, &args_count)) != -1) { switch (opt) { case 'b': parse_brightness_string(optarg, &brightness_day, &brightness_night); @@ -688,38 +690,47 @@ main(int argc, char *argv[]) break; case 'l': /* Print list of providers if argument is `list' */ - if (strcasecmp(optarg, "list") == 0) { + if (strcasecmp(*args, "list") == 0) { print_provider_list(); exit(EXIT_SUCCESS); } - char *provider_name = NULL; + provider_args = coalesce_args(args, args_count, '\0', '\0'); + if (provider_args == NULL) { + perror("coalesce_args"); + exit(EXIT_FAILURE); + } /* Don't save the result of strtof(); we simply want to know if optarg can be parsed as a float. */ errno = 0; char *end; - strtof(optarg, &end); - if (errno == 0 && *end == ':') { - /* Use instead as arguments to `manual'. */ - provider_name = "manual"; - provider_args = optarg; - } else { - /* Split off provider arguments. */ - s = strchr(optarg, ':'); - if (s != NULL) { - *(s++) = '\0'; - provider_args = s; + strtof(provider_args, &end); + if (errno == 0 && (end[0] == ':' || end[0] == '\0') && end[1] != '\0') { + /* Set delimiter to `\0'. */ + end[0] = '\0'; + /* Set provider method to `manual'. */ + char *old_buf = provider_args; + size_t n = 0; + while (provider_args[n] != '\0' || provider_args[n + 1] != '\0') + n++; + n += 2; + provider_args = realloc(old_buf, (strlen("manual") + 1 + n) * sizeof(char)); + if (provider_args == NULL) { + perror("realloc"); + free(old_buf); + exit(EXIT_FAILURE); } - - provider_name = optarg; + memmove(provider_args + strlen("manual") + 1, provider_args, n * sizeof(char)); + strcpy(provider_args, "manual"); } /* Lookup provider from name. */ - provider = find_location_provider(provider_name); + provider = find_location_provider(provider_args); if (provider == NULL) { fprintf(stderr, _("Unknown location provider" - " `%s'.\n"), provider_name); + " `%s'.\n"), provider_args); + free(provider_args); exit(EXIT_FAILURE); } @@ -727,6 +738,7 @@ main(int argc, char *argv[]) if (provider_args != NULL && strcasecmp(provider_args, "help") == 0) { provider->print_help(stdout); + free(provider_args); exit(EXIT_SUCCESS); } break; @@ -737,20 +749,20 @@ main(int argc, char *argv[]) exit(EXIT_SUCCESS); } - /* Split off method arguments. */ - s = strchr(optarg, ':'); - if (s != NULL) { - *(s++) = '\0'; - method_args = s; + method_args = coalesce_args(args, args_count, '\0', '\0'); + if (method_args == NULL) { + perror("coalesce_args"); + exit(EXIT_FAILURE); } /* Find adjustment method by name. */ - method = find_gamma_method(optarg); + method = find_gamma_method(method_args); if (method == NULL) { /* TRANSLATORS: This refers to the method used to adjust colors e.g VidMode */ fprintf(stderr, _("Unknown adjustment method" " `%s'.\n"), optarg); + free(method_args); exit(EXIT_FAILURE); } @@ -758,6 +770,7 @@ main(int argc, char *argv[]) if (method_args != NULL && strcasecmp(method_args, "help") == 0) { method->print_help(stdout); + free(method_args); exit(EXIT_SUCCESS); } break; @@ -927,8 +940,9 @@ main(int argc, char *argv[]) mode != PROGRAM_MODE_MANUAL) { if (provider != NULL) { /* Use provider specified on command line. */ - r = provider_try_start(provider, &location_state, - &config_state, provider_args); + r = provider_try_start(provider, &location_state, &config_state, + provider_args == NULL ? NULL : + provider_args + strlen(provider_args) + 1); if (r < 0) exit(EXIT_FAILURE); } else { /* Try all providers, use the first that works. */ @@ -1069,7 +1083,8 @@ main(int argc, char *argv[]) if (method != NULL) { /* Use method specified on command line. */ r = method_try_start(method, &state, &config_state, - method_args); + method_args == NULL ? NULL : + method_args + strlen(method_args) + 1); if (r < 0) exit(EXIT_FAILURE); } else { /* Try all methods, use the first that works. */ @@ -1342,5 +1357,11 @@ main(int argc, char *argv[]) /* Clean up gamma adjustment state */ method->free(&state); + /* Free memory */ + if (method_args != NULL) + free(method_args); + if (provider_args != NULL) + free(provider_args); + return EXIT_SUCCESS; } From d8dd370b4cd678e1e39f7464db2f0875c3bf3c6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Tue, 13 May 2014 04:00:56 +0200 Subject: [PATCH 07/29] Add adjustment datastructures to adjustments.h and prepare colorramp_fill for using it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/adjustments.h | 33 +++++++++++++++++++++++ src/colorramp.c | 69 +++++++++++++++++++++++++++++++++++++++++------ src/colorramp.h | 3 +++ 3 files changed, 97 insertions(+), 8 deletions(-) diff --git a/src/adjustments.h b/src/adjustments.h index d5e02609..2471693b 100644 --- a/src/adjustments.h +++ b/src/adjustments.h @@ -52,4 +52,37 @@ +/* Gamma ramp trio. */ +typedef struct { + /* The number of stops in each ramp. */ + size_t red_size; + size_t green_size; + size_t blue_size; + /* The actual ramps. */ + uint16_t *red; + uint16_t *green; + uint16_t *blue; +} gamma_ramps_t; + + +/* Color adjustment settings. */ +typedef struct { + /* The monitor's gamma correction. */ + float gamma_correction[3]; + /* Adjustments. + The gamma is only one value, rather than + three becuase it is not an correction, + but rather an adjustment as suggest in + . + This is included for performance: it takes + less work for Redshift to multiply the gamma + values than for a front-ent to create or + modify a lookup table. */ + float gamma; + float brightness; + float temperature; +} gamma_settings_t; + + + #endif diff --git a/src/colorramp.c b/src/colorramp.c index f1988d4c..146ee002 100644 --- a/src/colorramp.c +++ b/src/colorramp.c @@ -16,8 +16,13 @@ Copyright (c) 2013 Jon Lund Steffensen Copyright (c) 2013 Ingo Thies + Copyright (c) 2014 Mattias Andrée */ +#include "colorramp.h" +#include "adjustments.h" + +#include #include #include @@ -281,21 +286,69 @@ interpolate_color(float a, const float *c1, const float *c2, float *c) } void -colorramp_fill(uint16_t *gamma_r, uint16_t *gamma_g, uint16_t *gamma_b, - int size, int temp, float brightness, const float gamma[3]) +colorramp_fill_(gamma_ramps_t out_ramps, gamma_settings_t adjustments) { - /* Approximate white point */ + size_t gamma_sizes[3] = { + out_ramps.red_size, + out_ramps.green_size, + out_ramps.blue_size + }; + + uint16_t *filter[3] = { + out_ramps.red, + out_ramps.green, + out_ramps.blue + }; + + + /* Approximate white point. */ + int temp = (int)(adjustments.temperature + 0.5f); float white_point[3]; float alpha = (temp % 100) / 100.0; int temp_index = ((temp - 1000) / 100)*3; interpolate_color(alpha, &blackbody_color[temp_index], &blackbody_color[temp_index+3], white_point); -#define F(Y, C) pow((Y) * brightness * white_point[C], 1.0/gamma[C]) + float gamma[3] = { + adjustments.gamma_correction[0] * adjustments.gamma, + adjustments.gamma_correction[1] * adjustments.gamma, + adjustments.gamma_correction[2] * adjustments.gamma + }; + +#define F(Y, C) pow((Y) * adjustments.brightness * white_point[C], \ + 1.0f / gamma[C]) - for (int i = 0; i < size; i++) { - gamma_r[i] = F((float)i/size, 0) * (UINT16_MAX+1); - gamma_g[i] = F((float)i/size, 1) * (UINT16_MAX+1); - gamma_b[i] = F((float)i/size, 2) * (UINT16_MAX+1); + for (int c = 0; c < 3; c++) { + uint16_t *cfilter = filter[c]; + size_t gamma_size = gamma_sizes[c]; + for (size_t i = 0; i < gamma_size; i++) { + int32_t y = F((float)i / gamma_size, c) * (UINT16_MAX+1); + cfilter[i] = (uint16_t)(y < 0 ? 0 : y > UINT16_MAX ? UINT16_MAX : y); + } } + +#undef F +} + +void +colorramp_fill(uint16_t *gamma_r, uint16_t *gamma_g, uint16_t *gamma_b, + int size, int temp, float brightness, const float gamma[3]) +{ + gamma_ramps_t out_ramps = { + .red_size = (size_t)size, + .green_size = (size_t)size, + .blue_size = (size_t)size, + .red = gamma_r, + .green = gamma_g, + .blue = gamma_b + }; + gamma_settings_t adjustments = { + .gamma_correction[0] = gamma[0], + .gamma_correction[1] = gamma[1], + .gamma_correction[2] = gamma[2], + .gamma = 1, + .brightness = brightness, + .temperature = temp + }; + colorramp_fill_(out_ramps, adjustments); } diff --git a/src/colorramp.h b/src/colorramp.h index dd0fa97d..235535c2 100644 --- a/src/colorramp.h +++ b/src/colorramp.h @@ -15,11 +15,14 @@ along with Redshift. If not, see . Copyright (c) 2010 Jon Lund Steffensen + Copyright (c) 2014 Mattias Andrée */ #ifndef REDSHIFT_COLORRAMP_H #define REDSHIFT_COLORRAMP_H +#include "adjustments.h" + #include void colorramp_fill(uint16_t *gamma_r, uint16_t *gamma_g, uint16_t *gamma_b, From 1751578f560797fc06731b7822797863142bf5f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Mon, 12 May 2014 07:25:12 +0200 Subject: [PATCH 08/29] Add adjustment method common states MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/Makefile.am | 1 + src/gamma-common.h | 152 +++++++++++++++++++++++++++++++++++++++++++++ src/redshift.c | 1 + 3 files changed, 154 insertions(+) create mode 100644 src/gamma-common.h diff --git a/src/Makefile.am b/src/Makefile.am index 006dfebb..b08cfb25 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -16,6 +16,7 @@ redshift_SOURCES = \ solar.c solar.h \ systemtime.c systemtime.h \ adjustments.h \ + gamma-common.h \ gamma-dummy.c gamma-dummy.h \ opt-parser.c opt-parser.h diff --git a/src/gamma-common.h b/src/gamma-common.h new file mode 100644 index 00000000..126f4c13 --- /dev/null +++ b/src/gamma-common.h @@ -0,0 +1,152 @@ +/* gamma-common.h -- Gamma adjustment method common functionallity header + This file is part of Redshift. + + Redshift 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. + + Redshift 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 Redshift. If not, see . + + Copyright (c) 2014 Mattias Andrée +*/ + +#ifndef REDSHIFT_GAMMA_COMMON_H +#define REDSHIFT_GAMMA_COMMON_H + +#include "adjustments.h" + +#include +#include + + + +/* Prototypes for the structures, + this is required because the structures + and the function typedef:s depends + on each other. */ +struct gamma_crtc_state; +struct gamma_partition_state; +struct gamma_site_state; +struct gamma_selection_state; +struct gamma_server_state; + +/* Typedef:s of the structures. */ +typedef struct gamma_crtc_state gamma_crtc_state_t; +typedef struct gamma_partition_state gamma_partition_state_t; +typedef struct gamma_site_state gamma_site_state_t; +typedef struct gamma_selection_state gamma_selection_state_t; +typedef struct gamma_server_state gamma_server_state_t; + + + +typedef void gamma_data_free_func(void *data); + +typedef int gamma_open_site_func(gamma_server_state_t *state, + char *site, gamma_site_state_t *site_out); + +typedef int gamma_open_partition_func(gamma_server_state_t *state, + gamma_site_state_t *site, + size_t partition, gamma_partition_state_t *partition_out); + +typedef int gamma_open_crtc_func(gamma_server_state_t *state, + gamma_site_state_t *site, + gamma_partition_state_t *partition, + size_t crtc, gamma_crtc_state_t *crtc_out); + +typedef void gamma_invalid_partition_func(const gamma_site_state_t *site, size_t partition); + +typedef int gamma_set_ramps_func(gamma_server_state_t *state, gamma_crtc_state_t *crtc, gamma_ramps_t ramps); + +typedef int gamma_set_option_func(gamma_server_state_t *state, + const char *key, char *value, ssize_t section); + + + + +/* CRTC state. */ +struct gamma_crtc_state { + /* Adjustment method implementation specific data. */ + void *data; + /* The CRTC, partition (e.g. screen) and site (e.g. display) indices. */ + size_t crtc; + size_t partition; + size_t site_index; + /* Saved (restored to on exit) and + current (about to be applied) gamma ramps. */ + gamma_ramps_t saved_ramps; + gamma_ramps_t current_ramps; + /* Color adjustments. */ + gamma_settings_t settings; +}; + +/* Partition (e.g. screen) state. */ +struct gamma_partition_state { + /* Whether this partion is used. */ + int used; + /* Adjustment method implementation specific data. */ + void *data; + /* The number of CRTCs that is available on this site. */ + size_t crtcs_available; + /* The selected CRTCs. */ + size_t crtcs_used; + gamma_crtc_state_t *crtcs; +}; + +/* Site (e.g. display) state. */ +struct gamma_site_state { + /* Adjustment method implementation specific data. */ + void *data; + /* The site identifier. */ + char *site; + /* The number of partitions that is available on this site. */ + size_t partitions_available; + /* The partitions. */ + gamma_partition_state_t *partitions; +}; + +/* CRTC selection state. */ +struct gamma_selection_state { + /* The CRTC and partition (e.g. screen) indices. */ + ssize_t crtc; + ssize_t partition; + /* The site identifier. */ + char *site; + /* Color adjustments. */ + gamma_settings_t settings; +}; + +/* Method state. */ +struct gamma_server_state { + /* Adjustment method implementation specific data. */ + void *data; + /* The selected sites. */ + size_t sites_used; + gamma_site_state_t *sites; + /* The selections, zeroth determines the defaults. */ + size_t selections_made; + gamma_selection_state_t *selections; + /* Functions that releases adjustment method implementation specific data. */ + gamma_data_free_func *free_state_data; + gamma_data_free_func *free_site_data; + gamma_data_free_func *free_partition_data; + gamma_data_free_func *free_crtc_data; + /* Functions that open sites, partitions and CRTCs. */ + gamma_open_site_func *open_site; + gamma_open_partition_func *open_partition; + gamma_open_crtc_func *open_crtc; + /* Function that inform about invalid selection of partition. */ + gamma_invalid_partition_func *invalid_partition; + /* Function that applies a gamma ramp. */ + gamma_set_ramps_func *set_ramps; + gamma_set_option_func *set_option; +}; + + +#endif /* ! REDSHIFT_GAMMA_COMMON_H */ diff --git a/src/redshift.c b/src/redshift.c index 4f9184d1..67945504 100644 --- a/src/redshift.c +++ b/src/redshift.c @@ -48,6 +48,7 @@ #include "systemtime.h" #include "adjustments.h" #include "opt-parser.h" +#include "gamma-common.h" #define MIN(x,y) ((x) < (y) ? (x) : (y)) From 9bf932f9df930471edbb566c008763b299cd205e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Mon, 12 May 2014 09:17:03 +0200 Subject: [PATCH 09/29] Adjustment method common state initialisation and destruction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/Makefile.am | 2 +- src/gamma-common.c | 153 +++++++++++++++++++++++++++++++++++++++++++++ src/gamma-common.h | 15 +++++ 3 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 src/gamma-common.c diff --git a/src/Makefile.am b/src/Makefile.am index b08cfb25..48067579 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -16,7 +16,7 @@ redshift_SOURCES = \ solar.c solar.h \ systemtime.c systemtime.h \ adjustments.h \ - gamma-common.h \ + gamma-common.c gamma-common.h \ gamma-dummy.c gamma-dummy.h \ opt-parser.c opt-parser.h diff --git a/src/gamma-common.c b/src/gamma-common.c new file mode 100644 index 00000000..764930a8 --- /dev/null +++ b/src/gamma-common.c @@ -0,0 +1,153 @@ +/* gamma-common.c -- Gamma adjustment method common functionallity source + This file is part of Redshift. + + Redshift 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. + + Redshift 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 Redshift. If not, see . + + Copyright (c) 2014 Mattias Andrée +*/ + +#include "gamma-common.h" + +#include +#include +#include + +#ifdef ENABLE_NLS +# include +# define _(s) gettext(s) +#else +# define _(s) s +#endif + + + +/* Initialize the adjustment method common parts of a state, + this should be done before initialize the adjustment method + specific parts. */ +int +gamma_init(gamma_server_state_t *state) +{ + state->data = NULL; + state->sites_used = 0; + state->sites = NULL; + state->selections_made = 1; + state->selections = malloc(sizeof(gamma_selection_state_t)); + + if (state->selections == NULL) { + perror("malloc"); + state->selections_made = 0; + return -1; + } + + /* Defaults selection. */ + state->selections->crtc = -1; + state->selections->partition = -1; + state->selections->site = NULL; + state->selections->settings.gamma_correction[0] = DEFAULT_GAMMA; + state->selections->settings.gamma_correction[1] = DEFAULT_GAMMA; + state->selections->settings.gamma_correction[2] = DEFAULT_GAMMA; + state->selections->settings.gamma = DEFAULT_GAMMA; + state->selections->settings.brightness = DEFAULT_BRIGHTNESS; + state->selections->settings.temperature = NEUTRAL_TEMP; + + return 0; +} + + +/* Free all CRTC selection data in a state. */ +void +gamma_free_selections(gamma_server_state_t *state) +{ + size_t i; + + /* Free data in each selection. */ + for (i = 0; i < state->selections_made; i++) + if (state->selections[i].site != NULL) + free(state->selections[i].site); + state->selections_made = 0; + + /* Free the selection array. */ + if (state->selections != NULL) { + free(state->selections); + state->selections = NULL; + } +} + + +/* Free all data in a state. */ +void +gamma_free(gamma_server_state_t *state) +{ + size_t s, p, c; + gamma_site_state_t *site; + gamma_partition_state_t *partition; + gamma_crtc_state_t *crtc; + + /* Free selections. */ + gamma_free_selections(state); + + /* Free each site. */ + for (s = 0; s < state->sites_used; s++) { + site = state->sites + s; + /* Free each partition. */ + for (p = 0; p < site->partitions_available; p++) { + partition = site->partitions + p; + if (partition->used == 0) + continue; + /* Free each CRTC. */ + for (c = 0; c < partition->crtcs_used; c++) { + crtc = partition->crtcs + c; + + /* Free gamma ramps. */ + if (crtc->saved_ramps.red != NULL) + free(crtc->saved_ramps.red); + if (crtc->current_ramps.red != NULL) + free(crtc->current_ramps.red); + + /* Free method dependent CRTC data. */ + if (crtc->data != NULL) + state->free_crtc_data(crtc->data); + } + /* Free CRTC array. */ + if (partition->crtcs != NULL) + free(partition->crtcs); + + /* Free method dependent partition data. */ + if (partition->data != NULL) + state->free_partition_data(partition->data); + } + /* Free partition array. */ + if (site->partitions != NULL) + free(site->partitions); + + /* Free site identifier. */ + if (site->site != NULL) + free(site->site); + + /* Free method dependent site data. */ + if (site->data != NULL) + state->free_site_data(site->data); + } + /* Free site array. */ + if (state->sites != NULL) { + free(state->sites); + state->sites = NULL; + } + + /* Free method dependent state data. */ + if (state->data != NULL) { + state->free_state_data(state->data); + state->data = NULL; + } +} diff --git a/src/gamma-common.h b/src/gamma-common.h index 126f4c13..a6f94b2a 100644 --- a/src/gamma-common.h +++ b/src/gamma-common.h @@ -149,4 +149,19 @@ struct gamma_server_state { }; + +/* Initialize the adjustment method common parts of a state, + this should be done before initialize the adjustment method + specific parts. */ +int gamma_init(gamma_server_state_t *state); + + +/* Free all CRTC selection data in a state. */ +void gamma_free_selections(gamma_server_state_t *state); + +/* Free all data in a state. */ +void gamma_free(gamma_server_state_t *state); + + + #endif /* ! REDSHIFT_GAMMA_COMMON_H */ From addf27174e4b51d5949308799dbdecbee5f2c999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Fri, 16 May 2014 03:30:35 +0200 Subject: [PATCH 10/29] Add adjustment method commit CRTC iterator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/gamma-common.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++ src/gamma-common.h | 20 ++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/src/gamma-common.c b/src/gamma-common.c index 764930a8..e281a943 100644 --- a/src/gamma-common.c +++ b/src/gamma-common.c @@ -151,3 +151,69 @@ gamma_free(gamma_server_state_t *state) state->data = NULL; } } + + +/* Create CRTC iterator. */ +gamma_iterator_t +gamma_iterator(gamma_server_state_t *state) +{ + gamma_iterator_t iterator = { + .crtc = NULL, + .partition = NULL, + .site = NULL, + .state = state + }; + return iterator; +} + + +/* Get next CRTC. */ +int +gamma_iterator_next(gamma_iterator_t *iterator) +{ + /* First CRTC. */ + if (iterator->crtc == NULL) { + if (iterator->state->sites_used == 0) + return 0; + iterator->site = iterator->state->sites; + iterator->partition = iterator->site->partitions; + gamma_partition_state_t *partitions_end = + iterator->site->partitions + + iterator->site->partitions_available; + while (iterator->partition->used == 0) { + iterator->partition++; + if (iterator->partition == partitions_end) + return 0; + } + if (iterator->partition->crtcs_used == 0) + return 0; + iterator->crtc = iterator->partition->crtcs; + return 1; + } + + /* Next CRTC. */ + size_t crtc_i = (size_t)(iterator->crtc - iterator->partition->crtcs) + 1; + size_t partition_i = iterator->crtc->partition; + size_t site_i = iterator->crtc->site_index; + + if (crtc_i == iterator->partition->crtcs_used) { + crtc_i = 0; + partition_i += 1; + } +next_partition: + if (partition_i == iterator->site->partitions_available) { + partition_i = 0; + site_i += 1; + } + if (site_i == iterator->state->sites_used) + return 0; + if (iterator->state->sites[site_i].partitions[partition_i].used == 0) { + partition_i += 1; + goto next_partition; + } + + iterator->site = iterator->state->sites + site_i; + iterator->partition = iterator->site->partitions + partition_i; + iterator->crtc = iterator->partition->crtcs + crtc_i; + return 1; +} diff --git a/src/gamma-common.h b/src/gamma-common.h index a6f94b2a..27af9558 100644 --- a/src/gamma-common.h +++ b/src/gamma-common.h @@ -36,6 +36,7 @@ struct gamma_partition_state; struct gamma_site_state; struct gamma_selection_state; struct gamma_server_state; +struct gamma_iterator; /* Typedef:s of the structures. */ typedef struct gamma_crtc_state gamma_crtc_state_t; @@ -43,6 +44,7 @@ typedef struct gamma_partition_state gamma_partition_state_t; typedef struct gamma_site_state gamma_site_state_t; typedef struct gamma_selection_state gamma_selection_state_t; typedef struct gamma_server_state gamma_server_state_t; +typedef struct gamma_iterator gamma_iterator_t; @@ -149,6 +151,17 @@ struct gamma_server_state { }; +/* CRTC iterator. */ +struct gamma_iterator { + /* The current CRTC, partition and site. */ + gamma_crtc_state_t *crtc; + gamma_partition_state_t *partition; + gamma_site_state_t *site; + /* The gamma state whose CRTCs are being iterated. */ + gamma_server_state_t *state; +}; + + /* Initialize the adjustment method common parts of a state, this should be done before initialize the adjustment method @@ -163,5 +176,12 @@ void gamma_free_selections(gamma_server_state_t *state); void gamma_free(gamma_server_state_t *state); +/* Create CRTC iterator. */ +gamma_iterator_t gamma_iterator(gamma_server_state_t *state); + +/* Get next CRTC. */ +int gamma_iterator_next(gamma_iterator_t *iterator); + + #endif /* ! REDSHIFT_GAMMA_COMMON_H */ From f70127efcbc7b30a28a3f2d24946a31399965142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Fri, 16 May 2014 03:36:00 +0200 Subject: [PATCH 11/29] Add a function to find the alreadly loaded, if any, site (e.g. X display) that has a matching name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/gamma-common.c | 21 +++++++++++++++++++++ src/gamma-common.h | 4 ++++ 2 files changed, 25 insertions(+) diff --git a/src/gamma-common.c b/src/gamma-common.c index e281a943..3d70e1c8 100644 --- a/src/gamma-common.c +++ b/src/gamma-common.c @@ -217,3 +217,24 @@ gamma_iterator_next(gamma_iterator_t *iterator) iterator->crtc = iterator->partition->crtcs + crtc_i; return 1; } + + +/* Find the index of a site or the index for a new site. */ +size_t +gamma_find_site(const gamma_server_state_t *state, const char *site) +{ + size_t site_index; + + /* Find matching already opened site. */ + for (site_index = 0; site_index < state->sites_used; site_index++) { + char *test_site = state->sites[site_index].site; + if (test_site == NULL || site == NULL) { + if (test_site == NULL && site == NULL) + break; + } + else if (!strcmp(site, test_site)) + break; + } + + return site_index; +} diff --git a/src/gamma-common.h b/src/gamma-common.h index 27af9558..68f1cc22 100644 --- a/src/gamma-common.h +++ b/src/gamma-common.h @@ -183,5 +183,9 @@ gamma_iterator_t gamma_iterator(gamma_server_state_t *state); int gamma_iterator_next(gamma_iterator_t *iterator); +/* Find the index of a site or the index for a new site. */ +size_t gamma_find_site(const gamma_server_state_t *state, const char *site) __attribute__((pure)); + + #endif /* ! REDSHIFT_GAMMA_COMMON_H */ From ada4fba1e9dd24e34fec197faf93a7b16252a693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Fri, 16 May 2014 04:04:31 +0200 Subject: [PATCH 12/29] Implement adjustment method common CRTC selection logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/gamma-common.c | 179 +++++++++++++++++++++++++++++++++++++++++++++ src/gamma-common.h | 4 + 2 files changed, 183 insertions(+) diff --git a/src/gamma-common.c b/src/gamma-common.c index 3d70e1c8..d32a4387 100644 --- a/src/gamma-common.c +++ b/src/gamma-common.c @@ -238,3 +238,182 @@ gamma_find_site(const gamma_server_state_t *state, const char *site) return site_index; } + + +/* Resolve selections. */ +int +gamma_resolve_selections(gamma_server_state_t *state) +{ + int default_selection = state->selections_made == 1; + int rc = -1, r; + + /* Shift the selections so that the iteration finds the + default selection if no other selection is made. */ + if (default_selection) { + state->selections_made += 1; + state->selections--; + } + + for (size_t i = 1; i < state->selections_made; i++) { + gamma_selection_state_t *selection = state->selections + i; + gamma_site_state_t *site; + size_t site_index; + size_t partition_start; + size_t partition_end; + + /* Find matching already opened site. */ + site_index = gamma_find_site(state, selection->site); + + /* Open site if not found. */ + if (site_index == state->sites_used) { + /* Grow array with sites, we temporarily store the new array + a temporarily variable so that we can properly release + resources on error. */ + gamma_site_state_t *new_sites; + size_t alloc_size = (site_index + 1) * sizeof(gamma_site_state_t); + new_sites = state->sites != NULL ? + realloc(state->sites, alloc_size) : + malloc(alloc_size); + if (new_sites == NULL) { + perror(state->sites != NULL ? "realloc" : "malloc"); + goto fail; + } + state->sites = new_sites; + site = state->sites + site_index; + + /* Make sure these are not freed before allocated on error. */ + site->site = NULL; + site->partitions = NULL; + + r = state->open_site(state, selection->site, site); + if (r != 0) { + rc = r; + goto fail; + } + + /* Increment now (rather than earlier), so we do not get segfault on error. */ + state->sites_used += 1; + + if (selection->site != NULL) { + site->site = strdup(selection->site); + if (site->site == NULL) { + perror("strdup"); + goto fail; + } + } + + /* calloc is used so that `used` in each partition is set to false. */ + site->partitions = calloc(site->partitions_available, + sizeof(gamma_partition_state_t)); + if (site->partitions == NULL) { + site->partitions_available = 0; + perror(state->sites != NULL ? "realloc" : "malloc"); + goto fail; + } + } else { + site = state->sites + site_index; + } + + /* Select partitions. */ + if (selection->partition >= (ssize_t)(site->partitions_available)) { + state->invalid_partition(site, (size_t)(selection->partition)); + goto fail; + } + partition_start = selection->partition < 0 ? 0 : (size_t)(selection->partition); + partition_end = selection->partition < 0 ? site->partitions_available : partition_start + 1; + /* Open partitions. */ + for (size_t p = partition_start; p < partition_end; p++) { + gamma_partition_state_t *partition = site->partitions + p; + if (partition->used) continue; + + r = state->open_partition(state, site, p, partition); + if (r != 0) { + rc = r; + goto fail; + } + + partition->used = 1; + } + + /* Open CRTCs. */ + for (size_t p = partition_start; p < partition_end; p++) { + gamma_partition_state_t *partition = site->partitions + p; + size_t crtc_start = selection->crtc < 0 ? 0 : (size_t)(selection->crtc); + size_t crtc_end = selection->crtc < 0 ? partition->crtcs_available : crtc_start + 1; + + if (selection->crtc >= (ssize_t)(partition->crtcs_available)) { + fprintf(stderr, _("CRTC %ld does not exist. "), + selection->crtc); + if (partition->crtcs_available > 1) { + fprintf(stderr, _("Valid CRTCs are [0-%ld].\n"), + partition->crtcs_available - 1); + } else { + fprintf(stderr, _("Only CRTC 0 exists.\n")); + } + return -1; + } + + /* Grow array with selected CRTCs, we temporarily store + the new array a temporarily variable so that we can + properly release resources on error. */ + gamma_crtc_state_t *new_crtcs; + size_t alloc_size = partition->crtcs_used + crtc_end - crtc_start; + alloc_size *= sizeof(gamma_crtc_state_t); + new_crtcs = partition->crtcs != NULL ? + realloc(partition->crtcs, alloc_size) : + malloc(alloc_size); + if (new_crtcs == NULL) { + perror(partition->crtcs != NULL ? "realloc" : "malloc"); + goto fail; + } + partition->crtcs = new_crtcs; + + for (size_t c = crtc_start; c < crtc_end; c++) { + gamma_crtc_state_t *crtc = partition->crtcs + partition->crtcs_used; + size_t total_ramp_size = 0, rrs, grs; + uint16_t *ramps; + + r = state->open_crtc(state, site, partition, c, crtc); + if (r != 0) { + rc = r; + goto fail; + } + crtc->crtc = c; + crtc->partition = p; + crtc->site_index = site_index; + partition->crtcs_used += 1; + + /* Store adjustment settigns. */ + crtc->settings = selection->settings; + + /* Create crtc->current_ramps. */ + crtc->current_ramps = crtc->saved_ramps; + total_ramp_size += rrs = crtc->current_ramps.red_size; + total_ramp_size += grs = crtc->current_ramps.green_size; + total_ramp_size += crtc->current_ramps.blue_size; + total_ramp_size *= sizeof(uint16_t); + ramps = malloc(total_ramp_size); + crtc->current_ramps.red = ramps; + crtc->current_ramps.green = ramps + rrs; + crtc->current_ramps.blue = ramps + rrs + grs; + if (ramps == NULL) { + perror("malloc"); + goto fail; + } + } + } + } + + rc = 0; + +fail: + /* Undo the shift made in the beginning of this function. */ + if (default_selection) { + state->selections_made -= 1; + state->selections++; + } + + gamma_free_selections(state); + + return rc; +} diff --git a/src/gamma-common.h b/src/gamma-common.h index 68f1cc22..818c633c 100644 --- a/src/gamma-common.h +++ b/src/gamma-common.h @@ -187,5 +187,9 @@ int gamma_iterator_next(gamma_iterator_t *iterator); size_t gamma_find_site(const gamma_server_state_t *state, const char *site) __attribute__((pure)); +/* Resolve selections. */ +int gamma_resolve_selections(gamma_server_state_t *state); + + #endif /* ! REDSHIFT_GAMMA_COMMON_H */ From 062c9972c76e5ec95621b98db29b89c1ae80a825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Mon, 12 May 2014 17:22:21 +0200 Subject: [PATCH 13/29] Add adjustment method common gamma ramp update function and restore function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/gamma-common.c | 32 ++++++++++++++++++++++++++++++++ src/gamma-common.h | 7 +++++++ 2 files changed, 39 insertions(+) diff --git a/src/gamma-common.c b/src/gamma-common.c index d32a4387..5e01cde5 100644 --- a/src/gamma-common.c +++ b/src/gamma-common.c @@ -18,6 +18,8 @@ */ #include "gamma-common.h" +#include "adjustments.h" +#include "colorramp.h" #include #include @@ -417,3 +419,33 @@ gamma_resolve_selections(gamma_server_state_t *state) return rc; } + + +/* Restore gamma ramps. */ +void +gamma_restore(gamma_server_state_t *state) +{ + gamma_iterator_t iter = gamma_iterator(state); + while (gamma_iterator_next(&iter)) { + if (iter.crtc->saved_ramps.red == NULL) + continue; + state->set_ramps(state, iter.crtc, iter.crtc->saved_ramps); + } +} + +/* Update gamma ramps. */ +int +gamma_update(gamma_server_state_t *state) +{ + gamma_iterator_t iter = gamma_iterator(state); + int r; + while (gamma_iterator_next(&iter)) { + if (iter.crtc->current_ramps.red == NULL) + continue; + colorramp_fill_(iter.crtc->current_ramps, iter.crtc->settings); + r = state->set_ramps(state, iter.crtc, iter.crtc->current_ramps); + if (r != 0) return r; + } + return 0; +} + diff --git a/src/gamma-common.h b/src/gamma-common.h index 818c633c..7a750353 100644 --- a/src/gamma-common.h +++ b/src/gamma-common.h @@ -191,5 +191,12 @@ size_t gamma_find_site(const gamma_server_state_t *state, const char *site) __at int gamma_resolve_selections(gamma_server_state_t *state); +/* Restore gamma ramps. */ +void gamma_restore(gamma_server_state_t *state); + +/* Update gamma ramps. */ +int gamma_update(gamma_server_state_t *state); + + #endif /* ! REDSHIFT_GAMMA_COMMON_H */ From b59ece3a5b5066dfaa4e4786da3662761e07b7b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Mon, 12 May 2014 17:49:08 +0200 Subject: [PATCH 14/29] Add adjustment method common option parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/gamma-common.c | 107 +++++++++++++++++++++++++++++++++++++++++++++ src/gamma-common.h | 7 +++ src/redshift.c | 26 ----------- 3 files changed, 114 insertions(+), 26 deletions(-) diff --git a/src/gamma-common.c b/src/gamma-common.c index 5e01cde5..8320f530 100644 --- a/src/gamma-common.c +++ b/src/gamma-common.c @@ -449,3 +449,110 @@ gamma_update(gamma_server_state_t *state) return 0; } + +/* Parse and apply an option */ +int +gamma_set_option(gamma_server_state_t *state, const char *key, char *value, ssize_t section) +{ + int r; + + if (section == (ssize_t)(state->selections_made)) { + /* Grow array with selections, we temporarily store + the new array a temporarily variable so that we can + properly release resources on error. */ + gamma_selection_state_t *new_selections; + size_t alloc_size = state->selections_made + 1; + alloc_size *= sizeof(gamma_selection_state_t); + new_selections = realloc(state->selections, alloc_size); + if (new_selections == NULL) { + perror("realloc"); + return -1; + } + state->selections = new_selections; + + /* Copy default selection. */ + state->selections[section] = *(state->selections); + if (state->selections->site != NULL) { + state->selections[section].site = strdup(state->selections->site); + if (state->selections[section].site == NULL) { + perror("strdup"); + return -1; + } + } + + /* Increment this last, so we do not get segfault on error. */ + state->selections_made += 1; + } + + if (strcasecmp(key, "gamma") == 0) { + float gamma[3]; + if (parse_gamma_string(value, gamma) < 0) { + fputs(_("Malformed gamma setting.\n"), + stderr); + return -1; + } +#ifdef MAX_GAMMA + if (gamma[0] < MIN_GAMMA || gamma[0] > MAX_GAMMA || + gamma[1] < MIN_GAMMA || gamma[1] > MAX_GAMMA || + gamma[2] < MIN_GAMMA || gamma[2] > MAX_GAMMA) { + fprintf(stderr, + _("Gamma value must be between %.1f and %.1f.\n"), + MIN_GAMMA, MAX_GAMMA); + return -1; + } +#else + if (gamma[0] < MIN_GAMMA || + gamma[1] < MIN_GAMMA || + gamma[2] < MIN_GAMMA) { + fprintf(stderr, + _("Gamma value must be atleast %.1f.\n"), + MIN_GAMMA); + return -1; + } +#endif + if (section >= 0) { + state->selections[section].settings.gamma_correction[0] = gamma[0]; + state->selections[section].settings.gamma_correction[1] = gamma[1]; + state->selections[section].settings.gamma_correction[2] = gamma[2]; + } else { + for (size_t i = 0; i < state->selections_made; i++) { + state->selections[i].settings.gamma_correction[0] = gamma[0]; + state->selections[i].settings.gamma_correction[1] = gamma[1]; + state->selections[i].settings.gamma_correction[2] = gamma[2]; + } + } + } else { + r = state->set_option(state, key, value, section); + if (r <= 0) + return r; + fprintf(stderr, _("Unknown method parameter: `%s'.\n"), key); + return -1; + } + return 0; +} + +/* A gamma string contains either one floating point value, + or three values separated by colon. */ +int +parse_gamma_string(char *str, float gamma[3]) +{ + char *s = strchr(str, ':'); + if (s == NULL) { + /* Use value for all channels. */ + float g = atof(str); + gamma[0] = gamma[1] = gamma[2] = g; + } else { + /* Parse separate value for each channel. */ + *(s++) = '\0'; + char *g_s = s; + s = strchr(s, ':'); + if (s == NULL) return -1; + + *(s++) = '\0'; + gamma[0] = atof(str); /* Red */ + gamma[1] = atof(g_s); /* Blue */ + gamma[2] = atof(s); /* Green */ + } + + return 0; +} diff --git a/src/gamma-common.h b/src/gamma-common.h index 7a750353..7de3f727 100644 --- a/src/gamma-common.h +++ b/src/gamma-common.h @@ -198,5 +198,12 @@ void gamma_restore(gamma_server_state_t *state); int gamma_update(gamma_server_state_t *state); +/* Parse and apply an option. */ +int gamma_set_option(gamma_server_state_t *state, const char *key, char *value, ssize_t section); + +/* A gamma string contains either one floating point value, + or three values separated by colon. */ +int parse_gamma_string(char *str, float gamma[3]); + #endif /* ! REDSHIFT_GAMMA_COMMON_H */ diff --git a/src/redshift.c b/src/redshift.c index 67945504..6787a76b 100644 --- a/src/redshift.c +++ b/src/redshift.c @@ -547,32 +547,6 @@ method_try_start(const gamma_method_t *method, return 0; } -/* A gamma string contains either one floating point value, - or three values separated by colon. */ -static int -parse_gamma_string(const char *str, float gamma[]) -{ - char *s = strchr(str, ':'); - if (s == NULL) { - /* Use value for all channels */ - float g = atof(str); - gamma[0] = gamma[1] = gamma[2] = g; - } else { - /* Parse separate value for each channel */ - *(s++) = '\0'; - char *g_s = s; - s = strchr(s, ':'); - if (s == NULL) return -1; - - *(s++) = '\0'; - gamma[0] = atof(str); /* Red */ - gamma[1] = atof(g_s); /* Blue */ - gamma[2] = atof(s); /* Green */ - } - - return 0; -} - /* A brightness string contains either one floating point value, or two values separated by a colon. */ static void From 68b2b1008705f8d38173916e54aa7cb1480eff57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Mon, 12 May 2014 18:25:34 +0200 Subject: [PATCH 15/29] Add functions for updating adjustment settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/gamma-common.c | 83 +++++++++++++++++++++++++++++++++++++++++++++- src/gamma-common.h | 20 +++++++++++ 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/src/gamma-common.c b/src/gamma-common.c index 8320f530..45e04c2c 100644 --- a/src/gamma-common.c +++ b/src/gamma-common.c @@ -450,7 +450,88 @@ gamma_update(gamma_server_state_t *state) } -/* Parse and apply an option */ +/* Methods for updating adjustments on all CRTCs. */ + +static gamma_crtc_selection_t all_crtcs = { + .site = -1, + .partition = -1, + .crtc = -1 +}; + +#define __gamma_update_all(PROP) \ + void \ + gamma_update_all_##PROP(gamma_server_state_t *state, float PROP) \ + { \ + gamma_update_##PROP(state, all_crtcs, PROP); \ + } + +__gamma_update_all(gamma) +__gamma_update_all(brightness) +__gamma_update_all(temperature) + +#undef __gamma_update_all + + +/* Methods for updating adjustments on selected CRTCs. */ + +#define __test(ANCESTOR, THIS) (crtcs.THIS < 0 || iter.THIS == iter.ANCESTOR->THIS##s + crtcs.THIS) +#define __test_all() (__test(state, site) && __test(site, partition) && __test(partition, crtc)) + +#define __update(PROP) \ + if (crtcs.site < 0 || crtcs.partition < 0 || crtcs.crtc < 0) { \ + gamma_iterator_t iter = gamma_iterator(state); \ + if (crtcs.site < 0 && crtcs.partition < 0 && crtcs.crtc < 0) { \ + while (gamma_iterator_next(&iter)) \ + iter.crtc->settings.PROP = PROP; \ + } else { \ + while (gamma_iterator_next(&iter)) \ + if (__test_all()) \ + iter.crtc->settings.PROP = PROP; \ + } \ + } else if ((size_t)(crtcs.site) < state->sites_used) { \ + gamma_site_state_t site = state->sites[crtcs.site]; \ + gamma_partition_state_t partition; \ + if ((size_t)(crtcs.partition) < site.partitions_available) { \ + partition = site.partitions[crtcs.partition]; \ + if (partition.used && (size_t)(crtcs.crtc) < partition.crtcs_used) \ + partition.crtcs[crtcs.crtc].settings.PROP = PROP; \ + } \ + } + +void +gamma_update_gamma(gamma_server_state_t *state, gamma_crtc_selection_t crtcs, float gamma) +{ + if (gamma < MIN_GAMMA) gamma = MIN_GAMMA; +#ifdef MAX_BRIGHTNESS + if (gamma > MAX_GAMMA) gamma = MAX_GAMMA; +#endif + __update(gamma) +} + +void +gamma_update_brightness(gamma_server_state_t *state, gamma_crtc_selection_t crtcs, float brightness) +{ + if (brightness < MIN_BRIGHTNESS) brightness = MIN_BRIGHTNESS; +#ifdef MAX_BRIGHTNESS + if (brightness > MAX_BRIGHTNESS) brightness = MAX_BRIGHTNESS; +#endif + __update(brightness) +} + +void +gamma_update_temperature(gamma_server_state_t *state, gamma_crtc_selection_t crtcs, float temperature) +{ + if (temperature < MIN_TEMP) temperature = MIN_TEMP; + if (temperature > MAX_TEMP) temperature = MAX_TEMP; + __update(temperature) +} + +#undef __update +#undef __test_all +#undef __test + + +/* Parse and apply an option. */ int gamma_set_option(gamma_server_state_t *state, const char *key, char *value, ssize_t section) { diff --git a/src/gamma-common.h b/src/gamma-common.h index 7de3f727..3ba23437 100644 --- a/src/gamma-common.h +++ b/src/gamma-common.h @@ -37,6 +37,7 @@ struct gamma_site_state; struct gamma_selection_state; struct gamma_server_state; struct gamma_iterator; +struct gamma_crtc_selection; /* Typedef:s of the structures. */ typedef struct gamma_crtc_state gamma_crtc_state_t; @@ -45,6 +46,7 @@ typedef struct gamma_site_state gamma_site_state_t; typedef struct gamma_selection_state gamma_selection_state_t; typedef struct gamma_server_state gamma_server_state_t; typedef struct gamma_iterator gamma_iterator_t; +typedef struct gamma_crtc_selection gamma_crtc_selection_t; @@ -161,6 +163,13 @@ struct gamma_iterator { gamma_server_state_t *state; }; +/* CRTC selection. */ +struct gamma_crtc_selection { + ssize_t site; + ssize_t partition; + ssize_t crtc; +}; + /* Initialize the adjustment method common parts of a state, @@ -198,6 +207,17 @@ void gamma_restore(gamma_server_state_t *state); int gamma_update(gamma_server_state_t *state); +/* Methods for updating adjustments on all CRTCs. */ +void gamma_update_all_gamma(gamma_server_state_t *state, float gamma); +void gamma_update_all_brightness(gamma_server_state_t *state, float brightness); +void gamma_update_all_temperature(gamma_server_state_t *state, float temperature); + +/* Methods for updating adjustments on selected CRTCs. */ +void gamma_update_gamma(gamma_server_state_t *state, gamma_crtc_selection_t crtcs, float gamma); +void gamma_update_brightness(gamma_server_state_t *state, gamma_crtc_selection_t crtcs, float brightness); +void gamma_update_temperature(gamma_server_state_t *state, gamma_crtc_selection_t crtcs, float temperature); + + /* Parse and apply an option. */ int gamma_set_option(gamma_server_state_t *state, const char *key, char *value, ssize_t section); From 96f15616f74175ed42fb55fd4d614501cb7fd427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Mon, 12 May 2014 18:38:32 +0200 Subject: [PATCH 16/29] Add function to config-ini that gets multiple sections with the same name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/config-ini.c | 34 +++++++++++++++++++++++++++++++++- src/config-ini.h | 8 ++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/config-ini.c b/src/config-ini.c index 65751ddf..7652aa74 100644 --- a/src/config-ini.c +++ b/src/config-ini.c @@ -15,6 +15,7 @@ along with Redshift. If not, see . Copyright (c) 2010 Jon Lund Steffensen + Copyright (c) 2014 Mattias Andrée */ @@ -285,7 +286,7 @@ config_ini_free(config_ini_state_t *state) } config_ini_section_t * -config_ini_get_section(config_ini_state_t *state, const char *name) +config_ini_get_section(const config_ini_state_t *state, const char *name) { config_ini_section_t *section = state->sections; while (section != NULL) { @@ -297,3 +298,34 @@ config_ini_get_section(config_ini_state_t *state, const char *name) return NULL; } + +config_ini_section_t ** +config_ini_get_sections(const config_ini_state_t *state, const char *name) +{ + config_ini_section_t **sections = malloc(1 * sizeof(config_ini_section_t*)); + if (sections == NULL) { + perror("malloc"); + return NULL; + } + + size_t ptr = 0; + config_ini_section_t *section = state->sections; + while (section != NULL) { + if (strcasecmp(section->name, name) == 0) { + sections[ptr++] = section; + + if ((ptr & -ptr) == ptr) { + sections = realloc(sections, (ptr << 1) * sizeof(config_ini_section_t*)); + if (sections == NULL) { + perror("realloc"); + return NULL; + } + } + } + + section = section->next; + } + + sections[ptr] = NULL; + return sections; +} diff --git a/src/config-ini.h b/src/config-ini.h index 5cdcc729..a25d058e 100644 --- a/src/config-ini.h +++ b/src/config-ini.h @@ -15,6 +15,7 @@ along with Redshift. If not, see . Copyright (c) 2010 Jon Lund Steffensen + Copyright (c) 2014 Mattias Andrée */ #ifndef REDSHIFT_CONFIG_INI_H @@ -43,7 +44,10 @@ typedef struct { int config_ini_init(config_ini_state_t *state, const char *filepath); void config_ini_free(config_ini_state_t *state); -config_ini_section_t *config_ini_get_section(config_ini_state_t *state, - const char *name); +config_ini_section_t *config_ini_get_section(const config_ini_state_t *state, + const char *name) __attribute__((pure)); + +config_ini_section_t **config_ini_get_sections(const config_ini_state_t *state, + const char *name) __attribute__((pure)); #endif /* ! REDSHIFT_CONFIG_INI_H */ From b8b58dc33806077cb4c50c5a7ac6916b91cd4037 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Mon, 12 May 2014 21:23:58 +0200 Subject: [PATCH 17/29] Rewrite gamma-dummy to use gamma-common and update redshift.c to use gamma-common. When compiling, disable all other methods until they too have been rewritten to use gamma-common. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/gamma-dummy.c | 62 +++++++++------ src/gamma-dummy.h | 12 +-- src/redshift.c | 197 ++++++++++++++++++++++------------------------ src/redshift.h | 17 +--- 4 files changed, 134 insertions(+), 154 deletions(-) diff --git a/src/gamma-dummy.c b/src/gamma-dummy.c index 0ebb12d1..3f22e6b5 100644 --- a/src/gamma-dummy.c +++ b/src/gamma-dummy.c @@ -15,8 +15,11 @@ along with Redshift. If not, see . Copyright (c) 2013 Jon Lund Steffensen + Copyright (c) 2014 Mattias Andrée */ +#include "gamma-dummy.h" + #include #include @@ -28,47 +31,54 @@ #endif -int -gamma_dummy_init(void *state) -{ - return 0; -} -int -gamma_dummy_start(void *state) +static int +gamma_dummy_set_ramps(gamma_server_state_t *state, gamma_crtc_state_t *crtc, gamma_ramps_t ramps) { - fputs(_("WARNING: Using dummy gamma method! Display will not be affected by this gamma method.\n"), stderr); + (void) state; + (void) crtc; + (void) ramps; return 0; } -void -gamma_dummy_restore(void *state) +static int +gamma_dummy_set_option(gamma_server_state_t *state, const char *key, char *value, ssize_t section) { + (void) state; + (void) key; + (void) value; + (void) section; + return 1; } -void -gamma_dummy_free(void *state) -{ -} -void -gamma_dummy_print_help(FILE *f) +int +gamma_dummy_init(gamma_server_state_t *state) { - fputs(_("Does not affect the display but prints the color temperature to the terminal.\n"), f); - fputs("\n", f); + int r; + r = gamma_init(state); + if (r != 0) return r; + state->data = NULL; + state->set_ramps = gamma_dummy_set_ramps; + state->set_option = gamma_dummy_set_option; + return 0; } int -gamma_dummy_set_option(void *state, const char *key, const char *value) +gamma_dummy_start(gamma_server_state_t *state) { - fprintf(stderr, _("Unknown method parameter: `%s'.\n"), key); - return -1; + (void) state; + fputs(_("WARNING: Using dummy gamma method! " + "Display will not be affected by this gamma method.\n"), + stderr); + return 0; } -int -gamma_dummy_set_temperature(void *state, int temp, float brightness, - const float gamma[3]) +void +gamma_dummy_print_help(FILE *f) { - printf(_("Temperature: %i\n"), temp); - return 0; + fputs(_("Does not affect the display but prints " + "the color temperature to the terminal.\n"), + f); + fputs("\n", f); } diff --git a/src/gamma-dummy.h b/src/gamma-dummy.h index 64bc40d8..7af3dcd9 100644 --- a/src/gamma-dummy.h +++ b/src/gamma-dummy.h @@ -15,24 +15,20 @@ along with Redshift. If not, see . Copyright (c) 2013 Jon Lund Steffensen + Copyright (c) 2014 Mattias Andrée */ #ifndef REDSHIFT_GAMMA_DUMMY_H #define REDSHIFT_GAMMA_DUMMY_H #include "redshift.h" +#include "gamma-common.h" -int gamma_dummy_init(void *state); -int gamma_dummy_start(void *state); -void gamma_dummy_free(void *state); +int gamma_dummy_init(gamma_server_state_t *state); +int gamma_dummy_start(gamma_server_state_t *state); void gamma_dummy_print_help(FILE *f); -int gamma_dummy_set_option(void *state, const char *key, const char *value); - -void gamma_dummy_restore(void *state); -int gamma_dummy_set_temperature(void *state, int temp, float brightness, - const float gamma[3]); #endif /* ! REDSHIFT_GAMMA_DUMMY_H */ diff --git a/src/redshift.c b/src/redshift.c index 6787a76b..afde4fc8 100644 --- a/src/redshift.c +++ b/src/redshift.c @@ -15,6 +15,7 @@ along with Redshift. If not, see . Copyright (c) 2013 Jon Lund Steffensen + Copyright (c) 2014 Mattias Andrée */ #ifdef HAVE_CONFIG_H @@ -81,22 +82,6 @@ #endif -/* Union of state data for gamma adjustment methods */ -typedef union { -#ifdef ENABLE_DRM - drm_state_t drm; -#endif -#ifdef ENABLE_RANDR - randr_state_t randr; -#endif -#ifdef ENABLE_VIDMODE - vidmode_state_t vidmode; -#endif -#ifdef ENABLE_WINGDI - w32gdi_state_t w32gdi; -#endif -} gamma_state_t; - /* Gamma adjustment method structs */ static const gamma_method_t gamma_methods[] = { @@ -105,11 +90,7 @@ static const gamma_method_t gamma_methods[] = { "drm", 0, (gamma_method_init_func *)drm_init, (gamma_method_start_func *)drm_start, - (gamma_method_free_func *)drm_free, - (gamma_method_print_help_func *)drm_print_help, - (gamma_method_set_option_func *)drm_set_option, - (gamma_method_restore_func *)drm_restore, - (gamma_method_set_temperature_func *)drm_set_temperature + (gamma_method_print_help_func *)drm_print_help }, #endif #ifdef ENABLE_RANDR @@ -117,11 +98,7 @@ static const gamma_method_t gamma_methods[] = { "randr", 1, (gamma_method_init_func *)randr_init, (gamma_method_start_func *)randr_start, - (gamma_method_free_func *)randr_free, - (gamma_method_print_help_func *)randr_print_help, - (gamma_method_set_option_func *)randr_set_option, - (gamma_method_restore_func *)randr_restore, - (gamma_method_set_temperature_func *)randr_set_temperature + (gamma_method_print_help_func *)randr_print_help }, #endif #ifdef ENABLE_VIDMODE @@ -129,11 +106,7 @@ static const gamma_method_t gamma_methods[] = { "vidmode", 1, (gamma_method_init_func *)vidmode_init, (gamma_method_start_func *)vidmode_start, - (gamma_method_free_func *)vidmode_free, - (gamma_method_print_help_func *)vidmode_print_help, - (gamma_method_set_option_func *)vidmode_set_option, - (gamma_method_restore_func *)vidmode_restore, - (gamma_method_set_temperature_func *)vidmode_set_temperature + (gamma_method_print_help_func *)vidmode_print_help }, #endif #ifdef ENABLE_WINGDI @@ -141,22 +114,14 @@ static const gamma_method_t gamma_methods[] = { "wingdi", 1, (gamma_method_init_func *)w32gdi_init, (gamma_method_start_func *)w32gdi_start, - (gamma_method_free_func *)w32gdi_free, - (gamma_method_print_help_func *)w32gdi_print_help, - (gamma_method_set_option_func *)w32gdi_set_option, - (gamma_method_restore_func *)w32gdi_restore, - (gamma_method_set_temperature_func *)w32gdi_set_temperature + (gamma_method_print_help_func *)w32gdi_print_help }, #endif { "dummy", 0, (gamma_method_init_func *)gamma_dummy_init, (gamma_method_start_func *)gamma_dummy_start, - (gamma_method_free_func *)gamma_dummy_free, - (gamma_method_print_help_func *)gamma_dummy_print_help, - (gamma_method_set_option_func *)gamma_dummy_set_option, - (gamma_method_restore_func *)gamma_dummy_restore, - (gamma_method_set_temperature_func *)gamma_dummy_set_temperature + (gamma_method_print_help_func *)gamma_dummy_print_help }, { NULL } }; @@ -471,8 +436,9 @@ provider_try_start(const location_provider_t *provider, static int method_try_start(const gamma_method_t *method, - gamma_state_t *state, - config_ini_state_t *config, char *args) + gamma_server_state_t *state, + config_ini_state_t *config, char *args, + char *gamma) { int r; @@ -483,28 +449,44 @@ method_try_start(const gamma_method_t *method, return -1; } + + /* Set default gamma. */ + if (gamma != NULL) { + r = gamma_set_option(state, "gamma", gamma, 0); + free(gamma); + if (r < 0) { + gamma_free(state); + return -1; + } + } + /* Set method options from config file. */ - config_ini_section_t *section = - config_ini_get_section(config, method->name); - if (section != NULL) { - config_ini_setting_t *setting = section->settings; - while (setting != NULL) { - r = method->set_option(state, setting->name, - setting->value); - if (r < 0) { - method->free(state); - fprintf(stderr, _("Failed to set %s" - " option.\n"), - method->name); - /* TRANSLATORS: `help' must not be - translated. */ - fprintf(stderr, _("Try `-m %s:help' for more" - " information.\n"), - method->name); - return -1; + config_ini_section_t **sections = + config_ini_get_sections(config, method->name); + if (sections != NULL) { + int section_i = 0; + while (sections[section_i] != NULL) { + config_ini_setting_t *setting = sections[section_i]->settings; + while (setting != NULL) { + r = gamma_set_option(state, setting->name, + setting->value, section_i + 1); + if (r < 0) { + gamma_free(state); + fprintf(stderr, _("Failed to set %s option.\n"), + method->name); + /* TRANSLATORS: `help' must not be translated. */ + fprintf(stderr, _("Try `-m %s:help' for more information.\n"), + method->name); + return -1; + } + setting = setting->next; } - setting = setting->next; + section_i++; } + free(sections); + } else { + gamma_free(state); + return -1; } /* Set method options from command line. */ @@ -520,9 +502,9 @@ method_try_start(const gamma_method_t *method, *(value++) = '\0'; } - r = method->set_option(state, key, value); + r = gamma_set_option(state, key, value, -1); if (r < 0) { - method->free(state); + gamma_free(state); fprintf(stderr, _("Failed to set %s option.\n"), method->name); /* TRANSLATORS: `help' must not be translated. */ @@ -538,7 +520,7 @@ method_try_start(const gamma_method_t *method, /* Start method. */ r = method->start(state); if (r < 0) { - method->free(state); + gamma_free(state); fprintf(stderr, _("Failed to start adjustment method %s.\n"), method->name); return -1; @@ -593,6 +575,14 @@ find_location_provider(const char *name) return provider; } +static int +set_temperature(gamma_server_state_t *state, int temp, float brightness) +{ + gamma_update_all_brightness(state, brightness); + gamma_update_all_temperature(state, (float)temp); + return gamma_update(state); +} + int main(int argc, char *argv[]) @@ -615,7 +605,7 @@ main(int argc, char *argv[]) int temp_set = -1; int temp_day = -1; int temp_night = -1; - float gamma[3] = { NAN, NAN, NAN }; + char *gamma = NULL; float brightness_day = NAN; float brightness_night = NAN; @@ -641,6 +631,7 @@ main(int argc, char *argv[]) const char **args = alloca(argc * sizeof(char*)); int args_count; while ((opt = parseopt(argc, argv, "b:c:g:hl:m:oO:prt:vVx", args, &args_count)) != -1) { + float gamma_[3]; switch (opt) { case 'b': parse_brightness_string(optarg, &brightness_day, &brightness_night); @@ -648,9 +639,18 @@ main(int argc, char *argv[]) case 'c': if (config_filepath != NULL) free(config_filepath); config_filepath = strdup(optarg); + if (config_filepath == NULL) { + perror("strdup"); + abort(); + } break; case 'g': - r = parse_gamma_string(optarg, gamma); + gamma = strdup(optarg); + if (gamma == NULL) { + perror("strdup"); + abort(); + } + r = parse_gamma_string(optarg, gamma_); if (r < 0) { fputs(_("Malformed gamma argument.\n"), stderr); @@ -847,14 +847,11 @@ main(int argc, char *argv[]) "elevation-low") == 0) { transition_low = atof(setting->value); } else if (strcasecmp(setting->name, "gamma") == 0) { - if (isnan(gamma[0])) { - r = parse_gamma_string(setting->value, - gamma); - if (r < 0) { - fputs(_("Malformed gamma" - " setting.\n"), - stderr); - exit(EXIT_FAILURE); + if (gamma == NULL) { + gamma = strdup(setting->value); + if (gamma == NULL) { + perror("strdup"); + abort(); } } } else if (strcasecmp(setting->name, @@ -900,7 +897,6 @@ main(int argc, char *argv[]) if (temp_night < 0) temp_night = DEFAULT_NIGHT_TEMP; if (isnan(brightness_day)) brightness_day = DEFAULT_BRIGHTNESS; if (isnan(brightness_night)) brightness_night = DEFAULT_BRIGHTNESS; - if (isnan(gamma[0])) gamma[0] = gamma[1] = gamma[2] = DEFAULT_GAMMA; if (transition < 0) transition = 1; float lat = NAN; @@ -1039,19 +1035,9 @@ main(int argc, char *argv[]) printf(_("Brightness: %.2f:%.2f\n"), brightness_day, brightness_night); } - /* Gamma */ - if (gamma[0] < MIN_GAMMA || gamma[0] > MAX_GAMMA || - gamma[1] < MIN_GAMMA || gamma[1] > MAX_GAMMA || - gamma[2] < MIN_GAMMA || gamma[2] > MAX_GAMMA) { - fprintf(stderr, - _("Gamma value must be between %.1f and %.1f.\n"), - MIN_GAMMA, MAX_GAMMA); - exit(EXIT_FAILURE); - } - /* Initialize gamma adjustment method. If method is NULL try all methods until one that works is found. */ - gamma_state_t state; + gamma_server_state_t state; /* Gamma adjustment not needed for print mode */ if (mode != PROGRAM_MODE_PRINT) { @@ -1059,15 +1045,20 @@ main(int argc, char *argv[]) /* Use method specified on command line. */ r = method_try_start(method, &state, &config_state, method_args == NULL ? NULL : - method_args + strlen(method_args) + 1); - if (r < 0) exit(EXIT_FAILURE); + method_args + strlen(method_args) + 1, + gamma); + if (r < 0) { + config_ini_free(&config_state); + exit(EXIT_FAILURE); + } } else { /* Try all methods, use the first that works. */ for (int i = 0; gamma_methods[i].name != NULL; i++) { const gamma_method_t *m = &gamma_methods[i]; if (!m->autostart) continue; - r = method_try_start(m, &state, &config_state, NULL); + r = method_try_start(m, &state, &config_state, NULL, + gamma); if (r < 0) { fputs(_("Trying next method...\n"), stderr); continue; @@ -1098,7 +1089,7 @@ main(int argc, char *argv[]) r = systemtime_get_time(&now); if (r < 0) { fputs(_("Unable to read system time.\n"), stderr); - method->free(&state); + gamma_free(&state); exit(EXIT_FAILURE); } @@ -1126,10 +1117,10 @@ main(int argc, char *argv[]) } /* Adjust temperature */ - r = method->set_temperature(&state, temp, brightness, gamma); + r = set_temperature(&state, temp, brightness); if (r < 0) { fputs(_("Temperature adjustment failed.\n"), stderr); - method->free(&state); + gamma_free(&state); exit(EXIT_FAILURE); } } @@ -1139,10 +1130,10 @@ main(int argc, char *argv[]) if (verbose) printf(_("Color temperature: %uK\n"), temp_set); /* Adjust temperature */ - r = method->set_temperature(&state, temp_set, brightness_day, gamma); + r = set_temperature(&state, temp_set, brightness_day); if (r < 0) { fputs(_("Temperature adjustment failed.\n"), stderr); - method->free(&state); + gamma_free(&state); exit(EXIT_FAILURE); } @@ -1151,10 +1142,10 @@ main(int argc, char *argv[]) case PROGRAM_MODE_RESET: { /* Reset screen */ - r = method->set_temperature(&state, NEUTRAL_TEMP, 1.0, gamma); + r = set_temperature(&state, NEUTRAL_TEMP, 1.0); if (r < 0) { fputs(_("Temperature adjustment failed.\n"), stderr); - method->free(&state); + gamma_free(&state); exit(EXIT_FAILURE); } } @@ -1242,7 +1233,7 @@ main(int argc, char *argv[]) if (r < 0) { fputs(_("Unable to read system time.\n"), stderr); - method->free(&state); + gamma_free(&state); exit(EXIT_FAILURE); } @@ -1302,13 +1293,11 @@ main(int argc, char *argv[]) /* Adjust temperature */ if (!disabled || short_trans_delta || set_adjustments) { - r = method->set_temperature(&state, - temp, brightness, - gamma); + r = set_temperature(&state, temp, brightness); if (r < 0) { fputs(_("Temperature adjustment" " failed.\n"), stderr); - method->free(&state); + gamma_free(&state); exit(EXIT_FAILURE); } } @@ -1324,13 +1313,13 @@ main(int argc, char *argv[]) } /* Restore saved gamma ramps */ - method->restore(&state); + gamma_restore(&state); } break; } /* Clean up gamma adjustment state */ - method->free(&state); + gamma_free(&state); /* Free memory */ if (method_args != NULL) diff --git a/src/redshift.h b/src/redshift.h index c7b0d96b..1a154219 100644 --- a/src/redshift.h +++ b/src/redshift.h @@ -15,6 +15,7 @@ along with Redshift. If not, see . Copyright (c) 2013 Jon Lund Steffensen + Copyright (c) 2014 Mattias Andrée */ #ifndef REDSHIFT_REDSHIFT_H @@ -45,14 +46,7 @@ /* Gamma adjustment method */ typedef int gamma_method_init_func(void *state); typedef int gamma_method_start_func(void *state); -typedef void gamma_method_free_func(void *state); typedef void gamma_method_print_help_func(FILE *f); -typedef int gamma_method_set_option_func(void *state, const char *key, - const char *value); -typedef void gamma_method_restore_func(void *state); -typedef int gamma_method_set_temperature_func(void *state, int temp, - float brightness, - const float gamma[3]); typedef struct { char *name; @@ -64,18 +58,9 @@ typedef struct { gamma_method_init_func *init; /* Allocate storage and make connections that depend on options. */ gamma_method_start_func *start; - /* Free all allocated storage and close connections. */ - gamma_method_free_func *free; /* Print help on options for this adjustment method. */ gamma_method_print_help_func *print_help; - /* Set an option key, value-pair */ - gamma_method_set_option_func *set_option; - - /* Restore the adjustment to the state before start was called. */ - gamma_method_restore_func *restore; - /* Set a specific color temperature. */ - gamma_method_set_temperature_func *set_temperature; } gamma_method_t; From 04df1b4a6ba1e0cec8276f40bc83a5314fa8c285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Mon, 12 May 2014 21:47:16 +0200 Subject: [PATCH 18/29] Rewrite gamma-randr to use gamma-common MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/gamma-randr.c | 475 ++++++++++++++++++++++++---------------------- src/gamma-randr.h | 32 +--- 2 files changed, 256 insertions(+), 251 deletions(-) diff --git a/src/gamma-randr.c b/src/gamma-randr.c index 9e528b5d..9156837c 100644 --- a/src/gamma-randr.c +++ b/src/gamma-randr.c @@ -15,8 +15,11 @@ along with Redshift. If not, see . Copyright (c) 2010 Jon Lund Steffensen + Copyright (c) 2014 Mattias Andrée */ +#include "gamma-randr.h" + #include #include #include @@ -29,38 +32,49 @@ # define _(s) s #endif -#include -#include - -#include "gamma-randr.h" -#include "colorramp.h" - #define RANDR_VERSION_MAJOR 1 #define RANDR_VERSION_MINOR 3 -int -randr_init(randr_state_t *state) + +static void +randr_free_site(void *data) +{ + /* Close connection. */ + xcb_disconnect((xcb_connection_t *)data); +} + +static void +randr_free_partition(void *data) { - /* Initialize state. */ - state->screen_num = -1; - state->crtc_num = -1; + if (((randr_screen_data_t *)data)->crtcs) + free(((randr_screen_data_t *)data)->crtcs); + free(data); +} - state->crtc_count = 0; - state->crtcs = NULL; +static void +randr_free_crtc(void *data) +{ + (void) data; +} + +static int +randr_open_site(gamma_server_state_t *state, char *site, gamma_site_state_t *site_out) +{ + (void) state; xcb_generic_error_t *error; - /* Open X server connection */ - state->conn = xcb_connect(NULL, &state->preferred_screen); + /* Open X server connection. */ + xcb_connection_t *connection = xcb_connect(site, NULL); - /* Query RandR version */ + /* Query RandR version. */ xcb_randr_query_version_cookie_t ver_cookie = - xcb_randr_query_version(state->conn, RANDR_VERSION_MAJOR, + xcb_randr_query_version(connection, RANDR_VERSION_MAJOR, RANDR_VERSION_MINOR); xcb_randr_query_version_reply_t *ver_reply = - xcb_randr_query_version_reply(state->conn, ver_cookie, &error); + xcb_randr_query_version_reply(connection, ver_cookie, &error); /* TODO What does it mean when both error and ver_reply is NULL? Apparently, we have to check both to avoid seg faults. */ @@ -68,7 +82,7 @@ randr_init(randr_state_t *state) int ec = (error != 0) ? error->error_code : -1; fprintf(stderr, _("`%s' returned error %d\n"), "RANDR Query Version", ec); - xcb_disconnect(state->conn); + xcb_disconnect(connection); return -1; } @@ -77,48 +91,66 @@ randr_init(randr_state_t *state) fprintf(stderr, _("Unsupported RANDR version (%u.%u)\n"), ver_reply->major_version, ver_reply->minor_version); free(ver_reply); - xcb_disconnect(state->conn); + xcb_disconnect(connection); return -1; } - free(ver_reply); + site_out->data = connection; + /* Get the number of available screens. */ + const xcb_setup_t *setup = xcb_get_setup(connection); + xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup); + site_out->partitions_available = (size_t)(iter.rem); + + free(ver_reply); return 0; } -int -randr_start(randr_state_t *state) +static int +randr_open_partition(gamma_server_state_t *state, gamma_site_state_t *site, + size_t partition, gamma_partition_state_t *partition_out) { - xcb_generic_error_t *error; + (void) state; + + randr_screen_data_t *data = malloc(sizeof(randr_screen_data_t)); + partition_out->data = data; + if (data == NULL) { + perror("malloc"); + return -1; + } + data->crtcs = NULL; - int screen_num = state->screen_num; - if (screen_num < 0) screen_num = state->preferred_screen; + xcb_connection_t *connection = site->data; - /* Get screen */ - const xcb_setup_t *setup = xcb_get_setup(state->conn); + /* Get screen. */ + const xcb_setup_t *setup = xcb_get_setup(connection); xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup); - state->screen = NULL; + xcb_screen_t *screen = NULL; - for (int i = 0; iter.rem > 0; i++) { - if (i == screen_num) { - state->screen = iter.data; + for (size_t i = 0; iter.rem > 0; i++) { + if (i == partition) { + screen = iter.data; break; } xcb_screen_next(&iter); } - if (state->screen == NULL) { - fprintf(stderr, _("Screen %i could not be found.\n"), - screen_num); + if (screen == NULL) { + fprintf(stderr, _("Screen %ld could not be found.\n"), + partition); + free(data); return -1; } - /* Get list of CRTCs for the screen */ + data->screen = *screen; + + xcb_generic_error_t *error; + + /* Get list of CRTCs for the screen. */ xcb_randr_get_screen_resources_current_cookie_t res_cookie = - xcb_randr_get_screen_resources_current(state->conn, - state->screen->root); + xcb_randr_get_screen_resources_current(connection, screen->root); xcb_randr_get_screen_resources_current_reply_t *res_reply = - xcb_randr_get_screen_resources_current_reply(state->conn, + xcb_randr_get_screen_resources_current_reply(connection, res_cookie, &error); @@ -126,247 +158,236 @@ randr_start(randr_state_t *state) fprintf(stderr, _("`%s' returned error %d\n"), "RANDR Get Screen Resources Current", error->error_code); + free(data); return -1; } - state->crtc_count = res_reply->num_crtcs; - state->crtcs = calloc(state->crtc_count, sizeof(randr_crtc_state_t)); - if (state->crtcs == NULL) { - perror("malloc"); - state->crtc_count = 0; - return -1; - } + partition_out->crtcs_available = res_reply->num_crtcs; xcb_randr_crtc_t *crtcs = xcb_randr_get_screen_resources_current_crtcs(res_reply); - /* Save CRTC identifier in state */ - for (int i = 0; i < state->crtc_count; i++) { - state->crtcs[i].crtc = crtcs[i]; + data->crtcs = malloc(res_reply->num_crtcs * sizeof(xcb_randr_crtc_t)); + if (data->crtcs == NULL) { + perror("malloc"); + free(res_reply); + free(data); + return -1; } + memcpy(data->crtcs, crtcs, res_reply->num_crtcs * sizeof(xcb_randr_crtc_t)); free(res_reply); + return 0; +} - /* Save size and gamma ramps of all CRTCs. - Current gamma ramps are saved so we can restore them - at program exit. */ - for (int i = 0; i < state->crtc_count; i++) { - xcb_randr_crtc_t crtc = state->crtcs[i].crtc; - - /* Request size of gamma ramps */ - xcb_randr_get_crtc_gamma_size_cookie_t gamma_size_cookie = - xcb_randr_get_crtc_gamma_size(state->conn, crtc); - xcb_randr_get_crtc_gamma_size_reply_t *gamma_size_reply = - xcb_randr_get_crtc_gamma_size_reply(state->conn, - gamma_size_cookie, - &error); - - if (error) { - fprintf(stderr, _("`%s' returned error %d\n"), - "RANDR Get CRTC Gamma Size", - error->error_code); - return -1; - } +static int +randr_open_crtc(gamma_server_state_t *state, gamma_site_state_t *site, + gamma_partition_state_t *partition, size_t crtc, gamma_crtc_state_t *crtc_out) +{ + (void) state; - unsigned int ramp_size = gamma_size_reply->size; - state->crtcs[i].ramp_size = ramp_size; + randr_screen_data_t *screen_data = partition->data; + xcb_randr_crtc_t *crtc_id = screen_data->crtcs + crtc; + xcb_connection_t *connection = site->data; + xcb_generic_error_t *error; - free(gamma_size_reply); + crtc_out->data = crtc_id; - if (ramp_size == 0) { - fprintf(stderr, _("Gamma ramp size too small: %i\n"), - ramp_size); - return -1; - } + /* Request size of gamma ramps. */ + xcb_randr_get_crtc_gamma_size_cookie_t gamma_size_cookie = + xcb_randr_get_crtc_gamma_size(connection, *crtc_id); + xcb_randr_get_crtc_gamma_size_reply_t *gamma_size_reply = + xcb_randr_get_crtc_gamma_size_reply(connection, + gamma_size_cookie, + &error); - /* Request current gamma ramps */ - xcb_randr_get_crtc_gamma_cookie_t gamma_get_cookie = - xcb_randr_get_crtc_gamma(state->conn, crtc); - xcb_randr_get_crtc_gamma_reply_t *gamma_get_reply = - xcb_randr_get_crtc_gamma_reply(state->conn, - gamma_get_cookie, - &error); - - if (error) { - fprintf(stderr, _("`%s' returned error %d\n"), - "RANDR Get CRTC Gamma", error->error_code); - return -1; - } - - uint16_t *gamma_r = - xcb_randr_get_crtc_gamma_red(gamma_get_reply); - uint16_t *gamma_g = - xcb_randr_get_crtc_gamma_green(gamma_get_reply); - uint16_t *gamma_b = - xcb_randr_get_crtc_gamma_blue(gamma_get_reply); - - /* Allocate space for saved gamma ramps */ - state->crtcs[i].saved_ramps = - malloc(3*ramp_size*sizeof(uint16_t)); - if (state->crtcs[i].saved_ramps == NULL) { - perror("malloc"); - free(gamma_get_reply); - return -1; - } + if (error) { + fprintf(stderr, _("`%s' returned error %d\n"), + "RANDR Get CRTC Gamma Size", + error->error_code); + return -1; + } - /* Copy gamma ramps into CRTC state */ - memcpy(&state->crtcs[i].saved_ramps[0*ramp_size], gamma_r, - ramp_size*sizeof(uint16_t)); - memcpy(&state->crtcs[i].saved_ramps[1*ramp_size], gamma_g, - ramp_size*sizeof(uint16_t)); - memcpy(&state->crtcs[i].saved_ramps[2*ramp_size], gamma_b, - ramp_size*sizeof(uint16_t)); + ssize_t ramp_size = gamma_size_reply->size; + size_t ramp_memsize = (size_t)ramp_size * sizeof(uint16_t); + free(gamma_size_reply); - free(gamma_get_reply); + if (ramp_size < 2) { + fprintf(stderr, _("Gamma ramp size too small: %ld\n"), + ramp_size); + return -1; } - return 0; -} - -void -randr_restore(randr_state_t *state) -{ - xcb_generic_error_t *error; + crtc_out->saved_ramps.red_size = (size_t)ramp_size; + crtc_out->saved_ramps.green_size = (size_t)ramp_size; + crtc_out->saved_ramps.blue_size = (size_t)ramp_size; - /* Restore CRTC gamma ramps */ - for (int i = 0; i < state->crtc_count; i++) { - xcb_randr_crtc_t crtc = state->crtcs[i].crtc; - - unsigned int ramp_size = state->crtcs[i].ramp_size; - uint16_t *gamma_r = &state->crtcs[i].saved_ramps[0*ramp_size]; - uint16_t *gamma_g = &state->crtcs[i].saved_ramps[1*ramp_size]; - uint16_t *gamma_b = &state->crtcs[i].saved_ramps[2*ramp_size]; - - /* Set gamma ramps */ - xcb_void_cookie_t gamma_set_cookie = - xcb_randr_set_crtc_gamma_checked(state->conn, crtc, - ramp_size, gamma_r, - gamma_g, gamma_b); - error = xcb_request_check(state->conn, gamma_set_cookie); - - if (error) { - fprintf(stderr, _("`%s' returned error %d\n"), - "RANDR Set CRTC Gamma", error->error_code); - fprintf(stderr, _("Unable to restore CRTC %i\n"), i); - } + /* Allocate space for saved gamma ramps. */ + crtc_out->saved_ramps.red = malloc(3 * ramp_memsize); + crtc_out->saved_ramps.green = crtc_out->saved_ramps.red + ramp_size; + crtc_out->saved_ramps.blue = crtc_out->saved_ramps.green + ramp_size; + if (crtc_out->saved_ramps.red == NULL) { + perror("malloc"); + return -1; } -} -void -randr_free(randr_state_t *state) -{ - /* Free CRTC state */ - for (int i = 0; i < state->crtc_count; i++) { - free(state->crtcs[i].saved_ramps); + /* Request current gamma ramps. */ + xcb_randr_get_crtc_gamma_cookie_t gamma_get_cookie = + xcb_randr_get_crtc_gamma(connection, *crtc_id); + xcb_randr_get_crtc_gamma_reply_t *gamma_get_reply = + xcb_randr_get_crtc_gamma_reply(connection, + gamma_get_cookie, + &error); + + if (error) { + fprintf(stderr, _("`%s' returned error %d\n"), + "RANDR Get CRTC Gamma", error->error_code); + return -1; } - free(state->crtcs); - /* Close connection */ - xcb_disconnect(state->conn); -} + uint16_t *gamma_r = xcb_randr_get_crtc_gamma_red(gamma_get_reply); + uint16_t *gamma_g = xcb_randr_get_crtc_gamma_green(gamma_get_reply); + uint16_t *gamma_b = xcb_randr_get_crtc_gamma_blue(gamma_get_reply); -void -randr_print_help(FILE *f) -{ - fputs(_("Adjust gamma ramps with the X RANDR extension.\n"), f); - fputs("\n", f); + /* Copy gamma ramps into CRTC state. */ + memcpy(crtc_out->saved_ramps.red, gamma_r, ramp_memsize); + memcpy(crtc_out->saved_ramps.green, gamma_g, ramp_memsize); + memcpy(crtc_out->saved_ramps.blue, gamma_b, ramp_memsize); - /* TRANSLATORS: RANDR help output - left column must not be translated */ - fputs(_(" screen=N\tX screen to apply adjustments to\n" - " crtc=N\tCRTC to apply adjustments to\n"), f); - fputs("\n", f); + free(gamma_get_reply); + return 0; } -int -randr_set_option(randr_state_t *state, const char *key, const char *value) +static void +randr_invalid_partition(const gamma_site_state_t *site, size_t partition) { - if (strcasecmp(key, "screen") == 0) { - state->screen_num = atoi(value); - } else if (strcasecmp(key, "crtc") == 0) { - state->crtc_num = atoi(value); + fprintf(stderr, _("Screen %ld does not exist. "), + partition); + if (site->partitions_available > 1) { + fprintf(stderr, _("Valid screens are [0-%ld].\n"), + site->partitions_available - 1); } else { - fprintf(stderr, _("Unknown method parameter: `%s'.\n"), key); - return -1; + fprintf(stderr, _("Only screen 0 exists, did you mean CRTC %ld?\n"), + partition); } - - return 0; } static int -randr_set_temperature_for_crtc(randr_state_t *state, int crtc_num, int temp, - float brightness, const float gamma[3]) +randr_set_ramps(gamma_server_state_t *state, gamma_crtc_state_t *crtc, gamma_ramps_t ramps) { + xcb_connection_t *connection = state->sites[crtc->site_index].data; xcb_generic_error_t *error; - - if (crtc_num >= state->crtc_count || crtc_num < 0) { - fprintf(stderr, _("CRTC %d does not exist. "), - state->crtc_num); - if (state->crtc_count > 1) { - fprintf(stderr, _("Valid CRTCs are [0-%d].\n"), - state->crtc_count-1); - } else { - fprintf(stderr, _("Only CRTC 0 exists.\n")); - } - - return -1; - } - - xcb_randr_crtc_t crtc = state->crtcs[crtc_num].crtc; - unsigned int ramp_size = state->crtcs[crtc_num].ramp_size; - - /* Create new gamma ramps */ - uint16_t *gamma_ramps = malloc(3*ramp_size*sizeof(uint16_t)); - if (gamma_ramps == NULL) { - perror("malloc"); - return -1; - } - - uint16_t *gamma_r = &gamma_ramps[0*ramp_size]; - uint16_t *gamma_g = &gamma_ramps[1*ramp_size]; - uint16_t *gamma_b = &gamma_ramps[2*ramp_size]; - - colorramp_fill(gamma_r, gamma_g, gamma_b, ramp_size, - temp, brightness, gamma); /* Set new gamma ramps */ xcb_void_cookie_t gamma_set_cookie = - xcb_randr_set_crtc_gamma_checked(state->conn, crtc, - ramp_size, gamma_r, - gamma_g, gamma_b); - error = xcb_request_check(state->conn, gamma_set_cookie); + xcb_randr_set_crtc_gamma_checked(connection, *(xcb_randr_crtc_t *)(crtc->data), + ramps.red_size, ramps.red, ramps.green, ramps.blue); + error = xcb_request_check(connection, gamma_set_cookie); if (error) { fprintf(stderr, _("`%s' returned error %d\n"), "RANDR Set CRTC Gamma", error->error_code); - free(gamma_ramps); return -1; } - free(gamma_ramps); - return 0; } +static int +randr_set_option(gamma_server_state_t *state, const char *key, char *value, ssize_t section) +{ + if (strcasecmp(key, "screen") == 0) { + ssize_t screen = strcasecmp(value, "all") ? (ssize_t)atoi(value) : -1; + if (screen < 0 && strcasecmp(value, "all")) { + /* TRANSLATORS: `all' must not be translated. */ + fprintf(stderr, _("Screen must be `all' or a non-negative integer.\n")); + return -1; + } + if (section >= 0) { + state->selections[section].partition = screen; + } else { + for (size_t i = 0; i < state->selections_made; i++) + state->selections[i].partition = screen; + } + return 0; + } else if (strcasecmp(key, "crtc") == 0) { + ssize_t crtc = strcasecmp(value, "all") ? (ssize_t)atoi(value) : -1; + if (crtc < 0 && strcasecmp(value, "all")) { + /* TRANSLATORS: `all' must not be translated. */ + fprintf(stderr, _("CRTC must be `all' or a non-negative integer.\n")); + return -1; + } + if (section >= 0) { + state->selections[section].crtc = crtc; + } else { + for (size_t i = 0; i < state->selections_made; i++) + state->selections[i].crtc = crtc; + } + return 0; + } else if (strcasecmp(key, "display") == 0) { + if (section >= 0) { + state->selections[section].site = strdup(value); + if (state->selections[section].site == NULL) + goto strdup_fail; + } else { + for (size_t i = 0; i < state->selections_made; i++) { + state->selections[i].site = strdup(value); + if (state->selections[i].site == NULL) + goto strdup_fail; + } + } + return 0; + } + return 1; + +strdup_fail: + perror("strdup"); + return -1; +} + + int -randr_set_temperature(randr_state_t *state, int temp, float brightness, - const float gamma[3]) +randr_init(gamma_server_state_t *state) { int r; - - /* If no CRTC number has been specified, - set temperature on all CRTCs. */ - if (state->crtc_num < 0) { - for (int i = 0; i < state->crtc_count; i++) { - r = randr_set_temperature_for_crtc(state, i, - temp, brightness, - gamma); - if (r < 0) return -1; - } - } else { - return randr_set_temperature_for_crtc(state, state->crtc_num, - temp, brightness, gamma); + r = gamma_init(state); + if (r != 0) return r; + + state->selections->site = getenv("DISPLAY") ? strdup(getenv("DISPLAY")) : NULL; + state->free_site_data = randr_free_site; + state->free_partition_data = randr_free_partition; + state->free_crtc_data = randr_free_crtc; + state->open_site = randr_open_site; + state->open_partition = randr_open_partition; + state->open_crtc = randr_open_crtc; + state->invalid_partition = randr_invalid_partition; + state->set_ramps = randr_set_ramps; + state->set_option = randr_set_option; + + if (getenv("DISPLAY") != NULL && state->selections->site == NULL) { + perror("strdup"); + return -1; } return 0; } + +int +randr_start(gamma_server_state_t *state) +{ + return gamma_resolve_selections(state); +} + +void +randr_print_help(FILE *f) +{ + fputs(_("Adjust gamma ramps with the X RANDR extension.\n"), f); + fputs("\n", f); + + /* TRANSLATORS: RANDR help output + left column must not be translated */ + fputs(_(" crtc=N\tCRTC to apply adjustments to\n" + " screen=N\tX screen to apply adjustments to\n" + " display=NAME\tX display to apply adjustments to\n"), f); + fputs("\n", f); +} diff --git a/src/gamma-randr.h b/src/gamma-randr.h index cef0021e..3dc8cbcc 100644 --- a/src/gamma-randr.h +++ b/src/gamma-randr.h @@ -15,47 +15,31 @@ along with Redshift. If not, see . Copyright (c) 2010 Jon Lund Steffensen + Copyright (c) 2014 Mattias Andrée */ #ifndef REDSHIFT_GAMMA_RANDR_H #define REDSHIFT_GAMMA_RANDR_H +#include "gamma-common.h" + #include #include #include #include -#include "redshift.h" - typedef struct { - xcb_randr_crtc_t crtc; - unsigned int ramp_size; - uint16_t *saved_ramps; -} randr_crtc_state_t; + xcb_screen_t screen; + xcb_randr_crtc_t *crtcs; +} randr_screen_data_t; -typedef struct { - xcb_connection_t *conn; - xcb_screen_t *screen; - int preferred_screen; - int screen_num; - int crtc_num; - unsigned int crtc_count; - randr_crtc_state_t *crtcs; -} randr_state_t; - -int randr_init(randr_state_t *state); -int randr_start(randr_state_t *state); -void randr_free(randr_state_t *state); +int randr_init(gamma_server_state_t *state); +int randr_start(gamma_server_state_t *state); void randr_print_help(FILE *f); -int randr_set_option(randr_state_t *state, const char *key, const char *value); - -void randr_restore(randr_state_t *state); -int randr_set_temperature(randr_state_t *state, int temp, float brightness, - const float gamma[3]); #endif /* ! REDSHIFT_GAMMA_RANDR_H */ From c17850a899286b537dfb0ed9f428500da74cfb48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Mon, 12 May 2014 20:28:07 +0200 Subject: [PATCH 19/29] Rewrite gamma-vidmode to use gamma-common MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/gamma-vidmode.c | 268 +++++++++++++++++++++++++++----------------- src/gamma-vidmode.h | 23 +--- 2 files changed, 172 insertions(+), 119 deletions(-) diff --git a/src/gamma-vidmode.c b/src/gamma-vidmode.c index 656ce007..c91bda25 100644 --- a/src/gamma-vidmode.c +++ b/src/gamma-vidmode.c @@ -15,8 +15,11 @@ along with Redshift. If not, see . Copyright (c) 2010 Jon Lund Steffensen + Copyright (c) 2014 Mattias Andrée */ +#include "gamma-vidmode.h" + #include #include #include @@ -32,75 +35,115 @@ #include #include -#include "gamma-vidmode.h" -#include "colorramp.h" +static void +vidmode_free_site(void *data) +{ + /* Close display connection. */ + XCloseDisplay((Display *)data); +} + +static void +vidmode_free_partition(void *data) +{ + (void) data; +} -int -vidmode_init(vidmode_state_t *state) +static int +vidmode_open_site(gamma_server_state_t *state, char *site, gamma_site_state_t *site_out) { - state->screen_num = -1; - state->saved_ramps = NULL; + (void) state; - /* Open display */ - state->display = XOpenDisplay(NULL); - if (state->display == NULL) { + /* Open display. */ + Display *display = XOpenDisplay(site); + site_out->data = display; + if (display == NULL) { fprintf(stderr, _("X request failed: %s\n"), "XOpenDisplay"); + fprintf(stderr, _("Tried to open display `%s'\n"), + site); + return -1; + } + + /* Query extension version. */ + int r, major, minor; + r = XF86VidModeQueryVersion(display, &major, &minor); + if (!r) { + fprintf(stderr, _("X request failed: %s\n"), + "XF86VidModeQueryVersion"); + XCloseDisplay(display); return -1; } + /* Get the number of available screens. */ + site_out->partitions_available = (size_t)ScreenCount(display); + if (site_out->partitions_available < 1) { + fprintf(stderr, _("X request failed: %s\n"), + "ScreenCount"); + } + return 0; } -int -vidmode_start(vidmode_state_t *state) +static int +vidmode_open_partition(gamma_server_state_t *state, gamma_site_state_t *site, + size_t partition, gamma_partition_state_t *partition_out) { - int r; - int screen_num = state->screen_num; + (void) state; + (void) site; + (void) partition; + partition_out->data = (void *)partition; + partition_out->crtcs_available = 1; + return 0; +} - if (screen_num < 0) screen_num = DefaultScreen(state->display); - state->screen_num = screen_num; +static int +vidmode_open_crtc(gamma_server_state_t *state, gamma_site_state_t *site, + gamma_partition_state_t *partition, size_t crtc, gamma_crtc_state_t *crtc_out) +{ + (void) state; + (void) crtc; - /* Query extension version */ - int major, minor; - r = XF86VidModeQueryVersion(state->display, &major, &minor); - if (!r) { - fprintf(stderr, _("X request failed: %s\n"), - "XF86VidModeQueryVersion"); - return -1; - } + Display *display = site->data; + int screen = (int)(size_t)(partition->data); + int ramp_size; + int r; + + crtc_out->data = NULL; - /* Request size of gamma ramps */ - r = XF86VidModeGetGammaRampSize(state->display, state->screen_num, - &state->ramp_size); + /* Request size of gamma ramps. */ + r = XF86VidModeGetGammaRampSize(display, screen, &ramp_size); if (!r) { fprintf(stderr, _("X request failed: %s\n"), "XF86VidModeGetGammaRampSize"); return -1; } - if (state->ramp_size == 0) { + if (ramp_size < 2) { fprintf(stderr, _("Gamma ramp size too small: %i\n"), - state->ramp_size); + ramp_size); return -1; } - /* Allocate space for saved gamma ramps */ - state->saved_ramps = malloc(3*state->ramp_size*sizeof(uint16_t)); - if (state->saved_ramps == NULL) { + crtc_out->saved_ramps.red_size = (size_t)ramp_size; + crtc_out->saved_ramps.green_size = (size_t)ramp_size; + crtc_out->saved_ramps.blue_size = (size_t)ramp_size; + + /* Allocate space for saved gamma ramps. */ + crtc_out->saved_ramps.red = malloc(3 * (size_t)ramp_size * sizeof(uint16_t)); + if (crtc_out->saved_ramps.red == NULL) { perror("malloc"); return -1; } - uint16_t *gamma_r = &state->saved_ramps[0*state->ramp_size]; - uint16_t *gamma_g = &state->saved_ramps[1*state->ramp_size]; - uint16_t *gamma_b = &state->saved_ramps[2*state->ramp_size]; + crtc_out->saved_ramps.green = crtc_out->saved_ramps.red + ramp_size; + crtc_out->saved_ramps.blue = crtc_out->saved_ramps.green + ramp_size; /* Save current gamma ramps so we can restore them at program exit. */ - r = XF86VidModeGetGammaRamp(state->display, state->screen_num, - state->ramp_size, gamma_r, gamma_g, - gamma_b); + r = XF86VidModeGetGammaRamp(display, screen, (size_t)ramp_size, + crtc_out->saved_ramps.red, + crtc_out->saved_ramps.green, + crtc_out->saved_ramps.blue); if (!r) { fprintf(stderr, _("X request failed: %s\n"), "XF86VidModeGetGammaRamp"); @@ -110,90 +153,113 @@ vidmode_start(vidmode_state_t *state) return 0; } -void -vidmode_free(vidmode_state_t *state) +static void +vidmode_invalid_partition(const gamma_site_state_t *site, size_t partition) { - /* Free saved ramps */ - free(state->saved_ramps); - - /* Close display connection */ - XCloseDisplay(state->display); + fprintf(stderr, _("Screen %ld does not exist. "), + partition); + if (site->partitions_available > 1) { + fprintf(stderr, _("Valid screens are [0-%ld].\n"), + site->partitions_available - 1); + } else { + fprintf(stderr, _("Only screen 0 exists, did you mean CRTC %ld?\n"), + partition); + fprintf(stderr, _("If so, you need to use `randr' instead of `vidmode'.\n")); + } } -void -vidmode_print_help(FILE *f) +static int +vidmode_set_ramps(gamma_server_state_t *state, gamma_crtc_state_t *crtc, gamma_ramps_t ramps) { - fputs(_("Adjust gamma ramps with the X VidMode extension.\n"), f); - fputs("\n", f); - - /* TRANSLATORS: VidMode help output - left column must not be translated */ - fputs(_(" screen=N\tX screen to apply adjustments to\n"), f); - fputs("\n", f); + int r; + r = XF86VidModeSetGammaRamp((Display *)(state->sites[crtc->site_index].data), crtc->partition, + ramps.red_size, ramps.red, ramps.green, ramps.blue); + if (!r) { + fprintf(stderr, _("X request failed: %s\n"), + "XF86VidModeSetGammaRamp"); + } + return r; } -int -vidmode_set_option(vidmode_state_t *state, const char *key, const char *value) +static int +vidmode_set_option(gamma_server_state_t *state, const char *key, char *value, ssize_t section) { if (strcasecmp(key, "screen") == 0) { - state->screen_num = atoi(value); - } else { - fprintf(stderr, _("Unknown method parameter: `%s'.\n"), key); - return -1; + ssize_t screen = strcasecmp(value, "all") ? (ssize_t)atoi(value) : -1; + if (screen < 0 && strcasecmp(value, "all")) { + /* TRANSLATORS: `all' must not be translated. */ + fprintf(stderr, _("Screen must be `all' or a non-negative integer.\n")); + return -1; + } + if (section >= 0) { + state->selections[section].partition = screen; + } else { + for (size_t i = 0; i < state->selections_made; i++) + state->selections[i].partition = screen; + } + return 0; + } else if (strcasecmp(key, "display") == 0) { + if (section >= 0) { + state->selections[section].site = strdup(value); + if (state->selections[section].site == NULL) + goto strdup_fail; + } else { + for (size_t i = 0; i < state->selections_made; i++) { + state->selections[i].site = strdup(value); + if (state->selections[i].site == NULL) + goto strdup_fail; + } + } + return 0; } + return 1; - return 0; +strdup_fail: + perror("strdup"); + return -1; } -void -vidmode_restore(vidmode_state_t *state) -{ - uint16_t *gamma_r = &state->saved_ramps[0*state->ramp_size]; - uint16_t *gamma_g = &state->saved_ramps[1*state->ramp_size]; - uint16_t *gamma_b = &state->saved_ramps[2*state->ramp_size]; - - /* Restore gamma ramps */ - int r = XF86VidModeSetGammaRamp(state->display, state->screen_num, - state->ramp_size, gamma_r, gamma_g, - gamma_b); - if (!r) { - fprintf(stderr, _("X request failed: %s\n"), - "XF86VidModeSetGammaRamp"); - } -} int -vidmode_set_temperature(vidmode_state_t *state, int temp, float brightness, - const float gamma[3]) +vidmode_init(gamma_server_state_t *state) { int r; - - /* Create new gamma ramps */ - uint16_t *gamma_ramps = malloc(3*state->ramp_size*sizeof(uint16_t)); - if (gamma_ramps == NULL) { - perror("malloc"); + r = gamma_init(state); + if (r != 0) return r; + + state->selections->site = getenv("DISPLAY") ? strdup(getenv("DISPLAY")) : NULL; + state->free_site_data = vidmode_free_site; + state->free_partition_data = vidmode_free_partition; + state->open_site = vidmode_open_site; + state->open_partition = vidmode_open_partition; + state->open_crtc = vidmode_open_crtc; + state->invalid_partition = vidmode_invalid_partition; + state->set_ramps = vidmode_set_ramps; + state->set_option = vidmode_set_option; + + if (getenv("DISPLAY") != NULL && state->selections->site == NULL) { + perror("strdup"); return -1; } - uint16_t *gamma_r = &gamma_ramps[0*state->ramp_size]; - uint16_t *gamma_g = &gamma_ramps[1*state->ramp_size]; - uint16_t *gamma_b = &gamma_ramps[2*state->ramp_size]; - - colorramp_fill(gamma_r, gamma_g, gamma_b, state->ramp_size, - temp, brightness, gamma); + return 0; +} - /* Set new gamma ramps */ - r = XF86VidModeSetGammaRamp(state->display, state->screen_num, - state->ramp_size, gamma_r, gamma_g, - gamma_b); - if (!r) { - fprintf(stderr, _("X request failed: %s\n"), - "XF86VidModeSetGammaRamp"); - free(gamma_ramps); - return -1; - } +int +vidmode_start(gamma_server_state_t *state) +{ + return gamma_resolve_selections(state); +} - free(gamma_ramps); +void +vidmode_print_help(FILE *f) +{ + fputs(_("Adjust gamma ramps with the X VidMode extension.\n"), f); + fputs("\n", f); - return 0; + /* TRANSLATORS: VidMode help output + left column must not be translated. */ + fputs(_(" screen=N\tX screen to apply adjustments to\n" + " display=NAME\tX display to apply adjustments to\n"), f); + fputs("\n", f); } diff --git a/src/gamma-vidmode.h b/src/gamma-vidmode.h index 8afd8fd2..9e96cf0f 100644 --- a/src/gamma-vidmode.h +++ b/src/gamma-vidmode.h @@ -15,35 +15,22 @@ along with Redshift. If not, see . Copyright (c) 2010 Jon Lund Steffensen + Copyright (c) 2014 Mattias Andrée */ #ifndef REDSHIFT_GAMMA_VIDMODE_H #define REDSHIFT_GAMMA_VIDMODE_H +#include "gamma-common.h" + #include #include -#include - -typedef struct { - Display *display; - int screen_num; - int ramp_size; - uint16_t *saved_ramps; -} vidmode_state_t; - -int vidmode_init(vidmode_state_t *state); -int vidmode_start(vidmode_state_t *state); -void vidmode_free(vidmode_state_t *state); +int vidmode_init(gamma_server_state_t *state); +int vidmode_start(gamma_server_state_t *state); void vidmode_print_help(FILE *f); -int vidmode_set_option(vidmode_state_t *state, const char *key, - const char *value); - -void vidmode_restore(vidmode_state_t *state); -int vidmode_set_temperature(vidmode_state_t *state, int temp, float brightness, - const float gamma[3]); #endif /* ! REDSHIFT_GAMMA_VIDMODE_H */ From e105abb32d7e9a34cf5fa87acce92ad8460fe664 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Mon, 12 May 2014 19:42:37 +0200 Subject: [PATCH 20/29] Rewrite gramma-drm to use gamma-common MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/gamma-drm.c | 457 ++++++++++++++++++++++++++++-------------------- src/gamma-drm.h | 28 +-- 2 files changed, 276 insertions(+), 209 deletions(-) diff --git a/src/gamma-drm.c b/src/gamma-drm.c index e784b471..4937f1b0 100644 --- a/src/gamma-drm.c +++ b/src/gamma-drm.c @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include #ifdef ENABLE_NLS # include @@ -42,239 +45,317 @@ #include "colorramp.h" -int -drm_init(drm_state_t *state) +static void +drm_free_partition(void *data) { - /* Initialize state. */ - state->card_num = 0; - state->crtc_num = -1; - state->fd = -1; - state->res = NULL; - state->crtcs = NULL; - - return 0; + drm_card_data_t *card_data = data; + if (card_data->res != NULL) + drmModeFreeResources(card_data->res); + if (card_data->fd >= 0) + close(card_data->fd); + free(data); } -int -drm_start(drm_state_t *state) +static void +drm_free_crtc(void *data) { - /* Acquire access to a graphics card. */ - long maxlen = strlen(DRM_DIR_NAME) + strlen(DRM_DEV_NAME) + 10; - char *pathname = alloca(maxlen * sizeof(char)); + (void) data; +} - sprintf(pathname, DRM_DEV_NAME, DRM_DIR_NAME, state->card_num); +static int +drm_open_site(gamma_server_state_t *state, char *site, gamma_site_state_t *site_out) +{ + (void) state; + (void) site; + site_out->data = NULL; + site_out->partitions_available = 0; - state->fd = open(pathname, O_RDWR | O_CLOEXEC); - if (state->fd < 0) { - /* TODO check if access permissions, normally root or - membership of the video group is required. */ - perror("open"); - return -1; + /* Count the number of available graphics cards. */ + char pathname[PATH_MAX]; + struct stat _attr; + while (1) { + snprintf(pathname, PATH_MAX, DRM_DEV_NAME, DRM_DIR_NAME, + (int)site_out->partitions_available); + if (stat(pathname, &_attr)) + break; + site_out->partitions_available++; } - /* Acquire mode resources. */ - state->res = drmModeGetResources(state->fd); - if (state->res == NULL) { - fprintf(stderr, _("Failed to get DRM mode resources\n")); - close(state->fd); - state->fd = -1; + return 0; +} + +static int +drm_open_partition(gamma_server_state_t *state, gamma_site_state_t *site, + size_t partition, gamma_partition_state_t *partition_out) +{ + (void) state; + (void) site; + + partition_out->data = NULL; + drm_card_data_t *data = malloc(sizeof(drm_card_data_t)); + if (data == NULL) { + perror("malloc"); return -1; } + data->fd = -1; + data->res = NULL; + data->index = partition; + partition_out->data = data; + + /* Acquire access to a graphics card. */ + char pathname[PATH_MAX]; + snprintf(pathname, PATH_MAX, DRM_DEV_NAME, DRM_DIR_NAME, (int)partition); - /* Create entries for selected CRTCs. */ - int crtc_count = state->res->count_crtcs; - if (state->crtc_num >= 0) { - if (state->crtc_num >= crtc_count) { - fprintf(stderr, _("CRTC %d does not exist. "), - state->crtc_num); - if (crtc_count > 1) { - fprintf(stderr, _("Valid CRTCs are [0-%d].\n"), - crtc_count-1); + data->fd = open(pathname, O_RDWR | O_CLOEXEC); + if (data->fd < 0) { + if ((errno == ENXIO) || (errno == ENODEV)) { + goto card_removed; + } else if (errno == EACCES) { + struct stat attr; + int r; + r = stat(pathname, &attr); + if (r != 0) { + if (errno == EACCES) + goto card_removed; + perror("stat"); +#define __test(R, W) ((attr.st_mode & (R | W)) == (R | W)) + } else if ((attr.st_uid == geteuid() && __test(S_IRUSR, S_IWUSR)) || + (attr.st_gid == getegid() && __test(S_IRGRP, S_IWGRP)) || + __test(S_IROTH, S_IWOTH)) { + fprintf(stderr, _("Failed to access the graphics card.\n")); + fprintf(stderr, _("Perhaps it is locked.\n")); + } else if (attr.st_gid == 0 /* root group */ || __test(S_IRGRP, S_IWGRP)) { + fprintf(stderr, + _("It appears that your system administrator have\n" + "restricted access to the graphics card.")); +#undef __test } else { - fprintf(stderr, _("Only CRTC 0 exists.\n")); + gid_t supplemental_groups[NGROUPS_MAX]; + r = getgroups(NGROUPS_MAX, supplemental_groups); + if (r < 0) { + perror("getgroups"); + goto card_error; + } + int i, n = r; + for (i = 0; i < n; i++) { + if (supplemental_groups[i] == attr.st_gid) + break; + } + if (i != n) { + fprintf(stderr, _("Failed to access the graphics card.\n")); + } else { + struct group *group = getgrgid(attr.st_gid); + if (group == NULL || group->gr_name == NULL) { + fprintf(stderr, + _("You need to be in the group %i to used DRM.\n"), + attr.st_gid); + } else { + fprintf(stderr, + _("You need to be in the group `%s' to used DRM.\n"), + group->gr_name); + } + } } - close(state->fd); - state->fd = -1; - drmModeFreeResources(state->res); - state->res = NULL; - return -1; - } - - state->crtcs = malloc(2 * sizeof(drm_crtc_state_t)); - state->crtcs[1].crtc_num = -1; - - state->crtcs->crtc_num = state->crtc_num; - state->crtcs->crtc_id = -1; - state->crtcs->gamma_size = -1; - state->crtcs->r_gamma = NULL; - state->crtcs->g_gamma = NULL; - state->crtcs->b_gamma = NULL; - } else { - int crtc_num; - state->crtcs = malloc((crtc_count + 1) * sizeof(drm_crtc_state_t)); - state->crtcs[crtc_count].crtc_num = -1; - for (crtc_num = 0; crtc_num < crtc_count; crtc_num++) { - state->crtcs[crtc_num].crtc_num = crtc_num; - state->crtcs[crtc_num].crtc_id = -1; - state->crtcs[crtc_num].gamma_size = -1; - state->crtcs[crtc_num].r_gamma = NULL; - state->crtcs[crtc_num].g_gamma = NULL; - state->crtcs[crtc_num].b_gamma = NULL; + } else { + perror("open"); } + goto card_error; + card_removed: + fprintf(stderr, _("It appears that you have removed a graphics card.\n" + "Please do not do that, it's so rude. I cannot\n" + "imagine what issues it might have caused you.\n")); + card_error: + free(data); + return -1; } - /* Load CRTC information and gamma ramps. */ - drm_crtc_state_t *crtcs = state->crtcs; - for (; crtcs->crtc_num >= 0; crtcs++) { - crtcs->crtc_id = state->res->crtcs[crtcs->crtc_num]; - drmModeCrtc* crtc_info = drmModeGetCrtc(state->fd, crtcs->crtc_id); - if (crtc_info == NULL) { - fprintf(stderr, _("CRTC %i lost, skipping\n"), crtcs->crtc_num); - continue; - } - crtcs->gamma_size = crtc_info->gamma_size; - drmModeFreeCrtc(crtc_info); - if (crtcs->gamma_size <= 1) { - fprintf(stderr, _("Could not get gamma ramp size for CRTC %i\n" - "on graphics card %i, ignoring device.\n"), - crtcs->crtc_num, state->card_num); - continue; - } - /* Valgrind complains about us reading uninitialize memory if we just use malloc. */ - crtcs->r_gamma = calloc(3 * crtcs->gamma_size, sizeof(uint16_t)); - crtcs->g_gamma = crtcs->r_gamma + crtcs->gamma_size; - crtcs->b_gamma = crtcs->g_gamma + crtcs->gamma_size; - if (crtcs->r_gamma != NULL) { - int r = drmModeCrtcGetGamma(state->fd, crtcs->crtc_id, crtcs->gamma_size, - crtcs->r_gamma, crtcs->g_gamma, crtcs->b_gamma); - if (r < 0) { - fprintf(stderr, _("DRM could not read gamma ramps on CRTC %i on\n" - "graphics card %i, ignoring device.\n"), - crtcs->crtc_num, state->card_num); - free(crtcs->r_gamma); - crtcs->r_gamma = NULL; - } - } else { - perror("malloc"); - drmModeFreeResources(state->res); - state->res = NULL; - close(state->fd); - state->fd = -1; - while (crtcs-- != state->crtcs) - free(crtcs->r_gamma); - free(state->crtcs); - state->crtcs = NULL; - return -1; - } + /* Acquire mode resources. */ + data->res = drmModeGetResources(data->fd); + if (data->res == NULL) { + fprintf(stderr, _("Failed to get DRM mode resources.\n")); + close(data->fd); + free(data); + return -1; + } + if (data->res->count_crtcs < 0) { + fprintf(stderr, _("Got negative number of graphics cards from DRM.\n")); + close(data->fd); + free(data); + return -1; } + partition_out->crtcs_available = (size_t)(data->res->count_crtcs); return 0; } -void -drm_restore(drm_state_t *state) +static int +drm_open_crtc(gamma_server_state_t *state, gamma_site_state_t *site, + gamma_partition_state_t *partition, size_t crtc, gamma_crtc_state_t *crtc_out) { - drm_crtc_state_t *crtcs = state->crtcs; - while (crtcs->crtc_num >= 0) { - if (crtcs->r_gamma != NULL) { - drmModeCrtcSetGamma(state->fd, crtcs->crtc_id, crtcs->gamma_size, - crtcs->r_gamma, crtcs->g_gamma, crtcs->b_gamma); - } - crtcs++; + (void) state; + (void) site; + + drm_card_data_t *card = partition->data; + uint32_t crtc_id = card->res->crtcs[(size_t)crtc]; + crtc_out->data = (void*)(size_t)crtc_id; + drmModeCrtc *crtc_info = drmModeGetCrtc(card->fd, crtc_id); + if (crtc_info == NULL) { + fprintf(stderr, _("Please do not unplug monitors!\n")); + return -1; } -} -void -drm_free(drm_state_t *state) -{ - if (state->crtcs != NULL) { - drm_crtc_state_t *crtcs = state->crtcs; - while (crtcs->crtc_num >= 0) { - if (crtcs->r_gamma != NULL) - free(crtcs->r_gamma); - crtcs->crtc_num = -1; - crtcs++; - } - free(state->crtcs); - state->crtcs = NULL; + ssize_t gamma_size = crtc_info->gamma_size; + drmModeFreeCrtc(crtc_info); + if (gamma_size < 2) { + fprintf(stderr, _("Could not get gamma ramp size for CRTC %ld\n" + "on graphics card %ld.\n"), + crtc, card->index); + return -1; } - if (state->res != NULL) { - drmModeFreeResources(state->res); - state->res = NULL; + crtc_out->saved_ramps.red_size = (size_t)gamma_size; + crtc_out->saved_ramps.green_size = (size_t)gamma_size; + crtc_out->saved_ramps.blue_size = (size_t)gamma_size; + + /* Valgrind complains about us reading uninitialize memory if we just use malloc. */ + crtc_out->saved_ramps.red = calloc(3 * (size_t)gamma_size, sizeof(uint16_t)); + crtc_out->saved_ramps.green = crtc_out->saved_ramps.red + gamma_size; + crtc_out->saved_ramps.blue = crtc_out->saved_ramps.green + gamma_size; + if (crtc_out->saved_ramps.red == NULL) { + perror("malloc"); + return -1; } - if (state->fd >= 0) { - close(state->fd); - state->fd = -1; + + int r = drmModeCrtcGetGamma(card->fd, crtc_id, gamma_size, crtc_out->saved_ramps.red, + crtc_out->saved_ramps.green, crtc_out->saved_ramps.blue); + if (r < 0) { + fprintf(stderr, _("DRM could not read gamma ramps on CRTC %ld on\n" + "graphics card %ld.\n"), + crtc, card->index); + return -1; } + + return 0; } -void -drm_print_help(FILE *f) +static void +drm_invalid_partition(const gamma_site_state_t *site, size_t partition) { - fputs(_("Adjust gamma ramps with Direct Rendering Manager.\n"), f); - fputs("\n", f); + fprintf(stderr, _("Card %ld does not exist. "), + partition); + if (site->partitions_available > 1) { + fprintf(stderr, _("Valid cards are [0-%ld].\n"), + site->partitions_available - 1); + } else { + fprintf(stderr, _("Only card 0 exists.\n")); + } +} - /* TRANSLATORS: DRM help output - left column must not be translated */ - fputs(_(" card=N\tGraphics card to apply adjustments to\n" - " crtc=N\tCRTC to apply adjustments to\n"), f); - fputs("\n", f); +static int +drm_set_ramps(gamma_server_state_t *state, gamma_crtc_state_t *crtc, gamma_ramps_t ramps) +{ + drm_card_data_t *card_data = state->sites[crtc->site_index].partitions[crtc->partition].data; + int r; + r = drmModeCrtcSetGamma(card_data->fd, (uint32_t)(long)(crtc->data), + ramps.red_size, ramps.red, ramps.green, ramps.blue); + if (r) { + switch (errno) { + case EACCES: + case EAGAIN: + case EIO: + /* Permission denied errors must be ignored, because we do not + have permission to do this while a display server is active. + We are also checking for some other error codes just in case. */ + case EBUSY: + case EINPROGRESS: + /* It is hard to find documentation for DRM (in fact all of this is + just based on the functions names and some testing,) perhaps we + could get this if we are updating to fast. */ + break; + case EBADF: + case ENODEV: + case ENXIO: + /* XXX: I have not actually tested removing my graphics card or, + monitor but I imagine either of these is what would happen. */ + fprintf(stderr, + _("Please do not unplug your monitors or remove graphics cards.\n")); + return -1; + default: + perror("drmModeCrtcSetGamma"); + return -1; + } + } + return 0; } -int -drm_set_option(drm_state_t *state, const char *key, const char *value) +static int +drm_set_option(gamma_server_state_t *state, const char *key, char *value, ssize_t section) { if (strcasecmp(key, "card") == 0) { - state->card_num = atoi(value); + ssize_t card = strcasecmp(value, "all") ? (ssize_t)atoi(value) : -1; + if (card < 0 && strcasecmp(value, "all")) { + /* TRANSLATORS: `all' must not be translated. */ + fprintf(stderr, _("Card must be `all' or a non-negative integer.\n")); + return -1; + } + if (section >= 0) { + state->selections[section].partition = card; + } else { + for (size_t i = 0; i < state->selections_made; i++) + state->selections[i].partition = card; + } + return 0; } else if (strcasecmp(key, "crtc") == 0) { - state->crtc_num = atoi(value); - if (state->crtc_num < 0) { - fprintf(stderr, _("CRTC must be a non-negative integer\n")); + ssize_t crtc = strcasecmp(value, "all") ? (ssize_t)atoi(value) : -1; + if (crtc < 0 && strcasecmp(value, "all")) { + /* TRANSLATORS: `all' must not be translated. */ + fprintf(stderr, _("CRTC must be `all' or a non-negative integer.\n")); return -1; } - } else { - fprintf(stderr, _("Unknown method parameter: `%s'.\n"), key); - return -1; + if (section >= 0) { + state->selections[section].crtc = crtc; + } else { + for (size_t i = 0; i < state->selections_made; i++) + state->selections[i].crtc = crtc; + } + return 0; } + return 1; +} + +int +drm_init(gamma_server_state_t *state) +{ + int r; + r = gamma_init(state); + if (r != 0) return r; + + state->free_partition_data = drm_free_partition; + state->free_crtc_data = drm_free_crtc; + state->open_site = drm_open_site; + state->open_partition = drm_open_partition; + state->open_crtc = drm_open_crtc; + state->invalid_partition = drm_invalid_partition; + state->set_ramps = drm_set_ramps; + state->set_option = drm_set_option; return 0; } int -drm_set_temperature(drm_state_t *state, int temp, float brightness, const float gamma[3]) +drm_start(gamma_server_state_t *state) { - drm_crtc_state_t *crtcs = state->crtcs; - int last_gamma_size = 0; - uint16_t *r_gamma = NULL; - uint16_t *g_gamma = NULL; - uint16_t *b_gamma = NULL; - - for (; crtcs->crtc_num >= 0; crtcs++) { - if (crtcs->gamma_size <= 1) - continue; - if (crtcs->gamma_size != last_gamma_size) { - if (last_gamma_size == 0) { - r_gamma = malloc(3 * crtcs->gamma_size * sizeof(uint16_t)); - g_gamma = r_gamma + crtcs->gamma_size; - b_gamma = g_gamma + crtcs->gamma_size; - } else if (crtcs->gamma_size > last_gamma_size) { - r_gamma = realloc(r_gamma, 3 * crtcs->gamma_size * sizeof(uint16_t)); - g_gamma = r_gamma + crtcs->gamma_size; - b_gamma = g_gamma + crtcs->gamma_size; - } - if (r_gamma == NULL) { - perror(last_gamma_size == 0 ? "malloc" : "realloc"); - return -1; - } - last_gamma_size = crtcs->gamma_size; - } - colorramp_fill(r_gamma, g_gamma, b_gamma, crtcs->gamma_size, - temp, brightness, gamma); - drmModeCrtcSetGamma(state->fd, crtcs->crtc_id, crtcs->gamma_size, - r_gamma, g_gamma, b_gamma); - } + return gamma_resolve_selections(state); +} - free(r_gamma); +void +drm_print_help(FILE *f) +{ + fputs(_("Adjust gamma ramps with Direct Rendering Manager.\n"), f); + fputs("\n", f); - return 0; + /* TRANSLATORS: DRM help output + left column must not be translated */ + fputs(_(" card=N\tGraphics card to apply adjustments to\n" + " crtc=N\tCRTC to apply adjustments to\n"), f); + fputs("\n", f); } diff --git a/src/gamma-drm.h b/src/gamma-drm.h index f4c0df53..d05fd216 100644 --- a/src/gamma-drm.h +++ b/src/gamma-drm.h @@ -20,6 +20,8 @@ #ifndef REDSHIFT_GAMMA_DRM_H #define REDSHIFT_GAMMA_DRM_H +#include "gamma-common.h" + #include #include @@ -27,32 +29,16 @@ typedef struct { - int crtc_num; - int crtc_id; - int gamma_size; - uint16_t* r_gamma; - uint16_t* g_gamma; - uint16_t* b_gamma; -} drm_crtc_state_t; - -typedef struct { - int card_num; - int crtc_num; int fd; - drmModeRes* res; - drm_crtc_state_t* crtcs; -} drm_state_t; + drmModeRes *res; + size_t index; +} drm_card_data_t; -int drm_init(drm_state_t *state); -int drm_start(drm_state_t *state); -void drm_free(drm_state_t *state); +int drm_init(gamma_server_state_t *state); +int drm_start(gamma_server_state_t *state); void drm_print_help(FILE *f); -int drm_set_option(drm_state_t *state, const char *key, const char *value); - -void drm_restore(drm_state_t *state); -int drm_set_temperature(drm_state_t *state, int temp, float brightness, const float gamma[3]); #endif /* ! REDSHIFT_GAMMA_DRM_H */ From 50c66f8ae6e96eb1c6d01fc32a90f58d00d1a9ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Mon, 12 May 2014 20:41:27 +0200 Subject: [PATCH 21/29] Rewrite gamma-w32gdi to use gamma-common MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/gamma-w32gdi.c | 215 +++++++++++++++++++++++++++------------------ src/gamma-w32gdi.h | 19 ++-- 2 files changed, 134 insertions(+), 100 deletions(-) diff --git a/src/gamma-w32gdi.c b/src/gamma-w32gdi.c index d882c17c..f9f43731 100644 --- a/src/gamma-w32gdi.c +++ b/src/gamma-w32gdi.c @@ -15,6 +15,7 @@ along with Redshift. If not, see . Copyright (c) 2010 Jon Lund Steffensen + Copyright (c) 2014 Mattias Andrée */ #ifdef HAVE_CONFIG_H @@ -23,6 +24,7 @@ #include #include +#include #ifndef WINVER # define WINVER 0x0500 @@ -42,144 +44,185 @@ #endif #include "gamma-w32gdi.h" -#include "colorramp.h" #define GAMMA_RAMP_SIZE 256 -int -w32gdi_init(w32gdi_state_t *state) +static void +w32gdi_free_crtc(void *data) { - state->saved_ramps = NULL; + /* Release device context. */ + ReleaseDC(NULL, data); +} +static int +w32gdi_open_site(gamma_server_state_t *state, char *site, gamma_site_state_t *site_out) +{ + (void) state; + (void) site; + site_out->data = NULL; + site_out->partitions_available = 1; return 0; } -int -w32gdi_start(w32gdi_state_t *state) +static int +w32gdi_open_partition(gamma_server_state_t *state, gamma_site_state_t *site, + size_t partition, gamma_partition_state_t *partition_out) { + (void) state; + (void) site; + (void) partition; + BOOL r; - /* Open device context */ - HDC hDC = GetDC(NULL); + partition_out->data = NULL; + partition_out->crtcs_available = 0; + + /* Count number of displays */ + DISPLAY_DEVICE display; + display.cb = sizeof(DISPLAY_DEVICE); + while (1) { + r = EnumDisplayDevices(NULL, partition_out->crtcs_available, &display, 0); + if (!r) break; + partition_out->crtcs_available++; + } + + return 0; +} + +static int +w32gdi_open_crtc(gamma_server_state_t *state, gamma_site_state_t *site, + gamma_partition_state_t *partition, size_t crtc, gamma_crtc_state_t *crtc_out) +{ + (void) state; + (void) site; + (void) partition; + + crtc_out->data = NULL; + + int r; + + /* Open device context. */ + DISPLAY_DEVICE display; + display.cb = sizeof(DISPLAY_DEVICE); + r = (int)EnumDisplayDevices(NULL, crtc, &display, 0); + if (!r) { + fputs(_("Cannot find display, are you unplugging stuff?\n"), stderr); + return -1; + } + if (!(display.StateFlags & DISPLAY_DEVICE_ACTIVE)) { + fputs(_("Cannot to open device context, it is not active.\n"), stderr); + return -1; + } + HDC hDC = CreateDC(TEXT("DISPLAY"), display.DeviceName, NULL, NULL); if (hDC == NULL) { fputs(_("Unable to open device context.\n"), stderr); return -1; } - /* Check support for gamma ramps */ + /* Check support for gamma ramps. */ int cmcap = GetDeviceCaps(hDC, COLORMGMTCAPS); if (cmcap != CM_GAMMA_RAMP) { fputs(_("Display device does not support gamma ramps.\n"), stderr); + ReleaseDC(NULL, hDC); return -1; } - /* Allocate space for saved gamma ramps */ - state->saved_ramps = malloc(3*GAMMA_RAMP_SIZE*sizeof(WORD)); - if (state->saved_ramps == NULL) { + /* Specify gamma ramp dimensions. */ + crtc_out->saved_ramps.red_size = GAMMA_RAMP_SIZE; + crtc_out->saved_ramps.green_size = GAMMA_RAMP_SIZE; + crtc_out->saved_ramps.blue_size = GAMMA_RAMP_SIZE; + + /* Allocate space for saved gamma ramps. */ + crtc_out->saved_ramps.red = malloc(3*GAMMA_RAMP_SIZE*sizeof(WORD)); + if (crtc_out->saved_ramps.red == NULL) { perror("malloc"); ReleaseDC(NULL, hDC); return -1; } + crtc_out->saved_ramps.green = crtc_out->saved_ramps.red + GAMMA_RAMP_SIZE; + crtc_out->saved_ramps.blue = crtc_out->saved_ramps.green + GAMMA_RAMP_SIZE; - /* Save current gamma ramps so we can restore them at program exit */ - r = GetDeviceGammaRamp(hDC, state->saved_ramps); + /* Save current gamma ramps so we can restore them at program exit. */ + r = GetDeviceGammaRamp(hDC, crtc_out->saved_ramps.red); if (!r) { fputs(_("Unable to save current gamma ramp.\n"), stderr); ReleaseDC(NULL, hDC); return -1; } - /* Release device context */ - ReleaseDC(NULL, hDC); - + crtc_out->data = hDC; return 0; } -void -w32gdi_free(w32gdi_state_t *state) +static int +w32gdi_set_ramps(gamma_server_state_t *state, gamma_crtc_state_t *crtc, gamma_ramps_t ramps) { - /* Free saved ramps */ - free(state->saved_ramps); + (void) state; + int r = SetDeviceGammaRamp(crtc->data, ramps.red); + if (!r) { + /* TODO it happens that SetDeviceGammaRamp returns FALSE on + occasions where the adjustment seems to be successful. + Does this only happen with multiple monitors connected? */ + fputs(_("Unable to set gamma ramps.\n"), stderr); + } + return !r; } - -void -w32gdi_print_help(FILE *f) +static int +w32gdi_set_option(gamma_server_state_t *state, const char *key, char *value, ssize_t section) { - fputs(_("Adjust gamma ramps with the Windows GDI.\n"), f); - fputs("\n", f); + if (strcasecmp(key, "crtc") == 0) { + ssize_t crtc = strcasecmp(value, "all") ? (ssize_t)atoi(value) : -1; + if (crtc < 0 && strcasecmp(value, "all")) { + /* TRANSLATORS: `all' must not be translated. */ + fprintf(stderr, _("CRTC must be `all' or a non-negative integer.\n")); + return -1; + } + if (section >= 0) { + state->selections[section].crtc = crtc; + } else { + for (size_t i = 0; i < state->selections_made; i++) + state->selections[i].crtc = crtc; + } + return 0; + } + return 1; } -int -w32gdi_set_option(w32gdi_state_t *state, const char *key, const char *value) -{ - return -1; -} -void -w32gdi_restore(w32gdi_state_t *state) +int +w32gdi_init(gamma_server_state_t *state) { - /* Open device context */ - HDC hDC = GetDC(NULL); - if (hDC == NULL) { - fputs(_("Unable to open device context.\n"), stderr); - return; - } + int r; + r = gamma_init(state); + if (r != 0) return r; - /* Restore gamma ramps */ - BOOL r = SetDeviceGammaRamp(hDC, state->saved_ramps); - if (!r) fputs(_("Unable to restore gamma ramps.\n"), stderr); + state->free_crtc_data = w32gdi_free_crtc; + state->open_site = w32gdi_open_site; + state->open_partition = w32gdi_open_partition; + state->open_crtc = w32gdi_open_crtc; + state->set_ramps = w32gdi_set_ramps; + state->set_option = w32gdi_set_option; - /* Release device context */ - ReleaseDC(NULL, hDC); + return 0; } int -w32gdi_set_temperature(w32gdi_state_t *state, int temp, float brightness, - const float gamma[3]) +w32gdi_start(gamma_server_state_t *state) { - BOOL r; - - /* Open device context */ - HDC hDC = GetDC(NULL); - if (hDC == NULL) { - fputs(_("Unable to open device context.\n"), stderr); - return -1; - } - - /* Create new gamma ramps */ - WORD *gamma_ramps = malloc(3*GAMMA_RAMP_SIZE*sizeof(WORD)); - if (gamma_ramps == NULL) { - perror("malloc"); - ReleaseDC(NULL, hDC); - return -1; - } - - WORD *gamma_r = &gamma_ramps[0*GAMMA_RAMP_SIZE]; - WORD *gamma_g = &gamma_ramps[1*GAMMA_RAMP_SIZE]; - WORD *gamma_b = &gamma_ramps[2*GAMMA_RAMP_SIZE]; - - colorramp_fill(gamma_r, gamma_g, gamma_b, GAMMA_RAMP_SIZE, - temp, brightness, gamma); - - /* Set new gamma ramps */ - r = SetDeviceGammaRamp(hDC, gamma_ramps); - if (!r) { - /* TODO it happens that SetDeviceGammaRamp returns FALSE on - occasions where the adjustment seems to be successful. - Does this only happen with multiple monitors connected? */ - fputs(_("Unable to set gamma ramps.\n"), stderr); - free(gamma_ramps); - ReleaseDC(NULL, hDC); - return -1; - } - - free(gamma_ramps); + return gamma_resolve_selections(state); +} - /* Release device context */ - ReleaseDC(NULL, hDC); +void +w32gdi_print_help(FILE *f) +{ + fputs(_("Adjust gamma ramps with the Windows GDI.\n"), f); + fputs("\n", f); - return 0; + /* TRANSLATORS: Windows GDI help output + left column must not be translated. */ + fputs(_(" crtc=N\tX monitor to apply adjustments to\n"), f); + fputs("\n", f); } diff --git a/src/gamma-w32gdi.h b/src/gamma-w32gdi.h index 4b085fae..51384003 100644 --- a/src/gamma-w32gdi.h +++ b/src/gamma-w32gdi.h @@ -15,11 +15,14 @@ along with Redshift. If not, see . Copyright (c) 2010 Jon Lund Steffensen + Copyright (c) 2014 Mattias Andrée */ #ifndef REDSHIFT_GAMMA_W32GDI_H #define REDSHIFT_GAMMA_W32GDI_H +#include "gamma-common.h" + #ifdef HAVE_CONFIG_H # include "config.h" #endif @@ -32,22 +35,10 @@ #endif -typedef struct { - WORD *saved_ramps; -} w32gdi_state_t; - - -int w32gdi_init(w32gdi_state_t *state); -int w32gdi_start(w32gdi_state_t *state); -void w32gdi_free(w32gdi_state_t *state); +int w32gdi_init(gamma_server_state_t *state); +int w32gdi_start(gamma_server_state_t *state); void w32gdi_print_help(FILE *f); -int w32gdi_set_option(w32gdi_state_t *state, const char *key, - const char *value); - -void w32gdi_restore(w32gdi_state_t *state); -int w32gdi_set_temperature(w32gdi_state_t *state, int temp, float brightness, - const float gamma[3]); #endif /* ! REDSHIFT_GAMMA_W32GDI_H */ From 64d2f47e19b60aca90b2b677b47ce91f73d25220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Mon, 12 May 2014 22:06:00 +0200 Subject: [PATCH 22/29] Remove the old colorramp_fill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/colorramp.c | 25 +------------------------ src/colorramp.h | 5 +---- src/gamma-common.c | 2 +- 3 files changed, 3 insertions(+), 29 deletions(-) diff --git a/src/colorramp.c b/src/colorramp.c index 146ee002..31a9c805 100644 --- a/src/colorramp.c +++ b/src/colorramp.c @@ -286,7 +286,7 @@ interpolate_color(float a, const float *c1, const float *c2, float *c) } void -colorramp_fill_(gamma_ramps_t out_ramps, gamma_settings_t adjustments) +colorramp_fill(gamma_ramps_t out_ramps, gamma_settings_t adjustments) { size_t gamma_sizes[3] = { out_ramps.red_size, @@ -329,26 +329,3 @@ colorramp_fill_(gamma_ramps_t out_ramps, gamma_settings_t adjustments) #undef F } - -void -colorramp_fill(uint16_t *gamma_r, uint16_t *gamma_g, uint16_t *gamma_b, - int size, int temp, float brightness, const float gamma[3]) -{ - gamma_ramps_t out_ramps = { - .red_size = (size_t)size, - .green_size = (size_t)size, - .blue_size = (size_t)size, - .red = gamma_r, - .green = gamma_g, - .blue = gamma_b - }; - gamma_settings_t adjustments = { - .gamma_correction[0] = gamma[0], - .gamma_correction[1] = gamma[1], - .gamma_correction[2] = gamma[2], - .gamma = 1, - .brightness = brightness, - .temperature = temp - }; - colorramp_fill_(out_ramps, adjustments); -} diff --git a/src/colorramp.h b/src/colorramp.h index 235535c2..ed1f950b 100644 --- a/src/colorramp.h +++ b/src/colorramp.h @@ -23,9 +23,6 @@ #include "adjustments.h" -#include - -void colorramp_fill(uint16_t *gamma_r, uint16_t *gamma_g, uint16_t *gamma_b, - int size, int temp, float brightness, const float gamma[3]); +void colorramp_fill(gamma_ramps_t out_ramps, gamma_settings_t adjustments); #endif /* ! REDSHIFT_COLORRAMP_H */ diff --git a/src/gamma-common.c b/src/gamma-common.c index 45e04c2c..26281386 100644 --- a/src/gamma-common.c +++ b/src/gamma-common.c @@ -442,7 +442,7 @@ gamma_update(gamma_server_state_t *state) while (gamma_iterator_next(&iter)) { if (iter.crtc->current_ramps.red == NULL) continue; - colorramp_fill_(iter.crtc->current_ramps, iter.crtc->settings); + colorramp_fill(iter.crtc->current_ramps, iter.crtc->settings); r = state->set_ramps(state, iter.crtc, iter.crtc->current_ramps); if (r != 0) return r; } From a18884f55777e05c0d5c5d00cef41bda08c0a84b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Sat, 12 Apr 2014 00:50:56 +0200 Subject: [PATCH 23/29] Some code reduce MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/gamma-common.h | 12 ++++++++++++ src/gamma-drm.c | 14 ++------------ src/gamma-randr.c | 28 ++++++---------------------- src/gamma-vidmode.c | 21 +++++---------------- src/gamma-w32gdi.c | 7 +------ 5 files changed, 26 insertions(+), 56 deletions(-) diff --git a/src/gamma-common.h b/src/gamma-common.h index 3ba23437..0daf818e 100644 --- a/src/gamma-common.h +++ b/src/gamma-common.h @@ -225,5 +225,17 @@ int gamma_set_option(gamma_server_state_t *state, const char *key, char *value, or three values separated by colon. */ int parse_gamma_string(char *str, float gamma[3]); +/* Perform update on relevent selections. */ +#define on_selections(INSTRUCTION) \ + if (section >= 0) { \ + gamma_selection_state_t *sel = state->selections + section; \ + INSTRUCTION \ + } else { \ + gamma_selection_state_t *sel = state->selections; \ + gamma_selection_state_t *sel_end = sel + state->selections_made; \ + for (; sel != sel_end; sel++) \ + INSTRUCTION \ + } + #endif /* ! REDSHIFT_GAMMA_COMMON_H */ diff --git a/src/gamma-drm.c b/src/gamma-drm.c index 4937f1b0..cb791290 100644 --- a/src/gamma-drm.c +++ b/src/gamma-drm.c @@ -297,12 +297,7 @@ drm_set_option(gamma_server_state_t *state, const char *key, char *value, ssize_ fprintf(stderr, _("Card must be `all' or a non-negative integer.\n")); return -1; } - if (section >= 0) { - state->selections[section].partition = card; - } else { - for (size_t i = 0; i < state->selections_made; i++) - state->selections[i].partition = card; - } + on_selections({ sel->partition = card; }); return 0; } else if (strcasecmp(key, "crtc") == 0) { ssize_t crtc = strcasecmp(value, "all") ? (ssize_t)atoi(value) : -1; @@ -311,12 +306,7 @@ drm_set_option(gamma_server_state_t *state, const char *key, char *value, ssize_ fprintf(stderr, _("CRTC must be `all' or a non-negative integer.\n")); return -1; } - if (section >= 0) { - state->selections[section].crtc = crtc; - } else { - for (size_t i = 0; i < state->selections_made; i++) - state->selections[i].crtc = crtc; - } + on_selections({ sel->crtc = crtc; }); return 0; } return 1; diff --git a/src/gamma-randr.c b/src/gamma-randr.c index 9156837c..e70c72d0 100644 --- a/src/gamma-randr.c +++ b/src/gamma-randr.c @@ -303,12 +303,7 @@ randr_set_option(gamma_server_state_t *state, const char *key, char *value, ssiz fprintf(stderr, _("Screen must be `all' or a non-negative integer.\n")); return -1; } - if (section >= 0) { - state->selections[section].partition = screen; - } else { - for (size_t i = 0; i < state->selections_made; i++) - state->selections[i].partition = screen; - } + on_selections({ sel->partition = screen; }); return 0; } else if (strcasecmp(key, "crtc") == 0) { ssize_t crtc = strcasecmp(value, "all") ? (ssize_t)atoi(value) : -1; @@ -317,25 +312,14 @@ randr_set_option(gamma_server_state_t *state, const char *key, char *value, ssiz fprintf(stderr, _("CRTC must be `all' or a non-negative integer.\n")); return -1; } - if (section >= 0) { - state->selections[section].crtc = crtc; - } else { - for (size_t i = 0; i < state->selections_made; i++) - state->selections[i].crtc = crtc; - } + on_selections({ sel->crtc = crtc; }); return 0; } else if (strcasecmp(key, "display") == 0) { - if (section >= 0) { - state->selections[section].site = strdup(value); - if (state->selections[section].site == NULL) + on_selections({ + sel->site = strdup(value); + if (sel->site == NULL) goto strdup_fail; - } else { - for (size_t i = 0; i < state->selections_made; i++) { - state->selections[i].site = strdup(value); - if (state->selections[i].site == NULL) - goto strdup_fail; - } - } + }); return 0; } return 1; diff --git a/src/gamma-vidmode.c b/src/gamma-vidmode.c index c91bda25..de78fe7e 100644 --- a/src/gamma-vidmode.c +++ b/src/gamma-vidmode.c @@ -191,25 +191,14 @@ vidmode_set_option(gamma_server_state_t *state, const char *key, char *value, ss fprintf(stderr, _("Screen must be `all' or a non-negative integer.\n")); return -1; } - if (section >= 0) { - state->selections[section].partition = screen; - } else { - for (size_t i = 0; i < state->selections_made; i++) - state->selections[i].partition = screen; - } + on_selections({ sel->partition = screen; }); return 0; } else if (strcasecmp(key, "display") == 0) { - if (section >= 0) { - state->selections[section].site = strdup(value); - if (state->selections[section].site == NULL) + on_selections({ + sel->site = strdup(value); + if (sel->site == NULL) goto strdup_fail; - } else { - for (size_t i = 0; i < state->selections_made; i++) { - state->selections[i].site = strdup(value); - if (state->selections[i].site == NULL) - goto strdup_fail; - } - } + }); return 0; } return 1; diff --git a/src/gamma-w32gdi.c b/src/gamma-w32gdi.c index f9f43731..fed09843 100644 --- a/src/gamma-w32gdi.c +++ b/src/gamma-w32gdi.c @@ -180,12 +180,7 @@ w32gdi_set_option(gamma_server_state_t *state, const char *key, char *value, ssi fprintf(stderr, _("CRTC must be `all' or a non-negative integer.\n")); return -1; } - if (section >= 0) { - state->selections[section].crtc = crtc; - } else { - for (size_t i = 0; i < state->selections_made; i++) - state->selections[i].crtc = crtc; - } + on_selections({ sel->crtc = crtc; }); return 0; } return 1; From 289c88608f40c359f7ad156c2c8fd726d561372d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Sat, 12 Apr 2014 00:50:56 +0200 Subject: [PATCH 24/29] Minor documentation edits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/gamma-common.c | 2 +- src/gamma-common.h | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/gamma-common.c b/src/gamma-common.c index 26281386..2a30c95f 100644 --- a/src/gamma-common.c +++ b/src/gamma-common.c @@ -1,4 +1,4 @@ -/* gamma-common.c -- Gamma adjustment method common functionallity source +/* gamma-common.c -- Gamma adjustment method common functionality source This file is part of Redshift. Redshift is free software: you can redistribute it and/or modify diff --git a/src/gamma-common.h b/src/gamma-common.h index 0daf818e..e7c666f3 100644 --- a/src/gamma-common.h +++ b/src/gamma-common.h @@ -1,4 +1,4 @@ -/* gamma-common.h -- Gamma adjustment method common functionallity header +/* gamma-common.h -- Gamma adjustment method common functionality header This file is part of Redshift. Redshift is free software: you can redistribute it and/or modify @@ -73,7 +73,6 @@ typedef int gamma_set_option_func(gamma_server_state_t *state, - /* CRTC state. */ struct gamma_crtc_state { /* Adjustment method implementation specific data. */ @@ -149,6 +148,9 @@ struct gamma_server_state { gamma_invalid_partition_func *invalid_partition; /* Function that applies a gamma ramp. */ gamma_set_ramps_func *set_ramps; + /* Function that parses options not unrecognised by the + common infrastructure. Negative on failure, zero on success + and positive if the key was not unrecognised. */ gamma_set_option_func *set_option; }; From c081201c5992918d7563336ec7206ff94b2c78a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Sat, 12 Apr 2014 00:50:56 +0200 Subject: [PATCH 25/29] Add adjustment method private data for selections add hooks for gamma_resolve_selection so that things such as EDID and connector name can be resolved with all required resources already loaded and readily available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/gamma-common.c | 40 ++++++++++++++++++++++++++++++++++++++-- src/gamma-common.h | 24 +++++++++++++++++++++++- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/src/gamma-common.c b/src/gamma-common.c index 2a30c95f..1ddf7587 100644 --- a/src/gamma-common.c +++ b/src/gamma-common.c @@ -53,6 +53,7 @@ gamma_init(gamma_server_state_t *state) } /* Defaults selection. */ + state->selections->data = NULL; state->selections->crtc = -1; state->selections->partition = -1; state->selections->site = NULL; @@ -74,9 +75,12 @@ gamma_free_selections(gamma_server_state_t *state) size_t i; /* Free data in each selection. */ - for (i = 0; i < state->selections_made; i++) + for (i = 0; i < state->selections_made; i++) { + if (state->selections[i].data != NULL) + free(state->selections[i].data); if (state->selections[i].site != NULL) free(state->selections[i].site); + } state->selections_made = 0; /* Free the selection array. */ @@ -209,7 +213,11 @@ gamma_iterator_next(gamma_iterator_t *iterator) } if (site_i == iterator->state->sites_used) return 0; - if (iterator->state->sites[site_i].partitions[partition_i].used == 0) { + if (iterator->state->sites[site_i].partitions[partition_i].used == 0 || + iterator->state->sites[site_i].partitions[partition_i].crtcs_used == 0) { + /* Because of `state->parse_selection` it is possible to have + a partition that is initalise but not used. This is when + used ≠ 0 but crtcs_used = 0. */ partition_i += 1; goto next_partition; } @@ -263,6 +271,12 @@ gamma_resolve_selections(gamma_server_state_t *state) size_t partition_start; size_t partition_end; + /* Run site selection hook. */ + if (selection->data != NULL) { + r = state->parse_selection(state, NULL, selection, before_site); + if (r < 0) return r; + } + /* Find matching already opened site. */ site_index = gamma_find_site(state, selection->site); @@ -316,6 +330,12 @@ gamma_resolve_selections(gamma_server_state_t *state) site = state->sites + site_index; } + /* Run partition selection hook. */ + if (selection->data != NULL) { + r = state->parse_selection(state, site, selection, before_partition); + if (r < 0) return r; + } + /* Select partitions. */ if (selection->partition >= (ssize_t)(site->partitions_available)) { state->invalid_partition(site, (size_t)(selection->partition)); @@ -337,6 +357,12 @@ gamma_resolve_selections(gamma_server_state_t *state) partition->used = 1; } + /* Run CRTC selection hook. */ + if (selection->data != NULL) { + r = state->parse_selection(state, site, selection, before_crtc); + if (r < 0) return r; + } + /* Open CRTCs. */ for (size_t p = partition_start; p < partition_end; p++) { gamma_partition_state_t *partition = site->partitions + p; @@ -560,6 +586,16 @@ gamma_set_option(gamma_server_state_t *state, const char *key, char *value, ssiz return -1; } } + if (state->selections->data != NULL) { + state->selections[section].data = malloc(state->selections->sizeof_data); + if (state->selections[section].data == NULL) { + perror("malloc"); + return -1; + } + memcpy(state->selections[section].data, + state->selections->data, + state->selections->sizeof_data); + } /* Increment this last, so we do not get segfault on error. */ state->selections_made += 1; diff --git a/src/gamma-common.h b/src/gamma-common.h index e7c666f3..e1ea8c6b 100644 --- a/src/gamma-common.h +++ b/src/gamma-common.h @@ -27,6 +27,13 @@ +enum gamma_selection_hook { + before_site, + before_partition, + before_crtc +}; + + /* Prototypes for the structures, this is required because the structures and the function typedef:s depends @@ -71,6 +78,9 @@ typedef int gamma_set_ramps_func(gamma_server_state_t *state, gamma_crtc_state_t typedef int gamma_set_option_func(gamma_server_state_t *state, const char *key, char *value, ssize_t section); +typedef int gamma_parse_selection_func(gamma_server_state_t *state, gamma_site_state_t *site, + gamma_selection_state_t *selection, enum gamma_selection_hook when); + /* CRTC state. */ @@ -116,6 +126,12 @@ struct gamma_site_state { /* CRTC selection state. */ struct gamma_selection_state { + /* Adjustment method implementation specific selection details. + This must not be a numberical value casted a pointer, that + will most probably cause segmentation violation when it is + duplicated. */ + void *data; + size_t sizeof_data; /* The CRTC and partition (e.g. screen) indices. */ ssize_t crtc; ssize_t partition; @@ -152,6 +168,12 @@ struct gamma_server_state { common infrastructure. Negative on failure, zero on success and positive if the key was not unrecognised. */ gamma_set_option_func *set_option; + /* Function that evaluates and resolves adjustment method + implementation specific selection details. It is done + here rather than in `set_option` to ensure that the order + the options are specified does not change the behaviour + or the program. */ + gamma_parse_selection_func *parse_selection; }; @@ -230,7 +252,7 @@ int parse_gamma_string(char *str, float gamma[3]); /* Perform update on relevent selections. */ #define on_selections(INSTRUCTION) \ if (section >= 0) { \ - gamma_selection_state_t *sel = state->selections + section; \ + gamma_selection_state_t *sel = state->selections + (size_t)section; \ INSTRUCTION \ } else { \ gamma_selection_state_t *sel = state->selections; \ From 856227eab7b36349e5d43b108316b88ffd843127 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Fri, 16 May 2014 07:54:25 +0200 Subject: [PATCH 26/29] Update MAX_LINE_LENGTH in config-ini to fit extra long EDID:s MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/config-ini.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/config-ini.c b/src/config-ini.c index 7652aa74..9eaea7d8 100644 --- a/src/config-ini.c +++ b/src/config-ini.c @@ -40,7 +40,8 @@ #endif #define MAX_CONFIG_PATH 4096 -#define MAX_LINE_LENGTH 512 +#define MAX_LINE_LENGTH 600 +/* At least 512 is required for EDIDs by themself. */ static FILE * From 3fc9739f3cf570f3b6a9ef20118d38319ff32dfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Sat, 12 Apr 2014 04:42:25 +0200 Subject: [PATCH 27/29] Read EDID in gamma-randr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/gamma-randr.c | 275 ++++++++++++++++++++++++++++++++++++++++++++-- src/gamma-randr.h | 13 +++ 2 files changed, 278 insertions(+), 10 deletions(-) diff --git a/src/gamma-randr.c b/src/gamma-randr.c index e70c72d0..3f118fb9 100644 --- a/src/gamma-randr.c +++ b/src/gamma-randr.c @@ -24,6 +24,7 @@ #include #include #include +#include #ifdef ENABLE_NLS # include @@ -321,6 +322,59 @@ randr_set_option(gamma_server_state_t *state, const char *key, char *value, ssiz goto strdup_fail; }); return 0; + } else if (strcasecmp(key, "edid") == 0) { + int edid_length = (int)(strlen(value)); + if (edid_length == 0 || edid_length % 2 != 0) { + fputs(_("Malformated EDID, should be nonempty even-length hexadecimal.\n"), stderr); + return -1; + } + if (edid_length > MAX_EDID_LENGTH * 2) { + fprintf(stderr, _("EDID was too long, it may not be longer than %i characters.\n"), + MAX_EDID_LENGTH * 2); + return -1; + } +#define __range(LOWER, TEST, UPPER) ((LOWER) <= (TEST) && (TEST) <= (UPPER)) + /* Convert EDID to raw byte format, from hexadecimal. */ + unsigned char edid[MAX_EDID_LENGTH]; + for (int i = 0; i < edid_length; i += 2) { + char a = value[i]; + char b = value[i + 1]; + if (__range('0', a, '9')) + edid[i >> 1] = (unsigned char)((a & 15) << 4); + else if (__range('a', a | ('a' ^ 'A'), 'f')) { + a += 9; /* ('a' & 15) == 1*/ + edid[i >> 1] = (unsigned char)((a & 15) << 4); + } else { + fputs(_("Malformated EDID, should be nonempty even-length hexadecimal.\n"), + stderr); + return -1; + } + if (__range('0', b, '9')) + edid[i >> 1] |= (unsigned char)(b & 15); + else if (__range('a', b | ('a' ^ 'A'), 'f')) { + b += 9; + edid[i >> 1] |= (unsigned char)(b & 15); + } else { + fputs(_("Malformated EDID, should be nonempty even-length hexadecimal.\n"), + stderr); + return -1; + } + } + edid_length /= 2; +#undef __range + on_selections({ + randr_selection_data_t *sel_data = sel->data; + if (sel_data == NULL) { + sel_data = sel->data = malloc(sizeof(randr_selection_data_t)); + if (sel_data == NULL) { + perror("malloc"); + return -1; + } + } + sel_data->edid_length = edid_length; + memcpy(sel_data->edid, edid, (size_t)edid_length); + }); + return 0; } return 1; @@ -330,6 +384,204 @@ randr_set_option(gamma_server_state_t *state, const char *key, char *value, ssiz } +static int +randr_test_edid(xcb_connection_t *connection, xcb_randr_output_t output, xcb_atom_t atom, + gamma_selection_state_t *selection, xcb_randr_crtc_t *crtcs, int crtc_n, + xcb_randr_crtc_t output_crtc) +{ + randr_selection_data_t *selection_data = selection->data; + unsigned char *edid_seeked = selection_data->edid; + int edid_length = selection_data->edid_length; + + xcb_randr_get_output_property_cookie_t val_cookie; + xcb_randr_get_output_property_reply_t *val_reply; + xcb_generic_error_t *error; + + /* Acquire the property's value, we know that it is either 128 bytes + or 256 bytes long, and we have a limit defined in gamma-randr.h. */ + val_cookie = xcb_randr_get_output_property(connection, output, atom, + XCB_GET_PROPERTY_TYPE_ANY, + 0, MAX_EDID_LENGTH, 0, 0); + + val_reply = xcb_randr_get_output_property_reply(connection, val_cookie, &error); + + if (error) { + fprintf(stderr, _("`%s' returned error %d\n"), + "RANDR Get Output Property", error->error_code); + return -1; + } + + unsigned char* value = xcb_randr_get_output_property_data(val_reply); + int length = xcb_randr_get_output_property_data_length(val_reply); + + /* Compare found EDID against seeked EDID. */ + if ((length == edid_length) && !memcmp(value, edid_seeked, (size_t)length)) { + int crtc; + + /* Find CRTC index. */ + for (crtc = 0; crtc < crtc_n; crtc++) + if (crtcs[crtc] == output_crtc) + break; + + free(val_reply); + + if (crtc < crtc_n) { + selection->crtc = (ssize_t)crtc; + } else { + fputs(_("Monitor is not connected."), stderr); + return -1; + } + + return 0; + } + free(val_reply); + return 1; +} + + +static int +randr_parse_selection(gamma_server_state_t *state, gamma_site_state_t *site, + gamma_selection_state_t *selection, enum gamma_selection_hook when) { + (void) state; + + if (when != before_crtc) + return 0; + + xcb_connection_t *connection = site->data; + + /* Select screen to look in, only one will be used. */ + gamma_partition_state_t *screen_start; + gamma_partition_state_t *screen_end; + if (selection->partition >= 0) { + screen_start = site->partitions + selection->partition; + screen_end = screen_start + 1; + } else { + screen_start = site->partitions; + screen_end = screen_start + site->partitions_available; + } + + for (gamma_partition_state_t *screen = screen_start; screen != screen_end; screen++) { + if (screen->used == 0) + continue; + randr_screen_data_t *screen_data = screen->data; + + xcb_randr_get_screen_resources_current_cookie_t res_cookie; + xcb_randr_get_screen_resources_current_reply_t *res_reply; + xcb_generic_error_t *error; + + /* Acquire information about the screen. */ + res_cookie = xcb_randr_get_screen_resources_current(connection, screen_data->screen.root); + res_reply = xcb_randr_get_screen_resources_current_reply(connection, res_cookie, &error); + + if (error) { + fprintf(stderr, _("`%s' returned error %d\n"), + "RANDR Get Screen Resources Current", + error->error_code); + return -1; + } + + xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_current_outputs(res_reply); + + /* Iterate over all connectors to and look for one that is connected + and whose monitor is has matching extended display identification data. */ + for (int output_i = 0; output_i < res_reply->num_outputs; output_i++) { + xcb_randr_get_output_info_cookie_t out_cookie; + xcb_randr_get_output_info_reply_t *out_reply; + + /* Acquire information about the output. */ + out_cookie = xcb_randr_get_output_info(connection, outputs[output_i], + res_reply->config_timestamp); + out_reply = xcb_randr_get_output_info_reply(connection, out_cookie, &error); + + if (error) { + fprintf(stderr, _("`%s' returned error %d\n"), + "RANDR Get Output Information", + error->error_code); + free(res_reply); + return -1; + } + + /* Check connection status. */ + if (out_reply->connection != XCB_RANDR_CONNECTION_CONNECTED) { + free(out_reply); + continue; + } + + xcb_randr_list_output_properties_cookie_t prop_cookie; + xcb_randr_list_output_properties_reply_t *prop_reply; + xcb_atom_t *atoms; + xcb_atom_t *atoms_end; + + /* Acquire a list of all properties of the output. */ + prop_cookie = xcb_randr_list_output_properties(connection, outputs[output_i]); + prop_reply = xcb_randr_list_output_properties_reply(connection, prop_cookie, &error); + + if (error) { + fprintf(stderr, _("`%s' returned error %d\n"), + "RANDR List Output Properties", + error->error_code); + free(out_reply); + free(res_reply); + return -1; + } + + atoms = xcb_randr_list_output_properties_atoms(prop_reply); + atoms_end = atoms + xcb_randr_list_output_properties_atoms_length(prop_reply); + + for (; atoms != atoms_end; atoms++) { + xcb_get_atom_name_cookie_t atom_name_cookie; + xcb_get_atom_name_reply_t *atom_name_reply; + char *atom_name; + char *atom_name_; + int atom_name_len; + + /* Aqcuire the atom name. */ + atom_name_cookie = xcb_get_atom_name(connection, *atoms); + atom_name_reply = xcb_get_atom_name_reply(connection, atom_name_cookie, &error); + + if (error) { + fprintf(stderr, _("`%s' returned error %d\n"), + "RANDR Get Atom Name", + error->error_code); + free(prop_reply); + free(out_reply); + free(res_reply); + return -1; + } + + atom_name_ = xcb_get_atom_name_name(atom_name_reply); + atom_name_len = xcb_get_atom_name_name_length(atom_name_reply); + + /* NUL-terminate the atom name. */ + atom_name = alloca(((size_t)atom_name_len + 1) * sizeof(char)); + memcpy(atom_name, atom_name_, (size_t)atom_name_len * sizeof(char)); + *(atom_name + atom_name_len) = 0; + + if (!strcmp(atom_name, "EDID")) { + int r = randr_test_edid(connection, outputs[output_i], *atoms, + selection, screen_data->crtcs, + (int)(screen->crtcs_available), + out_reply->crtc); + if (r != 1) { + free(atom_name_reply); + free(prop_reply); + free(out_reply); + free(res_reply); + selection->partition = (ssize_t)(screen - screen_start); + return r; + } + } + free(atom_name_reply); + } + free(prop_reply); + free(out_reply); + } + free(res_reply); + } + return -1; +} + + int randr_init(gamma_server_state_t *state) { @@ -337,16 +589,18 @@ randr_init(gamma_server_state_t *state) r = gamma_init(state); if (r != 0) return r; - state->selections->site = getenv("DISPLAY") ? strdup(getenv("DISPLAY")) : NULL; - state->free_site_data = randr_free_site; - state->free_partition_data = randr_free_partition; - state->free_crtc_data = randr_free_crtc; - state->open_site = randr_open_site; - state->open_partition = randr_open_partition; - state->open_crtc = randr_open_crtc; - state->invalid_partition = randr_invalid_partition; - state->set_ramps = randr_set_ramps; - state->set_option = randr_set_option; + state->selections->sizeof_data = sizeof(randr_selection_data_t); + state->selections->site = getenv("DISPLAY") ? strdup(getenv("DISPLAY")) : NULL; + state->free_site_data = randr_free_site; + state->free_partition_data = randr_free_partition; + state->free_crtc_data = randr_free_crtc; + state->open_site = randr_open_site; + state->open_partition = randr_open_partition; + state->open_crtc = randr_open_crtc; + state->invalid_partition = randr_invalid_partition; + state->set_ramps = randr_set_ramps; + state->set_option = randr_set_option; + state->parse_selection = randr_parse_selection; if (getenv("DISPLAY") != NULL && state->selections->site == NULL) { perror("strdup"); @@ -371,6 +625,7 @@ randr_print_help(FILE *f) /* TRANSLATORS: RANDR help output left column must not be translated */ fputs(_(" crtc=N\tCRTC to apply adjustments to\n" + " edid=VALUE\tThe EDID of the monitor to apply adjustments to\n" " screen=N\tX screen to apply adjustments to\n" " display=NAME\tX display to apply adjustments to\n"), f); fputs("\n", f); diff --git a/src/gamma-randr.h b/src/gamma-randr.h index 3dc8cbcc..d93ac622 100644 --- a/src/gamma-randr.h +++ b/src/gamma-randr.h @@ -30,11 +30,24 @@ #include +/* EDID version 1.0 through 1.4 define it as 128 bytes long, but + version 2.0 define it as 256 bytes long. However, version 2.0 + is rare(?) and has been deprecated and replaced by version 1.3. */ +#ifndef MAX_EDID_LENGTH +#define MAX_EDID_LENGTH 256 +#endif + + typedef struct { xcb_screen_t screen; xcb_randr_crtc_t *crtcs; } randr_screen_data_t; +typedef struct { + unsigned char edid[MAX_EDID_LENGTH]; + int edid_length; +} randr_selection_data_t; + int randr_init(gamma_server_state_t *state); int randr_start(gamma_server_state_t *state); From 31156fdd095fe7f7a122642a2f0bb47836037999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Sat, 12 Apr 2014 06:26:40 +0200 Subject: [PATCH 28/29] Read EDID in gamma-drm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/gamma-drm.c | 200 +++++++++++++++++++++++++++++++++++++++++++--- src/gamma-drm.h | 14 ++++ src/gamma-randr.c | 3 +- 3 files changed, 205 insertions(+), 12 deletions(-) diff --git a/src/gamma-drm.c b/src/gamma-drm.c index cb791290..7e2e63ea 100644 --- a/src/gamma-drm.c +++ b/src/gamma-drm.c @@ -100,6 +100,7 @@ drm_open_partition(gamma_server_state_t *state, gamma_site_state_t *site, data->fd = -1; data->res = NULL; data->index = partition; + data->connectors = NULL; partition_out->data = data; /* Acquire access to a graphics card. */ @@ -308,10 +309,158 @@ drm_set_option(gamma_server_state_t *state, const char *key, char *value, ssize_ } on_selections({ sel->crtc = crtc; }); return 0; + } else if (strcasecmp(key, "edid") == 0) { + uint32_t edid_length = (uint32_t)(strlen(value)); + if (edid_length == 0 || edid_length % 2 != 0) { + fputs(_("Malformated EDID, should be nonempty even-length hexadecimal.\n"), stderr); + return -1; + } + if (edid_length > MAX_EDID_LENGTH * 2) { + fprintf(stderr, _("EDID was too long, it may not be longer than %i characters.\n"), + MAX_EDID_LENGTH * 2); + return -1; + } +#define __range(LOWER, TEST, UPPER) ((LOWER) <= (TEST) && (TEST) <= (UPPER)) + /* Convert EDID to raw byte format, from hexadecimal. */ + unsigned char edid[MAX_EDID_LENGTH]; + for (uint32_t i = 0; i < edid_length; i += 2) { + char a = value[i]; + char b = value[i + 1]; + if (__range('0', a, '9')) + edid[i >> 1] = (unsigned char)((a & 15) << 4); + else if (__range('a', a | ('a' ^ 'A'), 'f')) { + a += 9; /* ('a' & 15) == 1*/ + edid[i >> 1] = (unsigned char)((a & 15) << 4); + } else { + fputs(_("Malformated EDID, should be nonempty even-length hexadecimal.\n"), + stderr); + return -1; + } + if (__range('0', b, '9')) + edid[i >> 1] |= (unsigned char)(b & 15); + else if (__range('a', b | ('a' ^ 'A'), 'f')) { + b += 9; + edid[i >> 1] |= (unsigned char)(b & 15); + } else { + fputs(_("Malformated EDID, should be nonempty even-length hexadecimal.\n"), + stderr); + return -1; + } + } + edid_length /= 2; +#undef __range + on_selections({ + drm_selection_data_t *sel_data = sel->data; + if (sel_data == NULL) { + sel_data = sel->data = malloc(sizeof(drm_selection_data_t)); + if (sel_data == NULL) { + perror("malloc"); + return -1; + } + } + sel_data->edid_length = edid_length; + memcpy(sel_data->edid, edid, (size_t)edid_length); + }); + return 0; } return 1; } +static int +drm_parse_selection(gamma_server_state_t *state, gamma_site_state_t *site, + gamma_selection_state_t *selection, enum gamma_selection_hook when) +{ + (void) state; + + if (when != before_crtc) + return 0; + + drm_selection_data_t *selection_data = selection->data; + unsigned char *edid_seeked = selection_data->edid; + uint32_t edid_length = selection_data->edid_length; + + gamma_partition_state_t *card_start; + gamma_partition_state_t *card_end; + if (selection->partition >= 0) { + card_start = site->partitions + selection->partition; + card_end = card_start + 1; + } else { + card_start = site->partitions; + card_end = card_start + site->partitions_available; + } + + for (gamma_partition_state_t *card = card_start; card != card_end; card++) { + drm_card_data_t *card_data = card->data; + int fd = card_data->fd; + drmModeRes *res = card_data->res; + ssize_t cconnector_n = res->count_connectors; + + if (card_data->connectors == NULL) { + card_data->connectors = malloc((size_t)cconnector_n * sizeof(drmModeConnector *)); + if (card_data->connectors == NULL) { + perror("malloc"); + return -1; + } + + for (ssize_t connector_i = 0; connector_i < cconnector_n; connector_i++) { + card_data->connectors[connector_i] = + drmModeGetConnector(fd, res->connectors[connector_i]); + } + } + + for (ssize_t connector_i = 0; connector_i < cconnector_n; connector_i++) { + drmModeConnector *connector = card_data->connectors[connector_i]; + + if (connector->connection != DRM_MODE_CONNECTED) + /* This is required to avoid segmentation violation, + connector->count_props is non zero just because the + there is not connection, and we cannot reproperaties + when there is no connection .*/ + continue; + + int prop_n = connector->count_props; + + for (int prop_i = 0; prop_i < prop_n; prop_i++) { + drmModePropertyRes *prop; + drmModePropertyBlobRes *blob; + + prop = drmModeGetProperty(fd, connector->props[prop_i]); + if (!strcmp("EDID", prop->name)) { + uint64_t blob_id = connector->prop_values[prop_i]; + blob = drmModeGetPropertyBlob(fd, (uint32_t)blob_id); + uint32_t length = blob->length; + unsigned char *value = blob->data; + + if ((length == edid_length) && + !memcmp(value, edid_seeked, (size_t)length)) { + drmModeEncoder* encoder = + drmModeGetEncoder(fd, connector->encoder_id); + uint32_t crtc_id = encoder->crtc_id; + int crtc; + + for (crtc = 0; crtc < res->count_crtcs; crtc++) + if (res->crtcs[crtc] == crtc_id) + break; + + selection->crtc = crtc; + selection->partition = (ssize_t)(card - card_end); + + drmModeFreeEncoder(encoder); + drmModeFreeProperty(prop); + drmModeFreePropertyBlob(blob); + return crtc < res->count_crtcs ? 0 : -1; + } + drmModeFreePropertyBlob(blob); + } + drmModeFreeProperty(prop); + } + } + } + + return -1; +} + + int drm_init(gamma_server_state_t *state) { @@ -319,14 +468,16 @@ drm_init(gamma_server_state_t *state) r = gamma_init(state); if (r != 0) return r; - state->free_partition_data = drm_free_partition; - state->free_crtc_data = drm_free_crtc; - state->open_site = drm_open_site; - state->open_partition = drm_open_partition; - state->open_crtc = drm_open_crtc; - state->invalid_partition = drm_invalid_partition; - state->set_ramps = drm_set_ramps; - state->set_option = drm_set_option; + state->selections->sizeof_data = sizeof(drm_selection_data_t); + state->free_partition_data = drm_free_partition; + state->free_crtc_data = drm_free_crtc; + state->open_site = drm_open_site; + state->open_partition = drm_open_partition; + state->open_crtc = drm_open_crtc; + state->invalid_partition = drm_invalid_partition; + state->set_ramps = drm_set_ramps; + state->set_option = drm_set_option; + state->parse_selection = drm_parse_selection; return 0; } @@ -334,7 +485,33 @@ drm_init(gamma_server_state_t *state) int drm_start(gamma_server_state_t *state) { - return gamma_resolve_selections(state); + int r = gamma_resolve_selections(state); + + /* Release connectors acquried in drm_parse_selection */ + size_t s, p, c, n; + gamma_site_state_t *site; + gamma_partition_state_t *partition; + drm_card_data_t *card; + drmModeConnector** connectors; + for (s = 0; s < state->sites_used; s++) { + site = state->sites + s; + for (p = 0; p < site->partitions_available; p++) { + partition = site->partitions + p; + if (partition->used == 0) + continue; + card = partition->data; + connectors = card->connectors; + if (connectors != NULL) { + n = (size_t)(card->res->count_connectors); + for (c = 0; c < n; c++) + drmModeFreeConnector(connectors[c]); + free(connectors); + card->connectors = NULL; + } + } + } + + return r; } void @@ -345,7 +522,8 @@ drm_print_help(FILE *f) /* TRANSLATORS: DRM help output left column must not be translated */ - fputs(_(" card=N\tGraphics card to apply adjustments to\n" - " crtc=N\tCRTC to apply adjustments to\n"), f); + fputs(_(" crtc=N\tCRTC to apply adjustments to\n" + " edid=VALUE\tThe EDID of the monitor to apply adjustments to\n" + " card=N\tGraphics card to apply adjustments to\n"), f); fputs("\n", f); } diff --git a/src/gamma-drm.h b/src/gamma-drm.h index d05fd216..75c064e0 100644 --- a/src/gamma-drm.h +++ b/src/gamma-drm.h @@ -28,12 +28,26 @@ #include +/* EDID version 1.0 through 1.4 define it as 128 bytes long, but + version 2.0 define it as 256 bytes long. However, version 2.0 + is rare(?) and has been deprecated and replaced by version 1.3. */ +#ifndef MAX_EDID_LENGTH +#define MAX_EDID_LENGTH 256 +#endif + + typedef struct { int fd; drmModeRes *res; size_t index; + drmModeConnector** connectors; } drm_card_data_t; +typedef struct { + unsigned char edid[MAX_EDID_LENGTH]; + uint32_t edid_length; +} drm_selection_data_t; + int drm_init(gamma_server_state_t *state); int drm_start(gamma_server_state_t *state); diff --git a/src/gamma-randr.c b/src/gamma-randr.c index 3f118fb9..439877bc 100644 --- a/src/gamma-randr.c +++ b/src/gamma-randr.c @@ -441,7 +441,8 @@ randr_test_edid(xcb_connection_t *connection, xcb_randr_output_t output, xcb_ato static int randr_parse_selection(gamma_server_state_t *state, gamma_site_state_t *site, - gamma_selection_state_t *selection, enum gamma_selection_hook when) { + gamma_selection_state_t *selection, enum gamma_selection_hook when) +{ (void) state; if (when != before_crtc) From e55e27ddca1d97aa6c6bad8c8b049222cd94630e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Andr=C3=A9e?= Date: Sat, 12 Apr 2014 07:04:19 +0200 Subject: [PATCH 29/29] Add ignorable option to adjustment settings, this can be used if you want to have settings specified for monitors that are not currently plugged in as is now possible because of the EDID addition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/gamma-common.c | 67 ++++++++++++++++++++++++++++++---------------- src/gamma-common.h | 2 ++ 2 files changed, 46 insertions(+), 23 deletions(-) diff --git a/src/gamma-common.c b/src/gamma-common.c index 1ddf7587..4e09e758 100644 --- a/src/gamma-common.c +++ b/src/gamma-common.c @@ -57,6 +57,7 @@ gamma_init(gamma_server_state_t *state) state->selections->crtc = -1; state->selections->partition = -1; state->selections->site = NULL; + state->selections->ignorable = 0; state->selections->settings.gamma_correction[0] = DEFAULT_GAMMA; state->selections->settings.gamma_correction[1] = DEFAULT_GAMMA; state->selections->settings.gamma_correction[2] = DEFAULT_GAMMA; @@ -215,9 +216,9 @@ gamma_iterator_next(gamma_iterator_t *iterator) return 0; if (iterator->state->sites[site_i].partitions[partition_i].used == 0 || iterator->state->sites[site_i].partitions[partition_i].crtcs_used == 0) { - /* Because of `state->parse_selection` it is possible to have - a partition that is initalise but not used. This is when - used ≠ 0 but crtcs_used = 0. */ + /* Because of `state->parse_selection` and `ignorable` + it is possible to have a partition that is initalised + but not used. This is when used ≠ 0 but crtcs_used = 0. */ partition_i += 1; goto next_partition; } @@ -264,6 +265,9 @@ gamma_resolve_selections(gamma_server_state_t *state) state->selections--; } + +#define __ignorable if (selection->ignorable) continue; else + for (size_t i = 1; i < state->selections_made; i++) { gamma_selection_state_t *selection = state->selections + i; gamma_site_state_t *site; @@ -274,7 +278,9 @@ gamma_resolve_selections(gamma_server_state_t *state) /* Run site selection hook. */ if (selection->data != NULL) { r = state->parse_selection(state, NULL, selection, before_site); - if (r < 0) return r; + if (r < 0) { + __ignorable return r; + } } /* Find matching already opened site. */ @@ -304,7 +310,7 @@ gamma_resolve_selections(gamma_server_state_t *state) r = state->open_site(state, selection->site, site); if (r != 0) { rc = r; - goto fail; + __ignorable goto fail; } /* Increment now (rather than earlier), so we do not get segfault on error. */ @@ -333,13 +339,15 @@ gamma_resolve_selections(gamma_server_state_t *state) /* Run partition selection hook. */ if (selection->data != NULL) { r = state->parse_selection(state, site, selection, before_partition); - if (r < 0) return r; + if (r < 0) { + __ignorable return r; + } } /* Select partitions. */ if (selection->partition >= (ssize_t)(site->partitions_available)) { state->invalid_partition(site, (size_t)(selection->partition)); - goto fail; + __ignorable goto fail; } partition_start = selection->partition < 0 ? 0 : (size_t)(selection->partition); partition_end = selection->partition < 0 ? site->partitions_available : partition_start + 1; @@ -350,8 +358,10 @@ gamma_resolve_selections(gamma_server_state_t *state) r = state->open_partition(state, site, p, partition); if (r != 0) { - rc = r; - goto fail; + __ignorable { + rc = r; + goto fail; + } } partition->used = 1; @@ -360,12 +370,16 @@ gamma_resolve_selections(gamma_server_state_t *state) /* Run CRTC selection hook. */ if (selection->data != NULL) { r = state->parse_selection(state, site, selection, before_crtc); - if (r < 0) return r; + if (r < 0) { + __ignorable return r; + } } /* Open CRTCs. */ for (size_t p = partition_start; p < partition_end; p++) { gamma_partition_state_t *partition = site->partitions + p; + if (partition->used == 0) continue; + size_t crtc_start = selection->crtc < 0 ? 0 : (size_t)(selection->crtc); size_t crtc_end = selection->crtc < 0 ? partition->crtcs_available : crtc_start + 1; @@ -378,7 +392,7 @@ gamma_resolve_selections(gamma_server_state_t *state) } else { fprintf(stderr, _("Only CRTC 0 exists.\n")); } - return -1; + __ignorable return -1; } /* Grow array with selected CRTCs, we temporarily store @@ -403,8 +417,10 @@ gamma_resolve_selections(gamma_server_state_t *state) r = state->open_crtc(state, site, partition, c, crtc); if (r != 0) { - rc = r; - goto fail; + __ignorable { + rc = r; + goto fail; + } } crtc->crtc = c; crtc->partition = p; @@ -432,6 +448,8 @@ gamma_resolve_selections(gamma_server_state_t *state) } } +#undef __ignorable + rc = 0; fail: @@ -627,17 +645,20 @@ gamma_set_option(gamma_server_state_t *state, const char *key, char *value, ssiz return -1; } #endif - if (section >= 0) { - state->selections[section].settings.gamma_correction[0] = gamma[0]; - state->selections[section].settings.gamma_correction[1] = gamma[1]; - state->selections[section].settings.gamma_correction[2] = gamma[2]; - } else { - for (size_t i = 0; i < state->selections_made; i++) { - state->selections[i].settings.gamma_correction[0] = gamma[0]; - state->selections[i].settings.gamma_correction[1] = gamma[1]; - state->selections[i].settings.gamma_correction[2] = gamma[2]; - } + on_selections({ + sel->settings.gamma_correction[0] = gamma[0]; + sel->settings.gamma_correction[1] = gamma[1]; + sel->settings.gamma_correction[2] = gamma[2]; + }); + } else if (strcasecmp(key, "ignorable") == 0) { + int int_value = atoi(value); + if (int_value != 0 && int_value != 1) { + /* TRANSLATORS: `ignorable' must not be translated. */ + fprintf(stderr, + _("The value for `ignorable' must be either `1' or `0'.\n")); + return -1; } + on_selections({ sel->ignorable = int_value; }); } else { r = state->set_option(state, key, value, section); if (r <= 0) diff --git a/src/gamma-common.h b/src/gamma-common.h index e1ea8c6b..5d1d6e33 100644 --- a/src/gamma-common.h +++ b/src/gamma-common.h @@ -132,6 +132,8 @@ struct gamma_selection_state { duplicated. */ void *data; size_t sizeof_data; + /* Whether the selection can be ignored if it fails. */ + int ignorable; /* The CRTC and partition (e.g. screen) indices. */ ssize_t crtc; ssize_t partition;