diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/unstickpixels.c | 363 |
1 files changed, 331 insertions, 32 deletions
diff --git a/src/unstickpixels.c b/src/unstickpixels.c index 817ba83..0c75c78 100644 --- a/src/unstickpixels.c +++ b/src/unstickpixels.c @@ -20,11 +20,16 @@ #include <unistd.h> #include <stdlib.h> #include <signal.h> +#include <string.h> +#include <assert.h> #include <time.h> #include <errno.h> #include <ctype.h> +#include <signal.h> #include <sys/wait.h> +#include <libgamma.h> + #define t(...) do { if (__VA_ARGS__) goto fail; } while (0) @@ -78,6 +83,58 @@ */ const char* argv0; +/** + * Should we exit? + */ +static volatile sig_atomic_t please_exit = 0; + +/** + * The name of the site. + */ +static char* sitename = NULL; + +/** + * The site. + */ +static libgamma_site_state_t site; + +/** + * The partitions. + */ +static libgamma_partition_state_t* restrict* restrict parts = NULL; + +/** + * The CRTC:s. + */ +static libgamma_crtc_state_t* restrict* restrict crtcs = NULL; + +/** + * The ramps to use when displaying red. + */ +static libgamma_gamma_ramps16_t* restrict* restrict ramps_red = NULL; + +/** + * The ramps to use when displaying green. + */ +static libgamma_gamma_ramps16_t* restrict* restrict ramps_green = NULL; + +/** + * The ramps to use when displaying blue. + */ +static libgamma_gamma_ramps16_t* restrict* restrict ramps_blue = NULL; + +/** + * The original ramps. + */ +static libgamma_gamma_ramps16_t* restrict* restrict ramps_saved = NULL; + +/** + * 2: `site` initialised and all ramps saved. + * 1: `site` initialised. + * 0: nothing done. + */ +static int clut_state = 0; + /** @@ -89,13 +146,14 @@ const char* argv0; * otherwise it is set to a non-zero value (1). * @return 0 on success, -1 on error. */ -static int get_interval(const char* arg, struct timespec* interval, int* nonzero) +static int get_interval(const char* restrict arg, struct timespec* restrict interval, int* restrict nonzero) { char* end; *nonzero = 1; + errno = EINVAL; t (!isdigit(*arg)); interval->tv_sec = (time_t)(errno = 0, strtol)(arg, &end, 10); - t (errno || *end); + t (errno || (errno = EINVAL, *end)); interval->tv_nsec = interval->tv_sec % 1000; interval->tv_sec /= 1000; interval->tv_nsec *= 1000000L; @@ -126,61 +184,302 @@ static pid_t xfork(void) } -int main(int argc, char* argv[]) +/** + * Print usage information and exit. + */ +static void usage(void) { - int started = 0; - int with_sleep = 0; - struct timespec interval; - int _status; - pid_t pid; + exit(fprintf(stderr, "Usage: %s [-v] [--] [SLEEP_INTERVAL_MS]\n", + strrchr(argv0, '/') ? (strrchr(argv0, '/') + 1) : argv0 + ) < 0 ? 1 : 2); +} + + +/** + * Uninitialise the CLUT backend. + * + * @return 0 on success, -1 on error. + */ +static void term_clut() +{ + size_t i; + for (i = 0; crtcs && crtcs[i]; i++) + { + if (clut_state == 2) + libgamma_crtc_set_gamma_ramps16(crtcs[i], *(ramps_saved[i])); + libgamma_crtc_free(crtcs[i]); + libgamma_gamma_ramps16_free(ramps_red[i]); + libgamma_gamma_ramps16_free(ramps_green[i]); + libgamma_gamma_ramps16_free(ramps_blue[i]); + libgamma_gamma_ramps16_free(ramps_saved[i]); + } + free((void*)crtcs); + free((void*)ramps_red); + free((void*)ramps_green); + free((void*)ramps_blue); + free((void*)ramps_saved); + for (i = 0; parts && parts[i]; i++) + libgamma_partition_free(parts[i]); + free((void*)parts); + free((void*)sitename); + if (clut_state) + libgamma_site_destroy(&site); + sitename = NULL; + parts = NULL; + crtcs = NULL; + ramps_red = ramps_green = ramps_blue = ramps_saved = NULL; + clut_state = 0; +} + + +/** + * Initialise the CLUT backend. + * + * @return 0 on success, -1 on error. + */ +static int init_clut() +{ + int method, error = 0; + size_t i, j, k, n = 0; + libgamma_crtc_information_t info; - argv0 = (argc ? "unstickpixels" : *argv); + if (libgamma_list_methods(&method, 1, 0) < 1) + { + fprintf(stderr, "%s: could not get adjustment method, try '%s -v'.\n", argv0, argv0); + return -1; + } - if (argc > 1) - t (argv[1], &interval, &with_sleep); + sitename = libgamma_method_default_site(method); + if (sitename) + { + sitename = strdup(sitename); + t (sitename == NULL); + } + t ((error = libgamma_site_initialise(&site, method, sitename))); + sitename = NULL; - printf(WELCOME_MESSAGE); - fflush(stdout); + clut_state++; - if (read(STDIN_FILENO, &started, 1) < 0) - goto done; - started = 1; + if (site.partitions_available == 0) + { + fprintf(stderr, "%s: could find any graphics card/screen, try '%s -v'.\n", argv0, argv0); + errno = 0; + goto fail; + } - pid = xfork(); - if (pid == -1) - return perror(argv0), 1; - else if (pid) - goto parent; + t (!(parts = calloc(site.partitions_available + 1, sizeof(*parts)))); + for (i = 0; i < site.partitions_available; i++) + { + t (!(parts[i] = calloc(1, sizeof(**parts)))); + t ((errno = libgamma_partition_initialise(parts[i], &site, i))); + n += parts[i]->crtcs_available; + } - t (printf("\033[H") == -1); - t (fflush(stdout)); + if (n == 0) + { + fprintf(stderr, "%s: could find any CRTC, try '%s -v'.\n", argv0, argv0); + errno = 0; + goto fail; + } + + t (!(crtcs = calloc(n + 1, sizeof(*crtcs)))); + t (!(ramps_red = calloc(n + 1, sizeof(*ramps_red)))); + t (!(ramps_green = calloc(n + 1, sizeof(*ramps_green)))); + t (!(ramps_blue = calloc(n + 1, sizeof(*ramps_blue)))); + t (!(ramps_saved = calloc(n + 1, sizeof(*ramps_saved)))); + for (i = k = 0; i < site.partitions_available; i++) + for (j = 0; j < parts[i]->crtcs_available; j++, k++) + { + t (!(crtcs[k] = calloc(1, sizeof(**crtcs)))); + t (!(ramps_red[k] = calloc(1, sizeof(**ramps_red)))); + t (!(ramps_green[k] = calloc(1, sizeof(**ramps_green)))); + t (!(ramps_blue[k] = calloc(1, sizeof(**ramps_blue)))); + t (!(ramps_saved[k] = calloc(1, sizeof(**ramps_saved)))); + t ((error = libgamma_crtc_initialise(crtcs[k], parts[i], j))); + if (libgamma_get_crtc_information(&info, crtcs[k], LIBGAMMA_CRTC_INFO_GAMMA_SIZE)) + t ((error = info.gamma_size_error)); + ramps_red[k]->red_size = info.red_gamma_size; + ramps_red[k]->green_size = info.green_gamma_size; + ramps_red[k]->blue_size = info.blue_gamma_size; + *(ramps_saved[k]) = *(ramps_blue[k]) = *(ramps_green[k]) = *(ramps_red[k]); + t (libgamma_gamma_ramps16_initialise(ramps_red[k])); + t (libgamma_gamma_ramps16_initialise(ramps_green[k])); + t (libgamma_gamma_ramps16_initialise(ramps_blue[k])); + t (libgamma_gamma_ramps16_initialise(ramps_saved[k])); + memset(ramps_red[k]->red, ~0, ramps_red[k]->red_size * sizeof(*(ramps_red[k]->red))); + memset(ramps_red[k]->green, 0, ramps_red[k]->green_size * sizeof(*(ramps_red[k]->green))); + memset(ramps_red[k]->blue, 0, ramps_red[k]->blue_size * sizeof(*(ramps_red[k]->blue))); + memset(ramps_green[k]->red, 0, ramps_green[k]->red_size * sizeof(*(ramps_green[k]->red))); + memset(ramps_green[k]->green, ~0, ramps_green[k]->green_size * sizeof(*(ramps_green[k]->green))); + memset(ramps_green[k]->blue, 0, ramps_green[k]->blue_size * sizeof(*(ramps_green[k]->blue))); + memset(ramps_blue[k]->red, 0, ramps_blue[k]->red_size * sizeof(*(ramps_blue[k]->red))); + memset(ramps_blue[k]->green, 0, ramps_blue[k]->green_size * sizeof(*(ramps_blue[k]->green))); + memset(ramps_blue[k]->blue, ~0, ramps_blue[k]->blue_size * sizeof(*(ramps_blue[k]->blue))); + t ((error = libgamma_crtc_get_gamma_ramps16(crtcs[k], ramps_saved[k]))); + } - for (;;) + clut_state++; + + return 0; + fail: + if (error) + libgamma_perror(argv0, error); + else if (errno) + perror(argv0); + term_clut(); + errno = 0; + return -1; +} + + +/** + * The loop, using the graphic cards' colour lookup tables. + * + * @param interval The sleep interval, `NULL` for no sleep. + */ +static void loop_clut(const struct timespec* restrict interval) +{ + size_t i; + int error; + + while (!please_exit) + { + for (i = 0; crtcs[i]; i++) + t ((error = libgamma_crtc_set_gamma_ramps16(crtcs[i], *(ramps_red[i])))); + if (interval) + t ((errno = clock_nanosleep(CLOCK_MONOTONIC, 0, interval, NULL))); + + for (i = 0; crtcs[i]; i++) + t ((error = libgamma_crtc_set_gamma_ramps16(crtcs[i], *(ramps_green[i])))); + if (interval) + t ((errno = clock_nanosleep(CLOCK_MONOTONIC, 0, interval, NULL))); + + for (i = 0; crtcs[i]; i++) + t ((error = libgamma_crtc_set_gamma_ramps16(crtcs[i], *(ramps_blue[i])))); + if (interval) + t ((errno = clock_nanosleep(CLOCK_MONOTONIC, 0, interval, NULL))); + } + return; + fail: + if (error) + libgamma_perror(argv0, error), errno = 0; +} + + +/** + * The loop, using Linux VT. + * + * @param interval The sleep interval, `NULL` for no sleep. + */ +static void loop_vt(const struct timespec* restrict interval) +{ + while (!please_exit) { t (printf("\033]P0FF0000\033[2J") == -1); t (fflush(stdout)); - if (with_sleep) - clock_nanosleep(CLOCK_MONOTONIC, 0, &interval, NULL); + if (interval) + t ((errno = clock_nanosleep(CLOCK_MONOTONIC, 0, interval, NULL))); t (printf("\033]P000FF00\033[2J") == -1); t (fflush(stdout)); - if (with_sleep) - clock_nanosleep(CLOCK_MONOTONIC, 0, &interval, NULL); + if (interval) + t ((errno = clock_nanosleep(CLOCK_MONOTONIC, 0, interval, NULL))); t (printf("\033]P00000FF\033[2J") == -1); t (fflush(stdout)); - if (with_sleep) - clock_nanosleep(CLOCK_MONOTONIC, 0, &interval, NULL); + if (interval) + t ((errno = clock_nanosleep(CLOCK_MONOTONIC, 0, interval, NULL))); } + return; + fail: + return; +} + + +/** + * Signal handler for signals that exit the program. + * + * @param signo The caught signal. + */ +static void sigexit(int signo) +{ + signal(signo, sigexit); + please_exit = 1; +} + + +int main(int argc, char* argv[]) +{ + int started = 0; + int with_sleep = 0; + struct timespec interval; + int _status, vt = 0, c = 0; + pid_t pid = -1; + int saved_errno; + + for (argv0 = (argc ? "unstickpixels" : *argv); c != -1;) + switch ((c = getopt(argc, argv, "v"))) + { + case 'v': vt = 1; + case -1: break; + case '?': usage(); + default: assert(0), abort(); + } + if (optind < argc) + t (get_interval(argv[optind++], &interval, &with_sleep)); + if (optind < argc) + usage(); + + printf(WELCOME_MESSAGE); + fflush(stdout); + + siginterrupt(SIGHUP, 1); + siginterrupt(SIGTERM, 1); + siginterrupt(SIGINT, 1); + siginterrupt(SIGQUIT, 1); + signal(SIGHUP, sigexit); + signal(SIGTERM, sigexit); + signal(SIGINT, sigexit); + signal(SIGQUIT, sigexit); + + if (read(STDIN_FILENO, &started, 1) < 0) + goto done; + started = 1; + + if (vt) + t (printf("\033[H") == -1); + else + t (printf("\033[H\033[2JIf you can read this, try '%s -v'.\n", argv0) == -1); + t (fflush(stdout)); + + t (vt ? 0 : init_clut()); + + t (pid = xfork(), pid == -1); + if (pid) + goto parent; + + (vt ? loop_vt : loop_clut)(with_sleep ? &interval : NULL); fail: + saved_errno = errno; + if (!vt) + term_clut(); + errno = saved_errno; + if (pid == -1) + { + if (errno == EINVAL) + usage(); + return (errno && (errno != EINTR)) ? (perror(argv0), 1) : 1; + } return 0; parent: waitpid(pid, &_status, 0); done: - printf("%s\033[?25h\033[H\033[2J", started ? "\033]P0000000" : ""); + if (!vt) + term_clut(); + printf("%s\033[?25h\033[H\033[2J", (started && vt) ? "\033]P0000000" : ""); fflush(stdout); - return 0; + return 0; /* TODO return 1 on failure */ } |