/* redshift-ng - Automatically adjust display colour temperature according the Sun * * Copyright (c) 2009-2018 Jon Lund Steffensen * Copyright (c) 2014-2016, 2025 Mattias Andrée * * 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 . */ #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, NULL }; static int try_start(const struct location_provider *provider, LOCATION_STATE **state, 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->init(state) < 0) { weprintf(_("Initialization of %s failed."), provider->name); return -1; } /* 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, setting->name, setting->value) < 0) goto set_option_fail; /* Set provider options from command line. */ for (i = 0; args; i++) { next_arg = strchr(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 < ELEMSOF(manual_keys)) { key = manual_keys[i]; value = args; } else { weprintf(_("Failed to parse option `%s'."), args); return -1; } } else { *value++ = '\0'; } if (provider->set_option(*state, key, value) < 0) goto set_option_fail; args = next_arg; } /* Start provider. */ if (provider->start(*state) < 0) { provider->free(*state); weprintf(_("Failed to start provider %s."), provider->name); return -1; } return 0; set_option_fail: provider->free(*state); weprintf(_("Failed to set %s option."), provider->name); /* TRANSLATORS: `help' must not be translated. */ weprintf(_("Try `-l %s:help' for more information."), provider->name); return -1; } 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 } /* Wait for location to become available from provider. Waits until timeout (milliseconds) has elapsed or forever if timeout is -1. Writes location to loc. Returns -1 on error, 0 if timeout was reached, 1 if location became available. */ int get_location(const struct location_provider *provider, LOCATION_STATE *state, int timeout, struct location *loc) { int r, available; 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=, .events=EPOLLIN}} 1 %i:", timeout); return -1; } else { return 0; } } if (provider->handle(state, loc, &available) < 0) return -1; } while (!available); return 1; } 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.")); } }