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..006dfebb 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -15,13 +15,16 @@ 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-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 +56,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..d5e02609
--- /dev/null
+++ b/src/adjustments.h
@@ -0,0 +1,55 @@
+/* adjustments.h -- Adjustment constants and data structures header
+ This file is part of Redshift.
+
+ Redshift is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Redshift is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Redshift. If not, see .
+
+ Copyright (c) 2014 Mattias Andrée
+*/
+
+#ifndef REDSHIFT_ADJUSTMENTS_H
+#define REDSHIFT_ADJUSTMENTS_H
+
+#include
+#include
+
+
+
+/* Bounds for parameters. */
+#define MIN_TEMP 1000
+#define MAX_TEMP 25000
+#ifndef MIN_BRIGHTNESS
+# define MIN_BRIGHTNESS 0.1f
+#endif
+#if !defined(MAX_BRIGHTNESS) && !defined(NO_MAX_BRIGHTNESS)
+# define MAX_BRIGHTNESS 1.0f
+#endif
+#ifndef MIN_GAMMA
+# define MIN_GAMMA 0.1f
+#endif
+#if !defined(MAX_GAMMA) && !defined(NO_MAX_GAMMA)
+# define MAX_GAMMA 10.0f
+#endif
+
+/* Default values for parameters. */
+#define DEFAULT_DAY_TEMP 5500
+#define DEFAULT_NIGHT_TEMP 3500
+#define DEFAULT_BRIGHTNESS 1.0f
+#define DEFAULT_GAMMA 1.0f
+
+/* The color temperature when no adjustment is applied. */
+#define NEUTRAL_TEMP 6500
+
+
+
+#endif
diff --git a/src/fake-w32gdi.c b/src/fake-w32gdi.c
new file mode 100644
index 00000000..e0692fac
--- /dev/null
+++ b/src/fake-w32gdi.c
@@ -0,0 +1,275 @@
+/* fake-w32gdi.h -- Fake Windows library headers
+ This file is part of Redshift.
+
+ Redshift is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Redshift is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Redshift. If not, see .
+
+ Copyright (c) 2014 Mattias Andrée
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include
+#include
+#include
+
+#include "fake-w32gdi.h"
+
+
+#ifndef ENABLE_RANDR
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd144871(v=vs.85).aspx */
+HDC GetDC(HWND hWnd)
+{
+ (void) hWnd;
+ return (HDC*)16;
+}
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162920(v=vs.85).aspx */
+int ReleaseDC(HWND hWnd, HDC hDC)
+{
+ (void) hWnd;
+ (void) hDC;
+ return 1;
+}
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd144877(v=vs.85).aspx */
+int GetDeviceCaps(HDC hDC, int nIndex)
+{
+ (void) hDC;
+ return CM_GAMMA_RAMP + nIndex - COLORMGMTCAPS;
+}
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd372194(v=vs.85).aspx */
+BOOL SetDeviceGammaRamp(HDC hDC, LPVOID lpRamp)
+{
+ (void) hDC;
+ (void) lpRamp;
+ return TRUE;
+}
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd316946(v=vs.85).aspx */
+BOOL GetDeviceGammaRamp(HDC hDC, LPVOID lpRamp)
+{
+ (void) hDC;
+ (void) lpRamp;
+ return TRUE;
+}
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd183490(v=vs.85).aspx */
+HDC CreateDC(LPCTSTR lpszDriver, LPCTSTR lpszDevice, void *lpszOutput, void *lpInitData)
+{
+ (void) lpszOutput;
+ (void) lpInitData;
+ if (strcmp(lpszDriver, "DISPLAY"))
+ return NULL;
+ (void) lpszDevice;
+ return (HDC*)16;
+}
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162609(v=vs.85).aspx */
+BOOL EnumDisplayDevices(LPCTSTR lpDevice, DWORD iDevNum, PDISPLAY_DEVICE lpDisplayDevice, DWORD dwFlags)
+{
+ (void) dwFlags;
+ if (lpDevice != NULL) {
+ fprintf(stderr, "lpDevice (argument 1) for EnumDisplayDevices should be NULL\n");
+ abort();
+ return FALSE;
+ }
+ if (iDevNum >= 2)
+ return FALSE;
+ if (lpDisplayDevice->cb != sizeof(DISPLAY_DEVICE)) {
+ fprintf(stderr,
+ "lpDisplayDevice->cb for EnumDisplayDevices is not sizeof(DISPLAY_DEVICE)\n");
+ abort();
+ return FALSE;
+ }
+ strcmp(lpDisplayDevice->DeviceName, "some monitor");
+ lpDisplayDevice->StateFlags = DISPLAY_DEVICE_ACTIVE;
+ return TRUE;
+}
+
+#else
+
+
+#include
+#include
+
+
+#define GAMMA_RAMP_SIZE 256
+
+
+static xcb_connection_t *conn = NULL;
+static size_t dc_count = 0;
+static ssize_t crtc_count = -1;
+static xcb_randr_crtc_t *crtcs = NULL;
+static xcb_randr_get_screen_resources_current_reply_t *res_reply = NULL;
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd144871(v=vs.85).aspx */
+HDC GetDC(HWND hWnd)
+{
+ (void) hWnd;
+ return CreateDC(TEXT("DISPLAY"), "0", NULL, NULL);
+}
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162920(v=vs.85).aspx */
+int ReleaseDC(HWND hWnd, HDC hDC)
+{
+ (void) hWnd;
+ (void) hDC;
+ dc_count--;
+ if (dc_count == 0) {
+ if (conn != NULL)
+ xcb_disconnect(conn);
+ conn = NULL;
+ if (res_reply != NULL)
+ free(res_reply);
+ res_reply = NULL;
+ }
+ return 1;
+}
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd144877(v=vs.85).aspx */
+int GetDeviceCaps(HDC hDC, int nIndex)
+{
+ (void) hDC;
+ return CM_GAMMA_RAMP + nIndex - COLORMGMTCAPS;
+}
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd372194(v=vs.85).aspx */
+BOOL SetDeviceGammaRamp(HDC hDC, LPVOID lpRamp)
+{
+ xcb_void_cookie_t gamma_cookie =
+ xcb_randr_set_crtc_gamma_checked(
+ conn, *(xcb_randr_crtc_t *)hDC, GAMMA_RAMP_SIZE,
+ ((uint16_t *)lpRamp) + 0 * GAMMA_RAMP_SIZE,
+ ((uint16_t *)lpRamp) + 1 * GAMMA_RAMP_SIZE,
+ ((uint16_t *)lpRamp) + 2 * GAMMA_RAMP_SIZE);
+ xcb_generic_error_t *error = xcb_request_check(conn, gamma_cookie);
+ return error == NULL ? TRUE : FALSE;
+}
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd316946(v=vs.85).aspx */
+BOOL GetDeviceGammaRamp(HDC hDC, LPVOID lpRamp)
+{
+ xcb_randr_get_crtc_gamma_cookie_t gamma_cookie;
+ xcb_randr_get_crtc_gamma_reply_t *gamma_reply;
+ xcb_generic_error_t *error;
+
+ gamma_cookie = xcb_randr_get_crtc_gamma(conn, *(xcb_randr_crtc_t *)hDC);
+ gamma_reply = xcb_randr_get_crtc_gamma_reply(conn, gamma_cookie, &error);
+
+ if (error) return FALSE;
+
+#define DEST_RAMP(I) (((uint16_t *)lpRamp) + (I) * GAMMA_RAMP_SIZE)
+#define SRC_RAMP(C) (xcb_randr_get_crtc_gamma_##C(gamma_reply))
+
+ memcpy(DEST_RAMP(0), SRC_RAMP(red), GAMMA_RAMP_SIZE * sizeof(uint16_t));
+ memcpy(DEST_RAMP(1), SRC_RAMP(green), GAMMA_RAMP_SIZE * sizeof(uint16_t));
+ memcpy(DEST_RAMP(2), SRC_RAMP(blue), GAMMA_RAMP_SIZE * sizeof(uint16_t));
+
+#undef SRC_RAMP
+#undef DEST_RAMP
+
+ free(gamma_reply);
+ return TRUE;
+}
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd183490(v=vs.85).aspx */
+HDC CreateDC(LPCTSTR lpszDriver, LPCTSTR lpszDevice, void *lpszOutput, void *lpInitData)
+{
+ (void) lpszOutput;
+ (void) lpInitData;
+
+ if (strcmp(lpszDriver, "DISPLAY"))
+ return NULL;
+
+ int crtc_index = atoi(lpszDevice);
+
+ if (dc_count == 0) {
+ xcb_generic_error_t *error;
+ xcb_screen_iterator_t iter;
+ xcb_randr_get_screen_resources_current_cookie_t res_cookie;
+
+ conn = xcb_connect(NULL, NULL);
+
+ iter = xcb_setup_roots_iterator(xcb_get_setup(conn));
+ res_cookie = xcb_randr_get_screen_resources_current(conn, iter.data->root);
+ res_reply = xcb_randr_get_screen_resources_current_reply(conn, res_cookie, &error);
+
+ if (error) {
+ xcb_disconnect(conn);
+ crtc_count = -1;
+ return NULL;
+ }
+
+ crtc_count = res_reply->num_crtcs;
+ crtcs = xcb_randr_get_screen_resources_current_crtcs(res_reply);
+ }
+
+ if (crtc_index >= crtc_count) {
+ if (dc_count == 0) {
+ xcb_disconnect(conn);
+ crtc_count = -1;
+ }
+ return NULL;
+ }
+
+ dc_count++;
+ return crtcs + crtc_index;
+}
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162609(v=vs.85).aspx */
+BOOL EnumDisplayDevices(LPCTSTR lpDevice, DWORD iDevNum, PDISPLAY_DEVICE lpDisplayDevice, DWORD dwFlags)
+{
+ (void) dwFlags;
+ size_t count = (size_t)crtc_count;
+ if (lpDevice != NULL) {
+ fprintf(stderr, "lpDevice (argument 1) for EnumDisplayDevices should be NULL\n");
+ abort();
+ return FALSE;
+ }
+ if (crtc_count < 0) {
+ if (GetDC(NULL) == NULL)
+ return FALSE;
+ dc_count = 0;
+ count = (size_t)crtc_count;
+ ReleaseDC(NULL, NULL);
+ }
+ if (iDevNum >= count)
+ return FALSE;
+ if (lpDisplayDevice->cb != sizeof(DISPLAY_DEVICE)) {
+ fprintf(stderr,
+ "lpDisplayDevice->cb for EnumDisplayDevices is not sizeof(DISPLAY_DEVICE)\n");
+ abort();
+ return FALSE;
+ }
+ sprintf(lpDisplayDevice->DeviceName, "%i", iDevNum);
+ lpDisplayDevice->StateFlags = DISPLAY_DEVICE_ACTIVE;
+ return TRUE;
+}
+
+
+#endif
diff --git a/src/fake-w32gdi.h b/src/fake-w32gdi.h
new file mode 100644
index 00000000..b369e6ec
--- /dev/null
+++ b/src/fake-w32gdi.h
@@ -0,0 +1,77 @@
+/* fake-w32gdi.h -- Fake Windows library headers
+ This file is part of Redshift.
+
+ Redshift is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Redshift is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Redshift. If not, see .
+
+ Copyright (c) 2014 Mattias Andrée
+*/
+
+#ifndef REDSHIFT_FAKE_W32GDI_H
+#define REDSHIFT_FAKE_W32GDI_H
+
+#include
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx */
+typedef uint16_t WORD;
+typedef uint32_t DWORD;
+typedef int BOOL;
+typedef void *HDC;
+typedef void *HWND;
+typedef void *LPVOID;
+typedef const char *LPCTSTR;
+typedef char TCHAR;
+#define TRUE 1
+#define FALSE 0
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd144871(v=vs.85).aspx */
+HDC GetDC(HWND hWnd);
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162920(v=vs.85).aspx */
+int ReleaseDC(HWND hWnd, HDC hDC);
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd144877(v=vs.85).aspx */
+int GetDeviceCaps(HDC hDC, int nIndex) __attribute__((const));
+#define COLORMGMTCAPS 1
+#define CM_GAMMA_RAMP 1
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd372194(v=vs.85).aspx */
+BOOL SetDeviceGammaRamp(HDC hDC, LPVOID lpRamp);
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd316946(v=vs.85).aspx */
+BOOL GetDeviceGammaRamp(HDC hDC, LPVOID lpRamp);
+
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd183490(v=vs.85).aspx */
+HDC CreateDC(LPCTSTR lpszDriver, LPCTSTR lpszDevice, void *lpszOutput, void *lpInitData);
+#define TEXT(X) ((LPCTSTR)(X))
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd183569(v=vs.85).aspx */
+typedef struct {
+ DWORD cb;
+ TCHAR DeviceName[32];
+ DWORD StateFlags;
+} DISPLAY_DEVICE;
+typedef DISPLAY_DEVICE *PDISPLAY_DEVICE;
+#define DISPLAY_DEVICE_ACTIVE 1
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd162609(v=vs.85).aspx */
+BOOL EnumDisplayDevices(LPCTSTR lpDevice, DWORD iDevNum, PDISPLAY_DEVICE lpDisplayDevice, DWORD dwFlags);
+
+
+
+#endif /* ! REDSHIFT_FAKE_W32GDI_H */
+
diff --git a/src/gamma-w32gdi.c b/src/gamma-w32gdi.c
index 7193bc91..d882c17c 100644
--- a/src/gamma-w32gdi.c
+++ b/src/gamma-w32gdi.c
@@ -17,14 +17,22 @@
Copyright (c) 2010 Jon Lund Steffensen
*/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
#include
#include
#ifndef WINVER
# define WINVER 0x0500
#endif
-#include
-#include
+#ifdef FAKE_W32GDI
+# include "fake-w32gdi.h"
+#else
+# include
+# include
+#endif
#ifdef ENABLE_NLS
# include
diff --git a/src/gamma-w32gdi.h b/src/gamma-w32gdi.h
index e81f4c5e..4b085fae 100644
--- a/src/gamma-w32gdi.h
+++ b/src/gamma-w32gdi.h
@@ -20,8 +20,16 @@
#ifndef REDSHIFT_GAMMA_W32GDI_H
#define REDSHIFT_GAMMA_W32GDI_H
-#include
-#include
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef FAKE_W32GDI
+# include "fake-w32gdi.h"
+#else
+# include
+# include
+#endif
typedef struct {
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..fc34e8fb 100644
--- a/src/redshift.c
+++ b/src/redshift.c
@@ -21,6 +21,7 @@
# include "config.h"
#endif
+#include
#include
#include
#include
@@ -45,6 +46,8 @@
#include "config-ini.h"
#include "solar.h"
#include "systemtime.h"
+#include "adjustments.h"
+#include "opt-parser.h"
#define MIN(x,y) ((x) < (y) ? (x) : (y))
@@ -198,34 +201,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,
@@ -383,7 +358,7 @@ print_method_list()
fputs(_("Specify colon-separated options with"
" `-m METHOD:OPTIONS'.\n"), stdout);
/* TRANSLATORS: `help' must not be translated. */
- fputs(_("Try `-m METHOD:help' for help.\n"), stdout);
+ fputs(_("Try `-m METHOD help' for help.\n"), stdout);
}
static void
@@ -398,7 +373,7 @@ print_provider_list()
fputs(_("Specify colon-separated options with"
"`-l PROVIDER:OPTIONS'.\n"), stdout);
/* TRANSLATORS: `help' must not be translated. */
- fputs(_("Try `-l PROVIDER:help' for help.\n"), stdout);
+ fputs(_("Try `-l PROVIDER help' for help.\n"), stdout);
}
@@ -431,7 +406,7 @@ provider_try_start(const location_provider_t *provider,
provider->name);
/* TRANSLATORS: `help' must not be
translated. */
- fprintf(stderr, _("Try `-l %s:help' for more"
+ fprintf(stderr, _("Try `-l %s help' for more"
" information.\n"),
provider->name);
return -1;
@@ -443,43 +418,42 @@ provider_try_start(const location_provider_t *provider,
/* Set provider options from command line. */
const char *manual_keys[] = { "lat", "lon" };
int i = 0;
- while (args != NULL) {
- char *next_arg = strchr(args, ':');
- if (next_arg != NULL) *(next_arg++) = '\0';
-
- const char *key = args;
- char *value = strchr(args, '=');
- if (value == NULL) {
- /* The options for the "manual" method can be set
- without keys on the command line for convencience
- and for backwards compatability. We add the proper
- keys here before calling set_option(). */
- if (strcmp(provider->name, "manual") == 0 &&
- i < sizeof(manual_keys)/sizeof(manual_keys[0])) {
- key = manual_keys[i];
- value = args;
+ if (args != NULL) {
+ while (*args != '\0') {
+ const char *key = args;
+ char *value = strchr(args, '=');
+ if (value == NULL) {
+ /* The options for the "manual" method can be set
+ without keys on the command line for convencience
+ and for backwards compatability. We add the proper
+ keys here before calling set_option(). */
+ if (strcmp(provider->name, "manual") == 0 &&
+ i < sizeof(manual_keys)/sizeof(manual_keys[0])) {
+ key = manual_keys[i];
+ value = args;
+ } else {
+ fprintf(stderr, _("Failed to parse option `%s'.\n"),
+ args);
+ return -1;
+ }
} else {
- fprintf(stderr, _("Failed to parse option `%s'.\n"),
- args);
+ *(value++) = '\0';
+ }
+
+ r = provider->set_option(state, key, value);
+ if (r < 0) {
+ provider->free(state);
+ fprintf(stderr, _("Failed to set %s option.\n"),
+ provider->name);
+ /* TRANSLATORS: `help' must not be translated. */
+ fprintf(stderr, _("Try `-l %s help' for more"
+ " information.\n"), provider->name);
return -1;
}
- } else {
- *(value++) = '\0';
- }
- r = provider->set_option(state, key, value);
- if (r < 0) {
- provider->free(state);
- fprintf(stderr, _("Failed to set %s option.\n"),
- provider->name);
- /* TRANSLATORS: `help' must not be translated. */
- fprintf(stderr, _("Try `-l %s:help' for more"
- " information.\n"), provider->name);
- return -1;
+ args = value + strlen(value) + 1;
+ i += 1;
}
-
- args = next_arg;
- i += 1;
}
/* Start provider. */
@@ -523,7 +497,7 @@ method_try_start(const gamma_method_t *method,
method->name);
/* TRANSLATORS: `help' must not be
translated. */
- fprintf(stderr, _("Try `-m %s:help' for more"
+ fprintf(stderr, _("Try `-m %s help' for more"
" information.\n"),
method->name);
return -1;
@@ -533,32 +507,31 @@ method_try_start(const gamma_method_t *method,
}
/* Set method options from command line. */
- while (args != NULL) {
- char *next_arg = strchr(args, ':');
- if (next_arg != NULL) *(next_arg++) = '\0';
-
- const char *key = args;
- char *value = strchr(args, '=');
- if (value == NULL) {
- fprintf(stderr, _("Failed to parse option `%s'.\n"),
- args);
- return -1;
- } else {
- *(value++) = '\0';
- }
+ if (args != NULL) {
+ while (*args != '\0') {
+ const char *key = args;
+ char *value = strchr(args, '=');
+ if (value == NULL) {
+ fprintf(stderr, _("Failed to parse option `%s'.\n"),
+ args);
+ return -1;
+ } else {
+ *(value++) = '\0';
+ }
- r = method->set_option(state, key, value);
- if (r < 0) {
- method->free(state);
- fprintf(stderr, _("Failed to set %s option.\n"),
- method->name);
- /* TRANSLATORS: `help' must not be translated. */
- fprintf(stderr, _("Try -m %s:help' for more"
- " information.\n"), method->name);
- return -1;
- }
+ r = method->set_option(state, key, value);
+ if (r < 0) {
+ method->free(state);
+ fprintf(stderr, _("Failed to set %s option.\n"),
+ method->name);
+ /* TRANSLATORS: `help' must not be translated. */
+ fprintf(stderr, _("Try -m %s help' for more"
+ " information.\n"), method->name);
+ return -1;
+ }
- args = next_arg;
+ args = value + strlen(value) + 1;
+ }
}
/* Start method. */
@@ -690,7 +663,9 @@ main(int argc, char *argv[])
/* Parse command line arguments. */
int opt;
- while ((opt = getopt(argc, argv, "b:c:g:hl:m:oO:prt:vVx")) != -1) {
+ const char **args = alloca(argc * sizeof(char*));
+ int args_count;
+ while ((opt = parseopt(argc, argv, "b:c:g:hl:m:oO:prt:vVx", args, &args_count)) != -1) {
switch (opt) {
case 'b':
parse_brightness_string(optarg, &brightness_day, &brightness_night);
@@ -715,38 +690,47 @@ main(int argc, char *argv[])
break;
case 'l':
/* Print list of providers if argument is `list' */
- if (strcasecmp(optarg, "list") == 0) {
+ if (strcasecmp(*args, "list") == 0) {
print_provider_list();
exit(EXIT_SUCCESS);
}
- char *provider_name = NULL;
+ provider_args = coalesce_args(args, args_count, '\0', '\0');
+ if (provider_args == NULL) {
+ perror("coalesce_args");
+ exit(EXIT_FAILURE);
+ }
/* Don't save the result of strtof(); we simply want
to know if optarg can be parsed as a float. */
errno = 0;
char *end;
- strtof(optarg, &end);
- if (errno == 0 && *end == ':') {
- /* Use instead as arguments to `manual'. */
- provider_name = "manual";
- provider_args = optarg;
- } else {
- /* Split off provider arguments. */
- s = strchr(optarg, ':');
- if (s != NULL) {
- *(s++) = '\0';
- provider_args = s;
+ strtof(provider_args, &end);
+ if (errno == 0 && (end[0] == ':' || end[0] == '\0') && end[1] != '\0') {
+ /* Set delimiter to `\0'. */
+ end[0] = '\0';
+ /* Set provider method to `manual'. */
+ char *old_buf = provider_args;
+ size_t n = 0;
+ while (provider_args[n] != '\0' || provider_args[n + 1] != '\0')
+ n++;
+ n += 2;
+ provider_args = realloc(old_buf, (strlen("manual") + 1 + n) * sizeof(char));
+ if (provider_args == NULL) {
+ perror("realloc");
+ free(old_buf);
+ exit(EXIT_FAILURE);
}
-
- provider_name = optarg;
+ memmove(provider_args + strlen("manual") + 1, provider_args, n * sizeof(char));
+ strcpy(provider_args, "manual");
}
/* Lookup provider from name. */
- provider = find_location_provider(provider_name);
+ provider = find_location_provider(provider_args);
if (provider == NULL) {
fprintf(stderr, _("Unknown location provider"
- " `%s'.\n"), provider_name);
+ " `%s'.\n"), provider_args);
+ free(provider_args);
exit(EXIT_FAILURE);
}
@@ -754,6 +738,7 @@ main(int argc, char *argv[])
if (provider_args != NULL &&
strcasecmp(provider_args, "help") == 0) {
provider->print_help(stdout);
+ free(provider_args);
exit(EXIT_SUCCESS);
}
break;
@@ -764,20 +749,20 @@ main(int argc, char *argv[])
exit(EXIT_SUCCESS);
}
- /* Split off method arguments. */
- s = strchr(optarg, ':');
- if (s != NULL) {
- *(s++) = '\0';
- method_args = s;
+ method_args = coalesce_args(args, args_count, '\0', '\0');
+ if (method_args == NULL) {
+ perror("coalesce_args");
+ exit(EXIT_FAILURE);
}
/* Find adjustment method by name. */
- method = find_gamma_method(optarg);
+ method = find_gamma_method(method_args);
if (method == NULL) {
/* TRANSLATORS: This refers to the method
used to adjust colors e.g VidMode */
fprintf(stderr, _("Unknown adjustment method"
" `%s'.\n"), optarg);
+ free(method_args);
exit(EXIT_FAILURE);
}
@@ -785,6 +770,7 @@ main(int argc, char *argv[])
if (method_args != NULL &&
strcasecmp(method_args, "help") == 0) {
method->print_help(stdout);
+ free(method_args);
exit(EXIT_SUCCESS);
}
break;
@@ -954,8 +940,9 @@ main(int argc, char *argv[])
mode != PROGRAM_MODE_MANUAL) {
if (provider != NULL) {
/* Use provider specified on command line. */
- r = provider_try_start(provider, &location_state,
- &config_state, provider_args);
+ r = provider_try_start(provider, &location_state, &config_state,
+ provider_args == NULL ? NULL :
+ provider_args + strlen(provider_args) + 1);
if (r < 0) exit(EXIT_FAILURE);
} else {
/* Try all providers, use the first that works. */
@@ -1087,11 +1074,6 @@ main(int argc, char *argv[])
exit(EXIT_FAILURE);
}
- if (verbose) {
- printf(_("Gamma: %.3f, %.3f, %.3f\n"),
- gamma[0], gamma[1], gamma[2]);
- }
-
/* Initialize gamma adjustment method. If method is NULL
try all methods until one that works is found. */
gamma_state_t state;
@@ -1101,7 +1083,8 @@ main(int argc, char *argv[])
if (method != NULL) {
/* Use method specified on command line. */
r = method_try_start(method, &state, &config_state,
- method_args);
+ method_args == NULL ? NULL :
+ method_args + strlen(method_args) + 1);
if (r < 0) exit(EXIT_FAILURE);
} else {
/* Try all methods, use the first that works. */
@@ -1374,5 +1357,11 @@ main(int argc, char *argv[])
/* Clean up gamma adjustment state */
method->free(&state);
+ /* Free memory */
+ if (method_args != NULL)
+ free(method_args);
+ if (provider_args != NULL)
+ free(provider_args);
+
return EXIT_SUCCESS;
}
diff --git a/src/redshift.h b/src/redshift.h
index 3a878777..c7b0d96b 100644
--- a/src/redshift.h
+++ b/src/redshift.h
@@ -24,6 +24,24 @@
#include
+/* Bounds for parameters. */
+#define MIN_LAT -90.0
+#define MAX_LAT 90.0
+#define MIN_LON -180.0
+#define MAX_LON 180.0
+
+/* Angular elevation of the sun at which the color temperature
+ transition period starts and ends (in degress).
+ Transition during twilight, and while the sun is lower than
+ 3.0 degrees above the horizon. */
+#ifndef TRANSITION_LOW
+# define TRANSITION_LOW SOLAR_CIVIL_TWILIGHT_ELEV
+#endif
+#ifndef TRANSITION_HIGH
+# define TRANSITION_HIGH 3.0
+#endif
+
+
/* Gamma adjustment method */
typedef int gamma_method_init_func(void *state);
typedef int gamma_method_start_func(void *state);