diff options
Diffstat (limited to '')
-rw-r--r-- | src/Makefile.am | 7 | ||||
-rw-r--r-- | src/colorramp.c | 2 | ||||
-rw-r--r-- | src/config-ini.c | 237 | ||||
-rw-r--r-- | src/config-ini.h | 49 | ||||
-rw-r--r-- | src/gamma-randr.c | 7 | ||||
-rw-r--r-- | src/gamma-vidmode.c | 5 | ||||
-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 | 77 | ||||
-rw-r--r-- | src/gtk-redshift/utils.py | 66 | ||||
-rw-r--r-- | src/location-gnome-clock.c | 109 | ||||
-rw-r--r-- | src/redshift.c | 310 |
15 files changed, 745 insertions, 256 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index c07aea3..3680f4a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,11 +9,12 @@ 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 \ diff --git a/src/colorramp.c b/src/colorramp.c index a154a9e..7241a8d 100644 --- a/src/colorramp.c +++ b/src/colorramp.c @@ -130,7 +130,7 @@ void colorramp_fill(uint16_t *gamma_r, uint16_t *gamma_g, uint16_t *gamma_b, int size, int temp, 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; diff --git a/src/config-ini.c b/src/config-ini.c new file mode 100644 index 0000000..5231ba5 --- /dev/null +++ b/src/config-ini.c @@ -0,0 +1,237 @@ +/* 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; + } 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..be8bdd9 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; } diff --git a/src/gamma-vidmode.c b/src/gamma-vidmode.c index e6b9412..7b891d8 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; } 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..29801e8 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,76 @@ 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', + 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': + indicator.set_icon('redshift-status-off') + else: + indicator.set_icon('redshift-status-on') + else: + if status_icon.get_icon_name() == 'redshift': + 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-gnome-clock.c b/src/location-gnome-clock.c index 10b95eb..8c317ed 100644 --- a/src/location-gnome-clock.c +++ b/src/location-gnome-clock.c @@ -32,6 +32,41 @@ #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) +{ + GError *error = NULL; + + 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, &error); + + if (error) { + 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) { @@ -40,80 +75,74 @@ location_gnome_clock_init(location_gnome_clock_state_t *state) GError *error = NULL; GConfClient *client = gconf_client_get_default(); - GSList *applets = gconf_client_all_dirs(client, "/apps/panel/applets", - &error); + /* 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, &error); if (error) { - fputs(_("Cannot list dirs in `/apps/panel/applets'.\n"), - stderr); + fputs(_("Cannot list GNOME panel applets.\n"), stderr); g_object_unref(client); + g_slist_free(applets); 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); - } - g_free(bonobo_iid); + if (!error && bonobo_iid != NULL && + !strcmp(bonobo_iid, "OAFIID:GNOME_ClockApplet")) { + clock_applet_count += 1; + current_city = find_current_city(client, id); } + 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..f5a7762 100644 --- a/src/redshift.c +++ b/src/redshift.c @@ -42,6 +42,7 @@ #endif #include "redshift.h" +#include "config-ini.h" #include "solar.h" #include "systemtime.h" @@ -285,7 +286,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" @@ -301,6 +303,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 +320,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 +357,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 +369,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 +435,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 +447,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 +511,60 @@ 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 */ + } +} + +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 +581,12 @@ 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_day = -1; + int temp_night = -1; + float gamma[3] = { NAN, NAN, NAN }; const gamma_method_t *method = NULL; char *method_args = NULL; @@ -476,38 +594,27 @@ 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, "c:g:hl:m:ort:vx")) != -1) { switch (opt) { + 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 +650,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 +662,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 +679,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,7 +693,7 @@ 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': @@ -636,6 +728,91 @@ 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, "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(gamma[0])) gamma[0] = gamma[1] = gamma[2] = DEFAULT_GAMMA; + if (transition < 0) transition = 1; + /* Initialize location provider. If provider is NULL try all providers until one that works is found. */ location_state_t location_state; @@ -645,7 +822,7 @@ main(int argc, char *argv[]) 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 +831,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; } @@ -750,18 +929,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; } |