aboutsummaryrefslogblamecommitdiffstats
path: root/src/cg-query.c
blob: 490eb8e1f7433f09cdd6d1dc0b01163e61aed7e3 (plain) (tree)






































                                                                        
               
   





                                         








                                   
                                                                                    





















































































                                                   

                                              



                                
                                    
            

                           
  








                                                       
  

                                                                   








































                                                                                         
                                                      

                                                                   
                                                        

                                                                       
                                                       


                                                                     


















                                                                                   

                                        
                                            
                      



             
         

















































































                                                                                      
                      
                                            
                      









             














                                                    










                                                        





                                                      
                                      



                      




                                  












                              





                                  
                












                                                           








                                               
                                                 















                                                                  
                    

            
                               









                         
                                                




                
            
                   

                       











































                                                                                                  
/**
 * cg-tools -- Cooperative gamma-enabled tools
 * Copyright (C) 2016  Mattias Andrée (maandree@kth.se)
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#define _DEFAULT_SOURCE
#define _BSD_SOURCE

#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
 */
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 == NULL)
    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 == NULL)
    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;
  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;
	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);
    }
  
  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;
  
  query.high_priority = INT64_MAX;
  query.low_priority = INT64_MIN;
  query.crtc = NULL;
  query.coalesce = 0;
  
  ARGBEGIN
    {
    case 'M':
      if (method != NULL)
	usage();
      method = EARGF(usage());
      break;
    case 'S':
      if (site != NULL)
	usage();
      site = EARGF(usage());
      break;
    case 'c':
      if (query.crtc != NULL)
	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 != NULL)
	usage();
      class = EARGF(usage());
      if ((class[0] == '*') && (class[1] == '\0'))
	query.coalesce = 1;
      break;
    }
  ARGEND;
  
  if (initialise_proc() < 0)
    goto fail;
  
  if ((method != NULL) && !strcmp(method, "?"))
    {
      if ((site != NULL) || (query.crtc != NULL))
	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;
  {
    const char* side = cg.error.server_side ? "server" : "client";
    if (cg.error.custom)
      {
	if ((cg.error.number != 0) || (cg.error.description != NULL))
	  fprintf(stderr, "%s: %s-side error number %" PRIu64 ": %s\n",
		  argv0, side, cg.error.number, cg.error.description);
	else if (cg.error.number != 0)
	  fprintf(stderr, "%s: %s-side error number %" PRIu64 "\n", argv0, side, cg.error.number);
	else if (cg.error.description != NULL)
	  fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, cg.error.description);
      }
    else if (cg.error.description != NULL)
      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(cg.error.number));
  }
  goto done;
}