/* $Id: ps_dispatch.c,v 1.1.1.1 90/11/28 16:44:50 altenhof Exp $ */

/*
 * Copyright (C) 1990 by Digital Equipment Corporation.
 * 
 * Author: Michael P. Altenhofen, CEC Karlsruhe e-mail:
 * Altenhofen@kampus.enet.dec.com
 * 
 * This file ist part of Shared X
 * 
 * Permission to use, copy, modify, and distribute this software and its
 * documentation without fee is hereby granted, but only for non-profit  use
 * and distribution,  and provided  that the copyright notice and this notice
 * is preserved on all copies.
 * 
 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */

#define PUBLIC extern

#define _USE_STRUCTS_		/* we need all the stuff here ... */
#define _USE_PROTO_		/* ... we deal with protocol request ... */

#include "glob.h"

#include "WaitFor.h"
#include "utils.h"
#include "table.h"
#include "resources.h"
#include "io.h"

#undef PUBLIC


static ClientPtr onlyClient;
static Bool grabbingClient = FALSE;

static int padlength[4] =
{0, 3, 2, 1};

/* buffers for clients. legal values below */

static int nextFreeClientID = 1;/* 0 is for the server */
static int nextFreeServerID = 1;

static int nClients = 0;	/* number active clients */

void
SendConnectionSetupInfo (client)
  ClientPtr client;
{
  register Display *dpy;
  void CreateConnectionInfoFromDisplay ();
  if (client == NullClient)
    FatalError ("SendConnectionSetupInfo: No valid client!\n");
  dpy = XDisplay (client);
  /* -- assemble the connection info -- */
  CreateConnectionInfoFromDisplay (dpy);
  WriteToClient (client, sizeof (xConnSetupPrefix),
		 (char *) &connSetupPrefix);
  /* -- fill in resource base and mask -- */
  ((xConnSetup *) ConnectionInfo)->ridBase = dpy->resource_base;
  ((xConnSetup *) ConnectionInfo)->ridMask = dpy->resource_mask;
  WriteToClient (client, connSetupPrefix.length << 2, ConnectionInfo);
  /* -- free the connection info for later use -- */
  Xfree (ConnectionInfo);
}

#ifndef BUFSIZ
#define BUFSIZ 80
#endif

void
PrintError (dpy, error)
  Display *dpy;
  XErrorEvent *error;
{
  char buffer[BUFSIZ];
  char mesg[BUFSIZ];
  char number[32];
  char *mtype = "XlibMessage";

  XGetErrorText (dpy, error->error_code, buffer, BUFSIZ);
  XGetErrorDatabaseText (dpy, mtype, "XError", "X Error", mesg, BUFSIZ);
  ErrorF ("%s:   %s\n", mesg, buffer);
  XGetErrorDatabaseText (dpy, mtype, "MajorCode", "Request Major code %d",
			 mesg, BUFSIZ);
  ErrorF (mesg, error->request_code);
  sprintf (number, "%d", error->request_code);
  XGetErrorDatabaseText (dpy, "XRequest", number, "", buffer, BUFSIZ);
  ErrorF (" %s\n", buffer);
  XGetErrorDatabaseText (dpy, mtype, "ResourceID", "ResourceID 0x%x",
			 mesg, BUFSIZ);
  ErrorF (mesg, error->resourceid);
  ErrorF ("\n");
  XGetErrorDatabaseText (dpy, mtype, "ErrorSerial", "Error Serial #%d",
			 mesg, BUFSIZ);
  ErrorF (mesg, error->serial);
  ErrorF ("\n");
}

#undef BUFSIZ

void
Oops (client, reqCode, minorCode, status)
  ClientPtr client;
  unsigned reqCode, minorCode;
  int status;
{
  xError rep;
  XErrorEvent error;

  rep.type = X_Error;
  rep.sequenceNumber =
    error.serial = client->sequence;
  rep.errorCode =
    error.error_code = status;
  rep.majorCode =
    error.request_code = reqCode;
  rep.minorCode = minorCode;
  rep.resourceID = client->errorValue;

  PrintError (XDisplay (client), &error);
  WriteToClient (client, sizeof (xError), (char *) &rep);

  /* -- reset global error variables -- */
  clientErrorValue = Success;
  clientErrorRequest = 0;
}

