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 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/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 37a03080..48067579 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -15,13 +15,17 @@ redshift_SOURCES = \ location-manual.c location-manual.h \ solar.c solar.h \ systemtime.c systemtime.h \ - gamma-dummy.c gamma-dummy.h + adjustments.h \ + gamma-common.c gamma-common.h \ + gamma-dummy.c gamma-dummy.h \ + opt-parser.c opt-parser.h EXTRA_redshift_SOURCES = \ gamma-drm.c gamma-drm.h \ 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 +57,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/adjustments.h b/src/adjustments.h new file mode 100644 index 00000000..2471693b --- /dev/null +++ b/src/adjustments.h @@ -0,0 +1,88 @@ +/* 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 + + + +/* 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..31a9c805 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,46 @@ 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 } diff --git a/src/colorramp.h b/src/colorramp.h index dd0fa97d..ed1f950b 100644 --- a/src/colorramp.h +++ b/src/colorramp.h @@ -15,14 +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 +#include "adjustments.h" -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/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 */ 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-common.c b/src/gamma-common.c new file mode 100644 index 00000000..bcb45210 --- /dev/null +++ b/src/gamma-common.c @@ -0,0 +1,1013 @@ +/* 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 + 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 "adjustments.h" +#include "colorramp.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->crtcs = NULL; + state->selections->crtcs_count = 0; + state->selections->partitions = NULL; + state->selections->partitions_count = 0; + state->selections->sites = NULL; + state->selections->sites_count = 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; + 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, j; + + /* Free data in each selection. */ + for (i = 0; i < state->selections_made; i++) { + if (state->selections[i].crtcs != NULL) + free(state->selections[i].crtcs); + if (state->selections[i].partitions != NULL) + free(state->selections[i].partitions); + if (state->selections[i].sites != NULL) { + for (j = 0; j < state->selections[i].sites_count; j++) { + if (state->selections[i].sites[j] != NULL) + free(state->selections[i].sites[j]); + } + free(state->selections[i].sites); + } + } + 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; + } +} + + +/* 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; +} + + +/* 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; +} + + +/* Open a site. */ +static int +gamma_open_site(gamma_server_state_t *state, size_t site_index, + char *site_name, gamma_site_state_t **site_out) +{ + int rc = -1, r; + gamma_site_state_t *site; + + /* 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, site_name, 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 (site_name != NULL) { + site->site = strdup(site_name); + 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; + } + + *site_out = site; + return 0; + +fail: + return rc; +} + + +/* Open a CRTC. */ +static int +gamma_open_crtc(gamma_server_state_t *state, gamma_site_state_t *site, + gamma_partition_state_t *partition, size_t site_index, + size_t partition_index, size_t crtc_index, + gamma_selection_state_t *selection) +{ + int rc = -1, r; + + 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, crtc_index, crtc); + if (r != 0) { + rc = r; + goto fail; + } + crtc->crtc = crtc_index; + crtc->partition = partition_index; + 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; + } + + return 0; + +fail: + return rc; +} + + +/* Open partitions. */ +static int +gamma_open_partitions(gamma_server_state_t *state, gamma_site_state_t *site, + gamma_selection_state_t *selection, int all_partitions) +{ + int rc = -1, r; + + /* Select all partitions if none have been specified explicity. */ + if (all_partitions && selection->partitions_count < site->partitions_available) { + size_t p, n = site->partitions_available; + if (selection->partitions != NULL) + free(selection->partitions); + selection->partitions = malloc(n * sizeof(size_t)); + if (selection->partitions == NULL) { + perror("malloc"); + goto fail; + } + for (p = 0; p < n; p++) + selection->partitions[p] = p; + } + if (all_partitions) { + selection->partitions_count = site->partitions_available; + } + + /* Open partitions. */ + for (size_t p = 0; p < selection->partitions_count; p++) { + size_t partition_index = selection->partitions[p]; + + /* Validate partition index. */ + if (partition_index >= site->partitions_available) { + state->invalid_partition(site, partition_index); + goto fail; + } + + gamma_partition_state_t *partition = site->partitions + partition_index; + if (partition->used) continue; /* Already open. */ + + /* Open partition. */ + r = state->open_partition(state, site, partition_index, partition); + if (r != 0) { + rc = r; + goto fail; + } + + partition->used = 1; + } + + return 0; + +fail: + return rc; +} + + +/* Open multiple CRTCs. */ +static int +gamma_open_crtcs(gamma_server_state_t *state, gamma_site_state_t *site, + size_t site_index, size_t partition_index, + gamma_selection_state_t *selection, int all_crtcs) +{ + int rc = -1, r; + gamma_partition_state_t *partition = site->partitions + partition_index; + + /* Select all CRTCs if none have been specified explicity. */ + if (all_crtcs && selection->crtcs_count < partition->crtcs_available) { + size_t c, n = partition->crtcs_available; + if (selection->crtcs != NULL) + free(selection->crtcs); + selection->crtcs = malloc(n * sizeof(size_t)); + if (selection->crtcs == NULL) { + perror("malloc"); + goto fail; + } + for (c = 0; c < n; c++) + selection->crtcs[c] = c; + } + if (all_crtcs) { + selection->crtcs_count = partition->crtcs_available; + } + + /* 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 + selection->crtcs_count; + 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 = 0; c < selection->crtcs_count; c++) { + size_t crtc_index = selection->crtcs[c]; + + /* Validate CRTC index. */ + if (crtc_index >= partition->crtcs_available) { + fprintf(stderr, _("CRTC %ld does not exist. "), + crtc_index); + 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; + } + + /* Open CRTC. */ + r = gamma_open_crtc(state, site, partition, site_index, + partition_index, crtc_index, selection); + if (r != 0) { + rc = r; + goto fail; + } + } + + return 0; + +fail: + return rc; +} + + +/* 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; + int all_crtcs = selection->crtcs == NULL; + int all_partitions = selection->partitions == NULL; + gamma_site_state_t *site; + size_t site_index; + + /* Select default site if none have been specified. */ + if (selection->sites == NULL) { + selection->sites = malloc(1 * sizeof(char *)); + if (selection->sites == NULL) { + perror("malloc"); + goto fail; + } + selection->sites[0] = NULL; + selection->sites_count = 1; + } + + for (size_t s = 0; s < selection->sites_count; s++) { + /* Find matching already opened site. */ + site_index = gamma_find_site(state, selection->sites[s]); + + /* Open site if not found. */ + if (site_index == state->sites_used) { + r = gamma_open_site(state, site_index, selection->sites[s], &site); + if (r != 0) { + rc = r; + goto fail; + } + } else { + site = state->sites + site_index; + } + + /* Open partitions. */ + r = gamma_open_partitions(state, site, selection, all_partitions); + if (r != 0) { + rc = r; + goto fail; + } + + /* Open CRTCs. */ + for (size_t p = 0; p < selection->partitions_count; p++) { + size_t partition_index = selection->partitions[p]; + r = gamma_open_crtcs(state, site, site_index, + partition_index, selection, all_crtcs); + if (r != 0) { + rc = r; + 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; +} + + +/* 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; +} + + +/* 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 + + +/* Duplicate memory area. */ +static void * +memdup(void *src, size_t n) +{ + char *dest = malloc(n); + if (dest == NULL) + return NULL; + memcpy(dest, src, n); + return dest; +} + + +/* 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 *sels; + size_t alloc_size = state->selections_made + 1; + alloc_size *= sizeof(gamma_selection_state_t); + sels = realloc(state->selections, alloc_size); + if (sels == NULL) { + perror("realloc"); + return -1; + } + state->selections = sels; + + /* Copy default selection. */ + sels[section] = *sels; + if (sels->sites != NULL) { + size_t i; + sels[section].sites = memdup(sels->sites, + sels->sites_count * sizeof(char *)); + if (sels[section].sites == NULL) { + perror("memdup"); + return -1; + } + for (i = 0; i < sels->sites_count; i++) { + sels[section].sites[i] = strdup(sels->sites[i]); + if (sels[section].sites[i] == NULL) { + perror("strdup"); + return -1; + } + } + } + if (sels->partitions != NULL) { + sels[section].partitions = memdup(sels->partitions, + sels->partitions_count * sizeof(size_t)); + if (sels[section].partitions == NULL) { + perror("memdup"); + return -1; + } + } + if (sels->crtcs != NULL) { + sels[section].crtcs = memdup(sels->crtcs, + sels->crtcs_count * sizeof(size_t)); + if (sels[section].crtcs == NULL) { + perror("memdup"); + 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; +} + + +/* Parse an array for size_t:s. */ +static size_t * +parse_size_t_array(char *value, char delimiter, const char *name, size_t *n_out) +{ + size_t n = 0; + char *begin; + size_t *array; + + /* Count number of specified values and split the values. */ + begin = value; + while (1) { + char *end = strchr(begin, delimiter); + int last = end == NULL; + if (last) end = strchr(begin, '\0'); + + end[0] = '\0'; + n++; + + if (last) break; + begin = end + 1; + } + + /* Allocate array and store element count for caller. */ + array = malloc(n * sizeof(size_t)); + if (array == NULL) { + perror("malloc"); + return NULL; + } + *n_out = n; + + /* Convert to array. */ + begin = value; + for (size_t i = 0; i < n; i++) { + char *end; + ssize_t parsed = strtol(begin, &end, 10); + + if (begin[0] == '\0' || end[0] != '\0' || parsed < 0) { + /* TRANSLATORS: `all' must not be translated. */ + fprintf(stderr, _("%s must be `all' or a non-negative integer.\n"), name); + return NULL; + } + + array[i] = (size_t)parsed; + begin = end + 1; + } + + return array; +} + +/* Parse an array for char *:s. */ +static char ** +parse_str_array(char *value, char delimiter, size_t *n_out) +{ + size_t n = 0; + char *begin; + char **array; + + /* Count number of specified values and split the values. */ + begin = value; + while (1) { + char *end = strchr(begin, delimiter); + int last = end == NULL; + if (last) end = strchr(begin, '\0'); + + end[0] = '\0'; + n++; + + if (last) break; + begin = end + 1; + } + + /* Allocate array and store element count for caller. */ + array = malloc(n * sizeof(char *)); + if (array == NULL) { + perror("malloc"); + return NULL; + } + *n_out = n; + + /* Convert to array. */ + begin = value; + for (size_t i = 0; i < n; i++) { + char *end = strchr(begin, '\0'); + array[i] = begin; + begin = end + 1; + } + + return array; +} + + +/* Parse CRTC selection option. */ +int +gamma_select_crtcs(gamma_server_state_t *state, char *value, char delimiter, + ssize_t section, const char *name) +{ + if (strcasecmp(value, "all") == 0) { + on_selections({ + sel->crtcs = NULL; + sel->crtcs_count = 0; + }); + } else { + size_t n; + size_t *values = parse_size_t_array(value, delimiter, name, &n); + if (values == NULL) + return -1; + on_selections({ + sel->crtcs = memdup(values, n * sizeof(size_t)); + if (sel->crtcs == NULL) { + perror("memdup"); + free(values); + return -1; + } + sel->crtcs_count = n; + }); + free(values); + } + return 0; +} + + +/* Parse partition (e.g. screen) selection option. */ +int +gamma_select_partitions(gamma_server_state_t *state, char *value, char delimiter, + ssize_t section, const char *name) +{ + if (strcasecmp(value, "all") == 0) { + on_selections({ + sel->partitions = NULL; + sel->partitions_count = 0; + }); + } else { + size_t n; + size_t *values = parse_size_t_array(value, delimiter, name, &n); + if (values == NULL) + return -1; + on_selections({ + sel->partitions = memdup(values, n * sizeof(size_t)); + if (sel->partitions == NULL) { + perror("memdup"); + free(values); + return -1; + } + sel->partitions_count = n; + }); + free(values); + } + return 0; +} + + +/* Parse site (e.g. display) selection option. */ +int +gamma_select_sites(gamma_server_state_t *state, char *value, char delimiter, ssize_t section) +{ + size_t n; + char **values = parse_str_array(value, delimiter, &n); + if (values == NULL) + return -1; + on_selections({ + sel->sites = malloc(n * sizeof(char *)); + if (sel->sites == NULL) { + perror("malloc"); + goto fail; + } + sel->sites_count = 0; + for (size_t i = 0; i < n; i++) { + sel->sites[i] = strdup(values[i]); + if (sel->sites[i] == NULL) { + perror("strdup"); + goto fail; + } + sel->sites_count++; + } + }); + free(values); + return 0; + +fail: + free(values); + return -1; +} diff --git a/src/gamma-common.h b/src/gamma-common.h new file mode 100644 index 00000000..e0950e45 --- /dev/null +++ b/src/gamma-common.h @@ -0,0 +1,257 @@ +/* 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 + 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; +struct gamma_iterator; +struct gamma_crtc_selection; + +/* 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 struct gamma_iterator gamma_iterator_t; +typedef struct gamma_crtc_selection gamma_crtc_selection_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. */ + size_t *crtcs; + size_t crtcs_count; + size_t *partitions; + size_t partitions_count; + /* The site identifiers. */ + char **sites; + size_t sites_count; + /* 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; + /* 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; +}; + + +/* 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; +}; + +/* CRTC selection. */ +struct gamma_crtc_selection { + ssize_t site; + ssize_t partition; + ssize_t crtc; +}; + + + +/* 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); + + +/* Create CRTC iterator. */ +gamma_iterator_t gamma_iterator(gamma_server_state_t *state); + +/* Get next CRTC. */ +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)); + + +/* Resolve selections. */ +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); + + +/* 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); + +/* A gamma string contains either one floating point 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 \ + } + +/* Parse CRTC selection option. */ +int gamma_select_crtcs(gamma_server_state_t *state, char *value, char delimiter, + ssize_t section, const char *name); + +/* Parse partition (e.g. screen) selection option. */ +int gamma_select_partitions(gamma_server_state_t *state, char *value, char delimiter, + ssize_t section, const char *name); + +/* Parse site (e.g. display) selection option. */ +int gamma_select_sites(gamma_server_state_t *state, char *value, char delimiter, ssize_t section); + + +#endif /* ! REDSHIFT_GAMMA_COMMON_H */ diff --git a/src/gamma-drm.c b/src/gamma-drm.c index e784b471..7e909b19 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,293 @@ #include "colorramp.h" -int -drm_init(drm_state_t *state) +static void +drm_free_partition(void *data) +{ + 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); +} + +static void +drm_free_crtc(void *data) { - /* Initialize state. */ - state->card_num = 0; - state->crtc_num = -1; - state->fd = -1; - state->res = NULL; - state->crtcs = NULL; + (void) data; +} + +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; + + /* 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++; + } return 0; } -int -drm_start(drm_state_t *state) +static int +drm_open_partition(gamma_server_state_t *state, gamma_site_state_t *site, + size_t partition, gamma_partition_state_t *partition_out) { - /* Acquire access to a graphics card. */ - long maxlen = strlen(DRM_DIR_NAME) + strlen(DRM_DEV_NAME) + 10; - char *pathname = alloca(maxlen * sizeof(char)); + (void) state; + (void) site; - sprintf(pathname, DRM_DEV_NAME, DRM_DIR_NAME, state->card_num); + 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); - 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"); + 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 { + 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); + } + } + } + } 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; } /* 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; + 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; } - /* 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); - } else { - fprintf(stderr, _("Only CRTC 0 exists.\n")); - } - close(state->fd); - state->fd = -1; - drmModeFreeResources(state->res); - state->res = NULL; - return -1; - } + partition_out->crtcs_available = (size_t)(data->res->count_crtcs); + return 0; +} - state->crtcs = malloc(2 * sizeof(drm_crtc_state_t)); - state->crtcs[1].crtc_num = -1; +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) +{ + (void) state; + (void) site; - 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; - } + 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; } - /* 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; - } + 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; + } + 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; + } + + 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_restore(drm_state_t *state) +static void +drm_invalid_partition(const gamma_site_state_t *site, size_t partition) { - 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++; + 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")); } } -void -drm_free(drm_state_t *state) +static int +drm_set_ramps(gamma_server_state_t *state, gamma_crtc_state_t *crtc, gamma_ramps_t ramps) { - 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++; + 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; } - free(state->crtcs); - state->crtcs = NULL; - } - if (state->res != NULL) { - drmModeFreeResources(state->res); - state->res = NULL; - } - if (state->fd >= 0) { - close(state->fd); - state->fd = -1; } + return 0; } -void -drm_print_help(FILE *f) +static int +drm_set_option(gamma_server_state_t *state, const char *key, char *value, ssize_t section) { - fputs(_("Adjust gamma ramps with Direct Rendering Manager.\n"), f); - fputs("\n", 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("\n", f); + if (strcasecmp(key, "card") == 0) { + return gamma_select_partitions(state, value, ',', section, _("Card")); + } else if (strcasecmp(key, "crtc") == 0) { + return gamma_select_crtcs(state, value, ',', section, _("CRTC")); + } + return 1; } int -drm_set_option(drm_state_t *state, const char *key, const char *value) +drm_init(gamma_server_state_t *state) { - if (strcasecmp(key, "card") == 0) { - state->card_num = atoi(value); - } 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")); - return -1; - } - } else { - fprintf(stderr, _("Unknown method parameter: `%s'.\n"), key); - return -1; - } + 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\tList of comma separated graphics cards to apply adjustments to\n" + " crtc=N\tList of comma separated CRTCs 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 */ 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/gamma-randr.c b/src/gamma-randr.c index 9e528b5d..8d970678 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) +{ + if (((randr_screen_data_t *)data)->crtcs) + free(((randr_screen_data_t *)data)->crtcs); + free(data); +} + +static void +randr_free_crtc(void *data) { - /* Initialize state. */ - state->screen_num = -1; - state->crtc_num = -1; + (void) data; +} - state->crtc_count = 0; - state->crtcs = NULL; +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,212 @@ 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; - } - - unsigned int ramp_size = gamma_size_reply->size; - state->crtcs[i].ramp_size = ramp_size; +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; - free(gamma_size_reply); + 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; - if (ramp_size == 0) { - fprintf(stderr, _("Gamma ramp size too small: %i\n"), - ramp_size); - return -1; - } + crtc_out->data = crtc_id; - /* 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; - } + /* 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); - 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) { + return gamma_select_partitions(state, value, ',', section, _("Screen")); + } else if (strcasecmp(key, "crtc") == 0) { + return gamma_select_crtcs(state, value, ',', section, _("CRTC")); + } else if (strcasecmp(key, "display") == 0) { + return gamma_select_sites(state, value, ',', section); + } + 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; + r = gamma_init(state); + if (r != 0) return r; + + 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->sites = malloc(1 * sizeof(char *)); + if (state->selections->sites == NULL) { + perror("malloc"); + return -1; + } - /* 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; + if (getenv("DISPLAY") != NULL) { + state->selections->sites[0] = strdup(getenv("DISPLAY")); + if (state->selections->sites[0] == NULL) { + perror("strdup"); + return -1; } } else { - return randr_set_temperature_for_crtc(state, state->crtc_num, - temp, brightness, gamma); + state->selections->sites[0] = NULL; } + state->selections->sites_count = 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\tList of comma separated CRTCs to apply adjustments to\n" + " screen=N\tList of comma separated X screens to apply adjustments to\n" + " display=NAME\tList of comma separated X displays 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 */ diff --git a/src/gamma-vidmode.c b/src/gamma-vidmode.c index 656ce007..797002c9 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,97 @@ 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; + return gamma_select_partitions(state, value, ',', section, _("Screen")); + } else if (strcasecmp(key, "display") == 0) { + return gamma_select_sites(state, value, ',', section); } - - return 0; + 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) { + r = gamma_init(state); + if (r != 0) return r; + + 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; + + state->selections->sites = malloc(1 * sizeof(char *)); + if (state->selections->sites == NULL) { perror("malloc"); 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]; + if (getenv("DISPLAY") != NULL) { + state->selections->sites[0] = strdup(getenv("DISPLAY")); + if (state->selections->sites[0] == NULL) { + perror("strdup"); + return -1; + } + } else { + state->selections->sites[0] = NULL; + } + state->selections->sites_count = 1; - 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\tList of comma separated X screens to apply adjustments to\n" + " display=NAME\tList of comma separated X displays 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 */ diff --git a/src/gamma-w32gdi.c b/src/gamma-w32gdi.c index 7193bc91..a9cc854d 100644 --- a/src/gamma-w32gdi.c +++ b/src/gamma-w32gdi.c @@ -15,16 +15,26 @@ along with Redshift. If not, see . Copyright (c) 2010 Jon Lund Steffensen + Copyright (c) 2014 Mattias Andrée */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + #include #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 @@ -34,144 +44,173 @@ #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) { + return gamma_select_crtcs(state, value, ',', section, _("CRTC")); + } + 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\tList of comman separated monitors to apply adjustments to\n"), f); + fputs("\n", f); } diff --git a/src/gamma-w32gdi.h b/src/gamma-w32gdi.h index e81f4c5e..51384003 100644 --- a/src/gamma-w32gdi.h +++ b/src/gamma-w32gdi.h @@ -15,31 +15,30 @@ 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 -#include +#include "gamma-common.h" +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif -typedef struct { - WORD *saved_ramps; -} w32gdi_state_t; +#ifdef FAKE_W32GDI +# include "fake-w32gdi.h" +#else +# include +# include +#endif -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 */ 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 6fc28952..afde4fc8 100644 --- a/src/redshift.c +++ b/src/redshift.c @@ -15,12 +15,14 @@ along with Redshift. If not, see . Copyright (c) 2013 Jon Lund Steffensen + Copyright (c) 2014 Mattias Andrée */ #ifdef HAVE_CONFIG_H # include "config.h" #endif +#include #include #include #include @@ -45,6 +47,9 @@ #include "config-ini.h" #include "solar.h" #include "systemtime.h" +#include "adjustments.h" +#include "opt-parser.h" +#include "gamma-common.h" #define MIN(x,y) ((x) < (y) ? (x) : (y)) @@ -77,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[] = { @@ -101,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 @@ -113,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 @@ -125,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 @@ -137,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 } }; @@ -198,34 +167,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 -#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). - 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, @@ -443,43 +384,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. */ @@ -496,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; @@ -508,63 +449,78 @@ 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. */ - 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 = gamma_set_option(state, key, value, -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; + } - args = next_arg; + args = value + strlen(value) + 1; + } } /* 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; @@ -573,32 +529,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 @@ -645,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[]) @@ -667,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; @@ -690,7 +628,10 @@ 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) { + float gamma_[3]; switch (opt) { case 'b': parse_brightness_string(optarg, &brightness_day, &brightness_night); @@ -698,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); @@ -715,38 +665,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); } @@ -754,6 +713,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; @@ -764,20 +724,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); } @@ -785,6 +745,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; @@ -886,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, @@ -939,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; @@ -954,8 +911,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. */ @@ -1077,39 +1035,30 @@ 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); - } - - 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; + gamma_server_state_t state; /* Gamma adjustment not needed for print mode */ if (mode != PROGRAM_MODE_PRINT) { if (method != NULL) { /* Use method specified on command line. */ r = method_try_start(method, &state, &config_state, - method_args); - if (r < 0) exit(EXIT_FAILURE); + method_args == NULL ? NULL : + 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; @@ -1140,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); } @@ -1168,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); } } @@ -1181,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); } @@ -1193,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); } } @@ -1284,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); } @@ -1344,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); } } @@ -1366,13 +1313,19 @@ 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) + free(method_args); + if (provider_args != NULL) + free(provider_args); return EXIT_SUCCESS; } diff --git a/src/redshift.h b/src/redshift.h index 3a878777..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 @@ -24,17 +25,28 @@ #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); -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; @@ -46,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;