
/*
 * $XConsortium: XOpenDis.c,v 11.69 88/09/30 14:03:15 jim Exp $
 */

/*
 * $Header: /cec/src/nestor/servers/R3/shXlib/XOpenDis.c,v 1.2 90/07/26
 * 08:31:44 altenhof Exp $
 */

#include "copyright.h"
/* Copyright    Massachusetts Institute of Technology    1985, 1986	 */

/* Converted to V11 by jg */
/* Extended to allow multiplexing by ma */

#include <stdio.h>

#define  NEED_EVENTS		/* -- XmuXlibint.h refers to event structures
				 * -- */
#include "Xlibint.h"

/* -- additional includes -- */
#include "XmuXlibint.h"
#include "resources.h"

#include <X11/Xos.h>
#include <X11/Xatom.h>

#ifndef lint
static int lock;		/* get rid of ifdefs when locking implemented */
#endif

static int first_call = xTrue;

static
 OutOfMemory ();		/* to avoid gcc flag -traditional */

int _Xdebug = 0;
static xReq _dummy_request =
{
  0, 0, 0
};

/* head of the linked list of open displays */
Display *_XHeadOfDisplayList = NULL;

extern Bool _XWireToEvent ();
extern Status _XUnknownNativeEvent ();
extern Bool _XUnknownWireEvent ();

/*
 * Connects to a server, creates a Display object and returns a pointer to
 * the newly created Display back to the caller.
 */

