/*********************************************************************
 *
 *         EZWGL, the EZ Widget and Graphics Library
 *
 *             Copyright (C) 1996, 1997  Maorong Zou
 *  
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 **********************************************************************/
/*
 *  June 1996.  Beta Release.
 *  Sept 1996.  Release Version 1.0
 *  Dec 1996.  Release Version 1.1 Beta
 *  April 1997.  Release Version 1.2
 *  November 1997.  Release Version 1.3
 */
/*******************************************************************
 *
 *   Initialize X11. 
 */
#define _EZ_X11INIT_C_
/*******************************************************************/
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/keysym.h>
#ifdef XSHM
#include <X11/extensions/XShm.h>
#endif
#include <stdio.h>
#include <stdlib.h>

#include "EZ_Widget.h"

/*******************************************************************
 *
 * Functions implemented in this file:
 */
void EZ_InitX11 MY_ANSIARGS((int ac, char **av, int flag));
int  EZ_XErrorHandler MY_ANSIARGS((Display *dpy, XErrorEvent *error));

/********************************************************************
 * If run embeded, the following data will be set by the invoker.
 */
Window EZ_DnDCommuWin = (Window)NULL, EZ_DnDParentWin = (Window)NULL;

/*
 * For the -geometry option. If set, the geometry will be
 * used only once, for the first window created.
 */
int EZ_GeometryX = 0, EZ_GeometryY = 0, EZ_GeometryW = 0, EZ_GeometryH = 0; 
int EZ_GeometrySet = 0;
int EZ_EmbedingDepth = 0;  /* control the depth of embedings */

/*******************************************************************
 *
 * Local functions.
 */

static void EZ_GetVisualS MY_ANSIARGS((Display *dpy, int hint, int no_gl));
static void EZ_CheckForSHMExtension MY_ANSIARGS((void));
static void EZ_CheckForSHAPEExtension MY_ANSIARGS((void));
static void EZ_ParseGeometry MY_ANSIARGS((char *spec));
/****************************************************************
 *
 *  Connet to X server, get visual info and create a colormap 
 *  if needed.
 */
