aboutsummaryrefslogblamecommitdiffstats
path: root/cg-remove.c
blob: 20ca51e6ae3ee29652c05cd0961c8ab088de731e (plain) (tree)
1
                                                         























                                   

           
 



                                                                          








                                            

                     
 













                                                         








                                        

                  
 












                                          











                                            

                
 












                                                










                                                             

                                                                          
 

















































                                                                                                         
                         
                                         
      






                                                   
                 























                                                                                                    
                         
                                                         
      






                                                          
                 













                            





















                                                      

                            
 



































































                                                                            
  



















































                                                                                                                
 
/* See LICENSE file for copyright and license details. */
#include "arg.h"

#include <libcoopgamma.h>

#include <errno.h>
#include <inttypes.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>



/**
 * The libcoopgamma context
 */
static libcoopgamma_context_t cg;



/**
 * Print usage information and exit
 */
static void
usage(void)
{
	fprintf(stderr,
	        "usage: %s [-M method] [-S site] [-c crtc]... class...\n",
	        argv0);
	exit(1);
}


/**
 * Initialise the process, specifically
 * reset the signal mask and signal handlers
 * 
 * @return  Zero on success, -1 on error
 */
static int
initialise_proc(void)
{
	sigset_t sigmask;
	int sig;

	for (sig = 1; sig < _NSIG; sig++)
		if (signal(sig, SIG_DFL) == SIG_ERR)
			if (sig == SIGCHLD)
				return -1;

	if (sigemptyset(&sigmask) < 0)
		return -1;
	if (sigprocmask(SIG_SETMASK, &sigmask, NULL) < 0)
		return -1;

	return 0;
}


/**
 * Print, to stdout, a list of all
 * recognised adjustment methods
 * 
 * @return  Zero on success, -1 on error
 */
static int
list_methods(void)
{
	char **list;
	size_t i;

	list = libcoopgamma_get_methods();
	if (!list)
		return -1;
	for (i = 0; list[i]; i++)
		printf("%s\n", list[i]);
	free(list);
	if (fflush(stdout) < 0)
		return -1;

	return 0;
}


/**
 * Print, to stdout, a list of all CRTC:s
 * 
 * A connection to the coopgamma server
 * must have been made
 * 
 * @return  Zero on success, -1 on error, -2
 *          on libcoopgamma error
 */
static int
list_crtcs(void)
{
	char **list;
	size_t i;

	list = libcoopgamma_get_crtcs_sync(&cg);
	if (!list)
		return -2;
	for (i = 0; list[i]; i++)
		printf("%s\n", list[i]);
	free(list);
	if (fflush(stdout) < 0)
		return -1;

	return 0;
}


/**
 * Remove selected filters from selected CRTC:s
 * 
 * @param   crtcs    `NULL`-terminated list of CRTC names
 * @param   classes  `NULL`-terminated list of filter classes
 * @return           Zero on success, -1 on error, -2 on
 *                   libcoopgamma error
 */
static int
remove_filters(char *const *restrict crtcs, char *const *restrict classes)
{
	size_t n = 0, unsynced = 0, selected, i, j;
	char *synced = NULL;
	libcoopgamma_async_context_t *asyncs = NULL;
	int saved_errno, need_flush = 0, ret = 0;
	struct pollfd pollfd;
	libcoopgamma_filter_t command;

	for (i = 0; crtcs[i]; i++);
	for (j = 0; classes[j]; j++);
	synced = calloc(i, j * sizeof(*synced));
	if (!synced)
		goto fail;
	asyncs = calloc(i, j * sizeof(*asyncs));
	if (!asyncs)
		goto fail;

	i = j = 0;
	command.lifespan = LIBCOOPGAMMA_REMOVE;
	pollfd.fd = cg.fd;
	pollfd.events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI;

	while (unsynced > 0 || crtcs[i]) {
	wait:
		if (crtcs[i])
			pollfd.events |= POLLOUT;
		else
			pollfd.events &= ~POLLOUT;

		pollfd.revents = 0;
		if (poll(&pollfd, (nfds_t)1, -1) < 0)
			goto fail;

		if (pollfd.revents & (POLLOUT | POLLERR | POLLHUP | POLLNVAL)) {
			if (need_flush && (libcoopgamma_flush(&cg) < 0))
				goto send_fail;
			need_flush = 0;
			for (; crtcs[i]; i++, j = 0) {
				command.crtc = crtcs[i];
				while (classes[j]) {
					command.class = classes[j++];
					unsynced++;
					if (libcoopgamma_set_gamma_send(&command, &cg, asyncs + n++) < 0)
						goto send_fail;
				}
			}
			goto send_done;
		send_fail:
			switch (errno) {
			case EINTR:
			case EAGAIN:
#if EAGAIN != EWOULDBLOCK
			case EWOULDBLOCK:
#endif
				need_flush = 1;
				if (!classes[j])
					i++, j = 0;
				break;
			default:
				goto fail;
			}
		}
	send_done:

		if (!unsynced && !crtcs[i])
			break;

		if (pollfd.revents & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) {
			while (unsynced > 0) {
				switch (libcoopgamma_synchronise(&cg, asyncs, n, &selected)) {
				case 0:
					if (synced[selected]) {
						libcoopgamma_skip_message(&cg);
						break;
					}
					synced[selected] = 1;
					unsynced -= 1;
					if (libcoopgamma_set_gamma_recv(&cg, asyncs + selected) < 0)
						goto cg_fail;
					break;
				default:
					switch (errno) {
					case 0:
						break;
					case EINTR:
					case EAGAIN:
#if EAGAIN != EWOULDBLOCK
					case EWOULDBLOCK:
#endif
						goto wait;
					default:
						goto fail;
					}
					break;
				}
			}
		}
	}

done:
	saved_errno = errno;
	free(synced);
	free(asyncs);
	errno = saved_errno;
	return ret;
fail:
	ret = -1;
	goto done;
cg_fail:
	ret = -2;
	goto done;
}


