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;