diff options
Diffstat (limited to 'unstickpixels.c')
-rw-r--r-- | unstickpixels.c | 446 |
1 files changed, 446 insertions, 0 deletions
diff --git a/unstickpixels.c b/unstickpixels.c new file mode 100644 index 0000000..699f0ac --- /dev/null +++ b/unstickpixels.c @@ -0,0 +1,446 @@ +/* See LICENSE file for copyright and license details. */ +#include <sys/wait.h> +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <libgamma.h> + +#include "arg.h" + +#define t(...) do { if (__VA_ARGS__) goto fail; } while (0) + +#define WELCOME_MESSAGE\ + "\033[H\033[01;31mWARNING: Do not use this if you have epilepsia.\033[00m\n"\ + "\n"\ + "It is recommended to massage the defective dots, whilst\n"\ + "running this program.\n"\ + "\n"\ + "Press C-c to quit, and Enter to start (C-c will quit).\n"\ + "\n"\ + "\n"\ + "You should not have to run this program. If the manufacture or\n"\ + "reseller of your monitor refuse to replace or repair the monitor\n"\ + "that was broken, because of manufacturing error, when it was\n"\ + "chiped to you, please consider yelling at the support, threaten\n"\ + "to report the personally because of involvement in organised\n"\ + "crime (not replacing hardware that was chipped broken when you\n"\ + "selled it is a crime, and they do this in an organised fashion\n"\ + "to make money), call their executive director persistently at\n"\ + "inconvenient times, report them applicable board for consumer\n"\ + "disputes/complaints or equivalent agency. If this program does\n"\ + "not help, and the manufacture is Packard Bell, or its\n"\ + "supersidiary Acer, tell them that you have tried a program\n"\ + "that was prompted by the complete suckness and incapability\n"\ + "to repair defects that they have agreed to repair. They suck!" + +/** + * The name of the process. + */ +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; + + + +/** + * Parse interval argument. + * + * @param argv The interval argument. + * @param interval Output parameter for the interval. + * @param nonzero With be set to zero if the interval is zero, + * otherwise it is set to a non-zero value (1). + * @return 0 on success, -1 on error. + */ +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 || (errno = EINVAL, *end)); + interval->tv_nsec = interval->tv_sec % 1000; + interval->tv_sec /= 1000; + interval->tv_nsec *= 1000000L; + if (interval->tv_sec == 0) + if (interval->tv_nsec == 0) + *nonzero = 0; + return 0; +fail: + return -1; +} + + +/** + * Wrapper for fork(3) that clocks all signals in the parent. + * + * @return See fork(3). + */ +static pid_t +xfork(void) +{ + sigset_t set; + pid_t pid; + sigfillset(&set); + sigprocmask(SIG_BLOCK, &set, 0); + pid = fork(); + if (pid != -1) + sigprocmask(SIG_UNBLOCK, &set, 0); + return pid; +} + + +/** + * Print usage information and exit. + */ +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-v] [milliseconds]\n", argv0); + exit(1); +} + + +/** + * Uninitialise the CLUT backend. + * + * @return 0 on success, -1 on error. + */ +static void term_clut() +{ + size_t i, j; + if (clut_state == 2) + for (j = 0; j < 2; j++, usleep(1000000L / 10L)) + for (i = 0; crtcs && crtcs[i]; i++, usleep(1000000L / 100L)) + libgamma_crtc_set_gamma_ramps16(crtcs[i], *(ramps_saved[i])); + for (i = 0; crtcs && crtcs[i]; 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; + + 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 ((sitename = libgamma_method_default_site(method))) + t (!(sitename = strdup(sitename))); + t ((error = libgamma_site_initialise(&site, method, sitename))); + sitename = NULL; + + clut_state++; + + if (!site.partitions_available) { + fprintf(stderr, "%s: could find any graphics card/screen, try '%s -v'.\n", argv0, argv0); + errno = 0; + goto fail; + } + + 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; + } + + if (!n) { + 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]))); + } + } + + 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 (interval) + t ((errno = clock_nanosleep(CLOCK_MONOTONIC, 0, interval, NULL))); + + t (printf("\033]P000FF00\033[2J") == -1); + t (fflush(stdout)); + if (interval) + t ((errno = clock_nanosleep(CLOCK_MONOTONIC, 0, interval, NULL))); + + t (printf("\033]P00000FF\033[2J") == -1); + t (fflush(stdout)); + 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; + pid_t pid = -1; + int saved_errno; + + ARGBEGIN { + case 'v': + vt = 1; + break; + default: + usage(); + break; + } ARGEND; + + if (optind < argc) + t (get_interval(argv[optind++], &interval, &with_sleep)); + if (optind < argc) + usage(); + + printf("\033[?25l\033[H\033[2J"); + 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()) == -1); + if (pid) + goto parent; + + (vt ? loop_vt : loop_clut)(with_sleep ? &interval : NULL); + +fail: + saved_errno = errno; + printf("%s\033[?25h\033[H\033[2J", (started && vt) ? "\033]P0000000" : ""); + fflush(stdout); + 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: + if (!vt) + term_clut(); + printf("%s\033[?25h\033[H\033[2J", (started && vt) ? "\033]P0000000" : ""); + fflush(stdout); + return WIFEXITED(status) ? WEXITSTATUS(status) : 0; +} |