/** * Copyright © 2016 Mattias Andrée * * This program 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. * * This program 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 this program. If not, see . */ #include "state.h" #include "solar.h" #include "macros.h" #include #include #include #include /** * The name of the process. */ extern char *argv0; /** * All sites. */ static libgamma_site_state_t *sites = NULL; /** * The number of sites stored in `sites`. */ static size_t sites_n = 0; /** * All partitions. */ static libgamma_partition_state_t *parts = NULL; /** * The number of partitions stored in `parts`. */ static size_t parts_n = 0; /** * Is it daytime, night, perhaps some kind of twilight? * * @param elevation The Sun's apparent elevation. * @return The time of the day. */ enum darkness get_darkness(double elevation) { if (elevation > SOLAR_ELEVATION_SUNSET_SUNRISE) return DAYTIME; if (elevation > SOLAR_ELEVATION_CIVIL_DUSK_DAWN) return CIVIL_TWILIGHT; if (elevation > SOLAR_ELEVATION_NAUTICAL_DUSK_DAWN) return NAUTICAL_TWILIGHT; if (elevation > SOLAR_ELEVATION_ASTRONOMICAL_DUSK_DAWN) return ASTRONOMICAL_TWILIGHT; return NIGHT; } /** * Remove the screen number for a display server instance identifier. * * @param s Display server instance identifier. * @return Set the value to which this pointer points to '.'. Do nothing if it is `NULL`. */ static char * strip_screen(char *s) { #define S(V, CD) ((V = ((CD)[1] == 'r' ? strrchr : strchr)(V, (CD)[0]))) char *p = strchr(s, '='); if ((p++) && (*p != '/') && S(p, ":r") && S(p, ".l")) *p = '\0'; else p = NULL; return p; #undef S } /** * `stpmulcpy(o, a, b, c, NULL)` is equivalent to * `stpcpy(stpcpy(stpcpy(o, a), b), c)` */ #ifdef __GNUC__ __attribute__((__sentinel__)) #endif static char * stpmulcpy(char *out, ... /*, NULL */) { va_list args; char *p = out; const char *s; va_start(args, out); while ((s = va_arg(args, const char *))) p = stpcpy(p, s); va_end(args); return p; } /** * Compare two display server environment strings. * * @param a_ One of the string. * @param b_ The other string. * @return -1, 0, or +1. */ static int displayenvcmp(const void *a_, const void *b_) { #ifdef __GNUC__ # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wdiscarded-qualifiers" #endif char *a = a_; char *b = b_; #ifdef __GNUC__ # pragma GCC diagnostic pop #endif char *p = strip_screen(a); char *q = strip_screen(b); int rc; rc = strcmp(a, b); if (p) *p = '.'; if (q) *q = '.'; return rc; } /** * Make an display string safe for a pathname. * * @param str The string. * @return A safe version of the string, `NULL` on error. */ static char * escape_display(const char* str) { char *r, *w, *rc = NULL; int s = 0; xmalloc(&rc, 2 * strlen(str) + 1); strcpy(rc, str); for (r = w = strchr(rc, '=') + 1; *r; r++) { if (!s || (*r != '/')) { if (strchr("@=/", *r)) *w++ = '@'; *w++ = ((s = (*r == '/')) ? 's' : *r); } } w[s ? -2 : 0] = '\0'; fail: return rc; } /** * The string of display servers. * * @param settings The settings. * @return The string, `NULL` on error. */ static char * get_display_string(const struct settings *settings) { #define DISPLAY(VAR, D) p = strip_screen(D); try (VAR = escape_display(D)); if (p) *p = '.', p = NULL const char *var, *val; char *r, *p = NULL, *d = NULL, *rc = NULL, **displays; size_t i, n = 0, len = 0; int method; xmalloc(&displays, settings->monitors_n); for (i = 0; i < settings->monitors_n; i++) if ((settings->monitors_arg[i] == 'd') && strchr(settings->monitors_id[i], '=')) len += 1 + strlen(displays[n++] = settings->monitors_id[i]); if (n) goto custom; free(displays), displays = NULL; if (!libgamma_list_methods(&method, 1, 0)) { fprintf(stderr, "No display was found.\n" "DRM support missing.\n" "Can you even see?\n"); return errno = 0, NULL; } var = libgamma_method_default_site_variable(method); val = libgamma_method_default_site(method); if (!val || !*val) return strdup(""); xmalloc(&d, 3 + strlen(var) + strlen(val)); stpmulcpy(d, ".", var, "=", val, NULL); DISPLAY(rc, d); return free(d), rc; custom: qsort(displays, n, sizeof(*displays), displayenvcmp); xmalloc(&rc, 2 * len + 1); for (r = rc, i = 0; i < n; i++) { DISPLAY(d, displays[i]); r = stpmulcpy(r, ".", d, NULL), free(d), d = NULL; } free(displays); return rc; fail: if (p) *p = '.'; CLEANUP(free(rc), free(d), free(displays)); return NULL; } /** * Set $RADHARC_STATE. * * @param settings The settings. * @return 0 on success, -1 on error. */ int get_state_pathname(const struct settings *settings) { const char *dir = getenv("XGD_RUNTIME_DIR"); char *display = NULL; char *env = NULL; int rc = -1; try (display = get_display_string(settings)); if (!dir || !*dir) dir = "/run"; xmalloc(&env, strlen(dir) + sizeof("/radharc/") + strlen(display)); stpmulcpy(env, dir, "/radharc/", display, NULL); rc = setenv("RADHARC_STATE", env, 1); fail: CLEANUP(free(env), free(display)); return rc; } /** * Parse a value for the -d option, or select preferred adjustment method. * * @param display The argument for the -d option. `NULL` for the preferred adjustment method. * @return The adjustment method. -1 if not found. */ static int get_clut_method(const char *display) { #define HAIKU(TEXT) t ((msg = (TEXT))) #define TEST(STR, ID) if (!strcasecmp(display, STR)) return ID const char *env, *msg; int method; /* Default? */ if (!display) { if (!libgamma_list_methods(&method, 1, 0)) HAIKU("No display was found.\n" "DRM support missing.\n" "Can you even see?\n"); return method; } /* Single-sited? */ TEST("none", INT_MAX); TEST("drm", LIBGAMMA_METHOD_LINUX_DRM); /* Unrecognised single-sited? */ if (!strchr(display, '=')) HAIKU("Specified display\n" "cannot be recognised.\n" "Try something else.\n"); /* Multi-sited? */ for (method = 0; method < LIBGAMMA_METHOD_COUNT; method++) { env = libgamma_method_default_site_variable(method); if (env && (strstr(display, env) == display) && (display[strlen(env)] == '=')) return method; } /* Unrecognised multi-sited. */ HAIKU("Specified display\n" "cannot be recognised.\n" "Try to recompile.\n"); fail: fprintf(stderr, "%s", msg); return errno = 0, -1; } /** * Initialise the CLUT support. * * @param settings The settings. * @return 0 on success, -1 on error. */ int initialise_clut(const struct settings *settings) { #define NONE_METHOD (method == INT_MAX) int method = 0, error = 0; const char *sitename_; char *sitename = NULL; size_t i, j, parts_off = 0; libgamma_site_state_t site; xcalloc(&sites, settings->monitors_n + 1); for (i = 0; i < settings->monitors_n; i++) { switch (settings->monitors_arg[i]) { case 'd': parts_off = parts_n; t ((method = get_clut_method(sitename_ = settings->monitors_id[i])) < 0); if (NONE_METHOD) break; sitename_ = strchr(sitename_, '='); xstrdup(&sitename, sitename_ ? sitename_ + 1 : NULL); t ((error = libgamma_site_initialise(sites + sites_n, method, sitename))); sitename = NULL, site = sites[sites_n++]; xrealloc(&parts, parts_n + site.partitions_available); for (j = 0; j < site.partitions_available; j++) { t ((error = libgamma_partition_initialise(parts + parts_n, &site, j))); parts_n++; } break; case 'm': if (NONE_METHOD) break; /* TODO -m */ break; case 'e': if (NONE_METHOD) break; /* TODO -e */ break; } } SHRINK(&sites, sites_n + 1); return 0; fail: if (error) libgamma_perror(argv0, error), errno = 0; CLEANUP(free(sitename)); return -1; }