void  EZ_InitX11(ac,av, initgl)
     int ac; char **av;
     int initgl;
{
  int    i, private = 0, backing_store = 0, suggested_v = -1;
  char   *dpy_name = (char *)NULL;
  char   *bgColor = (char *)NULL;

  /*-----------------------------------------------
   * Parse args. Supported options are:
   *  -display dpy_name    + specify the display 
   *  -private             + use a private cmap
   *----------------------------------------------*/
  if(ac > 1)
    for(i = 1; i < ac; i++)
      {
	char *arg = av[i];
	if(arg[0] == '-')
	  {
	    switch(arg[1])
	      {
	      case 'd':                   /* -display */
		if(!strcmp(arg,"-display"))
		  {
		    if(++i < ac) dpy_name = av[i];
		    else --i;
		  }
		break;
	      case 'v':
		if(!strncmp(arg,"-visual",6))
		  {
		    if(++i < ac)
		      {
			if(!strncmp(av[i],"pseudo",4) || !strncmp(av[i],"Pseudo",4))
			  suggested_v = PseudoColor;
			else if(!strncmp(av[i],"true",4) || !strncmp(av[i],"True",4))
			  suggested_v = TrueColor;
			else if(!strncmp(av[i],"direct",4) || !strncmp(av[i],"Direct",4))
			  suggested_v = DirectColor;
			else if(!strncmp(av[i],"gray",4) || !strncmp(av[i],"Gray",4))
			  suggested_v = GrayScale;
			else if(!strncmp(av[i],"staticg",7) || !strncmp(av[i],"staticG",7) ||
				!strncmp(av[i],"Staticg",7) || !strncmp(av[i],"StaticG",7))
			  suggested_v = StaticGray;
			else if(!strncmp(av[i],"staticc",7) || !strncmp(av[i],"staticC",7) ||
				!strncmp(av[i],"Staticc",7) || !strncmp(av[i],"StaticC",7))
			  suggested_v = StaticColor;
		      }
		    else --i;
		  }
		break;
	      case 'p':                   /* -private */
		if(!strcmp(arg,"-private"))
		  private = 1;
		break;
	      case 'b':
		if(!strncmp(arg,"-backg",6) || !strncmp(arg,"-bg",3))
		  {
		    if(++i < ac) bgColor = av[i];
		    else --i;
		    break;
		  }
		else if(!strncmp(arg,"-backi",6))/* -backing_store  */
		  backing_store = 1;
		break;
	      case 'e': /* -embed  commuWin parentWin x y */
		if(!strncmp(av[i],"-embed", 4) && i + 2 < ac)
		  {
		    sscanf(av[i+1], "%lx",(unsigned long *)&EZ_DnDCommuWin);
		    sscanf(av[i+2], "%lx",(unsigned long *)&EZ_DnDParentWin);
		    i += 2;
		  }
		else if(!strncmp(av[i],"-edepth", 4))
		  {
		    if(++i < ac) EZ_EmbedingDepth = atoi(av[i]);
		    else --i;
		  }
		break;
	      case 'g':
		if(!strncmp(av[i],"-geom", 4))
		  {
		    if(++i < ac) EZ_ParseGeometry(av[i]);
		  }
		break;
	      default:
		break;
	      }
	  }
      }
  /*-----------------------------------------------
   * The first thing is to connect to X server 
   *----------------------------------------------*/
  if( (EZ_Display = XOpenDisplay(dpy_name)) == NULL)  
    {
      (void) fprintf( stderr, "Couldn't connect to X server %s\n",
		     XDisplayName(dpy_name));
      exit(EZ_EXIT_ERROR);
    }
  /*-----------------------------------------------
   * Get some basic info about the display. 
   *----------------------------------------------*/
  EZ_ScreenNum = DefaultScreen(EZ_Display);             /* the screen no     */
  if(backing_store == 1 && (DoesBackingStore(DefaultScreenOfDisplay(EZ_Display))))
    EZ_UseBackingStore = 1;
  else
    EZ_UseBackingStore = 0;
  
  EZ_GetVisualS(EZ_Display,suggested_v,(initgl==0));    /* select a visual   */

  EZ_XDisplayWidth =  (DisplayWidth(EZ_Display, EZ_ScreenNum));
  EZ_XDisplayHeight = (DisplayHeight(EZ_Display, EZ_ScreenNum));

  /* play safe */
  EZ_XServerDataReqSize = (XMaxRequestSize(EZ_Display)*31/32); /* 31/32 of the max size */
  
  EZ_CheckForSHMExtension();
  EZ_CheckForSHAPEExtension();
  EZ_XImageByteOrder = ImageByteOrder(EZ_Display);
  {
    long word = 1;
    EZ_HostByteOrder = ( *((char *)&word) == 0)? MSBFirst : LSBFirst;
  }
  
  if(EZ_Depth != 8 && EZ_Depth != 16 && EZ_Depth != 24)
    (void)fprintf(stderr,"Warning: EZWGL has not been tested on displays with depth= %d\n",
		  EZ_Depth);
  
  if(EZ_Visual->class == DirectColor || EZ_Visual->class == TrueColor)
    initgl = 1;      /* we have at least 256 colors anyway */
  else if(EZ_Visual->class == StaticColor || EZ_Visual->class == StaticGray)
    {
      if(initgl)
	(void)fprintf(stderr,"Warning: No TrueColor, DirectColor, PseudoColor or GrayScale Visual found.\
The EZ Graphics Library is not fully supported on Static*** visual.\n");
    }

  EZ_WidgetOnly = (initgl == 0);
  EZ_UsePrivateColormap = private;
  /*-------------------------------------------
   *  Find an appropriate colormap, 
   *  initialize the first few colors and
   *  setup color functions
   *------------------------------------------*/
  EZ_Initial_Color();
  EZ_BgSet = 0;
  if(bgColor != (char *)NULL) /* block future attempts of seting global bg */
    EZ_BgSet  = (char )EZ_SetGlobalBackground(bgColor);
  
  /* Now create a dummy window. This window is never mapped. It
   * is used for 
   *    1. to create GC's and
   *    2. communicate with ezwgl applications via window properties.
   */
  {
    XSetWindowAttributes   setWAttr;
    unsigned long          valuemask; 

    setWAttr.colormap          = EZ_Colormap;
    setWAttr.background_pixmap = None;
    setWAttr.border_pixel      = 0;
    setWAttr.override_redirect = True;

    valuemask = CWColormap | CWBackPixmap | CWBorderPixel |CWOverrideRedirect;
    EZ_DummyWindow = XCreateWindow(EZ_Display,
				   RootWindow(EZ_Display,EZ_ScreenNum),
				   0,0,1,1,0,
				   EZ_Depth,
				   InputOutput,
				   EZ_Visual,
				   valuemask,
				   &setWAttr);
    XSelectInput(EZ_Display, EZ_DummyWindow, PropertyChangeMask);
  }
  /* XSynchronize(EZ_Display, True); */
}
/********************************************************************
 *
 *  Get the visual information. Selection is based on
 *  + DirectColor with depth > 8
 *  + TrueColor   with depth > 8
 *  + PseudoColor  width depth = 8
 *  + PseudoColor  width depth != 8
 *  + GrayScale    width depth = 8
 *  + GrayScale    width depth != 8
 *  + StaticColor  width depth = 8
 *  + StaticColor  width depth != 8
 *  + StaticGray   width depth = 8
 *  + StaticGray   width depth != 8
 */
