diff options
Diffstat (limited to 'redshift/location.c')
-rw-r--r-- | redshift/location.c | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/redshift/location.c b/redshift/location.c new file mode 100644 index 0000000..5979a2d --- /dev/null +++ b/redshift/location.c @@ -0,0 +1,249 @@ +/*- + * redshift-ng - Automatically adjust display colour temperature according the Sun + * + * Copyright (c) 2009-2018 Jon Lund Steffensen <jonlst@gmail.com> + * Copyright (c) 2014-2016, 2025 Mattias Andrée <m@maandree.se> + * + * redshift-ng 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-ng 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-ng. If not, see <http://www.gnu.org/licenses/>. + */ +#include "common.h" + + +const struct location_provider *location_providers[] = { +#ifdef ENABLE_GEOCLUE2 + &geoclue2_location_provider, +#endif +#ifdef ENABLE_CORELOCATION + &corelocation_location_provider, +#endif + &manual_location_provider, +#ifndef WINDOWS + &geofile_location_provider, + &timezone_location_provider, +#endif + NULL +}; + + +/** + * Get the current monotonic time in milliseconds + * + * @return The number of milliseconds elapsed since some arbitrary fixed time + */ +static long long int +get_monotonic_millis(void) +{ +#if defined(WINDOWS) + return (long long int)GetTickCount64(); +#else + struct timespec now; + if (clock_gettime(CLOCK_MONOTONIC, &now)) + eprintf("clock_gettime CLOCK_MONOTONIC:"); + return (long long int)now.tv_sec * 1000LL + (long long int)now.tv_nsec / 1000000LL; +#endif +} + + +/** + * Attempt to start a specific location provider + * + * @param provider The location provider + * @param state_out Output parameter for the location provider state + * @param config Loaded information file + * @param args `NULL` or option part of the command line argument for the location provider + * @return 0 on success, -1 on failure + */ +static int +try_start(const struct location_provider *provider, LOCATION_STATE **state_out, struct config_ini_state *config, char *args) +{ + const char *manual_keys[] = {"lat", "lon"}; + struct config_ini_section *section; + struct config_ini_setting *setting; + char *next_arg, *value; + const char *key; + int i; + + if (provider->create(state_out) < 0) { + weprintf(_("Initialization of %s failed."), provider->name); + goto fail; + } + + /* Set provider options from config file */ + if ((section = config_ini_get_section(config, provider->name))) + for (setting = section->settings; setting; setting = setting->next) + if (provider->set_option(*state_out, setting->name, setting->value) < 0) + goto set_option_fail; + + /* Set provider options from command line */ + for (i = 0; args && *args; i++, args = next_arg) { + next_arg = &args[strcspn(args, ";:")]; + if (*next_arg) + *next_arg++ = '\0'; + + key = args; + value = strchr(args, '='); + if (!value) { + /* The options for the "manual" method can be set + * without keys on the command line for convencience + * and for backwards compatability. We add the proper + * keys here before calling set_option(). */ + if (!strcmp(provider->name, "manual") && i < (int)ELEMSOF(manual_keys)) { + key = manual_keys[i]; + value = args; + } else { + weprintf(_("Failed to parse option `%s'."), args); + goto fail; + } + } else { + *value++ = '\0'; + } + + if (provider->set_option(*state_out, key, value) < 0) + goto set_option_fail; + } + + /* Start provider */ + if (provider->start(*state_out) < 0) { + weprintf(_("Failed to start provider %s."), provider->name); + goto fail; + } + + return 0; + +set_option_fail: + weprintf(_("Failed to set %s option."), provider->name); + /* TRANSLATORS: `help' must not be translated. */ + weprintf(_("Try `-l %s:help' for more information."), provider->name); +fail: + if (*state_out) { + provider->free(*state_out); + *state_out = NULL; + } + return -1; +} + + +int +get_location(const struct location_provider *provider, LOCATION_STATE *state, int timeout, struct location *location_out) +{ +#ifdef WINDOWS /* we don't have poll on Windows, but neither do with have any dynamic location providers */ + int available; + return provider->fetch(state, location_out, &available) < 0 ? -1 : available; + +#else + int r, available = 0; + struct pollfd pollfds[1]; + long long int now = get_monotonic_millis(); + long long int end = now + (long long int)timeout; + + do { + pollfds[0].fd = provider->get_fd(state); + if (pollfds[0].fd >= 0) { + /* Poll on file descriptor until ready */ + pollfds[0].events = POLLIN; + timeout = (int)MAX(end - now, 0); + r = poll(pollfds, 1, timeout); + if (r > 0) { + now = get_monotonic_millis(); + } else if (r < 0) { +#ifndef WINDOWS + if (errno == EINTR) + continue; +#endif + weprintf("poll {{.fd=<location provider>, .events=EPOLLIN}} 1 %i:", timeout); + return -1; + } else { + return 0; + } + } + + if (provider->fetch(state, location_out, &available) < 0) + return -1; + } while (!available && !exiting); + + if (exiting) + eprintf(_("Terminated by user.")); + + return 1; +#endif +} + + +void +acquire_location_provider(struct settings *settings, LOCATION_STATE **location_state_out) +{ + size_t i; + + if (settings->provider) { + /* Use provider specified on command line */ + if (try_start(settings->provider, location_state_out, &settings->config, settings->provider_args) < 0) + exit(1); + } else { + /* Try all providers, use the first that works */ + for (i = 0; location_providers[i]; i++) { + weprintf(_("Trying location provider `%s'..."), location_providers[i]->name); + if (try_start(location_providers[i], location_state_out, &settings->config, NULL) < 0) { + weprintf(_("Trying next provider...")); + continue; + } + + /* Found provider that works */ + printf(_("Using provider `%s'.\n"), location_providers[i]->name); + settings->provider = location_providers[i]; + break; + } + + /* Failure if no providers were successful at this point */ + if (!settings->provider) + eprintf(_("No more location providers to try.")); + } +} + + +int +location_is_valid(const struct location *location) +{ + if (!WITHIN(MIN_LATITUDE, location->latitude, MAX_LATITUDE)) { + /* TRANSLATORS: Append degree symbols if possible. */ + weprintf(_("Latitude must be between %.1f and %.1f."), MIN_LATITUDE, MAX_LATITUDE); + return 0; + } + if (!WITHIN(MIN_LONGITUDE, location->longitude, MAX_LONGITUDE)) { + /* TRANSLATORS: Append degree symbols if possible. */ + weprintf(_("Longitude must be between %.1f and %.1f."), MIN_LONGITUDE, MAX_LONGITUDE); + return 0; + } + return 1; +} + + +void +print_location(const struct location *location) +{ + /* TRANSLATORS: Abbreviation for `north' */ + const char *north = _("N"); + /* TRANSLATORS: Abbreviation for `south' */ + const char *south = _("S"); + /* TRANSLATORS: Abbreviation for `east' */ + const char *east = _("E"); + /* TRANSLATORS: Abbreviation for `west' */ + const char *west = _("W"); + + /* TRANSLATORS: Append degree symbols after %f if possible. + * The string following each number is an abreviation for + * north, source, east or west (N, S, E, W). */ + printf(_("Location: %.2f %s, %.2f %s\n"), + fabs(location->latitude), signbit(location->latitude) ? south : north, + fabs(location->longitude), signbit(location->longitude) ? west : east); +} |