aboutsummaryrefslogblamecommitdiffstats
path: root/cg-query.c
blob: be3cb9e2810f8173bdaddec7fb4a985e8cf5d388 (plain) (tree)
1
                                                         


















                                 
               
   




                                         
                          





                                   

           
 



                                                                                          








                                            

                     
 













                                                         








                                        

                  
 












                                          











                                            

                
 












                                                







                                            

                                              
   

                
 

































                                                                                               
                














































                                                                                             
         















                                                  











                                              

                  
 























































                                                                                                
        


















                                                  














                                                    










                                                        



                                                      

                            
 





































































































































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

#include <libcoopgamma.h>

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



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

/**
 * Filter query
 */
static libcoopgamma_filter_query_t query;

/**
 * The class of the filter to print
 */
static char *class = NULL;



/**
 * Print usage information and exit
 */
static void
usage(void)
{
	fprintf(stderr,
	        "usage: %s [-M method] [-S site] [-h high] [-l low] [-f class] -c crtc\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;
}


/**
 * Print, to stdout, information about
 * the selected CRTC
 * 
 * @return  Zero on success, -1 on error, -2
 *          on libcoopgamma error, -3 on error
 *          with error message already printed
 */
static int
print_info(void)
{
	libcoopgamma_crtc_info_t info;
	libcoopgamma_filter_table_t table;
	const char *str;
	int saved_errno, ret = 0;
	size_t i;

	if (libcoopgamma_crtc_info_initialise(&info) < 0)
		return -1;
	if (libcoopgamma_filter_table_initialise(&table) < 0) {
		saved_errno = errno;
		libcoopgamma_crtc_info_destroy(&info);
		errno = saved_errno;
		return -1;
	}

	if (libcoopgamma_get_gamma_info_sync(query.crtc, &info, &cg) < 0)
		goto cg_fail;

	printf("Cooperative gamma server running: %s\n",
	       info.cooperative ? "yes" : "no");

	printf("Gamma adjustments supported: %s\n",
	       info.supported == LIBCOOPGAMMA_MAYBE ? "maybe" : info.supported ? "yes" : "no");

	printf("Gamma ramps stops (red green blue): %zu %zu %zu\n",
	       info.red_size, info.green_size, info.blue_size);

	switch (info.depth) {
	case LIBCOOPGAMMA_DOUBLE: str = "double-precision floating-point"; break;
	case LIBCOOPGAMMA_FLOAT:  str = "single-precision floating-point"; break;
	case LIBCOOPGAMMA_UINT8:  str = "unsigned 8-bit integer";          break;
	case LIBCOOPGAMMA_UINT16: str = "unsigned 16-bit integer";         break;
	case LIBCOOPGAMMA_UINT32: str = "unsigned 32-bit integer";         break;
	case LIBCOOPGAMMA_UINT64: str = "unsigned 64-bit integer";         break;
	default:
		errno = EPROTO;
		goto fail;
	}
	printf("Gamma ramps stops value type: %s\n", str);

	if (info.colourspace != LIBCOOPGAMMA_UNKNOWN) {
		switch (info.colourspace) {
		case LIBCOOPGAMMA_SRGB:    str = "sRGB";                             break;
		case LIBCOOPGAMMA_RGB:     str = "non-standard RGB";                 break;
		case LIBCOOPGAMMA_NON_RGB: str = "non-RGB multicolour";              break;
		case LIBCOOPGAMMA_GREY:    str = "monochrome or singlecolour scale"; break;
		case LIBCOOPGAMMA_UNKNOWN:
		default:
			errno = EPROTO;
			goto fail;
		}
		printf("Monitor's colourspace: %s\n", str);
	}

	if (info.have_gamut) {
		printf("Monitor's red colour (x, y): %lf, %lf\n",
		       info.red_x / (double)1024, info.red_y / (double)1024);

		printf("Monitor's green colour (x, y): %lf, %lf\n",
		       info.green_x / (double)1024, info.green_y / (double)1024);

		printf("Monitor's blue colour (x, y): %lf, %lf\n",
		       info.blue_x / (double)1024, info.blue_y / (double)1024);

		printf("Monitor's white point (x, y): %lf, %lf\n",
		       info.white_x / (double)1024, info.white_y / (double)1024);
	}

	if (libcoopgamma_get_gamma_sync(&query, &table, &cg) < 0)
		goto cg_fail;

	if (table.red_size != info.red_size || table.green_size != info.green_size ||
	    table.blue_size != info.blue_size || table.depth != info.depth) {
		fprintf(stderr, "%s: gamma ramp structure changed between queries\n", argv0);
		goto custom_fail;
	}

	printf("Filters: %zu\n", table.filter_count);
	for (i = 0; i < table.filter_count; i++) {
		printf("  Filter %zu:\n", i);
		printf("    Priority: %" PRIi64 "\n", table.filters[i].priority);
		printf("    Class: %s\n", table.filters[i].class);
	}

done:
	saved_errno = errno;
	libcoopgamma_crtc_info_destroy(&info);
	libcoopgamma_filter_table_destroy(&table);
	errno = saved_errno;
	return ret;
fail:
	ret = -1;
	goto done;
cg_fail:
	ret = -2;
	goto done;
custom_fail:
	ret = -3;
	goto done;
}



/**
 * Print, to stdout, the ramps of the select
 * filter on the select CRTC
 * 
 * @return  Zero on success, -1 on error, -2
 *          on libcoopgamma error, -3 on error
 *          with error message already printed
 */
static int
print_filter(void)
{
	libcoopgamma_filter_table_t table;
	libcoopgamma_ramps_t *restrict ramps;
	int saved_errno, ret = 0;
	size_t i, n;

	if (libcoopgamma_filter_table_initialise(&table) < 0)
		return -1;

	if (libcoopgamma_get_gamma_sync(&query, &table, &cg) < 0)
		goto cg_fail;

	if (query.coalesce) {
		i = 0;
	} else {
		for (i = 0; i < table.filter_count; i++)
			if (!strcmp(table.filters[i].class, class))
				break;
	}
	if (i == table.filter_count) {
		fprintf(stderr, "%s: selected filter does not exist on selected CRTC\n", argv0);
		goto custom_fail;
	}
	ramps = &table.filters[i].ramps;

	n = table.red_size;
	if (n < table.green_size)
		n = table.green_size;
	if (n < table.blue_size)
		n = table.blue_size;

	switch (table.depth) {
#define X(CONST, MEMBER, TYPE, FORMAT, DASH)\
	case CONST:\
		for (i = 0; i < n; i++) {\
			if (i < ramps->MEMBER.red_size)\
				printf("%" FORMAT " ", (TYPE)(ramps->MEMBER.red[i]));\
			else\
				printf(DASH " ");\
			\
			if (i < ramps->MEMBER.green_size)\
				printf("%" FORMAT " ", (TYPE)(ramps->MEMBER.green[i]));\
			else\
				printf(DASH " ");\
			\
			if (i < ramps->MEMBER.blue_size)\
				printf("%" FORMAT "\n", (TYPE)(ramps->MEMBER.blue[i]));\
			else\
				printf(DASH "\n");\
		}	\
		break
	X(LIBCOOPGAMMA_DOUBLE, d,   double,   "lf",         "----");
	X(LIBCOOPGAMMA_FLOAT,  f,   double,   "lf",         "----");
	X(LIBCOOPGAMMA_UINT8,  u8,  uint8_t,  "02"  PRIx8,  "--");
	X(LIBCOOPGAMMA_UINT16, u16, uint16_t, "04"  PRIx16, "----");
	X(LIBCOOPGAMMA_UINT32, u32, uint32_t, "08"  PRIx32, "--------");
	X(LIBCOOPGAMMA_UINT64, u64, uint64_t, "016" PRIx64, "----------------");
#undef X
	default:
		errno = EPROTO;
		goto fail;
	}

done:
	saved_errno = errno;
	libcoopgamma_filter_table_destroy(&table);
	errno = saved_errno;
	return ret;
fail:
	ret = -1;
	goto done;
cg_fail:
	ret = -2;
	goto done;
custom_fail:
	ret = -3;
	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.
 * 
 * -h HIGH
 *     Suppress filter with higher priority than HIGH.
 * 
 * -l LOW
 *     Suppress filter with lower priority than LOW.
 * 
 * -f CLASS
 *     Print gamma ramps of the filter with class CLASS
 *     on the selected CRTC. If CLASS is "*" all filters
 *     with a priority in [LOW, HIGH] are coalesced.
 * 
 * @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, haveh = 0, havel = 0;
	int rc = 0;
	char *method = NULL;
	char *site = NULL;
	const char *side;

	query.high_priority = INT64_MAX;
	query.low_priority = INT64_MIN;
	query.crtc = NULL;
	query.coalesce = 0;

	ARGBEGIN {
	case 'M':
		if (method)
			usage();
		method = EARGF(usage());
		break;
	case 'S':
		if (site)
			usage();
		site = EARGF(usage());
		break;
	case 'c':
		if (query.crtc)
			usage();
		query.crtc = EARGF(usage());
		break;
	case 'h':
		if (haveh++)
			usage();
		query.high_priority = (int64_t)atoll(EARGF(usage()));
		break;
	case 'l':
		if (havel++)
			usage();
		query.low_priority = (int64_t)atoll(EARGF(usage()));
		break;
	case 'f':
		if (class)
			usage();
		class = EARGF(usage());
		if (class[0] == '*' && class[1] == '\0')
			query.coalesce = 1;
		break;
	default:
		usage();
	}
	ARGEND;

	if (argc)
		usage();

	if (initialise_proc() < 0)
		goto fail;

	if (method && !strcmp(method, "?")) {
		if (site || query.crtc)
			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++;

	if (!query.crtc)
		usage();

	if (!strcmp(query.crtc, "?")) {
		switch (list_crtcs()) {
		case 0:
			goto done;
		case -1:
			goto fail;
		default:
			goto cg_fail;
		}
	}

	switch (class ? print_filter() : print_info()) {
	case 0:
		goto done;
	case -1:
		goto fail;
	case -2:
		goto cg_fail;
	default:
		goto custom_fail;
	}

	fflush(stdout);
	if (ferror(stdout))
		goto fail;
	if (fclose(stdout) < 0)
		goto fail;

done:
	if (stage >= 1)
		libcoopgamma_context_destroy(&cg, stage >= 2);
	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;
}