/**
 * -M METHOD
 *     Select adjustment method. If METHOD is "?",
 *     available methods will be printed to stdout.
 * 
 * -S SITE
 *     Select site (display server instance).
 * 
 * -c CRTC
 *     Select CRT controller. If CRTC is "?", CRTC:s
 *     will be printed to stdout.
 *     
 *     Can be used multiple times. If not used, all
 *     CRTC:s are selected.
 * 
 * @param   argc  The number of command line arguments
 * @param   argv  The command line arguments
 * @return        0 on success, 1 on error
 */
int
main(int argc, char *argv[])
{
	int stage = 0;
	int rc = 0;
	char *method = NULL;
	char *site = NULL;
	char **crtcs_ = NULL;
	char **crtcs;
	size_t i, crtcs_n = 0;
	const char *side;

	crtcs = alloca((size_t)argc * sizeof(char *));

	ARGBEGIN {
	case 'M':
		if (method)
			usage();
		method = EARGF(usage());
		break;
	case 'S':
		if (site)
			usage();
		site = EARGF(usage());
		break;
	case 'c':
		crtcs[crtcs_n++] = EARGF(usage());
		break;
	default:
		usage();
	}
	ARGEND;

	if (initialise_proc() < 0)
		goto fail;

	if (method && !strcmp(method, "?")) {
		if (site || crtcs_n > 0 || argc > 0)
			usage();
		if (list_methods() < 0)
			goto fail;
		return 0;
	}

	if (libcoopgamma_context_initialise(&cg) < 0)
		goto fail;
	stage++;
	if (libcoopgamma_connect(method, site, &cg) < 0) {
		fprintf(stderr, "%s: server failed to initialise\n", argv0);
		goto custom_fail;
	}
	stage++;

	for (i = 0; i < crtcs_n; i++) {
		if (!strcmp(crtcs[i], "?")) {
			if (argc > 0)
				usage();
			switch (list_crtcs())
				{
				case 0:
					goto done;
				case -1:
					goto fail;
				default:
					goto cg_fail;
				}
		}
	}

	if (!argc)
		usage();
  
	if (!crtcs_n) {
		crtcs = crtcs_ = libcoopgamma_get_crtcs_sync(&cg);
		if (!crtcs)
			goto cg_fail;
	} else {
		crtcs[crtcs_n] = NULL;
	}

	if (libcoopgamma_set_nonblocking(&cg, 1) < 0)
		goto fail;

	switch (remove_filters(crtcs, argv)) {
	case 0:
		break;
	case -1:
		goto fail;
	default:
		goto cg_fail;
	}

done:
	if (stage >= 1)
		libcoopgamma_context_destroy(&cg, stage >= 2);
	free(crtcs_);
	return rc;

custom_fail:
	rc = 1;
	goto done;

fail:
	rc = 1;
	perror(argv0);
	goto done;

cg_fail:
	rc = 1;
	side = cg.error.server_side ? "server" : "client";
	if (cg.error.custom) {
		if (cg.error.number || cg.error.description)
			fprintf(stderr, "%s: %s-side error number %" PRIu64 ": %s\n",
				argv0, side, cg.error.number, cg.error.description);
		else if (cg.error.number)
			fprintf(stderr, "%s: %s-side error number %" PRIu64 "\n", argv0, side, cg.error.number);
		else if (cg.error.description)
			fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, cg.error.description);
	} else if (cg.error.description) {
		fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, cg.error.description);
	} else {
		fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, strerror((int)cg.error.number));
	}
	goto done;
}