aboutsummaryrefslogtreecommitdiffstats
path: root/unstickpixels.c
diff options
context:
space:
mode:
Diffstat (limited to 'unstickpixels.c')
-rw-r--r--unstickpixels.c446
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;
+}