/*
 * Client application for querying drivers' configuration information
 * Copyright (C) 2003 Felix Kuehling
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * FELIX KUEHLING, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * 
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#define GLX_GLXEXT_PROTOTYPES
#include <GL/glx.h>
#include <GL/glxext.h>
#include <X11/Xlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

typedef const char * glXGetScreenDriver_t (Display *dpy, int scrNum);
typedef const char * glXGetDriverConfig_t (const char *driverName);

static glXGetScreenDriver_t *GetScreenDriver;
static glXGetDriverConfig_t *GetDriverConfig;

enum INFO_FUNC {
    LIST, NSCREENS, DRIVER, OPTIONS
};

void printUsage (void);

void printUsage (void) {
    fprintf (stderr,
"Usage: xdriinfo [-display <dpy>] [-version] [command]\n"
"Commands:\n"
"  nscreens               print the number of screens on display\n"
"  driver screen          print the DRI driver name of screen\n"
"  options screen|driver  print configuration information about screen or driver\n"
"If no command is given then the DRI drivers for all screens are listed.\n");
}

int main (int argc, char *argv[]) {
    Display *dpy;
    int nScreens, screenNum;
    enum INFO_FUNC func = LIST;
    char *funcArg = NULL;
    char *dpyName = NULL;

    GetScreenDriver = (glXGetScreenDriver_t *)glXGetProcAddressARB ((const GLubyte *)"glXGetScreenDriver");
    GetDriverConfig = (glXGetDriverConfig_t *)glXGetProcAddressARB ((const GLubyte *)"glXGetDriverConfig");
    if (!GetScreenDriver || !GetDriverConfig) {
	fprintf (stderr, "libGL is too old.\n");
	return 1;
    }

  /* parse the command line */
    for (int i = 1; i < argc; ++i) {
	char **argPtr = NULL;
	if (!strcmp (argv[i], "-display"))
	    argPtr = &dpyName;
	else if (!strcmp (argv[i], "nscreens"))
	    func = NSCREENS;
	else if (!strcmp (argv[i], "driver")) {
	    func = DRIVER;
	    argPtr = &funcArg;
	} else if (!strcmp (argv[i], "options")) {
	    func = OPTIONS;
	    argPtr = &funcArg;
	} else if (!strcmp (argv[i], "-version")) {
	    puts(PACKAGE_STRING);
	    return 0;
	} else {
	    fprintf (stderr, "%s: unrecognized argument '%s'\n",
		     argv[0], argv[i]);
	    printUsage ();
	    return 1;
	}
	if (argPtr) {
	    if (++i == argc) {
		fprintf (stderr, "%s: '%s' requires an argument\n",
			 argv[0], argv[i-1]);
		printUsage ();
		return 1;
	    }
	    *argPtr = argv[i];
	}
    }

  /* parse screen number argument */
    if (func == DRIVER || func == OPTIONS) {
	if (sscanf (funcArg, "%i", &screenNum) != 1)
	    screenNum = -1;
	else if (screenNum < 0) {
	    fprintf (stderr, "Negative screen number \"%s\".\n", funcArg);
	    return 1;
	}
    }
    else
        screenNum = -1;

  /* driver command needs a valid screen number */
    if (func == DRIVER && screenNum == -1) {
	fprintf (stderr, "Invalid screen number \"%s\".\n", funcArg);
	return 1;
    }

  /* open display and count the number of screens */
    if (!(dpy = XOpenDisplay (dpyName))) {
	fprintf (stderr, "Error: Couldn't open display\n");
	return 1;
    }
    nScreens = ScreenCount (dpy);

  /* final check on the screen number argument (if any)*/
    if ((func == DRIVER || func == OPTIONS) && screenNum >= nScreens) {
	fprintf (stderr, "Screen number \"%d\" out of range.\n", screenNum);
	return 1;
    }

   /* Call glXGetClientString to load vendor libs on glvnd enabled systems */
    glXGetClientString (dpy, GLX_EXTENSIONS);

    switch (func) {
      case NSCREENS:
	printf ("%d", nScreens);
	if (isatty (STDOUT_FILENO))
	    printf ("\n");
	break;
      case DRIVER: {
	  const char *name = (*GetScreenDriver) (dpy, screenNum);
	  if (!name) {
	      fprintf (stderr, "Screen \"%d\" is not direct rendering capable.\n",
		       screenNum);
	      return 1;
	  }
	  printf ("%s", name);
	  if (isatty (STDOUT_FILENO))
	      printf ("\n");
	  break;
      }
      case OPTIONS: {
	  const char *name, *options;
	  
	  if (screenNum == -1) {
	      name = funcArg;
	  } else {
	      name = (*GetScreenDriver) (dpy, screenNum);
	  }
	  if (!name) {
	      fprintf (stderr, "Screen \"%d\" is not direct rendering capable.\n",
		       screenNum);
	      return 1;
	  }
	  options = (*GetDriverConfig) (name);
	  if (!options) {
	      fprintf (stderr,
		       "Driver \"%s\" is not installed or does not support configuration.\n",
		       name);
	      return 1;
	  }
	  printf ("%s", options);
	  if (isatty (STDOUT_FILENO))
	      printf ("\n");
	  break;
      }
      case LIST:
	for (int i = 0; i < nScreens; ++i) {
	    const char *name = (*GetScreenDriver) (dpy, i);
	    if (name)
		printf ("Screen %d: %s\n", i, name);
	    else
		printf ("Screen %d: not direct rendering capable.\n", i);
	}
    }

    return 0;
}
