systemtime.c: Portably read current time on posix and windows.

w32gdi.c: Implements gamma ramp method for Windows GDI API.
Use double as a cross platform time representation.
Add WinGDI as a selectable method (currently limitied to minimum 4500K).
Fix a bug where redshift would crash if RANDR failed and VidMode wasn't
 compiled in.
This commit is contained in:
Jon Lund Steffensen
2010-05-06 23:56:52 +02:00
parent fc47840a2c
commit 52a5a370fd
9 changed files with 402 additions and 52 deletions
+26 -2
View File
@@ -20,6 +20,8 @@ PKG_CHECK_MODULES([XCB], [xcb], [have_xcb=yes], [have_xcb=no])
PKG_CHECK_MODULES([XCB_RANDR], [xcb-randr],
[have_xcb_randr=yes], [have_xcb_randr=no])
AC_CHECK_HEADER([windows.h], [have_windows_h=yes], [have_windows_h=no])
# Check for Python
AM_PATH_PYTHON([2.6], [have_python=yes], [have_python=no])
@@ -67,9 +69,31 @@ AS_IF([test "x$enable_vidmode" != xno], [
])
AM_CONDITIONAL([ENABLE_VIDMODE], [test "x$enable_vidmode" != 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], [
AC_DEFINE([ENABLE_WINGDI], 1,
[Define to 1 to enable WinGDI method])
AC_MSG_RESULT([yes])
], [
AC_MSG_RESULT([missing dependencies])
AS_IF([test "x$enable_wingdi" = xyes], [
AC_MSG_ERROR([missing Windows API headers for WinGDI method])
])
enable_wingdi=no
])
], [
AC_MSG_RESULT([no])
])
AM_CONDITIONAL([ENABLE_WINGDI], [test "x$enable_wingdi" != xno])
# Check that at least one method is enabled
AS_IF([test "x$enable_randr" = xno -a "x$enable_vidmode" = xno], [
AC_MSG_ERROR([either RANDR or VidMode must be enabled])
AS_IF([test "x$enable_randr" = xno -a "x$enable_vidmode" = xno -a "x$enable_wingdi" = xno], [
AC_MSG_ERROR([either RANDR, VidMode or WinGDI must be enabled])
])
# Check for GTK+ status icon
+9 -2
View File
@@ -11,11 +11,13 @@ bin_PROGRAMS = redshift
redshift_SOURCES = \
redshift.c \
colorramp.c colorramp.h \
solar.c solar.h
solar.c solar.h \
systemtime.c systemtime.h
EXTRA_redshift_SOURCES = \
randr.c randr.h \
vidmode.c vidmode.h
vidmode.c vidmode.h \
w32gdi.c w32gdi.h
AM_CFLAGS =
redshift_LDADD = @LIBINTL@
@@ -36,3 +38,8 @@ redshift_LDADD += \
$(X11_LIBS) $(X11_CFLAGS) \
$(XF86VM_LIBS) $(XF86VM_CFLAGS)
endif
if ENABLE_WINGDI
redshift_SOURCES += w32gdi.c w32gdi.h
redshift_LDADD += -lgdi32
endif
+93 -24
View File
@@ -28,20 +28,30 @@
#include <time.h>
#include <math.h>
#include <locale.h>
#include <sys/signal.h>
#include <libintl.h>
#define _(s) gettext(s)
#ifdef HAVE_SYS_SIGNAL_H
# include <sys/signal.h>
#endif
#ifdef ENABLE_NLS
# include <libintl.h>
# define _(s) gettext(s)
#else
# define _(s) s
#endif
#include "solar.h"
#include "systemtime.h"
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define MAX(x,y) ((x) > (y) ? (x) : (y))
#if !(defined(ENABLE_RANDR) || defined(ENABLE_VIDMODE))
# error "At least one of RANDR or VidMode must be enabled."
#if !(defined(ENABLE_RANDR) || \
defined(ENABLE_VIDMODE) || \
defined(ENABLE_WINGDI))
# error "At least one of RANDR, VidMode or WinGDI must be enabled."
#endif
#ifdef ENABLE_RANDR
@@ -52,6 +62,10 @@
# include "vidmode.h"
#endif
#ifdef ENABLE_WINGDI
# include "w32gdi.h"
#endif
/* Union of state data for gamma adjustment methods */
typedef union {
@@ -61,6 +75,9 @@ typedef union {
#ifdef ENABLE_VIDMODE
vidmode_state_t vidmode;
#endif
#ifdef ENABLE_WINGDI
w32gdi_state_t w32gdi;
#endif
} gamma_state_t;
@@ -128,6 +145,11 @@ gamma_state_restore(gamma_state_t *state, int use_randr)
case 1:
randr_restore(&state->randr);
break;
#endif
#ifdef ENABLE_WINGDI
case 2:
w32gdi_restore(&state->w32gdi);
break;
#endif
}
}
@@ -146,6 +168,11 @@ gamma_state_free(gamma_state_t *state, int use_randr)
case 1:
randr_free(&state->randr);
break;
#endif
#ifdef ENABLE_WINGDI
case 2:
w32gdi_free(&state->w32gdi);
break;
#endif
}
}
@@ -163,6 +190,10 @@ gamma_state_set_temperature(gamma_state_t *state, int use_randr,
#ifdef ENABLE_RANDR
case 1:
return randr_set_temperature(&state->randr, temp, gamma);
#endif
#ifdef ENABLE_WINGDI
case 2:
return w32gdi_set_temperature(&state->w32gdi, temp, gamma);
#endif
}
@@ -225,7 +256,7 @@ print_help(const char *program_name)
fputs(_(" -g R:G:B\tAdditional gamma correction to apply\n"
" -l LAT:LON\tYour current location\n"
" -m METHOD\tMethod to use to set color temperature"
" (RANDR or VidMode)\n"
" (RANDR, VidMode or WinGDI)\n"
" -o\t\tOne shot mode (do not continously adjust"
" color temperature)\n"
" -r\t\tDisable temperature transitions\n"
@@ -346,6 +377,16 @@ main(int argc, char *argv[])
" enabled at compile time.\n"),
stderr);
exit(EXIT_FAILURE);
#endif
} else if (strcmp(optarg, "wingdi") == 0 ||
strcmp(optarg, "WinGDI") == 0) {
#ifdef ENABLE_WINGDI
use_randr = 2;
#else
fputs(_("WinGDI method was not"
" enabled at compile time.\n"),
stderr);
exit(EXIT_FAILURE);
#endif
} else {
/* TRANSLATORS: This refers to the method
@@ -482,19 +523,47 @@ main(int argc, char *argv[])
if (r < 0) {
fputs(_("Initialization of VidMode failed.\n"),
stderr);
exit(EXIT_FAILURE);
if (use_randr < 0) {
fputs(_("Trying other method...\n"), stderr);
} else {
exit(EXIT_FAILURE);
}
} else {
use_randr = 0;
}
}
#endif
#ifdef ENABLE_WINGDI
if (use_randr < 0 || use_randr == 2) {
/* Initialize WinGDI state */
r = w32gdi_init(&state.w32gdi);
if (r < 0) {
fputs(_("Initialization of WinGDI failed.\n"),
stderr);
if (use_randr < 0) {
fputs(_("Trying other method...\n"), stderr);
} else {
exit(EXIT_FAILURE);
}
} else {
use_randr = 2;
}
}
#endif
/* Failure if no methods were successful at this point. */
if (use_randr < 0) {
fputs(_("No more methods to try.\n"), stderr);
exit(EXIT_FAILURE);
}
if (one_shot) {
/* Current angular elevation of the sun */
struct timespec now;
r = clock_gettime(CLOCK_REALTIME, &now);
double now;
r = systemtime_get_time(&now);
if (r < 0) {
perror("clock_gettime");
fputs(_("Unable to read system time.\n"), stderr);
gamma_state_free(&state, use_randr);
exit(EXIT_FAILURE);
}
@@ -522,7 +591,7 @@ main(int argc, char *argv[])
}
} else {
/* Transition state */
struct timespec short_trans_end;
double short_trans_end = 0;
int short_trans = 0;
int short_trans_done = 0;
@@ -599,10 +668,11 @@ main(int argc, char *argv[])
}
/* Read timestamp */
struct timespec now;
r = clock_gettime(CLOCK_REALTIME, &now);
double now;
r = systemtime_get_time(&now);
if (r < 0) {
perror("clock_gettime");
fputs(_("Unable to read system time.\n"),
stderr);
gamma_state_free(&state, use_randr);
exit(EXIT_FAILURE);
}
@@ -610,11 +680,8 @@ main(int argc, char *argv[])
/* Set up a new transition */
if (short_trans_create) {
if (transition) {
memcpy(&short_trans_end, &now,
sizeof(struct timespec));
short_trans_end.tv_sec +=
short_trans_len;
short_trans_end = now;
short_trans_end += short_trans_len;
short_trans = 1;
short_trans_create = 0;
} else {
@@ -631,11 +698,8 @@ main(int argc, char *argv[])
/* Ongoing short transition */
if (short_trans) {
double start = now.tv_sec +
now.tv_nsec / 1000000000.0;
double end = short_trans_end.tv_sec +
short_trans_end.tv_nsec /
1000000000.0;
double start = now;
double end = short_trans_end;
if (start > end) {
/* Transisiton done */
@@ -691,8 +755,13 @@ main(int argc, char *argv[])
}
/* Sleep for a while */
#ifndef _WIN32
if (short_trans) usleep(100000);
else usleep(5000000);
#else /* ! _WIN32 */
if (short_trans) Sleep(100);
else Sleep(5000);
#endif /* ! _WIN32 */
}
/* Restore saved gamma ramps */
+22 -20
View File
@@ -24,9 +24,9 @@
Jean Meeus. */
#include <math.h>
#include <time.h>
#include "solar.h"
#include "time.h"
#define RAD(x) ((x)*(M_PI/180))
#define DEG(x) ((x)*(180/M_PI))
@@ -46,23 +46,20 @@ static const double time_angle[] = {
};
/* Unix time from Julian day */
static struct timespec
timespec_from_jd(double jd)
/* Unix epoch from Julian day */
static double
epoch_from_jd(double jd)
{
struct timespec t;
double secs = 86400.0*(jd - 2440587.5);
t.tv_sec = floor(secs);
t.tv_nsec = (secs - t.tv_sec) * 1000000000.0;
return t;
return 86400.0*(jd - 2440587.5);
}
/* Julian day from unix time */
/* Julian day from unix epoch */
static double
jd_from_timespec(struct timespec t)
jd_from_epoch(double t)
{
return (t.tv_sec / 86400.0) +
(t.tv_nsec / 86400000000000.0) + 2440587.5;
return (t / 86400.0) + 2440587.5;
}
/* Julian centuries since J2000.0 from Julian day */
@@ -287,18 +284,23 @@ solar_elevation_from_time(double t, double lat, double lon)
return elevation_from_hour_angle(lat, decl, ha);
}
/* Solar angular elevation at the given location and time.
date: Seconds since unix epoch
lat: Latitude of location
lon: Longitude of location
Return: Solar angular elevation in degrees */
double
solar_elevation(struct timespec date, double lat, double lon)
solar_elevation(double date, double lat, double lon)
{
double jd = jd_from_timespec(date);
double jd = jd_from_epoch(date);
return DEG(solar_elevation_from_time(jcent_from_jd(jd), lat, lon));
}
void
solar_table_fill(struct timespec date, double lat, double lon, time_t *table)
solar_table_fill(double date, double lat, double lon, double *table)
{
/* Calculate Julian day */
double jd = jd_from_timespec(date);
double jd = jd_from_epoch(date);
/* Calculate Julian day number */
double jdn = round(jd);
@@ -308,16 +310,16 @@ solar_table_fill(struct timespec date, double lat, double lon, time_t *table)
double sol_noon = time_of_solar_noon(t, lon);
double j_noon = jdn - 0.5 + sol_noon/1440.0;
double t_noon = jcent_from_jd(j_noon);
table[SOLAR_TIME_NOON] = timespec_from_jd(j_noon).tv_sec;
table[SOLAR_TIME_NOON] = epoch_from_jd(j_noon);
/* Calculate solar midnight */
table[SOLAR_TIME_MIDNIGHT] = timespec_from_jd(j_noon + 0.5).tv_sec;
table[SOLAR_TIME_MIDNIGHT] = epoch_from_jd(j_noon + 0.5);
/* Calulate absoute time of other phenomena */
for (int i = 2; i < SOLAR_TIME_MAX; i++) {
double angle = time_angle[i];
double offset =
time_of_solar_elevation(t, t_noon, lat, lon, angle);
table[i] = timespec_from_jd(jdn - 0.5 + offset/1440.0).tv_sec;
table[i] = epoch_from_jd(jdn - 0.5 + offset/1440.0);
}
}
+4 -4
View File
@@ -20,7 +20,7 @@
#ifndef _SOLAR_H
#define _SOLAR_H
#include <time.h>
#include "time.h"
/* Model of atmospheric refraction near horizon (in degrees). */
#define SOLAR_ATM_REFRAC 0.833
@@ -44,8 +44,8 @@ typedef enum {
SOLAR_TIME_MAX
} solar_time_t;
double solar_elevation(struct timespec date, double lat, double lon);
void solar_table_fill(struct timespec date, double lat, double lon,
time_t *table);
double solar_elevation(double date, double lat, double lon);
void solar_table_fill(double date, double lat, double lon, double *table);
#endif /* ! _SOLAR_H */
+52
View File
@@ -0,0 +1,52 @@
/* systemtime.c -- Portable system time 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 <http://www.gnu.org/licenses/>.
Copyright (c) 2010 Jon Lund Steffensen <jonlst@gmail.com>
*/
#include <stdio.h>
#ifndef _WIN32
# include <time.h>
#endif
#include "systemtime.h"
int
systemtime_get_time(double *t)
{
#ifndef _WIN32
struct timespec now;
int r = clock_gettime(CLOCK_REALTIME, &now);
if (r < 0) {
perror("clock_gettime");
return -1;
}
*t = now.tv_sec + (now.tv_nsec / 1000000000.0);
#else /* _WIN32 */
FILETIME now;
ULARGE_INTEGER i;
GetSystemTimeAsFileTime(&now);
i.LowPart = now.dwLowDateTime;
i.HighPart = now.dwHighDateTime;
/* FILETIME is tenths of microseconds since 1601-01-01 UTC */
*t = (i.QuadPart / 10000000.0) - 11644473600.0;
#endif /* _WIN32 */
return 0;
}
+32
View File
@@ -0,0 +1,32 @@
/* systemtime.h -- Portable system time 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 <http://www.gnu.org/licenses/>.
Copyright (c) 2010 Jon Lund Steffensen <jonlst@gmail.com>
*/
#ifndef _REDSHIFT_SYSTEMTIME_H
#define _REDSHIFT_SYSTEMTIME_H
#ifndef _WIN32
# include <time.h>
#else
# include <windows.h>
#endif
int systemtime_get_time(double *now);
#endif /* ! _REDSHIFT_SYSTEMTIME_H */
+127
View File
@@ -0,0 +1,127 @@
/* w32gdi.c -- Windows GDI gamma adjustment 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 <http://www.gnu.org/licenses/>.
Copyright (c) 2010 Jon Lund Steffensen <jonlst@gmail.com>
*/
#include <stdlib.h>
#include <stdio.h>
#define WINVER 0x0500
#include <windows.h>
#include <wingdi.h>
#ifdef ENABLE_NLS
# include <libintl.h>
# define _(s) gettext(s)
#else
# define _(s) s
#endif
#include "w32gdi.h"
#include "colorramp.h"
#define GAMMA_RAMP_SIZE 256
int
w32gdi_init(w32gdi_state_t *state)
{
BOOL r;
/* Open device context */
state->hDC = GetDC(NULL);
if (state->hDC == NULL) {
fputs(_("Unable to open device context.\n"), stderr);
return -1;
}
/* Check support for gamma ramps */
int cmcap = GetDeviceCaps(state->hDC, COLORMGMTCAPS);
if (cmcap != CM_GAMMA_RAMP) {
fputs(_("Display device does not support gamma ramps.\n"),
stderr);
return -1;
}
/* Allocate space for saved gamma ramps */
state->saved_ramps = malloc(3*GAMMA_RAMP_SIZE*sizeof(WORD));
if (state->saved_ramps == NULL) {
perror("malloc");
ReleaseDC(NULL, state->hDC);
return -1;
}
/* Save current gamma ramps so we can restore them at program exit */
r = GetDeviceGammaRamp(state->hDC, state->saved_ramps);
if (!r) {
fputs(_("Unable to save current gamma ramp.\n"), stderr);
ReleaseDC(NULL, state->hDC);
return -1;
}
return 0;
}
void
w32gdi_free(w32gdi_state_t *state)
{
/* Free saved ramps */
free(state->saved_ramps);
/* Release device context */
ReleaseDC(NULL, state->hDC);
}
void
w32gdi_restore(w32gdi_state_t *state)
{
/* Restore gamma ramps */
BOOL r = SetDeviceGammaRamp(state->hDC, state->saved_ramps);
if (!r) fputs(_("Unable to restore gamma ramps.\n"), stderr);
}
int
w32gdi_set_temperature(w32gdi_state_t *state, int temp, float gamma[3])
{
BOOL r;
/* Create new gamma ramps */
WORD *gamma_ramps = malloc(3*GAMMA_RAMP_SIZE*sizeof(WORD));
if (gamma_ramps == NULL) {
perror("malloc");
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, gamma);
/* Set new gamma ramps */
r = SetDeviceGammaRamp(state->hDC, gamma_ramps);
if (!r) {
fputs(_("Unable to set gamma ramps.\n"), stderr);
free(gamma_ramps);
return -1;
}
free(gamma_ramps);
return 0;
}
+37
View File
@@ -0,0 +1,37 @@
/* w32gdi.h -- Windows GDI gamma adjustment 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 <http://www.gnu.org/licenses/>.
Copyright (c) 2010 Jon Lund Steffensen <jonlst@gmail.com>
*/
#ifndef _REDSHIFT_W32GDI_H
#define _REDSHIFT_W32GDI_H
#include <windows.h>
#include <wingdi.h>
typedef struct {
HDC hDC;
WORD *saved_ramps;
} w32gdi_state_t;
int w32gdi_init(w32gdi_state_t *state);
void w32gdi_free(w32gdi_state_t *state);
void w32gdi_restore(w32gdi_state_t *state);
int w32gdi_set_temperature(w32gdi_state_t *state, int temp, float gamma[3]);
#endif /* ! _REDSHIFT_W32GDI_H */