Dispatch ()
{
  ClientPtr *clientReady;	/* mask of request ready clients */
  ClientPtr *newClients;	/* mask of new clients */
  int result, ErrorStatus;
  ClientPtr client;
  int nready, nnew;
  xReq *request;
  void CloseDownClient ();

  nextFreeClientID = 1;
  nClients = 0;
  clientsDoomed = FALSE;

  clientReady = (ClientPtr *)
    Xmalloc (sizeof (ClientPtr) * MaxClients);
  newClients = (ClientPtr *)
    Xmalloc (sizeof (ClientPtr) * MaxClients);
  request = (xReq *) NULL;

  setjmp (jmpenv);

  while (1) {

    ProcessServerInput (ALLSERVERS);

    WaitForSomething (clientReady, &nready, newClients, &nnew);

    /*****************
     *  Establish any new connections
     *****************/

    while (nnew--) {
      client = newClients[nnew];
      SendConnectionSetupInfo (client);
      client->sequence = 0;
      nClients++;
    }

    ProcessServerInput (READYSERVERS);

    while (nready-- > 0) {
      client = clientReady[nready];

      if (!client)
	continue;

      isItTimeToYield = FALSE;

      requestingClient = client;

      while (!isItTimeToYield) {


	request = (xReq *) ReadRequestFromClient (
					 client, &result, (char *) request);

	if (result < 0) {
	  CloseDownClient (client, FALSE);
	  break;
	}
	else if (result == 0) {
#ifdef VERBOSE
	  ErrorF ("Blocked read in dispatcher\n");
	  ErrorF ("reqType %d\n",
		  (request ? request->reqType : -1));
#endif
	  continue;
	}
	client->requestBuffer = (pointer) request;
	client->sequence++;

	ErrorStatus = (*(Request_ProcVector[request->reqType]))
	  (client);

	if (ErrorStatus != Success) {
	  if (client->noClientException != Success)
	    CloseDownClient (client, FALSE);
	  else
	    Oops (client,
		  clientErrorRequest ? clientErrorRequest
		  : request->reqType,
		  0, ErrorStatus);
	  break;
	}
      }
      FlushAllOutput ();

    }
    if (nClients == -1 || clientsDoomed)
      break;
  }
}

ClientPtr
NextAvailableClient ()
{
  register int i;
  register ClientPtr client;

  i = nextFreeClientID;

  while (clients[i]) {
    i++;
    if (i >= currentMaxClients)
      i = 1;
    if (i != nextFreeClientID)
      continue;
    if (currentMaxClients == MAXCLIENTS)
      return (ClientPtr) NULL;
    i = currentMaxClients;
    currentMaxClients++;
    clients = (ClientPtr *)
      Xrealloc (clients,
		currentMaxClients * sizeof (ClientPtr));
    if (clients == (ClientPtr *) NULL)
      FatalError ("Can't realloc client array!\n");
    break;
  }
  nextFreeClientID = i + 1;
  if (nextFreeClientID == currentMaxClients)
    nextFreeClientID = 1;

  clients[i] = client = (ClientPtr) Xmalloc (sizeof (ClientRec));

  if (client != NullClient) {
    client->index = i;
    client->sequence = 0;
    client->events = 0L;
    client->clientGone = FALSE;
    client->closeDownMode = DestroyAll;
    client->noClientException = Success;
    client->gc_list = (GCInfo *) NULL;
  }
  return (client);
}