Display *
XOpenDisplay (display)
  register char *display;
{
  register Display *dpy;	/* New Display object being created. */
  register int i;
  int j, k;			/* random iterator indexes */
  char *display_name;		/* pointer to display name */
  int indian;			/* to determine which indian. */
  xConnClientPrefix client;	/* client information */
  xConnSetupPrefix prefix;	/* prefix information */
  int vendorlen;		/* length of vendor string */
  char *setup;			/* memory allocated at startup */
  char displaybuf[256];		/* buffer to receive expanded name */
  char prop_name[256];		/* buffer to receive property name */
  int screen_num;		/* screen number */
  union {
    xConnSetup *setup;
    char *failure;
    char *vendor;
    xPixmapFormat *sf;
    xWindowRoot *rp;
    xDepth *dp;
    xVisualType *vp;
  } u;
  long setuplength;		/* number of bytes in setup message */

  extern int _XSendClientPrefix ();
  extern int _XConnectDisplay ();
  extern char *getenv ();
  extern XID _XAllocID ();

  /*
   * -- Call XmuX initialisation routine on first call
   */
  if (first_call)
    XmuXInitialize ();

  /*
   * If the display specifier string supplied as an argument to this routine
   * is NULL or a pointer to NULL, read the DISPLAY variable.
   */
  if (display == NULL || *display == '\0') {
    if ((display_name = getenv ("DISPLAY")) == NULL) {
      /* Oops! No DISPLAY environment variable - error. */
      return (NULL);
    }
  }
  else {
    /* Display is non-NULL, copy the pointer */
    display_name = display;
  }

  /*
   * Attempt to allocate a display structure. Return NULL if allocation
   * fails.
   */
  LockMutex (&lock);
  if ((dpy = (Display *) Xcalloc (1, sizeof (Display))) == NULL) {
    errno = ENOMEM;
    UnlockMutex (&lock);
    return (NULL);
  }

  /*
   * Call the Connect routine to get the network socket. If 0 is returned,
   * the connection failed. The connect routine will return the expanded
   * display name in displaybuf.
   */

  displaybuf[0] = prop_name[0] = '\0';
  if ((dpy->fd = _XConnectDisplay (display_name, displaybuf,
				   prop_name, &screen_num))
      < 0) {
    Xfree ((char *) dpy);
    UnlockMutex (&lock);
    return (NULL);		/* errno set by XConnectDisplay */
  }

  /*
   * -- Create default entry in Connection translation table -- On success ,
   * return xTrue , otherwise xFalse -- XXX values will be overwritten for
   * multiplex connections outside of this
   */
  if (!CreateDefaultEntry (dpy)) {
    /* -- we've failed -- */
    _XDisconnectDisplay (dpy->fd);
    _XFreeDisplayStructure (dpy);
    errno = ENOMEM;
    return (NULL);
  }

  /*
   * First byte is the byte order byte. Authentication key is normally sent
   * right after the connection. This (in MIT's case) will be kerberos.
   */
  indian = 1;
  if (*(char *) &indian)
    client.byteOrder = 'l';
  else
    client.byteOrder = 'B';
  client.majorVersion = X_PROTOCOL;
  client.minorVersion = X_PROTOCOL_REVISION;
  _XSendClientPrefix (dpy, &client);

  /*
   * Now see if connection was accepted...
   */
  _XRead (dpy, (char *) &prefix, (long) SIZEOF (xConnSetupPrefix));

  if (prefix.majorVersion < X_PROTOCOL) {
    (void) fputs ("Warning: Client built for newer server!\n", stderr);
  }
  if (prefix.minorVersion != X_PROTOCOL_REVISION) {
    (void) fputs (
		"Warning: Protocol rev. of client does not match server!\n",
		   stderr);
  }

  setuplength = prefix.length << 2;
  if ((u.setup = (xConnSetup *) (setup = Xmalloc ((unsigned) setuplength)))
      == NULL) {
    errno = ENOMEM;
    Xfree ((char *) dpy);
    UnlockMutex (&lock);
    return (NULL);
  }
  _XRead (dpy, (char *) u.setup, setuplength);

  /*
   * If the connection was not accepted by the server due to problems, give
   * error message to the user....
   */
  if (prefix.success != xTrue) {

    /*
     * (void) fwrite (u.failure, (int)*u.failure, sizeof(char), stderr);
     */
    (void) fwrite (u.failure, sizeof (char),
		   (int) prefix.lengthReason, stderr);
    (void) fwrite ("\n", sizeof (char), 1, stderr);
    Xfree ((char *) dpy);
    Xfree (setup);
    UnlockMutex (&lock);
    return (NULL);
  }

  /*
   * We succeeded at authorization, so let us move the data into the display
   * structure.
   */
  dpy->next = (Display *) NULL;
  dpy->proto_major_version = prefix.majorVersion;
  dpy->proto_minor_version = prefix.minorVersion;
  dpy->release = u.setup->release;
  dpy->resource_base = u.setup->ridBase;
  dpy->resource_mask = u.setup->ridMask;
  dpy->min_keycode = u.setup->minKeyCode;
  dpy->max_keycode = u.setup->maxKeyCode;
  dpy->keysyms = (KeySym *) NULL;
  dpy->modifiermap = NULL;
  dpy->lock_meaning = NoSymbol;
  dpy->keysyms_per_keycode = 0;
  dpy->current = None;
  dpy->xdefaults = (char *) NULL;
  dpy->scratch_length = 0L;
  dpy->scratch_buffer = NULL;
  dpy->key_bindings = NULL;

  /*
   * -- we are not interested in handling motion buffers
   */
  dpy->motion_buffer = 0;	/* u.setup->motionBufferSize; */
  dpy->nformats = u.setup->numFormats;
  dpy->nscreens = u.setup->numRoots;
  dpy->byte_order = u.setup->imageByteOrder;
  dpy->bitmap_unit = u.setup->bitmapScanlineUnit;
  dpy->bitmap_pad = u.setup->bitmapScanlinePad;
  dpy->bitmap_bit_order = u.setup->bitmapBitOrder;
  dpy->max_request_size = u.setup->maxRequestSize;
  dpy->ext_procs = (_XExtension *) NULL;
  dpy->ext_data = (XExtData *) NULL;
  dpy->ext_number = 0;
  dpy->event_vec[X_Error] = _XUnknownWireEvent;
  dpy->event_vec[X_Reply] = _XUnknownWireEvent;
  dpy->wire_vec[X_Error] = _XUnknownNativeEvent;
  dpy->wire_vec[X_Reply] = _XUnknownNativeEvent;
  for (i = KeyPress; i < LASTEvent; i++) {
    dpy->event_vec[i] = _XWireToEvent;
    dpy->wire_vec[i] = NULL;
  }
  for (i = LASTEvent; i < 128; i++) {
    dpy->event_vec[i] = _XUnknownWireEvent;
    dpy->wire_vec[i] = _XUnknownNativeEvent;
  }
  dpy->resource_id = 0;
  dpy->resource_shift = ffs (dpy->resource_mask) - 1;
  dpy->db = (struct _XrmHashBucketRec *) NULL;
  dpy->cursor_font = None;

  /*
   * Initialize pointers to NULL so that XFreeDisplayStructure will work if
   * we run out of memory
   */

  dpy->screens = NULL;
  dpy->display_name = NULL;
  dpy->vendor = NULL;
  dpy->buffer = NULL;

  /*
   * now extract the vendor string...  String must be null terminated, padded
   * to multiple of 4 bytes.
   */
  dpy->vendor = (char *) Xmalloc (u.setup->nbytesVendor + 1);
  vendorlen = u.setup->nbytesVendor;
  u.setup += 1;			/* can't touch information in XConnSetup
				 * anymore.. */
  (void) strncpy (dpy->vendor, u.vendor, vendorlen);
  u.vendor += (vendorlen + 3) & ~3;

  /*
   * Now iterate down setup information.....
   */
  dpy->pixmap_format =
    (ScreenFormat *) Xmalloc (
			(unsigned) (dpy->nformats * sizeof (ScreenFormat)));
  if (dpy->pixmap_format == NULL) {
    OutOfMemory (dpy, setup);
    UnlockMutex (&lock);
    return (NULL);
  }

  /*
   * First decode the Z axis Screen format information.
   */
  for (i = 0; i < dpy->nformats; i++) {
    register ScreenFormat *fmt = &dpy->pixmap_format[i];
    fmt->depth = u.sf->depth;
    fmt->bits_per_pixel = u.sf->bitsPerPixel;
    fmt->scanline_pad = u.sf->scanLinePad;
    fmt->ext_data = NULL;
    u.sf += 1;
  }

  /*
   * next the Screen structures.
   */
  dpy->screens =
    (Screen *) Xmalloc ((unsigned) dpy->nscreens * sizeof (Screen));
  if (dpy->screens == NULL) {
    OutOfMemory (dpy, setup);
    UnlockMutex (&lock);
    return (NULL);
  }

  /*
   * Now go deal with each screen structure.
   */
  for (i = 0; i < dpy->nscreens; i++) {
    register Screen *sp = &dpy->screens[i];
    VisualID root_visualID = u.rp->rootVisualID;
    sp->display = dpy;
    sp->root = u.rp->windowId;
    sp->cmap = u.rp->defaultColormap;
    sp->white_pixel = u.rp->whitePixel;
    sp->black_pixel = u.rp->blackPixel;
    sp->root_input_mask = u.rp->currentInputMask;
    sp->width = u.rp->pixWidth;
    sp->height = u.rp->pixHeight;
    sp->mwidth = u.rp->mmWidth;
    sp->mheight = u.rp->mmHeight;
    sp->min_maps = u.rp->minInstalledMaps;
    sp->max_maps = u.rp->maxInstalledMaps;
    sp->root_visual = NULL;	/* filled in later, when we alloc Visuals */

    /*
     * -- NO backing store
     */
    sp->backing_store = NotUseful;	/* u.rp->backingStore; */

    /*
     * -- NO save unders
     */
    sp->save_unders = False;	/* u.rp->saveUnders; */
    sp->root_depth = u.rp->rootDepth;
    sp->ndepths = u.rp->nDepths;
    sp->ext_data = NULL;
    u.rp += 1;

    /*
     * lets set up the depth structures.
     */
    sp->depths = (Depth *) Xmalloc (
				   (unsigned) sp->ndepths * sizeof (Depth));
    if (sp->depths == NULL) {
      OutOfMemory (dpy, setup);
      UnlockMutex (&lock);
      return (NULL);
    }

    /*
     * for all depths on this screen.
     */
    for (j = 0; j < sp->ndepths; j++) {
      Depth *dp = &sp->depths[j];
      dp->depth = u.dp->depth;
      dp->nvisuals = u.dp->nVisuals;
      u.dp += 1;
      dp->visuals =
	(Visual *) Xmalloc ((unsigned) dp->nvisuals * sizeof (Visual));
      if (dp->visuals == NULL) {
	OutOfMemory (dpy, setup);
	UnlockMutex (&lock);
	return (NULL);
      }
      for (k = 0; k < dp->nvisuals; k++) {
	register Visual *vp = &dp->visuals[k];

	/*
	 * -- RESTRICTION : No read/write color cells
	 */
	vp->class = u.vp->class;

	/*
	 * -- if( vp->class == GrayScale ) vp->class = StaticGray ; else if(
	 * vp->class == PseudoColor ) vp->class = StaticColor ; else if(
	 * vp->class == DirectColor ) vp->class = TrueColor ; --
	 */
	vp->bits_per_rgb = u.vp->bitsPerRGB;
	vp->map_entries = u.vp->colormapEntries;
	vp->red_mask = u.vp->redMask;
	vp->green_mask = u.vp->greenMask;
	vp->blue_mask = u.vp->blueMask;
	vp->ext_data = NULL;
	vp->visualid = u.vp->visualID;
	u.vp += 1;
      }
      if (!XmuXSortVisuals (dpy, dp->visuals, dp->nvisuals,
			    dp->depth)) {
	errno = ENOMEM;
	Xfree ((char *) dpy);
	UnlockMutex (&lock);
	return (NULL);
      }
      for (k = 0; k < dp->nvisuals; k++) {
	register Visual *vp = &dp->visuals[k];

	if (vp->visualid == root_visualID) {
	  unsigned long black = sp->black_pixel, white = sp->white_pixel;
	  sp->root_visual = vp;

	  /*
	   * -- Create a colormap entry for screen's default -- Second last
	   * argument says -- "no private colormap" -- and insert "black" and
	   * "white"
	   */
	  XmuXCreateColormapInfo (dpy, sp->cmap,
				  vp, sp->root, False,
				  AllocNone);
	  XmuXUpdateColormapInfo (dpy, sp->cmap,
				  &black, 1, NULL, 0,
				  True, False);
	  XmuXUpdateColormapInfo (dpy, sp->cmap,
				  &white, 1, NULL, 0,
				  True, False);
	}
      }
    }
  }


  /*
   * Setup other information in this display structure.
   */
  dpy->vnumber = X_PROTOCOL;
  dpy->resource_alloc = _XAllocID;
  dpy->synchandler = NULL;
  dpy->request = 0;
  dpy->last_request_read = 0;
  dpy->default_screen = screen_num;	/* Value returned by ConnectDisplay */
  dpy->last_req = (char *) &_dummy_request;

  /* Salt away the host:display string for later use */
  if ((dpy->display_name = Xmalloc (
			   (unsigned) (strlen (displaybuf) + 1))) == NULL) {
    OutOfMemory (dpy, setup);
    UnlockMutex (&lock);
    return (NULL);
  }
  (void) strcpy (dpy->display_name, displaybuf);

  /* Set up the output buffers. */
  if ((dpy->bufptr = dpy->buffer = Xmalloc (BUFSIZE)) == NULL) {
    OutOfMemory (dpy, setup);
    UnlockMutex (&lock);
    return (NULL);
  }
  dpy->bufmax = dpy->buffer + BUFSIZE;

  /* Set up the input event queue and input event queue parameters. */
  dpy->head = dpy->tail = NULL;
  dpy->qlen = 0;

  /*
   * Now start talking to the server to setup all other information...
   */

  Xfree (setup);		/* all finished with setup information */

  /*
   * Make sure default screen is legal.
   */
  if (screen_num >= dpy->nscreens) {
    _XDisconnectDisplay (dpy->fd);
    _XFreeDisplayStructure (dpy);
    errno = EINVAL;
    UnlockMutex (&lock);
    return (NULL);
  }

  /*
   * Set up other stuff clients are always going to use.
   */
  for (i = 0; i < dpy->nscreens; i++) {
    register Screen *sp = &dpy->screens[i];
    XGCValues values;
    values.foreground = sp->black_pixel;
    values.background = sp->white_pixel;
    sp->default_gc = XCreateGC (dpy, sp->root,
				GCForeground | GCBackground, &values);
  }

  /*
   * call into synchronization routine so that all programs can be forced
   * synchronize
   */
  (void) XSynchronize (dpy, _Xdebug);

  /*
   * chain this stucture onto global list.
   */
  dpy->next = _XHeadOfDisplayList;
  _XHeadOfDisplayList = dpy;

  /*
   * W A R N I N G
   * 
   * This is experimental code for implementing pseudo-root windows as specified
   * by the Inter-Client Communications Conventions Manual.  The structures
   * that it provides should be considered private to the MIT implementation
   * of Xlib and are SUBJECT TO CHANGE WITHOUT NOTICE.  They should not be
   * incorporated into any toolkits or applications.  When they change, no
   * effort will be made to provide backwards compatibility.
   * 
   */
  if (prop_name[0] != '\0') {
    extern Status _XGetPseudoRoot ();

    /*
     * If a bad property name is specified we want to fail so that the
     * application doesn't get started up in the original root or, worse yet,
     * in a messed up one.  Again, the interfaces to the pseudo root code are
     * to be considered private to this implementation of Xlib and should not
     * be used in any toolkits or applications.
     */
    if (!_XGetPseudoRoot (dpy, prop_name)) {
      _XDisconnectDisplay (dpy);
      _XFreeDisplayStructure (dpy);
      errno = EINVAL;
      UnlockMutex (&lock);
      return (NULL);
    }
  }

  /*
   * and done mucking with the display
   */
  UnlockDisplay (dpy);		/* didn't exist, so didn't lock */
  UnlockMutex (&lock);

  /*
   * get the resource manager database off the root window.
   */
  {
    Atom actual_type;
    int actual_format;
    unsigned long nitems;
    unsigned long leftover;
    if (XGetWindowProperty (dpy, RootWindow (dpy, 0),
		      XA_RESOURCE_MANAGER, 0L, 100000000L, False, XA_STRING,
			    &actual_type, &actual_format, &nitems, &leftover,
			    (unsigned char **) &dpy->xdefaults) != Success) {
      dpy->xdefaults = (char *) NULL;
    }
    else {
      if ((actual_type != XA_STRING) || (actual_format != 8)) {
	if (dpy->xdefaults != NULL)
	  Xfree (dpy->xdefaults);
      }
    }
  }

  /*
   * -- we must define some atoms
   */
  XmuXDefineAtoms (dpy);
  if (first_call) {
    first_call = xFalse;
    XmuXAddDisplays (dpy);
  }
  return (dpy);
}