#define CHOOSE_VISUAL(o,n) (((o) == -1 ||xv[o].depth< xv[n].depth) ? (n):(o))
#define CHOOSE_VISUAL8(o,n) ((xv[n].depth == 8) ? (n):\
			     ((o) == -1? (n): (xv[o].depth == 8 ? (o): \
					       xv[o].depth < xv[n].depth ? (n): (o))))

static void EZ_GetVisualS(dpy, suggested_v, no_gl)
     Display *dpy; 
     int     suggested_v, no_gl;
{
  XVisualInfo VInfoList, *xv;
  int nv, i, selected;
  int  pseudo, truec, direct, scolor, sgray, gray;


  if(no_gl && suggested_v == -1)   /* try default visual */
    {
      EZ_Depth = DefaultDepth(EZ_Display, EZ_ScreenNum);
      EZ_Visual = DefaultVisual(EZ_Display, EZ_ScreenNum);

      switch(EZ_Depth)
	{
	case 8:
	  return;
	  break;
	case 16:
	case 24:
	  if(EZ_Visual->class == TrueColor ||
	     EZ_Visual->class ==  DirectColor)
	    return;
	  break;
	default:
	  break;
	}
    }

  
  VInfoList.screen = DefaultScreen(dpy);
  xv = XGetVisualInfo(dpy, VisualScreenMask, &VInfoList, &nv);

  selected = pseudo = truec = direct = scolor = sgray = gray = -1;
  for(i=0; i < nv; i++)
    switch(xv[i].class) 
      {
      case StaticGray: 
	sgray  = CHOOSE_VISUAL8(sgray, i); 
	break;
      case GrayScale:
	gray = CHOOSE_VISUAL8(gray, i);
	break;
      case StaticColor:
	scolor = CHOOSE_VISUAL8(scolor, i);
	break;
      case PseudoColor:
	pseudo = CHOOSE_VISUAL8(pseudo, i);
	break;
      case TrueColor: 
	truec   = CHOOSE_VISUAL(truec, i);
	break;
      case DirectColor:
	direct = CHOOSE_VISUAL(direct, i); 
	break;
      default:
	(void)fprintf(stderr, "Unknown visual class: %d.\n",
		      xv[i].class); 
	break;
      }
  /*-----------------------------------------
   * If a visual is suggested on the command
   * line, honor that.
   *----------------------------------------*/
  if(suggested_v != -1)
    {
      switch(suggested_v) 
	{
	case StaticGray: 
	  selected = sgray;
	  break;
	case GrayScale:
	  selected = gray;
	  break;
	case StaticColor:
	  selected = scolor;
	  break;
	case PseudoColor:
	  selected = pseudo;
	  break;
	case TrueColor: 
	  selected = truec;
	  break;
	case DirectColor:
	  selected = direct;
	  break;
	default:
	  break;      
	}
      if(selected != -1)
	{
	  EZ_Visual = (xv[selected]).visual;
	  EZ_Depth  = (xv[selected]).depth;
	  XFree( (char*)xv );
	  return;
	}
      else
	(void)fprintf(stderr, "Cannot find the suggested visual. Use the default.\n");
    }
  
  if(direct >= 0 && xv[direct].depth > 8)
    selected = direct;
  else if(truec >= 0 && xv[truec].depth > 8)
    selected = truec;
  else if(pseudo >= 0) 
    selected = pseudo;
  else if(gray >= 0)
    selected = gray;
  else if(scolor >= 0) 
    selected = scolor; 
  else if(sgray >= 0)
    selected = sgray;

  if(selected != -1)
    {
      EZ_Visual = (xv[selected]).visual;
      EZ_Depth  = (xv[selected]).depth;
    }

  XFree( (char*)xv );
}

