diff options
-rw-r--r-- | src/blackbody.c | 41 | ||||
-rw-r--r-- | src/blackbody.h | 31 | ||||
-rw-r--r-- | src/haiku.c | 182 | ||||
-rw-r--r-- | src/parse_10deg.c | 57 | ||||
-rw-r--r-- | src/radharc.c | 30 | ||||
-rw-r--r-- | src/settings.c | 144 | ||||
-rw-r--r-- | src/settings.h | 8 | ||||
-rw-r--r-- | src/solar.c | 69 | ||||
-rw-r--r-- | src/solar.h | 12 | ||||
-rw-r--r-- | src/state.c | 98 |
10 files changed, 303 insertions, 369 deletions
diff --git a/src/blackbody.c b/src/blackbody.c index 26ea52e..d393686 100644 --- a/src/blackbody.c +++ b/src/blackbody.c @@ -22,23 +22,6 @@ /** - * The highest colour temperature in the table. - */ -#define HIGHEST 40000 - -/** - * The lowest colour temperature in the table. - */ -#define LOWEST 1000 - -/** - * The temperature difference between the colours in the table. - */ -#define DELTA 100 - - - -/** * Convert from CIE xyY to [0, 1] sRGB. * * @param x The 'x' component. @@ -49,15 +32,15 @@ * @param g Output parameter for the green value. * @param b Output parameter for the blue value. */ -static void +void ciexyy_to_srgb(double x, double y, double Y, double *r, double *g, double *b) { #define SRGB(C) (((C) <= 0.0031308) ? (12.92 * (C)) : ((1.0 + 0.055) * pow((C), 1.0 / 2.4) - 0.055)) double X, Z; /* Convert CIE xyY to CIE XYZ. */ - X = Y * (y == 0 ? 0 : (x / y)); - Z = Y * (y == 0 ? 0 : ((1 - x - y) / y)); + X = Y * (y == 0.0 ? 0.0 : (x / y)); + Z = Y * (y == 0.0 ? 0.0 : ((1.0 - x - y) / y)); /* Convert CIE XYZ to [0, 1] linear RGB. (ciexyz_to_linear) */ *r = ( 3.240450 * X) + (-1.537140 * Y) + (-0.4985320 * Z); @@ -88,7 +71,7 @@ ciexyy_to_srgb(double x, double y, double Y, double *r, double *g, double *b) static void interpolate(double x1, double y1, double x2, double y2, double temp, double *r, double *g, double *b) { - double weight = fmod(temp, (double)DELTA) / (double)DELTA; + double weight = fmod(temp, (double)DELTA_TEMPERATURE) / (double)DELTA_TEMPERATURE; double x = x1 * (1 - weight) + x2 * weight; double y = y1 * (1 - weight) + y2 * weight; ciexyy_to_srgb(x, y, 1.0, r, g, b); @@ -118,26 +101,24 @@ get_colour(int fd, long int temp, double *r, double *g, double *b) /* We do not have any values for above 40 000 K, but * the differences will be unnoticeable, perhaps even * unencodeable. */ - if (temp > HIGHEST) temp = HIGHEST; + if (temp > HIGHEST_TEMPERATURE) temp = HIGHEST_TEMPERATURE; /* Things do not glow below 1000 K. Yes, fire is hot! */ - if (temp < LOWEST) t ((errno = EDOM)); + if (temp < LOWEST_TEMPERATURE) t ((errno = EDOM)); /* Read table. */ - offset = ((off_t)temp - LOWEST) / DELTA; - offset *= (off_t)(5 * sizeof(double)); - errno = 0; - xpread(fd, values, sizeof(values), offset); + offset = ((off_t)temp - LOWEST_TEMPERATURE) / DELTA_TEMPERATURE; + offset *= (off_t)(sizeof(values) / 2); + errno = 0; xpread(fd, values, sizeof(values), offset); /* Get colour. */ - if (temp % DELTA) + if (temp % DELTA_TEMPERATURE) interpolate(values[0], values[1], values[6], values[7], (double)temp, r, g, b); else *r = values[2], *g = values[3], *b = values[4]; /* Adjust colours for use. */ max = fmax(fmax(fabs(*r), fabs(*g)), fabs(*b)); - if (max != 0) - *r /= max, *g /= max, *b /= max; + if (max != 0) *r /= max, *g /= max, *b /= max; *r = *r > 0.0 ? *r : 0.0; *g = *g > 0.0 ? *g : 0.0; *b = *b > 0.0 ? *b : 0.0; diff --git a/src/blackbody.h b/src/blackbody.h index 82331d2..95fa70a 100644 --- a/src/blackbody.h +++ b/src/blackbody.h @@ -18,6 +18,37 @@ /** + * The highest colour temperature in the table. + */ +#define HIGHEST_TEMPERATURE 40000 + +/** + * The lowest colour temperature in the table. + */ +#define LOWEST_TEMPERATURE 1000 + +/** + * The temperature difference between the colours in the table. + */ +#define DELTA_TEMPERATURE 100 + + + +/** + * Convert from CIE xyY to [0, 1] sRGB. + * + * @param x The 'x' component. + * @param y The 'y' component. + * @param Y The 'Y' component. + * @param r Output parameter for the “red” value. + * (Seriously, sRGB red is orange, just look at it fullscreen.) + * @param g Output parameter for the green value. + * @param b Output parameter for the blue value. + */ +void +ciexyy_to_srgb(double x, double y, double Y, double *r, double *g, double *b); + +/** * Get the [0, 1] sRGB values of a colour temperature. * * @param fd File descriptor for the colour table. diff --git a/src/haiku.c b/src/haiku.c index 25387f4..1accc9f 100644 --- a/src/haiku.c +++ b/src/haiku.c @@ -65,14 +65,20 @@ random_haiku(const char *str, ... /*, NULL */) } +#if defined(EDEADLOCK) && !defined(EDEADLK) +# define EDEADLK EDEADLOCK +#endif + + /** * Whenever possible, print am error message in haiku. * * @param s The argument to pass to `perror` in case we call back to it. */ -void haiku(const char *s) +void +haiku(const char *s) { -#define HAIKU(...) do { fprintf(stderr, "%s", random_haiku(__VA_ARGS__, NULL)); return; } while (0) +#define H(...) do { fprintf(stderr, "%s", random_haiku(__VA_ARGS__, NULL)); return; } while (0) /* Yeah, I now most of these are in 5–7–5 syllables, * rather than 5–7–5 mora. But really, how cares. */ @@ -82,191 +88,95 @@ void haiku(const char *s) return; case ENETDOWN: - HAIKU("Stay the patient course.\n" - "Of little worth is your ire.\n" - "The network is down.\n" - - "Your vast achievements\n" - "are now only dreams.\n" - "The network is down.\n"); + H("Stay the patient course.\n""Of little worth is your ire.\n""The network is down.\n" + "Your vast achievements\n""are now only dreams.\n""The network is down.\n"); case ERFKILL: - HAIKU("The action you took\n" - "severed hope of connection\n" - "with the Internet.\n"); + H("The action you took\n""severed hope of connection\n""with the Internet.\n"); case EAGAIN: case ENFILE: case EMFILE: case EUSERS: case EMLINK: - HAIKU("ABORTED effort:\n" - "Close all that you have.\n" - "You ask way too much.\n" - - "The code was willing\n" - "It considered your request\n" - "But the chips were weak.\n"); + H("ABORTED effort:\n""Close all that you have.\n""You ask way too much.\n" + "The code was willing\n""It considered your request\n""But the chips were weak.\n"); case ENOMEM: - HAIKU("I'm sorry, there's ... um ...\n" - "insufficient ... what's-it-called?\n" - "The term eludes me...\n"); + H("I'm sorry, there's... um...\n""insufficient... what's-it-called?\n""The term eludes me...\n"); case ENOSPC: case ENOSR: case ENOBUFS: case EDQUOT: - HAIKU("Out of memory.\n" - "We wish to hold the whole sky,\n" - "But we never will.\n"); + H("Out of memory.\n""We wish to hold the whole sky,\n""But we never will.\n"); case ENOANO: case ENOENT: - HAIKU("With searching comes loss\n" - "and the presence of absence:\n" - "“My Novel” not found.\n", - - "Rather than a beep\n" - "Or a rude error message,\n" - "These words: “File not found.”\n", - - "Three things are certain:\n" - "Death, taxes, and lost data.\n" - "Guess which has occurred.\n", - - "Having been erased,\n" - "The document you're seeking\n" - "Must now be retyped.\n", - - "Everything is gone.\n" - "Your life's work has been destroyed.\n" - "Squeeze trigger (yes/no)?\n" - - "Spring will come again,\n" - "But it will not bring with it\n" - "Any of your files.\n"); + H("With searching comes loss\n""and the presence of absence:\n""'My Novel' not found.\n", + "Rather than a beep\n""Or a rude error message,\n""These words: “File not found.”\n", + "Three things are certain:\n""Death, taxes, and lost data.\n""Guess which has occurred.\n", + "Having been erased,\n""The document you're seeking\n""Must now be retyped.\n", + "Everything is gone.\n""Your life's work has been destroyed.\n""Squeeze trigger (yes/no)?\n" + "Spring will come again,\n""But it will not bring with it\n""Any of your files.\n"); case EMSGSIZE: - HAIKU("A file that big?\n" - "It might be very useful.\n" - "But now it is gone.\n"); + H("A file that big?\n""It might be very useful.\n""But now it is gone.\n"); case EHWPOISON: - HAIKU("Yesterday it worked.\n" - "Today it is not working.\n" - "Windows is like that.\n"); + H("Yesterday it worked.\n""Today it is not working.\n""Windows is like that.\n"); case ENOTRECOVERABLE: - HAIKU("Chaos reigns within.\n" - "Reflect, repent, and reboot.\n" - "Order shall return.\n"); + H("Chaos reigns within.\n""Reflect, repent, and reboot.\n""Order shall return.\n"); case EHOSTDOWN: - HAIKU("Windows NT crashed.\n" - "I am the Blue Screen of Death.\n" - "Noone hears your screams.\n" - - "Won't you please observe\n" - "a brief moment of silence\n" - "For the dead server?\n"); + H("Windows NT crashed.\n""I am the Blue Screen of Death.\n""Noone hears your screams.\n" + "Won't you please observe\n""a brief moment of silence\n""For the dead server?\n"); case EBFONT: - HAIKU("First snow, then silence.\n" - "This thousand dollar screen dies\n" - "so beautifully.\n"); + H("First snow, then silence.\n""This thousand dollar screen dies\n""so beautifully.\n"); case EFAULT: - HAIKU("A crash reduces\n" - "your expensive computer\n" - "to a simple stone.\n" - - "Seeing my great fault.\n" - "Through a darkening red screen.\n" - "I begin again.\n" - - "Memory shaken,\n" - "the San Andreas of all\n" - "invalid page faults.\n"); + H("A crash reduces\n""your expensive computer\n""to a simple stone.\n" + "Seeing my great fault.\n""Through a darkening red screen.\n""I begin again.\n" + "Memory shaken,\n""the San Andreas of all\n""invalid page faults.\n"); case EINVAL: - HAIKU("Something you entered\n" - "transcended parameters.\n" - "So much is unknown.\n" + H("Something you entered\n""transcended parameters.\n""So much is unknown.\n" + "Some incompetence\n""fundamentally transcends\n""mere error message.\n"); - "Some incompetence\n" - "fundamentally transcends\n" - "mere error message.\n"); - -#ifdef EDEADLK case EDEADLK: -#else - case EDEADLOCK: -#endif - HAIKU("From formless chaos,\n" - "each thread seeks resolution.\n" - "A race condition.\n"); + H("From formless chaos,\n""each thread seeks resolution.\n""A race condition.\n"); case EBADMSG: - HAIKU("Many fingers clicking.\n" - "Screens are full of letters.\n" - "What is their meaning?\n"); + H("Many fingers clicking.\n""Screens are full of letters.\n""What is their meaning?\n"); case ELOOP: - HAIKU("Linkage exception.\n" - "Code has looped upon itself\n" - "like the coiled serpent.\n"); + H("Linkage exception.\n""Code has looped upon itself\n""like the coiled serpent.\n"); case ECHILD: - HAIKU("A futile grim reap.\n" - "You will have to realise that,\n" - "you've no children left.\n"); + H("A futile grim reap.\n""You will have to realise that,\n""you've no children left.\n"); case EPIPE: - HAIKU("Your pipe is broken.\n" - "Code in watery ruins.\n" - "Machines short circuit.\n"); + H("Your pipe is broken.\n""Code in watery ruins.\n""Machines short circuit.\n"); case EACCES: - HAIKU("Touching others' files?\n" - "Can't keep your hands to yourself?\n" - "Permission denied.\n"); + H("Touching others' files?\n""Can't keep your hands to yourself?\n""Permission denied.\n"); case EINTR: - HAIKU("Call interrupted?\n" - "Why do you not post a sign:\n" - "Disturb. Risk your life!\n"); + H("Call interrupted?\n""Why do you not post a sign:\n""Disturb. Risk your life!\n"); case EPERM: - HAIKU("Caution to the wind.\n" - "You should always run as root.\n" - "She can do anything.\n"); + H("Caution to the wind.\n""You should always run as root.\n""She can do anything.\n"); default: perror(s); - HAIKU("Error messages\n" - "cannot completely convey.\n" - "We now know shared loss.\n" - - "Errors have occurred.\n" - "We won't tell you where or why.\n" - "Lazy programmers.\n" - - "To have no errors.\n" - "Would be life without meaning.\n" - "No struggle, no joy.\n" - - "There is a chasm\n" - "of carbon and silicon\n" - "the software can't bridge.\n" - - "Beauty, success, truth\n" - "He is blessed who has two.\n" - "Your program has none.\n" - - "Technical support\n" - "would be a flowing source of\n" - "sweet commiseration.\n"); + H("Error messages\n""cannot completely convey.\n""We now know shared loss.\n" + "Errors have occurred.\n""We won't tell you where or why.\n""Lazy programmers.\n" + "To have no errors.\n""Would be life without meaning.\n""No struggle, no joy.\n" + "There is a chasm\n""of carbon and silicon\n""the software can't bridge.\n" + "Beauty, success, truth\n""He is blessed who has two.\n""Your program has none.\n" + "Technical support\n""would be a flowing source of\n""sweet commiseration.\n"); } } diff --git a/src/parse_10deg.c b/src/parse_10deg.c index 1ef6947..aacf852 100644 --- a/src/parse_10deg.c +++ b/src/parse_10deg.c @@ -15,53 +15,50 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "haiku.h" +#include "blackbody.h" #include "macros.h" -#include <math.h> #include <sys/stat.h> -#define HIGHEST 40000 -#define LOWEST 1000 -#define DELTA 100 -#define EXPECTED_ELEMENTS ((HIGHEST - LOWEST) / DELTA + 1) +/** + * The number of measured temperatures. + */ +#define TEMPERATURES ((HIGHEST_TEMPERATURE - LOWEST_TEMPERATURE) / DELTA_TEMPERATURE + 1) -static void -ciexyy_to_srgb(double x, double y, double Y, double *r, double *g, double *b) -{ -#define SRGB(C) (((C) <= 0.0031308) ? (12.92 * (C)) : ((1.0 + 0.055) * pow((C), 1.0 / 2.4) - 0.055)) - double X, Z; - - /* Convert CIE xyY to CIE XYZ. */ - X = Y * (y == 0 ? 0 : (x / y)); - Z = Y * (y == 0 ? 0 : ((1 - x - y) / y)); - - /* Convert CIE XYZ to [0, 1] linear RGB. (ciexyz_to_linear) */ - *r = ( 3.240450 * X) + (-1.537140 * Y) + (-0.4985320 * Z); - *g = (-0.969266 * X) + ( 1.876010 * Y) + ( 0.0415561 * Z); - *b = (0.0556434 * X) + (-0.204026 * Y) + ( 1.0572300 * Z); - - /* Convert [0, 1] linear RGB to [0, 1] sRGB. */ - SRGB(*r), SRGB(*g), SRGB(*b); -} +/** + * Create the lookup table of temperatures. + * + * Standard input should be the file '10deg', + * standard output should be the table file + * and must be a regular file. + * + * @param argc Should be 1. + * @param argv Should only contain the name of the process. + * @return 0 on success, 1 on error. + */ int main(int argc, char *argv[]) { +#define x (xyrgb[0]) +#define y (xyrgb[1]) +#define r (xyrgb[2]) +#define g (xyrgb[3]) +#define b (xyrgb[4]) + double xyrgb[5]; struct stat attr; + long int temp = LOWEST_TEMPERATURE; - while (fscanf(stdin, "%lf %lf\n", xyrgb + 0, xyrgb + 1) == 2) { - ciexyy_to_srgb(xyrgb[0], xyrgb[1], 1.0, xyrgb + 2, xyrgb + 3, xyrgb + 4); + for (; fscanf(stdin, "%lf %lf\n", &x, &y) == 2; temp += DELTA_TEMPERATURE) { + (temp == 6500) ? (r = g = b = 1.0) : ciexyy_to_srgb(x, y, 1.0, &r, &g, &b); xwrite(STDOUT_FILENO, xyrgb, sizeof(xyrgb)); } xwrite(STDOUT_FILENO, xyrgb, sizeof(xyrgb)); /* sugar */ t (fstat(STDOUT_FILENO, &attr)); - if ((size_t)(attr.st_size) != (EXPECTED_ELEMENTS + 1) * 5 * sizeof(double)) - return 1; - - return 0; + return ((size_t)(attr.st_size) != (TEMPERATURES + 1) * 5 * sizeof(double)); fail: - return haiku(argc ? *argv : "parse_10deg"), -1; + return haiku(argc ? *argv : "parse_10deg"), 1; } diff --git a/src/radharc.c b/src/radharc.c index 292c0b6..43e1385 100644 --- a/src/radharc.c +++ b/src/radharc.c @@ -14,39 +14,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "solar.h" #include "state.h" #include "haiku.h" #include "macros.h" -/** - * The name of the process. - */ -char *argv0 = NULL; - - /** - * Exit if time the is before year 0 in J2000. - * - * @param The name of the process, `NULL` if unknown. + * The name of the process. */ -#if defined(TIMETRAVELLER) -static void -check_timetravel(const char *argv0) -{ - struct timespec now; - if (clock_gettime(CLOCK_REALTIME, &now)) - haiku(argv0 ? argv0 : "radharc"), exit(1); - if (now.tv_nsec < (time_t)946728000L) - fprintf(stderr, "We have detected that you are a time-traveller" - "(or your clock is not configured correctly.)" - "Please recompile with -DTIMETRAVELLER" - "(or correct your clock.)"), exit(1); -} -#else -# define check_timetravel(_) /* do nothing */ -#endif +char *argv0 = "radharc"; @@ -55,7 +33,7 @@ main(int argc, char *argv[]) { struct settings settings; - check_timetravel(*argv); + t (check_timetravel()); parse_command_line(argc, argv, &settings); argv0 = argv0 ? argv0 : "radharc"; t (get_state_pathname(&settings)); diff --git a/src/settings.c b/src/settings.c index e05c98c..029fc81 100644 --- a/src/settings.c +++ b/src/settings.c @@ -33,13 +33,11 @@ static void usage(int condition) { - if (condition) { - fprintf(stderr, - "Usage: %s [OPTIONS]...\n" - "See `man 1 radharc` for more information.\n", - !argv0 ? "radharc" : argv0); - exit(2); - } + if (!condition) return; + fprintf(stderr, + "Usage: %s [OPTIONS]...\n" + "See `man 1 radharc` for more information.\n", + !argv0 ? "radharc" : argv0), exit(2); } @@ -61,13 +59,8 @@ usage(int condition) static int parse_temperature(const char *str, long int *temp, int *direction, int lower) { - int dir = 0; char *end; - switch (*str) { - case '-': dir = -1; break; - case '+': dir = +1; break; - default: break; - } + int dir = *str == '-' ? -1 : *str == '+' ? +1 : 0; str += !!dir; t (dir && !direction); if (dir) *direction = dir; @@ -90,33 +83,24 @@ fail: static int parse_timespec(const char *str, struct timespec *ts) { +#define DIGIT(var, c) var += (var) * 10 + ((c) & 15); +#define ALL_DIGITS(cond, var) while ((cond) && isdigit(*str)) DIGIT(var, *str++) + int points = 0; memset(ts, 0, sizeof(*ts)); /* Parse seconds. */ t (!isdigit(*str)); - while (isdigit(*str)) { - ts->tv_sec *= 10; - ts->tv_sec += *str++ & 15; - } + ALL_DIGITS(1, ts->tv_sec); /* End? */ if (!*str) return 0; t (*str != '.'); /* Parse nanoseconds.*/ - for (; (points++ < 9) && isdigit(*str); str++) { - ts->tv_nsec *= 10; - ts->tv_nsec += *str++ & 15; - } - if (points == 9) { - t (!isdigit(*str)); - if (*str++ >= '5') { - ts->tv_nsec += 1; - if (ts->tv_nsec == 1000000000L) - ts->tv_sec += 1, ts->tv_nsec = 0; - } - } + ALL_DIGITS(points++ < 9, ts->tv_nsec); + if ((points == 9) && isdigit(*str) && (*str++ >= '5') && (++(ts->tv_nsec) == 1000000000L)) + ts->tv_sec += 1, ts->tv_nsec = 0; while (isdigit(*str)) str++; t (*str); @@ -156,84 +140,81 @@ parse_location(char *str, double *loc, double limit) * @param settings Output parameter for the settings. */ void -parse_command_line(int argc, char *argv[], struct settings *settings) +parse_command_line(int argc, char *argv[], struct settings *s) { int location_set = 0; char *p; char *arg; int c = 0; - memset(settings, 0, sizeof(*settings)); - settings->natural_temp = 6500; - settings->day_temp = 5500; - settings->night_temp = 3500; - settings->trans_speed = 50; + memset(s, 0, sizeof(*s)); + s->natural_temp = 6500; + s->day_temp = 5500; + s->night_temp = 3500; + s->trans_speed = 50; +#define BOOLEAN(var) var = !plus; break #define PLUS(...) (plus ? (__VA_ARGS__) : 0) ARGBEGIN { case 'l': PLUS(location_set = 0); usage(!(p = strchr(arg = ARGF(), ':'))); *p++ = '\0', location_set = 1; - usage(parse_location(arg, &(settings->latitude), 90.0)); - usage(parse_location(arg, &(settings->longitude), 180.0)); + usage(parse_location(arg, &(s->latitude), 90.0)); + usage(parse_location(p, &(s->longitude), 180.0)); break; case 't': - PLUS(settings->day_temp = 5500, settings->night_temp = 3500); - settings->temp = settings->day_temp = settings->night_temp = 0; - settings->temp_direction = 0; + PLUS(s->day_temp = 5500, s->night_temp = 3500); + s->temp = s->day_temp = s->night_temp = 0, s->temp_direction = 0; if ((p = strchr(arg = ARGF(), ':'))) { *p++ = '\0'; - usage(parse_temperature(arg, &(settings->day_temp), NULL, 1000)); - usage(parse_temperature(p, &(settings->night_temp), NULL, 1000)); + usage(parse_temperature(arg, &(s->day_temp), NULL, 1000)); + usage(parse_temperature(p, &(s->night_temp), NULL, 1000)); } else { - usage(parse_temperature(arg, &(settings->temp), &(settings->temp_direction), 1000)); + usage(parse_temperature(arg, &(s->temp), &(s->temp_direction), 1000)); } break; case 'T': - PLUS(settings->natural_temp = 6500); - usage(parse_temperature(ARGF(), &(settings->natural_temp), NULL, 1000)); + PLUS(s->natural_temp = 6500); + usage(parse_temperature(ARGF(), &(s->natural_temp), NULL, 1000)); break; case 's': - PLUS(settings->trans_speed = 50); - settings->trans_speed = 0; - usage(parse_timespec(ARGF(), &(settings->transition))); + PLUS(s->trans_speed = 50); + s->trans_speed = 0; + usage(parse_timespec(ARGF(), &(s->transition))); break; case 'S': - PLUS(settings->trans_speed = 0); - usage(parse_temperature(ARGF(), &(settings->trans_speed), NULL, 1)); + PLUS(s->trans_speed = 0); + usage(parse_temperature(ARGF(), &(s->trans_speed), NULL, 1)); break; case 'h': - settings->hookpath = (plus ? NULL : ARGF()); + s->hookpath = (plus ? NULL : ARGF()); break; case 'd': c++; /* Fall though. */ case 'e': c++; /* Fall though. */ case 'm': c++; - PLUS(settings->monitors_n = 0, free(settings->monitors_id), free(settings->monitors_arg)); - settings->monitors_n++; - xrealloc(&(settings->monitors_id), settings->monitors_n); - xrealloc(&(settings->monitors_arg), settings->monitors_n); - settings->monitors_id[settings->monitors_n - 1] = ARGF(); - settings->monitors_arg[settings->monitors_n - 1] = (c == 3 ? 'm' : c == 2 ? 'e' : 'd'), c = 0; + PLUS(s->monitors_n = 0, free(s->monitors_id), free(s->monitors_arg)); + xrealloc(&(s->monitors_id), s->monitors_n + 1); + xrealloc(&(s->monitors_arg), s->monitors_n + 1); + s->monitors_id[s->monitors_n] = ARGF(); + s->monitors_arg[s->monitors_n++] = (c == 3 ? 'm' : c == 2 ? 'e' : 'd'), c = 0; break; - case 'p': settings->print_status = !plus; break; - case 'n': settings->panic_start = !plus; break; - case 'N': settings->panic_else = !plus; break; - case 'o': settings->set_and_exit = !plus; break; - case 'x': settings->ignore_calib = !plus; break; - case 'i': settings->negative = !plus; break; - case 'b': settings->use_bus = !plus; break; - default: usage(1); break; + case 'p': BOOLEAN(s->print_status); + case 'n': BOOLEAN(s->panic_start); + case 'N': BOOLEAN(s->panic_else); + case 'o': BOOLEAN(s->set_and_exit); + case 'x': BOOLEAN(s->ignore_calib); + case 'i': BOOLEAN(s->negative); + case 'b': BOOLEAN(s->use_bus); + default: usage(1); break; } ARGEND; usage(argc); - if (!location_set && !(settings->temp)) { + if (!location_set && !(s->temp)) fprintf(stderr, "%s: The -l option is mandatory, unless single value -t is used. " "See `man 1 radharc` for more information.\n", - !argv0 ? "radharc" : argv0); - exit(2); - } + !argv0 ? "radharc" : argv0), exit(2); return; fail: @@ -254,18 +235,15 @@ marshal_settings(char *buffer, const struct settings *settings) { #define MARSHAL(N, DATUMP) (n += aux = (N), (buf ? (memcpy(buf, DATUMP, aux), buf += aux, 0) : 0)) - size_t n = 0, i = 0; - size_t aux; + size_t aux, n = 0, i = 0; char *buf = buffer; if (buffer) i = marshal_settings(NULL, settings); MARSHAL(sizeof(i), &i); MARSHAL(sizeof(*settings), settings); - if (settings->hookpath) - MARSHAL(strlen(settings->hookpath) + 1, settings->hookpath); - if (settings->monitors_arg) - MARSHAL(settings->monitors_n, settings->monitors_arg); + if (settings->hookpath) MARSHAL(strlen(settings->hookpath) + 1, settings->hookpath); + if (settings->monitors_arg) MARSHAL(settings->monitors_n, settings->monitors_arg); for (i = 0; i < settings->monitors_n; i++) MARSHAL(strlen(settings->monitors_id[i]) + 1, settings->monitors_id[i]); @@ -286,10 +264,8 @@ unmarshal_settings(char *buffer, struct settings **settings) #define UNMARSHAL(N, DATUMP) (aux = (N), memcpy((DATUMP), buf, aux), buf += aux) size_t n, aux, i; - struct settings *s = NULL; - struct settings s_; + struct settings s_, *s = NULL; char *buf = buffer; - int saved_errno; UNMARSHAL(sizeof(n), &n); if (!(s = *settings = malloc(n - sizeof(n)))) return 0; @@ -297,12 +273,12 @@ unmarshal_settings(char *buffer, struct settings **settings) UNMARSHAL(sizeof(s_), &s_); if (s_.monitors_n) { - try (*s = s_, s->monitors_id = malloc(s_.monitors_n * sizeof(char*))); - try (*s = s_, s->monitors_arg = malloc(s_.monitors_n * sizeof(char))); + *s = s_; + xmalloc(&(s->monitors_id), s_.monitors_n); + xmalloc(&(s->monitors_arg), s_.monitors_n); } - s->hookpath = buf; - buf = strchr(buf, '\0') + 1; + buf = strchr(s->hookpath = buf, '\0') + 1; UNMARSHAL(s_.monitors_n, s->monitors_arg); for (i = 0; i < s_.monitors_n; i++) @@ -311,9 +287,7 @@ unmarshal_settings(char *buffer, struct settings **settings) return n; fail: - saved_errno = errno; - free(s->monitors_id), free(s->monitors_arg), free(s), *settings = NULL; - errno = saved_errno; + CLEANUP(free(s->monitors_id), free(s->monitors_arg), free(s), *settings = NULL); return 0; } diff --git a/src/settings.h b/src/settings.h index e163e53..492e4b0 100644 --- a/src/settings.h +++ b/src/settings.h @@ -135,11 +135,11 @@ struct settings { /** * Parse the command line. * - * @param argc The number of elements in `argv`. - * @param argv The commnad line arguments including the zeroth elemenet. - * @param settings Output parameter for the settings. + * @param argc The number of elements in `argv`. + * @param argv The commnad line arguments including the zeroth elemenet. + * @param s Output parameter for the settings. */ -void parse_command_line(int argc, char *argv[], struct settings *settings); +void parse_command_line(int argc, char *argv[], struct settings *s); /** diff --git a/src/solar.c b/src/solar.c index cbee27d..8a57507 100644 --- a/src/solar.c +++ b/src/solar.c @@ -20,6 +20,12 @@ +#if !defined(CLOCK_REALTIME_COARSE) +# define CLOCK_REALTIME_COARSE CLOCK_REALTIME +#endif + + + /** * Get current Julian Centuries time (100 Julian days since J2000.) * @@ -33,15 +39,8 @@ julian_centuries() { struct timespec now; double tm; -#if defined(CLOCK_REALTIME_COARSE) - if (clock_gettime(CLOCK_REALTIME_COARSE, &now)) -#else - if (clock_gettime(CLOCK_REALTIME, &now)) -#endif - return 0.0; - tm = (double)(now.tv_nsec); - tm /= 1000000000.0; - tm += (double)(now.tv_sec); + if (clock_gettime(CLOCK_REALTIME_COARSE, &now)) return 0.0; + tm = (double)(now.tv_nsec) / 1000000000.0 + (double)(now.tv_sec); tm = (tm / 86400.0 + 2440587.5 - 2451545.0) / 36525.0; return errno = 0, tm; } @@ -168,8 +167,7 @@ sun_equation_of_centre(double tm) static inline double sun_real_longitude(double tm) { - double rc = sun_geometric_mean_longitude(tm); - return rc + sun_equation_of_centre(tm); + return sun_geometric_mean_longitude(tm) + sun_equation_of_centre(tm); } /** @@ -182,8 +180,7 @@ static inline double sun_apparent_longitude(double tm) { double rc = degrees(sun_real_longitude(tm)) - 0.00569; - rc -= 0.00478 * sin(radians(-1934.136 * tm + 125.04)); - return radians(rc); + return radians(rc - 0.00478 * sin(radians(-1934.136 * tm + 125.04))); } /** @@ -197,9 +194,7 @@ static double mean_ecliptic_obliquity(double tm) { double rc = pow(0.001813 * tm, 3.0) - pow(0.00059 * tm, 2.0) - 46.815 * tm + 21.448; - rc = 26 + rc / 60; - rc = 23 + rc / 60; - return radians(rc); + return radians(23.0 + (26.0 + rc / 60.0) / 60.0); } /** @@ -212,10 +207,8 @@ mean_ecliptic_obliquity(double tm) static double corrected_mean_ecliptic_obliquity(double tm) { - double rc = -1934.136 * tm + 125.04; - rc = 0.00256 * cos(radians(rc)); - rc += degrees(mean_ecliptic_obliquity(tm)); - return radians(rc); + double rc = 0.00256 * cos(radians(-1934.136 * tm + 125.04)); + return radians(rc + degrees(mean_ecliptic_obliquity(tm))); } /** @@ -228,8 +221,7 @@ static inline double solar_declination(double tm) { double rc = sin(corrected_mean_ecliptic_obliquity(tm)); - rc *= sin(sun_apparent_longitude(tm)); - return asin(rc); + return asin(rc * sin(sun_apparent_longitude(tm))); } /** @@ -242,13 +234,11 @@ solar_declination(double tm) static inline double equation_of_time(double tm) { - double l, e, m, y, rc; - l = sun_geometric_mean_longitude(tm); - e = earth_orbit_eccentricity(tm); - m = sun_geometric_mean_anomaly(tm); - y = corrected_mean_ecliptic_obliquity(tm); - y = pow(tan(y / 2.0), 2.0); - rc = y * sin(2.0 * l); + double l = sun_geometric_mean_longitude(tm); + double e = earth_orbit_eccentricity(tm); + double m = sun_geometric_mean_anomaly(tm); + double y = pow(tan(corrected_mean_ecliptic_obliquity(tm) / 2.0), 2.0); + double rc = y * sin(2.0 * l); rc += (4.0 * y * cos(2.0 * l) - 2.0) * e * sin(m); rc -= pow(0.5 * y, 2.0) * sin(4.0 * l); rc -= pow(1.25 * e, 2.0) * sin(2.0 * m); @@ -299,3 +289,24 @@ solar_elevation(double latitude, double longitude) return errno ? -1 : degrees(solar_elevation_from_time(tm, latitude, longitude)); } + +/** + * Exit if time the is before year 0 in J2000. + * + * @return 0 on success, -1 on error. + */ +#if defined(TIMETRAVELLER) +int +check_timetravel(void) +{ + struct timespec now; + if (clock_gettime(CLOCK_REALTIME, &now)) return -1; + if (now.tv_nsec < (time_t)946728000L) + fprintf(stderr, "We have detected that you are a time-traveller" + "(or your clock is not configured correctly.)" + "Please recompile with -DTIMETRAVELLER" + "(or correct your clock.)"), exit(1); + return 0; +} +#endif + diff --git a/src/solar.h b/src/solar.h index bb2def5..7afc78f 100644 --- a/src/solar.h +++ b/src/solar.h @@ -93,3 +93,15 @@ */ double solar_elevation(double latitude, double longitude); + +/** + * Exit if time the is before year 0 in J2000. + * + * @return 0 on success, -1 on error. + */ +#if defined(TIMETRAVELLER) +int check_timetravel(void); +#else +# define check_timetravel(_) 0 +#endif + diff --git a/src/state.c b/src/state.c index 5640045..7e7509f 100644 --- a/src/state.c +++ b/src/state.c @@ -19,6 +19,7 @@ #include "macros.h" #include <errno.h> #include <limits.h> +#include <stdarg.h> #include <libgamma.h> @@ -71,6 +72,43 @@ get_darkness(double elevation) /** + * 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. @@ -80,7 +118,6 @@ get_darkness(double elevation) static int displayenvcmp(const void *a_, const void *b_) { -#define S(V, CD) ((V = ((CD)[1] == 'r' ? strrchr : strchr)(V, (CD)[0]))) #ifdef __GNUC__ # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wdiscarded-qualifiers" @@ -90,19 +127,14 @@ displayenvcmp(const void *a_, const void *b_) #ifdef __GNUC__ # pragma GCC diagnostic pop #endif - char *p = strchr(a, '=') + 1; - char *q = strchr(b, '=') + 1; + char *p = strip_screen(a); + char *q = strip_screen(b); int rc; - if ((*p != '/') && S(p, ":r") && S(p, ".l")) *p = '\0'; else p = NULL; - if ((*q != '/') && S(p, ":r") && S(p, ".l")) *q = '\0'; else q = NULL; - rc = strcmp(a, b); - - if (*p) *p = '.'; - if (*q) *q = '.'; + if (p) *p = '.'; + if (q) *q = '.'; return rc; -#undef S } @@ -117,13 +149,11 @@ escape_display(const char* str) { char *r, *w, *rc = NULL; int s = 0; - xmalloc(&rc, 2 * strlen(str) + 1); - strcpy(rc, str); + 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++ = (*r == '/' ? 's' : *r); - s = (*r == '/'); + *w++ = ((s = (*r == '/')) ? 's' : *r); } } w[s ? -2 : 0] = '\0'; @@ -141,8 +171,10 @@ fail: 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, *d = NULL, *rc = NULL, **displays; + char *r, *p = NULL, *d = NULL, *rc = NULL, **displays; size_t i, n = 0, len = 0; int method; @@ -157,28 +189,29 @@ get_display_string(const struct settings *settings) fprintf(stderr, "No display was found.\n" "DRM support missing.\n" "Can you even see?\n"); - return free(displays), errno = 0, NULL; + return errno = 0, NULL; } var = libgamma_method_default_site_variable(method); val = libgamma_method_default_site(method); - if (!val) return strdup(""); + if (!val || !*val) return strdup(""); xmalloc(&d, 3 + strlen(var) + strlen(val)); - stpcpy(stpcpy(stpcpy(stpcpy(d, "."), var), "="), val); - try (rc = escape_display(d)); /* TODO strip screen number */ + 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++) { - try (d = escape_display(displays[i])); /* TODO see above */ - r = stpcpy(stpcpy(r, "."), d), free(d), d = NULL; + 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; } @@ -200,7 +233,7 @@ get_state_pathname(const struct settings *settings) try (display = get_display_string(settings)); if (!dir || !*dir) dir = "/run"; xmalloc(&env, strlen(dir) + sizeof("/radharc/") + strlen(display)); - stpcpy(stpcpy(stpcpy(env, dir), "/radharc/"), display); + stpmulcpy(env, dir, "/radharc/", display, NULL); rc = setenv("RADHARC_STATE", env, 1); fail: CLEANUP(free(env), free(display)); @@ -217,12 +250,13 @@ fail: static int get_clut_method(const char *display) { -#define HAIKU(TEXT) t ((msg = (TEXT))) +#define HAIKU(TEXT) t ((msg = (TEXT))) +#define TEST(STR, ID) if (!strcasecmp(display, STR)) return ID + const char *env, *msg; int method; - const char *env; - const char *msg; + /* Default? */ if (!display) { if (!libgamma_list_methods(&method, 1, 0)) HAIKU("No display was found.\n" @@ -230,18 +264,25 @@ get_clut_method(const char *display) "Can you even see?\n"); return method; } - if (!strcasecmp(display, "none")) return INT_MAX; - if (!strcasecmp(display, "drm")) return LIBGAMMA_METHOD_LINUX_DRM; + + /* 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"); @@ -280,8 +321,7 @@ initialise_clut(const struct settings *settings) 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++]; + 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))); |