/*
 * OutOfMemory is called if malloc fails.  XOpenDisplay returns NULL after
 * this returns.
 */

static
OutOfMemory (dpy, setup)
  Display *dpy;
  char *setup;
{
  _XDisconnectDisplay (dpy->fd);
  _XFreeDisplayStructure (dpy);
  Xfree (setup);
  errno = ENOMEM;
}


/*
 * XFreeDisplayStructure frees all the storage associated with a Display.  It
 * is used by XOpenDisplay if it runs out of memory, and also by
 * XCloseDisplay.   It needs to check whether all pointers are non-NULL
 * before dereferencing them, since it may be called by XOpenDisplay before
 * the Display structure is fully formed. XOpenDisplay must be sure to
 * initialize all the pointers to NULL before the first possible call on
 * this.
 */

_XFreeDisplayStructure (dpy)
  register Display *dpy;
{
  if (dpy->screens) {
    register int i;

    for (i = 0; i < dpy->nscreens; i++) {
      Screen *sp = &dpy->screens[i];

      if (sp->depths) {
	register int j;

	for (j = 0; j < sp->ndepths; j++) {
	  Depth *dp = &sp->depths[j];

	  if (dp->visuals) {
	    register int k;

	    for (k = 0; k < dp->nvisuals; k++)
	      _XFreeExtData (dp->visuals[k].ext_data);
	    Xfree ((char *) dp->visuals);
	  }
	}

	Xfree ((char *) sp->depths);
      }

      _XFreeExtData (sp->ext_data);
    }

    Xfree ((char *) dpy->screens);
  }

  if (dpy->pixmap_format) {
    register int i;

    for (i = 0; i < dpy->nformats; i++)
      _XFreeExtData (dpy->pixmap_format[i].ext_data);
    Xfree ((char *) dpy->pixmap_format);
  }

  if (dpy->display_name)
    Xfree (dpy->display_name);
  if (dpy->vendor)
    Xfree (dpy->vendor);

  if (dpy->buffer)
    Xfree (dpy->buffer);
  if (dpy->keysyms)
    Xfree ((char *) dpy->keysyms);
  if (dpy->modifiermap)
    XFreeModifiermap (dpy->modifiermap);
  if (dpy->xdefaults)
    Xfree (dpy->xdefaults);
  if (dpy->key_bindings)
    _XFreeKeyBindings (dpy);

  _XFreeExtData (dpy->ext_data);

  Xfree ((char *) dpy);
}