/**************************************************************/

int EZ_XErrorCode = 0;

int  EZ_XErrorHandler(display,error)
     Display *display;
     XErrorEvent *error;
{
  EZ_XErrorCode = error->error_code;
  return(0);
}

static void EZ_CheckForSHMExtension()
{
#ifdef XSHM
  if(XShmQueryExtension(EZ_Display) == True)
    EZ_XServerHasSHMExt = 1;
  else 
#endif
    EZ_XServerHasSHMExt = 0;
}

static void EZ_CheckForSHAPEExtension()
{
  int junk;

  if(XShapeQueryExtension(EZ_Display, &junk, &junk) == True)
    EZ_XServerHasSHAPEExt = 1;
  else 
    EZ_XServerHasSHAPEExt = 0;
}
/**************************************************************/

static void EZ_ParseGeometry(geo)
     char *geo;
{
  /* [{width}][x{height}][{+-}{xoff}[{+-}{yoff}]]] */
  if(geo)
    {
      char str[256], *p = geo;

      if(*p != '+' && *p != '-')
	{
	  if(*p != 'x')
	    {
	      int i = 0;
	      while(*p && *p != 'x' && *p != '+' && *p != '-')
		str[i++] = *p++; str[i] = 0;
	      EZ_GeometryW = atoi(str); 
	      EZ_GeometrySet |= 1;
	    }
	  if(*p == 'x')
	    {
	      int i = 0;
	      p++;
	      while(*p && *p != '+' && *p != '-') str[i++] = *p++; str[i] = 0;
	      EZ_GeometryH = atoi(str); 
	      EZ_GeometrySet |= 2;
	    }
	}
      if(*p == '+' || *p == '-')
	{
	  int sign = (*p == '-');
	  int i = 0;
	  p++;
	  while(*p && *p != '+' && *p != '-') str[i++] = *p++; str[i] = 0;
	  EZ_GeometryX = (sign == 0? atoi(str) : -atoi(str));
	  EZ_GeometrySet |= 4;
	}
      if(*p == '+' || *p == '-')
	{
	  int sign = (*p == '-');
	  int i = 0;
	  p++;
	  while(*p && *p != '+' && *p != '-') str[i++] = *p++; str[i] = 0;
	  EZ_GeometryY = (sign == 0? atoi(str) : -atoi(str));
	  EZ_GeometrySet |= 8;
	}      
    }
}
/****************************************************************************/

void EZ_ResetGVX11InitC()
{
  EZ_DnDCommuWin = (Window)NULL;
  EZ_DnDParentWin = (Window)NULL;

  EZ_GeometryX = 0;
  EZ_GeometryY = 0;
  EZ_GeometryW = 0;
  EZ_GeometryH = 0; 
  EZ_GeometrySet = 0;
  EZ_EmbedingDepth = 0;

  EZ_XErrorCode = 0;
}
/****************************************************************************/
#undef _EZ_X11INIT_C_
