diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 17 | ||||
-rw-r--r-- | src/colorramp.c | 10 | ||||
-rw-r--r-- | src/colorramp.h | 2 | ||||
-rw-r--r-- | src/config-ini.c | 243 | ||||
-rw-r--r-- | src/config-ini.h | 49 | ||||
-rw-r--r-- | src/gamma-randr.c | 19 | ||||
-rw-r--r-- | src/gamma-randr.h | 3 | ||||
-rw-r--r-- | src/gamma-vidmode.c | 10 | ||||
-rw-r--r-- | src/gamma-vidmode.h | 3 | ||||
-rw-r--r-- | src/gamma-w32gdi.c | 53 | ||||
-rw-r--r-- | src/gamma-w32gdi.h | 4 | ||||
-rw-r--r-- | src/gtk-redshift/Makefile.am | 29 | ||||
-rw-r--r-- | src/gtk-redshift/__init__.py | 1 | ||||
-rw-r--r-- | src/gtk-redshift/defs.py.in | 1 | ||||
-rw-r--r-- | src/gtk-redshift/gtk-redshift (renamed from src/gtk-redshift/gtk-redshift.in) | 2 | ||||
-rw-r--r-- | src/gtk-redshift/rsappindicator.py | 99 | ||||
-rw-r--r-- | src/gtk-redshift/statusicon.py | 78 | ||||
-rw-r--r-- | src/gtk-redshift/utils.py | 66 | ||||
-rw-r--r-- | src/location-geoclue.c | 186 | ||||
-rw-r--r-- | src/location-geoclue.h | 44 | ||||
-rw-r--r-- | src/location-gnome-clock.c | 135 | ||||
-rw-r--r-- | src/redshift.c | 477 | ||||
-rw-r--r-- | src/redshift.h | 1 |
23 files changed, 1198 insertions, 334 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index c07aea3..6c4a613 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,17 +9,19 @@ INCLUDES = -DLOCALEDIR=\"$(localedir)\" bin_PROGRAMS = redshift redshift_SOURCES = \ - redshift.c \ + redshift.c redshift.h \ colorramp.c colorramp.h \ + config-ini.c config-ini.h \ + location-manual.c location-manual.h \ solar.c solar.h \ - systemtime.c systemtime.h \ - location-manual.c location-manual.h + systemtime.c systemtime.h EXTRA_redshift_SOURCES = \ gamma-randr.c gamma-randr.h \ gamma-vidmode.c gamma-vidmode.h \ gamma-w32gdi.c gamma-w32gdi.h \ - location-gnome-clock.c location-gnome-clock.h + location-gnome-clock.c location-gnome-clock.h \ + location-geoclue.c location-geoclue.h AM_CFLAGS = redshift_LDADD = @LIBINTL@ @@ -53,3 +55,10 @@ redshift_LDADD += \ $(GLIB_LIBS) $(GLIB_CFLAGS) \ $(GCONF_LIBS) $(GCONF_CFLAGS) endif + +if ENABLE_GEOCLUE +redshift_SOURCES += location-geoclue.c location-geoclue.h +AM_CFLAGS += $(GEOCLUE_CFLAGS) $(GEOCLUE_LIBS) +redshift_LDADD += \ + $(GEOCLUE_LIBS) $(GEOCLUE_CFLAGS) +endif diff --git a/src/colorramp.c b/src/colorramp.c index a154a9e..ed399f8 100644 --- a/src/colorramp.c +++ b/src/colorramp.c @@ -128,9 +128,9 @@ 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 gamma[3]) + int size, int temp, float brightness, float gamma[3]) { - /* Calculate white point */ + /* Approximate white point */ float white_point[3]; float alpha = (temp % 100) / 100.0; int temp_index = ((temp - 1000) / 100)*3; @@ -139,10 +139,10 @@ colorramp_fill(uint16_t *gamma_r, uint16_t *gamma_g, uint16_t *gamma_b, for (int i = 0; i < size; i++) { gamma_r[i] = pow((float)i/size, 1.0/gamma[0]) * - UINT16_MAX * white_point[0]; + UINT16_MAX * brightness * white_point[0]; gamma_g[i] = pow((float)i/size, 1.0/gamma[1]) * - UINT16_MAX * white_point[1]; + UINT16_MAX * brightness * white_point[1]; gamma_b[i] = pow((float)i/size, 1.0/gamma[2]) * - UINT16_MAX * white_point[2]; + UINT16_MAX * brightness * white_point[2]; } } diff --git a/src/colorramp.h b/src/colorramp.h index 88aa984..326969e 100644 --- a/src/colorramp.h +++ b/src/colorramp.h @@ -23,6 +23,6 @@ #include <stdint.h> void colorramp_fill(uint16_t *gamma_r, uint16_t *gamma_g, uint16_t *gamma_b, - int size, int temp, float gamma[3]); + int size, int temp, float brightness, float gamma[3]); #endif /* ! _REDSHIFT_COLORRAMP_H */ diff --git a/src/config-ini.c b/src/config-ini.c new file mode 100644 index 0000000..eae19c7 --- /dev/null +++ b/src/config-ini.c @@ -0,0 +1,243 @@ +/* config-ini.c -- INI config file parser + 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> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "config-ini.h" + +#ifdef ENABLE_NLS +# include <libintl.h> +# define _(s) gettext(s) +#else +# define _(s) s +#endif + +#define MAX_CONFIG_PATH 4096 +#define MAX_LINE_LENGTH 512 + + +static FILE * +open_config_file(const char *filepath) +{ + FILE *f = NULL; + + if (filepath == NULL) { + char cp[MAX_CONFIG_PATH]; + char *env; + + if ((env = getenv("XDG_CONFIG_HOME")) != NULL && + env[0] != '\0') { + snprintf(cp, sizeof(cp), "%s/redshift.conf", env); + filepath = cp; +#ifdef _WIN32 + } else if ((env = getenv("userprofile")) != NULL && env[0] != '\0') { + snprintf(cp, sizeof(cp), + "%s/.config/redshift.conf", env); + filepath = cp; +#endif + } else if ((env = getenv("HOME")) != NULL && env[0] != '\0') { + snprintf(cp, sizeof(cp), + "%s/.config/redshift.conf", env); + filepath = cp; + } + + if (filepath != NULL) { + f = fopen(filepath, "r"); + if (f != NULL) return f; + else if (f == NULL && errno != ENOENT) return NULL; + } + + /* TODO look in getenv("XDG_CONFIG_DIRS") */ + } else { + f = fopen(filepath, "r"); + if (f == NULL) { + perror("fopen"); + return NULL; + } + } + + return f; +} + +int +config_ini_init(config_ini_state_t *state, const char *filepath) +{ + config_ini_section_t *section = NULL; + state->sections = NULL; + + FILE *f = open_config_file(filepath); + if (f == NULL) { + /* Only a serious error if a file was explicitly requested. */ + if (filepath != NULL) return -1; + return 0; + } + + char line[MAX_LINE_LENGTH]; + char *s; + + while (1) { + /* Handle the file input linewise. */ + char *r = fgets(line, sizeof(line), f); + if (r == NULL) break; + + /* Strip leading blanks and trailing newline. */ + s = line + strspn(line, " \t"); + s[strcspn(s, "\r\n")] = '\0'; + + /* Skip comments and empty lines. */ + if (s[0] == ';' || s[0] == '\0') continue; + + if (s[0] == '[') { + /* Read name of section. */ + const char *name = s+1; + char *end = strchr(s, ']'); + if (end == NULL || end[1] != '\0' || end == name) { + fputs(_("Malformed section header in config" + " file.\n"), stderr); + fclose(f); + config_ini_free(state); + return -1; + } + + *end = '\0'; + + /* Create section. */ + section = malloc(sizeof(config_ini_section_t)); + if (section == NULL) { + fclose(f); + config_ini_free(state); + return -1; + } + + /* Insert into section list. */ + section->name = NULL; + section->settings = NULL; + section->next = state->sections; + state->sections = section; + + /* Copy section name. */ + section->name = malloc(end - name + 1); + if (section->name == NULL) { + fclose(f); + config_ini_free(state); + return -1; + } + + memcpy(section->name, name, end - name + 1); + } else { + /* Split assignment at equals character. */ + char *end = strchr(s, '='); + if (end == NULL || end == s) { + fputs(_("Malformed assignment in config" + " file.\n"), stderr); + fclose(f); + config_ini_free(state); + return -1; + } + + *end = '\0'; + char *value = end + 1; + + if (section == NULL) { + fputs(_("Assignment outside section in config" + " file.\n"), stderr); + fclose(f); + config_ini_free(state); + return -1; + } + + /* Create section. */ + config_ini_setting_t *setting = + malloc(sizeof(config_ini_setting_t)); + if (setting == NULL) { + fclose(f); + config_ini_free(state); + return -1; + } + + /* Insert into section list. */ + setting->name = NULL; + setting->value = NULL; + setting->next = section->settings; + section->settings = setting; + + /* Copy name of setting. */ + setting->name = malloc(end - s + 1); + if (setting->name == NULL) { + fclose(f); + config_ini_free(state); + return -1; + } + + memcpy(setting->name, s, end - s + 1); + + /* Copy setting value. */ + size_t value_len = strlen(value) + 1; + setting->value = malloc(value_len); + if (setting->value == NULL) { + fclose(f); + config_ini_free(state); + return -1; + } + + memcpy(setting->value, value, value_len); + } + } + + fclose(f); + + return 0; +} + +void +config_ini_free(config_ini_state_t *state) +{ + config_ini_section_t *section = state->sections; + + while (section != NULL) { + config_ini_setting_t *setting = section->settings; + + while (setting != NULL) { + free(setting->name); + free(setting->value); + setting = setting->next; + } + + free(section->name); + section = section->next; + } +} + +config_ini_section_t * +config_ini_get_section(config_ini_state_t *state, const char *name) +{ + config_ini_section_t *section = state->sections; + while (section != NULL) { + if (strcasecmp(section->name, name) == 0) { + return section; + } + section = section->next; + } + + return NULL; +} diff --git a/src/config-ini.h b/src/config-ini.h new file mode 100644 index 0000000..e1bff73 --- /dev/null +++ b/src/config-ini.h @@ -0,0 +1,49 @@ +/* config-ini.h -- INI config file parser 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_CONFIG_INI_H +#define _REDSHIFT_CONFIG_INI_H + +typedef struct _config_ini_section config_ini_section_t; +typedef struct _config_ini_setting config_ini_setting_t; + +struct _config_ini_setting { + config_ini_setting_t *next; + char *name; + char *value; +}; + +struct _config_ini_section { + config_ini_section_t *next; + char *name; + config_ini_setting_t *settings; +}; + +typedef struct { + config_ini_section_t *sections; +} config_ini_state_t; + + +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); + +#endif /* ! _REDSHIFT_CONFIG_INI_H */ diff --git a/src/gamma-randr.c b/src/gamma-randr.c index 66d5c48..8781e92 100644 --- a/src/gamma-randr.c +++ b/src/gamma-randr.c @@ -62,9 +62,12 @@ randr_init(randr_state_t *state) xcb_randr_query_version_reply_t *ver_reply = xcb_randr_query_version_reply(state->conn, ver_cookie, &error); - if (error) { + /* TODO What does it mean when both error and ver_reply is NULL? + Apparently, we have to check both to avoid seg faults. */ + if (error || ver_reply == NULL) { + int ec = (error != 0) ? error->error_code : -1; fprintf(stderr, _("`%s' returned error %d\n"), - "RANDR Query Version", error->error_code); + "RANDR Query Version", ec); xcb_disconnect(state->conn); return -1; } @@ -298,7 +301,7 @@ randr_set_option(randr_state_t *state, const char *key, const char *value) static int randr_set_temperature_for_crtc(randr_state_t *state, int crtc_num, int temp, - float gamma[3]) + float brightness, float gamma[3]) { xcb_generic_error_t *error; @@ -330,7 +333,7 @@ randr_set_temperature_for_crtc(randr_state_t *state, int crtc_num, int temp, uint16_t *gamma_b = &gamma_ramps[2*ramp_size]; colorramp_fill(gamma_r, gamma_g, gamma_b, ramp_size, - temp, gamma); + temp, brightness, gamma); /* Set new gamma ramps */ xcb_void_cookie_t gamma_set_cookie = @@ -352,7 +355,8 @@ randr_set_temperature_for_crtc(randr_state_t *state, int crtc_num, int temp, } int -randr_set_temperature(randr_state_t *state, int temp, float gamma[3]) +randr_set_temperature(randr_state_t *state, int temp, float brightness, + float gamma[3]) { int r; @@ -361,12 +365,13 @@ randr_set_temperature(randr_state_t *state, int temp, float gamma[3]) if (state->crtc_num < 0) { for (int i = 0; i < state->crtc_count; i++) { r = randr_set_temperature_for_crtc(state, i, - temp, gamma); + temp, brightness, + gamma); if (r < 0) return -1; } } else { return randr_set_temperature_for_crtc(state, state->crtc_num, - temp, gamma); + temp, brightness, gamma); } return 0; diff --git a/src/gamma-randr.h b/src/gamma-randr.h index 4ccad8f..11818fa 100644 --- a/src/gamma-randr.h +++ b/src/gamma-randr.h @@ -54,7 +54,8 @@ 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 gamma[3]); +int randr_set_temperature(randr_state_t *state, int temp, float brightness, + float gamma[3]); #endif /* ! _REDSHIFT_GAMMA_RANDR_H */ diff --git a/src/gamma-vidmode.c b/src/gamma-vidmode.c index e6b9412..a083658 100644 --- a/src/gamma-vidmode.c +++ b/src/gamma-vidmode.c @@ -68,7 +68,6 @@ vidmode_start(vidmode_state_t *state) if (!r) { fprintf(stderr, _("X request failed: %s\n"), "XF86VidModeQueryVersion"); - XCloseDisplay(state->display); return -1; } @@ -78,14 +77,12 @@ vidmode_start(vidmode_state_t *state) if (!r) { fprintf(stderr, _("X request failed: %s\n"), "XF86VidModeGetGammaRampSize"); - XCloseDisplay(state->display); return -1; } if (state->ramp_size == 0) { fprintf(stderr, _("Gamma ramp size too small: %i\n"), state->ramp_size); - XCloseDisplay(state->display); return -1; } @@ -93,7 +90,6 @@ vidmode_start(vidmode_state_t *state) state->saved_ramps = malloc(3*state->ramp_size*sizeof(uint16_t)); if (state->saved_ramps == NULL) { perror("malloc"); - XCloseDisplay(state->display); return -1; } @@ -108,7 +104,6 @@ vidmode_start(vidmode_state_t *state) if (!r) { fprintf(stderr, _("X request failed: %s\n"), "XF86VidModeGetGammaRamp"); - XCloseDisplay(state->display); return -1; } @@ -174,7 +169,8 @@ vidmode_restore(vidmode_state_t *state) } int -vidmode_set_temperature(vidmode_state_t *state, int temp, float gamma[3]) +vidmode_set_temperature(vidmode_state_t *state, int temp, float brightness, + float gamma[3]) { int r; @@ -190,7 +186,7 @@ vidmode_set_temperature(vidmode_state_t *state, int temp, float gamma[3]) uint16_t *gamma_b = &gamma_ramps[2*state->ramp_size]; colorramp_fill(gamma_r, gamma_g, gamma_b, state->ramp_size, - temp, gamma); + temp, brightness, gamma); /* Set new gamma ramps */ r = XF86VidModeSetGammaRamp(state->display, state->screen_num, diff --git a/src/gamma-vidmode.h b/src/gamma-vidmode.h index 18a4a88..735ba1f 100644 --- a/src/gamma-vidmode.h +++ b/src/gamma-vidmode.h @@ -42,7 +42,8 @@ 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 gamma[3]); +int vidmode_set_temperature(vidmode_state_t *state, int temp, float brightness, + float gamma[3]); #endif /* ! _REDSHIFT_GAMMA_VIDMODE_H */ diff --git a/src/gamma-w32gdi.c b/src/gamma-w32gdi.c index aa2474d..d23bf72 100644 --- a/src/gamma-w32gdi.c +++ b/src/gamma-w32gdi.c @@ -43,7 +43,6 @@ int w32gdi_init(w32gdi_state_t *state) { state->saved_ramps = NULL; - state->hDC = NULL; return 0; } @@ -54,14 +53,14 @@ w32gdi_start(w32gdi_state_t *state) BOOL r; /* Open device context */ - state->hDC = GetDC(NULL); - if (state->hDC == NULL) { + HDC hDC = GetDC(NULL); + if (hDC == NULL) { fputs(_("Unable to open device context.\n"), stderr); return -1; } /* Check support for gamma ramps */ - int cmcap = GetDeviceCaps(state->hDC, COLORMGMTCAPS); + int cmcap = GetDeviceCaps(hDC, COLORMGMTCAPS); if (cmcap != CM_GAMMA_RAMP) { fputs(_("Display device does not support gamma ramps.\n"), stderr); @@ -72,18 +71,21 @@ w32gdi_start(w32gdi_state_t *state) state->saved_ramps = malloc(3*GAMMA_RAMP_SIZE*sizeof(WORD)); if (state->saved_ramps == NULL) { perror("malloc"); - ReleaseDC(NULL, state->hDC); + ReleaseDC(NULL, hDC); return -1; } /* Save current gamma ramps so we can restore them at program exit */ - r = GetDeviceGammaRamp(state->hDC, state->saved_ramps); + r = GetDeviceGammaRamp(hDC, state->saved_ramps); if (!r) { fputs(_("Unable to save current gamma ramp.\n"), stderr); - ReleaseDC(NULL, state->hDC); + ReleaseDC(NULL, hDC); return -1; } + /* Release device context */ + ReleaseDC(NULL, hDC); + return 0; } @@ -92,9 +94,6 @@ w32gdi_free(w32gdi_state_t *state) { /* Free saved ramps */ free(state->saved_ramps); - - /* Release device context */ - if (state->hDC != NULL) ReleaseDC(NULL, state->hDC); } @@ -114,20 +113,39 @@ w32gdi_set_option(w32gdi_state_t *state, const char *key, const char *value) void w32gdi_restore(w32gdi_state_t *state) { + /* Open device context */ + HDC hDC = GetDC(NULL); + if (hDC == NULL) { + fputs(_("Unable to open device context.\n"), stderr); + return; + } + /* Restore gamma ramps */ - BOOL r = SetDeviceGammaRamp(state->hDC, state->saved_ramps); + BOOL r = SetDeviceGammaRamp(hDC, state->saved_ramps); if (!r) fputs(_("Unable to restore gamma ramps.\n"), stderr); + + /* Release device context */ + ReleaseDC(NULL, hDC); } int -w32gdi_set_temperature(w32gdi_state_t *state, int temp, float gamma[3]) +w32gdi_set_temperature(w32gdi_state_t *state, int temp, float brightness, + float gamma[3]) { 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; } @@ -136,17 +154,24 @@ w32gdi_set_temperature(w32gdi_state_t *state, int temp, float gamma[3]) WORD *gamma_b = &gamma_ramps[2*GAMMA_RAMP_SIZE]; colorramp_fill(gamma_r, gamma_g, gamma_b, GAMMA_RAMP_SIZE, - temp, gamma); + temp, brightness, gamma); /* Set new gamma ramps */ - r = SetDeviceGammaRamp(state->hDC, 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); + /* Release device context */ + ReleaseDC(NULL, hDC); + return 0; } diff --git a/src/gamma-w32gdi.h b/src/gamma-w32gdi.h index 6cb9799..57a604f 100644 --- a/src/gamma-w32gdi.h +++ b/src/gamma-w32gdi.h @@ -25,7 +25,6 @@ typedef struct { - HDC hDC; WORD *saved_ramps; } w32gdi_state_t; @@ -39,7 +38,8 @@ 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 gamma[3]); +int w32gdi_set_temperature(w32gdi_state_t *state, int temp, float brightness, + float gamma[3]); #endif /* ! _REDSHIFT_GAMMA_W32GDI_H */ diff --git a/src/gtk-redshift/Makefile.am b/src/gtk-redshift/Makefile.am index ddeafd6..bb69459 100644 --- a/src/gtk-redshift/Makefile.am +++ b/src/gtk-redshift/Makefile.am @@ -1,38 +1,19 @@ -if ENABLE_STATUSICON -gui_module=statusicon +if ENABLE_GUI gtk_redshift_PYTHON = \ __init__.py \ + utils.py \ statusicon.py nodist_gtk_redshift_PYTHON = \ defs.py gtk_redshiftdir = $(pythondir)/gtk_redshift -bin_SCRIPTS = gtk-redshift +dist_bin_SCRIPTS = gtk-redshift endif -if ENABLE_APPINDICATOR -gui_module=rsappindicator -gtk_redshift_PYTHON = \ - __init__.py \ - rsappindicator.py -nodist_gtk_redshift_PYTHON = \ - defs.py -gtk_redshiftdir = $(pythondir)/gtk_redshift - -bin_SCRIPTS = gtk-redshift -endif - -EXTRA_DIST = gtk-redshift.in \ - defs.py.in - -CLEANFILES = defs.py \ - gtk-redshift - +EXTRA_DIST = defs.py.in +CLEANFILES = defs.py -# Main GUI script -gtk-redshift: gtk-redshift.in - sed -e "s|\@gui_module\@|$(gui_module)|g" $< > $@ # Local python definitions defs.py: defs.py.in diff --git a/src/gtk-redshift/__init__.py b/src/gtk-redshift/__init__.py index 51ab2ef..0e4f254 100644 --- a/src/gtk-redshift/__init__.py +++ b/src/gtk-redshift/__init__.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # __init__.py -- gtk-redshift package __init__ file # This file is part of Redshift. diff --git a/src/gtk-redshift/defs.py.in b/src/gtk-redshift/defs.py.in index d3ca5ed..026fefd 100644 --- a/src/gtk-redshift/defs.py.in +++ b/src/gtk-redshift/defs.py.in @@ -1,4 +1,3 @@ -#!/usr/bin/env python # defs.py -- GTK+ redshift local definitions # This file is part of Redshift. diff --git a/src/gtk-redshift/gtk-redshift.in b/src/gtk-redshift/gtk-redshift index 120e05c..56d940e 100644 --- a/src/gtk-redshift/gtk-redshift.in +++ b/src/gtk-redshift/gtk-redshift @@ -19,5 +19,5 @@ if __name__ == '__main__': - from gtk_redshift.@gui_module@ import run + from gtk_redshift.statusicon import run run() diff --git a/src/gtk-redshift/rsappindicator.py b/src/gtk-redshift/rsappindicator.py deleted file mode 100644 index 59fa725..0000000 --- a/src/gtk-redshift/rsappindicator.py +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/env python -# rsappindicator.py -- Application Panel Indicator 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> - - -import sys, os -import subprocess, signal -import gettext - -import pygtk -pygtk.require("2.0") - -import gtk, glib -try: - import appindicator -except ImportError as ie: - # No module named appindicator - sys.exit(str(ie)) - -import defs - - -def run(): - # Internationalisation - gettext.bindtextdomain('redshift', defs.LOCALEDIR) - gettext.textdomain('redshift') - _ = gettext.gettext - - # Start redshift with arguments from the command line - args = sys.argv[1:] - args.insert(0, os.path.join(defs.BINDIR, 'redshift')) - process = subprocess.Popen(args) - - try: - # Create status icon - indicator = appindicator.Indicator ("redshift", - "redshift", - appindicator.CATEGORY_APPLICATION_STATUS) - indicator.set_status (appindicator.STATUS_ACTIVE) - - def toggle_cb(widget, data=None): - if indicator.get_icon() == 'redshift': - indicator.set_icon('redshift-idle') - else: - indicator.set_icon('redshift') - process.send_signal(signal.SIGUSR1) - - def destroy_cb(widget, data=None): - gtk.main_quit() - return False - - # Create popup menu - status_menu = gtk.Menu() - - toggle_item = gtk.ImageMenuItem(_('Toggle')) - toggle_item.connect('activate', toggle_cb) - status_menu.append(toggle_item) - - quit_item = gtk.ImageMenuItem(gtk.STOCK_QUIT) - quit_item.connect('activate', destroy_cb) - status_menu.append(quit_item) - - status_menu.show_all() - - # Set the menu - indicator.set_menu(status_menu) - - def child_cb(pid, cond, data=None): - sys.exit(-1) - - # Add watch on child process - glib.child_watch_add(process.pid, child_cb) - - # Run main loop - gtk.main() - - except KeyboardInterrupt: - # Ignore user interruption - pass - - finally: - # Always terminate redshift - process.terminate() - process.wait() diff --git a/src/gtk-redshift/statusicon.py b/src/gtk-redshift/statusicon.py index 2295963..9b80a6b 100644 --- a/src/gtk-redshift/statusicon.py +++ b/src/gtk-redshift/statusicon.py @@ -1,5 +1,4 @@ -#!/usr/bin/env python -# statusicon.py -- GTK+ status icon source +# statusicon.py -- GUI status icon source # This file is part of Redshift. # Redshift is free software: you can redistribute it and/or modify @@ -18,6 +17,12 @@ # Copyright (c) 2010 Jon Lund Steffensen <jonlst@gmail.com> +'''GUI status icon for Redshift. + +The run method will try to start an appindicator for Redshift. If the +appindicator module isn't present it will fall back to a GTK status icon. +''' + import sys, os import subprocess, signal import gettext @@ -26,8 +31,13 @@ import pygtk pygtk.require("2.0") import gtk, glib +try: + import appindicator +except ImportError: + appindicator = None import defs +import utils def run(): @@ -42,39 +52,77 @@ def run(): process = subprocess.Popen(args) try: - # Create status icon - status_icon = gtk.StatusIcon() - status_icon.set_from_icon_name('redshift') - status_icon.set_tooltip('Redshift') + if appindicator: + # Create indicator + indicator = appindicator.Indicator('redshift', + 'redshift-status-on', + appindicator.CATEGORY_APPLICATION_STATUS) + indicator.set_status(appindicator.STATUS_ACTIVE) + else: + # Create status icon + status_icon = gtk.StatusIcon() + status_icon.set_from_icon_name('redshift-status-on') + status_icon.set_tooltip('Redshift') def toggle_cb(widget, data=None): process.send_signal(signal.SIGUSR1) + if appindicator: + if indicator.get_icon() == 'redshift-status-on': + indicator.set_icon('redshift-status-off') + else: + indicator.set_icon('redshift-status-on') + else: + if status_icon.get_icon_name() == 'redshift-status-on': + status_icon.set_from_icon_name('redshift-status-off') + else: + status_icon.set_from_icon_name('redshift-status-on') + + def autostart_cb(widget, data=None): + utils.set_autostart(widget.get_active()) def destroy_cb(widget, data=None): - status_icon.set_visible(False) + if not appindicator: + status_icon.set_visible(False) gtk.main_quit() return False # Create popup menu status_menu = gtk.Menu() - toggle_item = gtk.ImageMenuItem(_('Toggle')) + toggle_item = gtk.MenuItem(_('Toggle')) toggle_item.connect('activate', toggle_cb) status_menu.append(toggle_item) + autostart_item = gtk.CheckMenuItem(_('Autostart')) + try: + autostart_item.set_active(utils.get_autostart()) + except IOError as strerror: + print strerror + autostart_item.set_property('sensitive', False) + else: + autostart_item.connect('activate', autostart_cb) + finally: + status_menu.append(autostart_item) + quit_item = gtk.ImageMenuItem(gtk.STOCK_QUIT) quit_item.connect('activate', destroy_cb) status_menu.append(quit_item) - def popup_menu_cb(widget, button, time, data=None): + if appindicator: status_menu.show_all() - status_menu.popup(None, None, gtk.status_icon_position_menu, - button, time, status_icon) - # Connect signals for status icon and show - status_icon.connect('activate', toggle_cb) - status_icon.connect('popup-menu', popup_menu_cb) - status_icon.set_visible(True) + # Set the menu + indicator.set_menu(status_menu) + else: + def popup_menu_cb(widget, button, time, data=None): + status_menu.show_all() + status_menu.popup(None, None, gtk.status_icon_position_menu, + button, time, status_icon) + + # Connect signals for status icon and show + status_icon.connect('activate', toggle_cb) + status_icon.connect('popup-menu', popup_menu_cb) + status_icon.set_visible(True) def child_cb(pid, cond, data=None): sys.exit(-1) diff --git a/src/gtk-redshift/utils.py b/src/gtk-redshift/utils.py new file mode 100644 index 0000000..93e0195 --- /dev/null +++ b/src/gtk-redshift/utils.py @@ -0,0 +1,66 @@ +# utils.py -- utility functions 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 Francesco Marella <francesco.marella@gmail.com> + +import os +from xdg import BaseDirectory as base +from xdg import DesktopEntry as desktop + +REDSHIFT_DESKTOP = 'gtk-redshift.desktop' + + +def get_autostart(): + AUTOSTART_KEY = "X-GNOME-Autostart-enabled" + autostart_dir = base.save_config_path("autostart") + autostart_file = os.path.join(autostart_dir, REDSHIFT_DESKTOP) + if not os.path.exists(autostart_file): + desktop_files = list(base.load_data_paths("applications", + REDSHIFT_DESKTOP)) + if not desktop_files: + raise IOError("Installed redshift desktop file not found!") + desktop_file_path = desktop_files[0] + # Read installed file and modify it + dfile = desktop.DesktopEntry(desktop_file_path) + dfile.set(AUTOSTART_KEY, "false") + dfile.write(filename=autostart_file) + return False + else: + dfile = desktop.DesktopEntry(autostart_file) + if dfile.get(AUTOSTART_KEY) == 'false': + return False + else: + return True + +def set_autostart(active): + AUTOSTART_KEY = "X-GNOME-Autostart-enabled" + autostart_dir = base.save_config_path("autostart") + autostart_file = os.path.join(autostart_dir, REDSHIFT_DESKTOP) + if not os.path.exists(autostart_file): + desktop_files = list(base.load_data_paths("applications", + REDSHIFT_DESKTOP)) + if not desktop_files: + raise IOError("Installed redshift desktop file not found!") + return + desktop_file_path = desktop_files[0] + # Read installed file and modify it + dfile = desktop.DesktopEntry(desktop_file_path) + else: + dfile = desktop.DesktopEntry(autostart_file) + activestr = str(bool(active)).lower() + # print "Setting autostart to %s" % activestr + dfile.set(AUTOSTART_KEY, activestr) + dfile.write(filename=autostart_file) diff --git a/src/location-geoclue.c b/src/location-geoclue.c new file mode 100644 index 0000000..6946dd8 --- /dev/null +++ b/src/location-geoclue.c @@ -0,0 +1,186 @@ +/* location-geoclue.c -- Geoclue location provider 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 Mathieu Trudel-Lapierre <mathieu-tl@ubuntu.com> +*/ + +#include <stdio.h> +#include <string.h> + +#include <geoclue/geoclue-master.h> +#include <geoclue/geoclue-position.h> + +#include "location-geoclue.h" + +#ifdef ENABLE_NLS +# include <libintl.h> +# define _(s) gettext(s) +#else +# define _(s) s +#endif + +#define DEFAULT_PROVIDER "org.freedesktop.Geoclue.Providers.UbuntuGeoIP" +#define DEFAULT_PROVIDER_PATH "/org/freedesktop/Geoclue/Providers/UbuntuGeoIP" + +int +location_geoclue_init(location_geoclue_state_t *state) +{ + g_type_init(); + + state->position = NULL; + state->provider = NULL; + state->provider_path = NULL; + + return 0; +} + +int +location_geoclue_start(location_geoclue_state_t *state) +{ + GeoclueMaster *master = NULL; + GeoclueMasterClient *client = NULL; + GError *error = NULL; + gchar *name = NULL; + + if (!(state->provider && state->provider_path)) { + master = geoclue_master_get_default(); + client = geoclue_master_create_client(master, NULL, NULL); + + if (!geoclue_master_client_set_requirements(client, + GEOCLUE_ACCURACY_LEVEL_REGION, + 0, FALSE, + GEOCLUE_RESOURCE_NETWORK, + &error)) { + g_printerr(_("Can't set requirements for master: %s"), + error->message); + g_error_free(error); + g_object_unref(client); + + return -1; + } + + state->position = geoclue_master_client_create_position(client, NULL); + } else { + state->position = geoclue_position_new(state->provider, + state->provider_path); + } + + if (geoclue_provider_get_provider_info(GEOCLUE_PROVIDER(state->position), + &name, NULL, NULL)) { + fprintf(stdout, _("Started Geoclue provider `%s'.\n"), name); + g_free(name); + } else { + fputs(_("Could not find a usable Geoclue provider.\n"), stderr); + fputs(_("Try setting name and path to specify which to use.\n"), stderr); + return -1; + } + + return 0; +} + +void +location_geoclue_free(location_geoclue_state_t *state) +{ + if (state->position != NULL) g_object_unref(state->position); +} + +void +location_geoclue_print_help(FILE *f) +{ + fputs(_("Use the location as discovered by a Geoclue provider.\n"), f); + fputs("\n", f); + + /* TRANSLATORS: Geoclue help output + left column must not be translated */ + fputs(_(" name=N\tName of Geoclue provider (or `default')\n" + " path=N\tPath of Geoclue provider (or `default')\n"), f); + fputs("\n", f); +} + +int +location_geoclue_set_option(location_geoclue_state_t *state, + const char *key, const char *value) +{ + const char *provider = NULL; + const char *path = NULL; + + /* Parse string value */ + if (key != NULL && strcasecmp(key, "name") == 0) { + if (value != NULL && strcasecmp(value, "default") == 0) { + provider = DEFAULT_PROVIDER; + } else if (value != NULL) { + provider = value; + } else { + fputs(_("Must specify a provider `name' (or use `default').\n"), stderr); + return -1; + } + + /* TODO I don't think we own the string here, should be copied. */ + state->provider = provider; + } else if (key != NULL && strcasecmp(key, "path") == 0) { + if (value != NULL && strcasecmp(value, "default") == 0) { + path = DEFAULT_PROVIDER_PATH; + } else if (value != NULL) { + path = value; + } else { + fputs(_("Must specify a provider `path' (or use `default').\n"), stderr); + return -1; + } + + /* TODO I don't think we own the string here, should be copied. */ + state->provider_path = path; + } else if (key == NULL) { + return -1; + } else { + fprintf(stderr, _("Unknown method parameter: `%s'.\n"), key); + return -1; + } + + return 0; +} + +int +location_geoclue_get_location(location_geoclue_state_t *state, + float *lat, float *lon) +{ + GeocluePositionFields fields; + GError *error = NULL; + double latitude = 0, longitude = 0; + + fields = geoclue_position_get_position(state->position, NULL, + &latitude, &longitude, NULL, + NULL, &error); + if (error) { + g_printerr(_("Could not get location: %s.\n"), error->message); + g_error_free(error); + return -1; + } + + if (fields & GEOCLUE_POSITION_FIELDS_LATITUDE && + fields & GEOCLUE_POSITION_FIELDS_LONGITUDE) { + fprintf(stdout, _("According to the geoclue provider" + " we're at: %.2f, %.2f\n"), + latitude, longitude); + } else { + g_warning(_("Provider does not have a valid location available.")); + return -1; + } + + *lat = latitude; + *lon = longitude; + + return 0; +} diff --git a/src/location-geoclue.h b/src/location-geoclue.h new file mode 100644 index 0000000..40ab22c --- /dev/null +++ b/src/location-geoclue.h @@ -0,0 +1,44 @@ +/* location-geoclue.h -- Geoclue location provider 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 Mathieu Trudel-Lapierre <mathieu-tl@ubuntu.com> +*/ + +#ifndef _REDSHIFT_LOCATION_GEOCLUE_H +#define _REDSHIFT_LOCATION_GEOCLUE_H + +#include <stdio.h> +#include <geoclue/geoclue-position.h> + +typedef struct { + GeocluePosition *position; /* main geoclue object */ + const char *provider; /* name of a geoclue provider */ + const char *provider_path; /* path of the geoclue provider */ +} location_geoclue_state_t; + +int location_geoclue_init(location_geoclue_state_t *state); +int location_geoclue_start(location_geoclue_state_t *state); +void location_geoclue_free(location_geoclue_state_t *state); + +void location_geoclue_print_help(FILE *f); +int location_geoclue_set_option(location_geoclue_state_t *state, + const char *key, const char *value); + +int location_geoclue_get_location(location_geoclue_state_t *state, + float *lat, float *lon); + + +#endif /* ! _REDSHIFT_LOCATION_GEOCLUE_H */ diff --git a/src/location-gnome-clock.c b/src/location-gnome-clock.c index 10b95eb..2bb1949 100644 --- a/src/location-gnome-clock.c +++ b/src/location-gnome-clock.c @@ -32,88 +32,137 @@ #endif +/* Find current selected city for the clock applet with the specified id. + Returns NULL if not found. */ +static char * +find_current_city(GConfClient *client, const char *id) +{ + char *current_city = NULL; + char *cities_key = g_strdup_printf("/apps/panel/applets/%s" + "/prefs/cities", id); + GSList *cities = gconf_client_get_list(client, + cities_key, + GCONF_VALUE_STRING, NULL); + + if (cities == NULL) { + fprintf(stderr, _("Error reading city list: `%s'.\n"), + cities_key); + g_free(cities_key); + return NULL; + } + + g_free(cities_key); + + for (GSList *city = cities; city != NULL; + city = g_slist_next(city)) { + char *city_spec = city->data; + char *c = strstr(city_spec, "current=\"true\""); + if (c) current_city = g_strdup(city_spec); + g_free(city->data); + } + g_slist_free(cities); + + return current_city; +} + int location_gnome_clock_init(location_gnome_clock_state_t *state) { g_type_init(); - GError *error = NULL; GConfClient *client = gconf_client_get_default(); - GSList *applets = gconf_client_all_dirs(client, "/apps/panel/applets", - &error); - if (error) { - fputs(_("Cannot list dirs in `/apps/panel/applets'.\n"), - stderr); + /* Get a list of active applets in the panel. */ + GSList *applets = gconf_client_get_list(client, + "/apps/panel/general/applet_id_list", + GCONF_VALUE_STRING, NULL); + if (applets == NULL) { + /* JDS has an alternate list of applets. */ + applets = gconf_client_get_list(client, + "/apps/panel/general/applet_id_list_jds", + GCONF_VALUE_STRING, NULL); + } + + if (applets == NULL) { + fputs(_("Cannot list GNOME panel applets.\n"), stderr); + g_slist_free(applets); g_object_unref(client); return -1; } - char *cities_key = NULL; + /* Go through each applet and check if it is a clock applet. + When a clock applet is found, check whether there is a + city selected as the current city. */ + char *current_city = NULL; + + /* Keep track of the number of clock applets found to be able to give + better error output if something fails. */ + int clock_applet_count = 0; + for (GSList *applet = applets; applet != NULL; applet = g_slist_next(applet)) { - char *path = applet->data; - if (cities_key == NULL) { - char *key = g_strdup_printf("%s/bonobo_iid", path); + char *id = applet->data; + if (current_city == NULL) { + char *key = g_strdup_printf("/apps/panel/applets/%s" + "/bonobo_iid", id); char *bonobo_iid = gconf_client_get_string(client, key, - &error); - - if (!error && bonobo_iid != NULL) { - if (!strcmp(bonobo_iid, - "OAFIID:GNOME_ClockApplet")) { - cities_key = g_strdup_printf( - "%s/prefs/cities", path); + NULL); + + /* Try both gnome-panel 2.30.x and earlier bonobo_iid + key and newer applet_iid. */ + if (bonobo_iid != NULL && + !strcmp(bonobo_iid, "OAFIID:GNOME_ClockApplet")) { + clock_applet_count += 1; + current_city = find_current_city(client, id); + } else { + g_free(key); + key = g_strdup_printf("/apps/panel/applets/%s" + "/applet_iid", id); + char *applet_iid = gconf_client_get_string(client, key, + NULL); + + if (applet_iid != NULL && + !strcmp(applet_iid, "ClockAppletFactory::ClockApplet")) { + clock_applet_count += 1; + current_city = find_current_city(client, id); } - g_free(bonobo_iid); + + g_free(applet_iid); } + g_free(bonobo_iid); g_free(key); } - g_free(path); + g_free(id); } g_slist_free(applets); + g_object_unref(client); - if (cities_key == NULL) { - fputs(_("No clock applet was found.\n"), stderr); - g_object_unref(client); - return -1; - } + /* Check whether an applet and a current city was found. */ - GSList *cities = gconf_client_get_list(client, cities_key, - GCONF_VALUE_STRING, &error); - if (error) { - fprintf(stderr, _("Error reading city list: `%s'.\n"), - cities_key); - g_free(cities_key); - g_object_unref(client); + if (clock_applet_count == 0) { + fputs(_("No clock applet was found.\n"), stderr); return -1; } - g_free(cities_key); - - char *current_city = NULL; - for (GSList *city = cities; city != NULL; - city = g_slist_next(city)) { - char *city_spec = city->data; - char *c = strstr(city_spec, "current=\"true\""); - if (c) current_city = g_strdup(city_spec); - g_free(city->data); - } - g_slist_free(cities); - if (current_city == NULL) { fputs(_("No city selected as current city.\n"), stderr); return -1; } + /* Find coords for selected city and parse as number. */ + char *lat_str = strstr(current_city, "latitude=\""); char *lon_str = strstr(current_city, "longitude=\""); if (lat_str == NULL || lon_str == NULL) { fputs(_("Location not specified for city.\n"), stderr); + g_free(current_city); return -1; } + g_free(current_city); + char *lat_num_str = lat_str + strlen("latitude=\""); char *lon_num_str = lon_str + strlen("longitude=\""); diff --git a/src/redshift.c b/src/redshift.c index 18cc2c5..af154b8 100644 --- a/src/redshift.c +++ b/src/redshift.c @@ -30,8 +30,8 @@ #include <locale.h> #include <errno.h> -#ifdef HAVE_SYS_SIGNAL_H -# include <sys/signal.h> +#ifdef HAVE_SIGNAL_H +# include <signal.h> #endif #ifdef ENABLE_NLS @@ -42,6 +42,7 @@ #endif #include "redshift.h" +#include "config-ini.h" #include "solar.h" #include "systemtime.h" @@ -75,6 +76,10 @@ # include "location-gnome-clock.h" #endif +#ifdef ENABLE_GEOCLUE +# include "location-geoclue.h" +#endif + /* Union of state data for gamma adjustment methods */ typedef union { @@ -138,11 +143,28 @@ typedef union { #ifdef ENABLE_GNOME_CLOCK location_gnome_clock_state_t gnome_clock; #endif +#ifdef ENABLE_GEOCLUE + location_geoclue_state_t geoclue; +#endif } location_state_t; /* Location provider method structs */ static const location_provider_t location_providers[] = { +#ifdef ENABLE_GEOCLUE + { + "geoclue", + (location_provider_init_func *)location_geoclue_init, + (location_provider_start_func *)location_geoclue_start, + (location_provider_free_func *)location_geoclue_free, + (location_provider_print_help_func *) + location_geoclue_print_help, + (location_provider_set_option_func *) + location_geoclue_set_option, + (location_provider_get_location_func *) + location_geoclue_get_location + }, +#endif #ifdef ENABLE_GNOME_CLOCK { "gnome-clock", @@ -179,12 +201,15 @@ static const location_provider_t location_providers[] = { #define MAX_LON 180.0 #define MIN_TEMP 1000 #define MAX_TEMP 10000 +#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 3700 +#define DEFAULT_BRIGHTNESS 1.0 #define DEFAULT_GAMMA 1.0 /* The color temperature when no adjustment is applied. */ @@ -201,11 +226,12 @@ static const location_provider_t location_providers[] = { typedef enum { PROGRAM_MODE_CONTINUAL, PROGRAM_MODE_ONE_SHOT, - PROGRAM_MODE_RESET + PROGRAM_MODE_RESET, + PROGRAM_MODE_MANUAL } program_mode_t; -#ifdef HAVE_SYS_SIGNAL_H +#ifdef HAVE_SIGNAL_H static volatile sig_atomic_t exiting = 0; static volatile sig_atomic_t disable = 0; @@ -224,12 +250,12 @@ sigdisable(int signo) disable = 1; } -#else /* ! HAVE_SYS_SIGNAL_H */ +#else /* ! HAVE_SIGNAL_H */ static int exiting = 0; static int disable = 0; -#endif /* ! HAVE_SYS_SIGNAL_H */ +#endif /* ! HAVE_SIGNAL_H */ /* Calculate color temperature for the specified solar elevation. */ @@ -285,7 +311,8 @@ print_help(const char *program_name) /* TRANSLATORS: help output 4 `list' must not be translated no-wrap */ - fputs(_(" -g R:G:B\tAdditional gamma correction to apply\n" + fputs(_(" -c FILE\tLoad settings from specified configuration file\n" + " -g R:G:B\tAdditional gamma correction to apply\n" " -l LAT:LON\tYour current location\n" " -l PROVIDER\tSelect provider for automatic" " location updates\n" @@ -294,6 +321,7 @@ print_help(const char *program_name) " \t\t(Type `list' to see available methods)\n" " -o\t\tOne shot mode (do not continously adjust" " color temperature)\n" + " -O TEMP\tOne shot manual mode (set color temperature)\n" " -x\t\tReset mode (remove adjustment from screen)\n" " -r\t\tDisable temperature transitions\n" " -t DAY:NIGHT\tColor temperature to set at daytime/night\n"), @@ -301,6 +329,16 @@ print_help(const char *program_name) fputs("\n", stdout); /* TRANSLATORS: help output 5 */ + printf(_("The neutral temperature is %uK. Using this value will not\n" + "change the color temperature of the display. Setting the\n" + "color temperature to a value higher than this results in\n" + "more blue light, and setting a lower value will result in\n" + "more red light.\n"), + NEUTRAL_TEMP); + + fputs("\n", stdout); + + /* TRANSLATORS: help output 6 */ printf(_("Default values:\n\n" " Daytime temperature: %uK\n" " Night temperature: %uK\n"), @@ -308,7 +346,7 @@ print_help(const char *program_name) fputs("\n", stdout); - /* TRANSLATORS: help output 6 */ + /* TRANSLATORS: help output 7 */ printf(_("Please report bugs to <%s>\n"), PACKAGE_BUGREPORT); } @@ -345,7 +383,8 @@ print_provider_list() static int provider_try_start(const location_provider_t *provider, - location_state_t *state, char *args) + location_state_t *state, + config_ini_state_t *config, char *args) { int r; @@ -356,7 +395,31 @@ provider_try_start(const location_provider_t *provider, return -1; } - /* Set provider options. */ + /* Set provider options from config file. */ + config_ini_section_t *section = + config_ini_get_section(config, provider->name); + if (section != NULL) { + config_ini_setting_t *setting = section->settings; + while (setting != NULL) { + r = provider->set_option(state, setting->name, + setting->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; + } + setting = setting->next; + } + } + + /* Set provider options from command line. */ while (args != NULL) { char *next_arg = strchr(args, ':'); if (next_arg != NULL) *(next_arg++) = '\0'; @@ -398,7 +461,8 @@ provider_try_start(const location_provider_t *provider, static int method_try_start(const gamma_method_t *method, - gamma_state_t *state, char *args) + gamma_state_t *state, + config_ini_state_t *config, char *args) { int r; @@ -409,7 +473,31 @@ method_try_start(const gamma_method_t *method, return -1; } - /* Set method options. */ + /* 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; + } + setting = setting->next; + } + } + + /* Set method options from command line. */ while (args != NULL) { char *next_arg = strchr(args, ':'); if (next_arg != NULL) *(next_arg++) = '\0'; @@ -449,6 +537,62 @@ 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; +} + +static const gamma_method_t * +find_gamma_method(const char *name) +{ + const gamma_method_t *method = NULL; + for (int i = 0; gamma_methods[i].name != NULL; i++) { + const gamma_method_t *m = &gamma_methods[i]; + if (strcasecmp(name, m->name) == 0) { + method = m; + break; + } + } + + return method; +} + +static const location_provider_t * +find_location_provider(const char *name) +{ + const location_provider_t *provider = NULL; + for (int i = 0; location_providers[i].name != NULL; i++) { + const location_provider_t *p = &location_providers[i]; + if (strcasecmp(name, p->name) == 0) { + provider = p; + break; + } + } + + return provider; +} + int main(int argc, char *argv[]) @@ -465,10 +609,14 @@ main(int argc, char *argv[]) textdomain(PACKAGE); #endif - /* Initialize to defaults */ - int temp_day = DEFAULT_DAY_TEMP; - int temp_night = DEFAULT_NIGHT_TEMP; - float gamma[3] = { DEFAULT_GAMMA, DEFAULT_GAMMA, DEFAULT_GAMMA }; + /* Initialize settings to NULL values. */ + char *config_filepath = NULL; + + int temp_set = -1; + int temp_day = -1; + int temp_night = -1; + float gamma[3] = { NAN, NAN, NAN }; + float brightness = NAN; const gamma_method_t *method = NULL; char *method_args = NULL; @@ -476,38 +624,30 @@ main(int argc, char *argv[]) const location_provider_t *provider = NULL; char *provider_args = NULL; - int transition = 1; + int transition = -1; program_mode_t mode = PROGRAM_MODE_CONTINUAL; int verbose = 0; char *s; - /* Parse arguments. */ + /* Parse command line arguments. */ int opt; - while ((opt = getopt(argc, argv, "g:hl:m:ort:vx")) != -1) { + while ((opt = getopt(argc, argv, "b:c:g:hl:m:oO:rt:vx")) != -1) { switch (opt) { + case 'b': + brightness = atof(optarg); + break; + case 'c': + if (config_filepath != NULL) free(config_filepath); + config_filepath = strdup(optarg); + break; case 'g': - s = strchr(optarg, ':'); - if (s == NULL) { - /* Use value for all channels */ - float g = atof(optarg); - gamma[0] = gamma[1] = gamma[2] = g; - } else { - /* Parse separate value for each channel */ - *(s++) = '\0'; - gamma[0] = atof(optarg); /* Red */ - char *g_s = s; - s = strchr(s, ':'); - if (s == NULL) { - fputs(_("Malformed gamma argument.\n"), - stderr); - fputs(_("Try `-h' for more" - " information.\n"), stderr); - exit(EXIT_FAILURE); - } - - *(s++) = '\0'; - gamma[1] = atof(g_s); /* Blue */ - gamma[2] = atof(s); /* Green */ + r = parse_gamma_string(optarg, gamma); + if (r < 0) { + fputs(_("Malformed gamma argument.\n"), + stderr); + fputs(_("Try `-h' for more" + " information.\n"), stderr); + exit(EXIT_FAILURE); } break; case 'h': @@ -543,16 +683,8 @@ main(int argc, char *argv[]) provider_name = optarg; } - /* Lookup argument in location provider table */ - for (int i = 0; location_providers[i].name != NULL; - i++) { - const location_provider_t *p = - &location_providers[i]; - if (strcasecmp(provider_name, p->name) == 0) { - provider = p; - } - } - + /* Lookup provider from name. */ + provider = find_location_provider(provider_name); if (provider == NULL) { fprintf(stderr, _("Unknown location provider" " `%s'.\n"), provider_name); @@ -563,7 +695,7 @@ main(int argc, char *argv[]) if (provider_args != NULL && strcasecmp(provider_args, "help") == 0) { provider->print_help(stdout); - exit(EXIT_FAILURE); + exit(EXIT_SUCCESS); } break; case 'm': @@ -580,20 +712,13 @@ main(int argc, char *argv[]) method_args = s; } - /* Lookup argument in gamma methods table */ - for (int i = 0; gamma_methods[i].name != NULL; i++) { - const gamma_method_t *m = - &gamma_methods[i]; - if (strcasecmp(optarg, m->name) == 0) { - method = m; - } - } - + /* Find adjustment method by name. */ + method = find_gamma_method(optarg); if (method == NULL) { /* TRANSLATORS: This refers to the method used to adjust colors e.g VidMode */ - fprintf(stderr, _("Unknown method `%s'.\n"), - optarg); + fprintf(stderr, _("Unknown adjustment method" + " `%s'.\n"), optarg); exit(EXIT_FAILURE); } @@ -601,12 +726,16 @@ main(int argc, char *argv[]) if (method_args != NULL && strcasecmp(method_args, "help") == 0) { method->print_help(stdout); - exit(EXIT_FAILURE); + exit(EXIT_SUCCESS); } break; case 'o': mode = PROGRAM_MODE_ONE_SHOT; break; + case 'O': + mode = PROGRAM_MODE_MANUAL; + temp_set = atoi(optarg); + break; case 'r': transition = 0; break; @@ -636,16 +765,111 @@ main(int argc, char *argv[]) } } + /* Load settings from config file. */ + config_ini_state_t config_state; + r = config_ini_init(&config_state, config_filepath); + if (r < 0) { + fputs("Unable to load config file.\n", stderr); + exit(EXIT_FAILURE); + } + + if (config_filepath != NULL) free(config_filepath); + + /* Read global config settings. */ + config_ini_section_t *section = config_ini_get_section(&config_state, + "redshift"); + if (section != NULL) { + config_ini_setting_t *setting = section->settings; + while (setting != NULL) { + if (strcasecmp(setting->name, "temp-day") == 0) { + if (temp_day < 0) { + temp_day = atoi(setting->value); + } + } else if (strcasecmp(setting->name, + "temp-night") == 0) { + if (temp_night < 0) { + temp_night = atoi(setting->value); + } + } else if (strcasecmp(setting->name, + "transition") == 0) { + if (transition < 0) { + transition = !!atoi(setting->value); + } + } else if (strcasecmp(setting->name, + "brightness") == 0) { + if (isnan(brightness)) { + brightness = 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); + } + } + } else if (strcasecmp(setting->name, + "adjustment-method") == 0) { + if (method == NULL) { + method = find_gamma_method( + setting->value); + if (method == NULL) { + fprintf(stderr, _("Unknown" + " adjustment" + " method" + " `%s'.\n"), + setting->value); + exit(EXIT_FAILURE); + } + } + } else if (strcasecmp(setting->name, + "location-provider") == 0) { + if (provider == NULL) { + provider = find_location_provider( + setting->value); + if (provider == NULL) { + fprintf(stderr, _("Unknown" + " location" + " provider" + " `%s'.\n"), + setting->value); + exit(EXIT_FAILURE); + } + } + } else { + fprintf(stderr, _("Unknown configuration" + " setting `%s'.\n"), + setting->name); + } + setting = setting->next; + } + } + + /* Use default values for settings that were neither defined in + the config file nor on the command line. */ + if (temp_day < 0) temp_day = DEFAULT_DAY_TEMP; + if (temp_night < 0) temp_night = DEFAULT_NIGHT_TEMP; + if (isnan(brightness)) brightness = DEFAULT_BRIGHTNESS; + if (isnan(gamma[0])) gamma[0] = gamma[1] = gamma[2] = DEFAULT_GAMMA; + if (transition < 0) transition = 1; + + float lat = NAN; + float lon = NAN; + /* Initialize location provider. If provider is NULL try all providers until one that works is found. */ location_state_t location_state; - /* Location is not needed for reset mode. */ - if (mode != PROGRAM_MODE_RESET) { + /* Location is not needed for reset mode + or for manual temperature setting. */ + if (mode != PROGRAM_MODE_RESET && mode != PROGRAM_MODE_MANUAL) { if (provider != NULL) { /* Use provider specified on command line. */ r = provider_try_start(provider, &location_state, - provider_args); + &config_state, provider_args); if (r < 0) exit(EXIT_FAILURE); } else { /* Try all providers, use the first that works. */ @@ -654,13 +878,15 @@ main(int argc, char *argv[]) const location_provider_t *p = &location_providers[i]; r = provider_try_start(p, &location_state, - NULL); + &config_state, NULL); if (r < 0) { - fputs(_("Trying other provider...\n"), + fputs(_("Trying next provider...\n"), stderr); continue; } + /* Found provider that works. */ + printf(_("Using provider `%s'.\n"), p->name); provider = p; break; } @@ -673,60 +899,77 @@ main(int argc, char *argv[]) exit(EXIT_FAILURE); } } - } - - float lat = NAN; - float lon = NAN; - if (mode != PROGRAM_MODE_RESET) { /* Get current location. */ r = provider->get_location(&location_state, &lat, &lon); if (r < 0) { - fputs(_("Unable to get location from provider.\n"), - stderr); - exit(EXIT_FAILURE); + fputs(_("Unable to get location from provider.\n"), + stderr); + exit(EXIT_FAILURE); } - + provider->free(&location_state); - + if (verbose) { - /* TRANSLATORS: Append degree symbols if possible. */ - printf(_("Location: %f, %f\n"), lat, lon); + /* TRANSLATORS: Append degree symbols if possible. */ + printf(_("Location: %f, %f\n"), lat, lon); } - + /* Latitude */ if (lat < MIN_LAT || lat > MAX_LAT) { - /* TRANSLATORS: Append degree symbols if possible. */ + /* TRANSLATORS: Append degree symbols if possible. */ + fprintf(stderr, + _("Latitude must be between %.1f and %.1f.\n"), + MIN_LAT, MAX_LAT); + exit(EXIT_FAILURE); + } + + /* Longitude */ + if (lon < MIN_LON || lon > MAX_LON) { + /* TRANSLATORS: Append degree symbols if possible. */ + fprintf(stderr, + _("Longitude must be between" + " %.1f and %.1f.\n"), MIN_LON, MAX_LON); + exit(EXIT_FAILURE); + } + + /* Color temperature at daytime */ + if (temp_day < MIN_TEMP || temp_day >= MAX_TEMP) { + fprintf(stderr, + _("Temperature must be between %uK and %uK.\n"), + MIN_TEMP, MAX_TEMP); + exit(EXIT_FAILURE); + } + + /* Color temperature at night */ + if (temp_night < MIN_TEMP || temp_night >= MAX_TEMP) { fprintf(stderr, - _("Latitude must be between %.1f and %.1f.\n"), - MIN_LAT, MAX_LAT); + _("Temperature must be between %uK and %uK.\n"), + MIN_TEMP, MAX_TEMP); exit(EXIT_FAILURE); } + } - /* Longitude */ - if (lon < MIN_LON || lon > MAX_LON) { - /* TRANSLATORS: Append degree symbols if possible. */ + if (mode == PROGRAM_MODE_MANUAL) { + /* Check color temperature to be set */ + if (temp_set < MIN_TEMP || temp_set >= MAX_TEMP) { fprintf(stderr, - _("Longitude must be between" - " %.1f and %.1f.\n"), MIN_LON, MAX_LON); + _("Temperature must be between %uK and %uK.\n"), + MIN_TEMP, MAX_TEMP); exit(EXIT_FAILURE); } } - /* Color temperature at daytime */ - if (temp_day < MIN_TEMP || temp_day >= MAX_TEMP) { + /* Brightness */ + if (brightness < MIN_BRIGHTNESS || brightness > MAX_BRIGHTNESS) { fprintf(stderr, - _("Temperature must be between %uK and %uK.\n"), - MIN_TEMP, MAX_TEMP); + _("Brightness value must be between %.1f and %.1f.\n"), + MIN_BRIGHTNESS, MAX_BRIGHTNESS); exit(EXIT_FAILURE); } - /* Color temperature at night */ - if (temp_night < MIN_TEMP || temp_night >= MAX_TEMP) { - fprintf(stderr, - _("Temperature must be between %uK and %uK.\n"), - MIN_TEMP, MAX_TEMP); - exit(EXIT_FAILURE); + if (verbose) { + printf(_("Brightness: %.2f\n"), brightness); } /* Gamma */ @@ -750,18 +993,21 @@ main(int argc, char *argv[]) if (method != NULL) { /* Use method specified on command line. */ - r = method_try_start(method, &state, method_args); + r = method_try_start(method, &state, &config_state, + method_args); if (r < 0) 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]; - r = method_try_start(m, &state, NULL); + r = method_try_start(m, &state, &config_state, NULL); if (r < 0) { - fputs(_("Trying other method...\n"), stderr); + fputs(_("Trying next method...\n"), stderr); continue; } + /* Found method that works. */ + printf(_("Using method `%s'.\n"), m->name); method = m; break; } @@ -799,18 +1045,32 @@ main(int argc, char *argv[]) if (verbose) printf(_("Color temperature: %uK\n"), temp); /* Adjust temperature */ - r = method->set_temperature(&state, temp, gamma); + r = method->set_temperature(&state, temp, brightness, gamma); + if (r < 0) { + fputs(_("Temperature adjustment failed.\n"), stderr); + method->free(&state); + exit(EXIT_FAILURE); + } + } + break; + case PROGRAM_MODE_MANUAL: + { + if (verbose) printf(_("Color temperature: %uK\n"), temp_set); + + /* Adjust temperature */ + r = method->set_temperature(&state, temp_set, brightness, gamma); if (r < 0) { fputs(_("Temperature adjustment failed.\n"), stderr); method->free(&state); exit(EXIT_FAILURE); } + } break; case PROGRAM_MODE_RESET: { /* Reset screen */ - r = method->set_temperature(&state, NEUTRAL_TEMP, gamma); + r = method->set_temperature(&state, NEUTRAL_TEMP, 1.0, gamma); if (r < 0) { fputs(_("Temperature adjustment failed.\n"), stderr); method->free(&state); @@ -835,7 +1095,7 @@ main(int argc, char *argv[]) will be exactly 6500K. */ float adjustment_alpha = 0.0; -#ifdef HAVE_SYS_SIGNAL_H +#ifdef HAVE_SIGNAL_H struct sigaction sigact; sigset_t sigset; sigemptyset(&sigset); @@ -852,7 +1112,7 @@ main(int argc, char *argv[]) sigact.sa_mask = sigset; sigact.sa_flags = 0; sigaction(SIGUSR1, &sigact, NULL); -#endif /* HAVE_SYS_SIGNAL_H */ +#endif /* HAVE_SIGNAL_H */ /* Continously adjust color temperature */ int done = 0; @@ -974,7 +1234,8 @@ main(int argc, char *argv[]) /* Adjust temperature */ if (!disabled || short_trans) { r = method->set_temperature(&state, - temp, gamma); + temp, brightness, + gamma); if (r < 0) { fputs(_("Temperature adjustment" " failed.\n"), stderr); diff --git a/src/redshift.h b/src/redshift.h index 8f488f6..c3d1239 100644 --- a/src/redshift.h +++ b/src/redshift.h @@ -33,6 +33,7 @@ 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, float gamma[3]); typedef struct { |