void
CreateConnectionInfoFromDisplay (dpy)
  Display *dpy;
{
  xConnSetup setup;
  xWindowRoot root;
  xDepth depth;
  xVisualType visual;
  xPixmapFormat format;
  int i, j, k, lenofblock, sizesofar = 0;
  char *pBuf;

  setup.release = dpy->release;
  setup.ridMask = dpy->resource_mask;
  setup.ridBase = dpy->resource_base;
  setup.imageByteOrder = dpy->byte_order;
  setup.bitmapScanlineUnit = dpy->bitmap_unit;
  setup.bitmapScanlinePad = dpy->bitmap_pad;
  setup.bitmapBitOrder = dpy->bitmap_bit_order;
  setup.motionBufferSize = 0;	/* XXX -- or 0 ? */
  setup.numRoots = dpy->nscreens;
  setup.nbytesVendor = strlen (dpy->vendor);
  setup.numFormats = dpy->nformats;
  setup.maxRequestSize = dpy->max_request_size;
  setup.minKeyCode = dpy->min_keycode;
  setup.maxKeyCode = dpy->max_keycode;

  lenofblock = sizeof (xConnSetup) +
    ((setup.nbytesVendor + 3) & ~3) +
    (setup.numFormats * sizeof (xPixmapFormat)) +
    (setup.numRoots * sizeof (xWindowRoot));
  ConnectionInfo = (char *) Xmalloc (lenofblock);

  bcopy ((char *) &setup, ConnectionInfo, sizeof (xConnSetup));
  sizesofar = sizeof (xConnSetup);
  pBuf = ConnectionInfo + sizeof (xConnSetup);

  bcopy (dpy->vendor, pBuf, setup.nbytesVendor);
  sizesofar += setup.nbytesVendor;
  pBuf += setup.nbytesVendor;
  i = padlength[setup.nbytesVendor & 3];
  sizesofar += i;
  while (--i >= 0)
    *pBuf++ = 0;

  for (i = 0; i < setup.numFormats; i++) {
    register ScreenFormat *fmt = &dpy->pixmap_format[i];
    format.depth = fmt->depth;
    format.bitsPerPixel = fmt->bits_per_pixel;
    format.scanLinePad = fmt->scanline_pad;
    bcopy ((char *) &format, pBuf, sizeof (xPixmapFormat));
    pBuf += sizeof (xPixmapFormat);
    sizesofar += sizeof (xPixmapFormat);
  }

  for (i = 0; i < dpy->nscreens; i++) {
    Screen *pScreen;
    Depth *pDepth;
    Visual *pVisual;

    pScreen = &dpy->screens[i];
    root.windowId = pScreen->root;
    root.defaultColormap = pScreen->cmap;
    root.whitePixel = pScreen->white_pixel;
    root.blackPixel = pScreen->black_pixel;
    root.currentInputMask = pScreen->root_input_mask;
    root.pixWidth = pScreen->width;
    root.pixHeight = pScreen->height;
    root.mmWidth = pScreen->mwidth;
    root.mmHeight = pScreen->mheight;
    root.minInstalledMaps = pScreen->min_maps;
    root.maxInstalledMaps = pScreen->min_maps;
    root.rootVisualID = pScreen->root_visual->visualid;
    root.backingStore = FALSE;
    root.saveUnders = NotUseful;
    root.rootDepth = pScreen->root_depth;
    root.nDepths = pScreen->ndepths;
    bcopy ((char *) &root, pBuf, sizeof (xWindowRoot));
    sizesofar += sizeof (xWindowRoot);
    pBuf += sizeof (xWindowRoot);

    pDepth = pScreen->depths;
    for (j = 0; j < pScreen->ndepths; j++, pDepth++) {
      lenofblock += sizeof (xDepth) +
	(pDepth->nvisuals * sizeof (xVisualType));
      ConnectionInfo = (char *) Xrealloc (ConnectionInfo, lenofblock);
      pBuf = ConnectionInfo + sizesofar;
      depth.depth = pDepth->depth;
      depth.nVisuals = pDepth->nvisuals;
      bcopy ((char *) &depth, pBuf, sizeof (xDepth));
      pBuf += sizeof (xDepth);
      sizesofar += sizeof (xDepth);
      for (k = 0; k < pDepth->nvisuals; k++) {
	pVisual = (Visual *) & pDepth->visuals[k];
	visual.visualID = pVisual->visualid;
	visual.class = pVisual->class;
	visual.bitsPerRGB = pVisual->bits_per_rgb;
	visual.colormapEntries = pVisual->map_entries;
	visual.redMask = pVisual->red_mask;
	visual.greenMask = pVisual->green_mask;
	visual.blueMask = pVisual->blue_mask;
	bcopy ((char *) &visual, pBuf, sizeof (xVisualType));
	pBuf += sizeof (xVisualType);
	sizesofar += sizeof (xVisualType);
      }
    }
  }
  connSetupPrefix.success = xTrue;
  connSetupPrefix.length = lenofblock / 4;
  connSetupPrefix.majorVersion = X_PROTOCOL;
  connSetupPrefix.minorVersion = X_PROTOCOL_REVISION;

}

void
CloseDownServer (dpy, broken)
  Display *dpy;
  int broken;
{
  register int server_fd = ConnectionNumber (dpy);

  if (!broken)
    XCloseDisplay (dpy);
  else {
    CloseDownFileDescriptor (ConnectionNumber (dpy));
#ifndef NOT_SHARED_X
    XmuXFreeEntry (dpy);
#endif
    Xfree ((char *) dpy);
  }
  DeleteIDMap (server_fd);

  CloseDownServerConnection (server_fd);
}

/**********************
 * CloseDownClient
 *
 *  Client can either mark his resources destroy or retain.  If retained and
 *  then killed again, the client is really destroyed.
 *********************/

void
CloseDownClient (client, broken)
  register ClientPtr client;
  int broken;			/* called after X I/O error ? */
{
  register int i;

  FreeClientResources (client);
  FreeEventMap (ClientConnection (client));

  CloseDownServer (XDisplay (client), broken);

  /* ungrab server if grabbing client dies */
  if (grabbingClient && (onlyClient == client)) {
    grabbingClient = FALSE;
    ListenToAllClients ();
  }

  if (client->closeDownMode == DestroyAll) {
    client->clientGone = TRUE;	/* so events aren't sent to client */
    CloseDownClientConnection (client);
    nextFreeClientID = client->index;
    clients[client->index] = NullClient;
    Xfree ((char *) client);
    if (--nClients == 0)
      nClients = -1;
  }
  /* really kill resources this time */
  else if (client->clientGone) {
    nextFreeClientID = client->index;
    clients[client->index] = NullClient;
    Xfree ((char *) client);
  }
  else {
    client->clientGone = TRUE;
    CloseDownClientConnection (client);
    --nClients;
  }
}

void
MarkClientException (client)
  ClientPtr client;
{
  client->noClientException = -1;
}
