/* $Id: utils.c,v 1.1.1.1 90/11/28 17:03:55 altenhof Exp $ */

/***********************************************************
Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts,
and the Massachusetts Institute of Technology, Cambridge, Massachusetts.

                        All Rights Reserved

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the names of Digital or MIT not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.

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.

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

/*
 * $Log:	utils.c,v $
 * Revision 1.1.1.1  90/11/28  17:03:55  altenhof
 * First public release
 * 
 * Revision 1.1  90/11/28  17:03:53  altenhof
 * Initial revision
 * 
 * Revision 1.1.1.1  90/11/28  16:26:32  spanachi
 * First public version of shX
 * 
 * Revision 1.1  90/11/28  16:26:30  spanachi
 * Initial revision
 *  Revision 1.4  90/07/26  08:35:06  altenhof
 * Changed parameter in XmuXReplicate (from xFalse to 0)
 * 
 * Revision 1.2  90/07/23  14:18:22  altenhof New function added: --
 * XmuXAddDisplays: --> reads the environment variable XMUXDISPLAYS and adds
 * these displays to the primary display.
 * 
 * Revision 1.1.1.1  90/04/23  10:14:51  spanachi First version of 'shared X'
 * library (X11R3).
 * 
 * Revision 1.1  90/04/23  10:14:48  spanachi Initial revision
 * 
 * Revision 1.1  90/04/04  09:41:15  altenhof Initial revision
 * 
 * Revision 1.1  89/11/03  14:45:23  neideck Original send to X consortium
 * 
 * Revision 1.5  89/08/23  11:52:05  michael *** empty log message ***
 * 
 * Revision 1.4  89/08/15  12:50:35  michael Had to change XmuXChalkDisplay
 * 
 * Revision 1.3  89/08/10  18:55:12  michael Like resources.c: There's always a
 * new procedure we have to invent
 * 
 * Revision 1.2  89/07/07  08:47:08  michael *** empty log message ***
 * 
 * Revision 1.1  89/06/07  17:40:53  michael Initial revision
 * 
 */

#define ULONG_SHIFT  ( sizeof( unsigned long ) / sizeof( char ) )
#include <errno.h>
#include <X11/Xlib.h>

#include "XmuXdefs.h"
#include "XmuXglobals.h"
#include "XmuX.h"
#include "resources.h"
#include "ApplCtx.h"
#include "X11EventMaskNames.h"

extern char *sbrk ();
extern char *
getenv ();
extern int (*_XIOErrorFunction) ();

static int ErrorfOn = 1;
static int MessagefOn = 0;


extern int
  do_sort,
  do_put_get,
  do_map_pmap,
  dont_map_b_and_w;

void
PrintMask (win, mask, print_it, exclusion)
  XID win;
  Mask mask;
  int print_it;
  Bool exclusion;
{
  register long bit, bit_set;
  void XmuXdebug ();

  if (!print_it)
    return;
  if (exclusion)
    XmuXdebug (print_it, "XmuX exclusive events on window 0x%lx :\n", win);
  else
    XmuXdebug (print_it, "Window 0x%lx wants events :\n", win);

  for (bit = 0, bit_set = 1; bit < sizeof (long) * 8;
       bit++, bit_set <<= 1)
    if (mask & bit_set)
      XmuXdebug (print_it, "==>%s\n", EventMaskNames[bit].name);
}

static void
ParseOptions (string)
  char *string;
{
  register char *opt, *end;
  void XmuXErrorF ();

  if (!string)
    return;
  opt = string;
  end = opt + strlen (string);

  while (opt < end) {
    while ((*opt == ' ') && (opt < end))
      opt++;

    if (!strncmp (opt, "-requests", 9)) {
      debug_requests = 1;
      opt += 9;
    }
    else if (!strncmp (opt, "-resources", 10)) {
      debug_resources = 1;
      opt += 10;
    }
    else if (!strncmp (opt, "-events", 7)) {
      debug_events = 1;
      opt += 7;
    }
    else if (!strncmp (opt, "-deb", 4)) {
      debug_requests =
	debug_events =
	debug_resources =
	debug_mux = 1;
      opt += 4;
    }
    else if (!strncmp (opt, "-mux", 4)) {
      debug_mux = 1;
      opt += 4;
    }
    else if (!strncmp (opt, "-sort", 5)) {
      do_sort = 1;
      opt += 5;
    }
    else if (!strncmp (opt, "-pmap", 5)) {
      /* -- don't want pixmap mapping -- */
      do_map_pmap = 0;
      opt += 5;
    }
    else if (!strncmp (opt, "-pg", 3)) {
      do_put_get = 1;
      opt += 3;
    }
    else if (!strncmp (opt, "-nobaw", 6)) {
      dont_map_b_and_w = 1;
      opt += 6;
    }
    else if (*opt != '-') {
      /* -- assume this is the last option ! -- */
      if ((logfile = fopen (opt, "w")) == (FILE *) 0)
	XmuXErrorF ("Can't open file %s\n", opt);
      opt = end;
    }
    else
      opt = end;
  }
}

/* Force connections to close on SIGHUP from init */

AutoResetServer ()
{
  exit (0);
}

/* Force connections to close and then exit on SIGTERM, SIGINT */

GiveUp ()
{
  exit (0);
}


/* VARARGS1 */
void				/* limit of ten args */
XmuXdebug (enabled, f, s0, s1, s2, s3, s4, s5, s6, s7, s8, s9)
  int enabled;
  char *f;
  char *s0, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9;
{
#ifdef DEBUG
  if (enabled)
    if (logfile) {
      fprintf (logfile, f, s0, s1, s2, s3, s4, s5, s6, s7, s8, s9);
      fflush (logfile);
    }
    else {
      fprintf (stderr, f, s0, s1, s2, s3, s4, s5, s6, s7, s8, s9);
      fflush (stderr);
    }
#endif
}

/* VARARGS1 */

void				/* limit of ten args */
XmuXErrorF (f, s0, s1, s2, s3, s4, s5, s6, s7, s8, s9)
  char *f;
  char *s0, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9;
{
  if (ErrorfOn)
    if (logfile) {
      fprintf (logfile, f, s0, s1, s2, s3, s4, s5, s6, s7, s8, s9);
      fflush (logfile);
    }
    else {
      fprintf (stderr, f, s0, s1, s2, s3, s4, s5, s6, s7, s8, s9);
      fflush (stderr);
    }
}

/* VARARGS1 */
void				/* limit of ten args */
XmuXMessageF (f, s0, s1, s2, s3, s4, s5, s6, s7, s8, s9)
  char *f;
  char *s0, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9;
{
  if (MessagefOn)
    if (logfile)
      fprintf (logfile, f, s0, s1, s2, s3, s4, s5, s6, s7, s8, s9);
    else
      fprintf (stderr, f, s0, s1, s2, s3, s4, s5, s6, s7, s8, s9);
}

void
XmuXError (str)
  char *str;
{
  perror (str);
}

Notice ()
{
}

/* VARARGS1 */
void				/* limit of ten args */
XmuXFatalError (f, s0, s1, s2, s3, s4, s5, s6, s7, s8, s9)
  char *f;
  char *s0, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9;
{
  XmuXErrorF ("\nFatal XmuXlib bug!\n");
  XmuXErrorF (f, s0, s1, s2, s3, s4, s5, s6, s7, s8, s9);
  XmuXErrorF ("\n");
  GiveUp ();
  /* NOTREACHED */
}

void
XmuXfree (ptr)
  char *ptr;
{
  void free ();

  if (ptr) {
    XmuXdebug (debug_resources, "free addr = %d ( %lx )\n", ptr, ptr);
    free (ptr);
  }
  else
    XmuXdebug (debug_resources, "free NULL !\n");
}

char *
XmuXmalloc (size)
  int size;
{
  char *ptr, *malloc ();

  ptr = NULL;
  XmuXdebug (debug_resources, "alloc %d bytes ", size);
  if ((ptr = (char *) malloc (size)) != NULL)
    XmuXdebug (debug_resources, "addr = %d ( %lx )\n", ptr, ptr);
  else
    XmuXdebug (debug_resources, "FAILED!\n");
  return (ptr);
}

char *
XmuXrealloc (ptr, size)
  char *ptr;
  int size;
{
  char *new_ptr, *realloc (), *malloc ();

  XmuXdebug (debug_alloc, "realloc %d bytes addr = %d ( %lx )",
	     size, ptr, ptr);
  if (!ptr)
    new_ptr = malloc (size);
  else
    new_ptr = realloc (ptr, size);
  XmuXdebug (debug_alloc, " new addr = %d ( %lx )\n", new_ptr, new_ptr);
  return (new_ptr);
}

void
XmuXbcopy (src_ptr, dst_ptr, n)
  char *src_ptr, *dst_ptr;
  int n;
{
  void bcopy ();

  XmuXdebug (debug_alloc, "bcopy %d bytes from %d ( %lx ) to %d ( %lx )\n",
	     n, src_ptr, src_ptr, dst_ptr, dst_ptr);
  bcopy (src_ptr, dst_ptr, n);
}

XmuXAddDisplays (dpy)
  Display *dpy;
{
  char **dpy_list = (char **) NULL;
  extern char **CreateDpyNameList ();

  int n;

  if ((dpy_list = CreateDpyNameList (getenv ("XMUXDISPLAYS"), &n)) != NULL) {
    int *fds;
    /* don't ring the bell */
    if (XmuXReplicate (dpy, dpy_list, n, &fds, 0) == XmuXSuccess)
      XmuXAddHosts (dpy, dpy_list, fds, n);
    Xfree ((char *) fds);
  }
}

void
XmuXInitialize ()
{
  register int i;

  for (i = 0; i < MAXSOCKS; i++)
    ConnectionTranslation[i] = NullServer;
  InitClientResources ();
  InitPixelMaps ();
  InitIDMaps ();
  XmuXInitApplCtx ();
  debug_requests =
    debug_events =
    debug_resources =
    do_sort =
    do_put_get =
    dont_map_b_and_w =
    debug_mux = False;
  do_map_pmap = True;
  ParseOptions (getenv ("XMUXOPTIONS"));
}

void
CheckDisplay (dpy, routine)
  Display *dpy;
  char *routine;
{
  if (dpy == NULL)
    XmuXFatalError ("%s: No valid display (NULL)!\n",
		    routine ? routine : "???");
  if (dpy->fd > MAXSOCKS || dpy->fd <= 0)
    XmuXFatalError ("%s: No valid connection (%d)!\n",
		    routine ? routine : "???", dpy->fd);
  if (ConnectionTranslation[dpy->fd] == NullServer)
    XmuXFatalError ("%s: No server info (# %d)!\n",
		    routine ? routine : "???", dpy->fd);
}

/*
 * The following two routines are internal ones used by XmuX to find
 * the "correct" Display information
 */

/*
 * PrimaryDisplay -
 * Return the primary display (i.e. the one that is known to the application)
 * from the connection translation table
 */

Display *
XmuXPrimaryDisplayFromDisplay (dpy)
  Display *dpy;
{
  register Display *found_dpy;
  register int index;

#ifdef DEBUG
  CheckDisplay (dpy, "XmuXPrimaryDisplayFromDisplay");
#endif

  index = ConnectionTranslation[dpy->fd]->prim_server;
  found_dpy = ConnectionTranslation[index]->xdpy;

  if (found_dpy == NULL)
    XmuXFatalError ("Didn't find application's display! Good bye!\n");

  return (found_dpy);
}

Bool
XmuXMustMultiplex (dpy)
  Display *dpy;
{
#ifdef DEBUG
  CheckDisplay (dpy, "XmuXMustMultiplex");
#endif
  return (ConnectionTranslation[dpy->fd]->mustMux);
}


/*
 * GetDisplay - find the "i"th Display pointer that is used in combination
 * with an application. The 0th display is the one known to to the
 * application. The result depends on the display that is passed as an
 * argument: if you pass a display whose requests must be multiplexed, you
 * will get the list of all associated displays on consecutive calls. If not,
 * you will get only the one you passed on the first call. All consecutive
 * calls return NULL in this case. This allows us to use ordinary Xlib
 * routines to do the "multiplex job" (e.g. create the resources only on the
 * multiplexed screen), since they will affect only ONE connection. You may
 * find this routine a bit tricky; but I think it's better to hide the ugly
 * code here rather than constructing a lot of ugly loops in request.c
 */

Display *
XmuXGetDisplay (dpy, i)
  Display *dpy;
  int i;
{
  register int bit_set, bit;
  register unsigned long mask, base;
  register Display *ret_dpy;

#ifdef DEBUG
  CheckDisplay (dpy, "XmuXGetDisplay");
#endif

  if (i == 0)

    /*
     * return the display that is passed as an argument
     */
    return (dpy);
  else if (XmuXMustMultiplex (dpy)) {

    /*
     * We want to find the "ith" display in the list by using a bit
     * array that stores the socket numbers.  So you can say we do a
     * repetitive "ffs" on the bit array, but we calculate the index directly
     * rather than calling "ffs" for "i" times
     */
    base = 0;
    bit_set = 0;
    while ((base < MSKCNT) && (bit_set < i)) {
      mask = ConnectionTranslation[dpy->fd]->other_servers[base];
      bit = 0;
      while (mask && (bit < ULONG_SHIFT * 8) && (bit_set < i)) {
	if ((1L << bit) & mask)
	  bit_set++;
	if (bit_set < i)
	  bit++;
      }
      if (bit_set < i)
	base++;
    }
    if (bit_set == i)
      ret_dpy =
	ConnectionTranslation[bit +
			      (base << (ULONG_SHIFT + 1))]->xdpy;
    else
      ret_dpy = NULL;
  }
  else
    ret_dpy = NULL;
  return (ret_dpy);
}

/*
 * Could have been coded as a macro...but we don't want to be surprised
 * by unexpected errors
 */

Display *
XmuXPrimaryDisplayFromConnection (connection)
  int connection;
{
  register Display *dpy;

  if ((connection <= 0 || connection > MAXSOCKS) ||
      ConnectionTranslation[connection] == NullServer)
    XmuXFatalError ("You're using an invalid connection number: %d\n",
		    connection);

  dpy = ConnectionTranslation[connection]->xdpy;

  if (dpy == NULL)
    XmuXFatalError ("Connection %d has no display!\n", connection);

  return (XmuXPrimaryDisplayFromDisplay (dpy));
}

int
IsClientDisplay (dpy)
  Display *dpy;
{
  /* No error checking here */
  return (ConnectionTranslation[dpy->fd]->prim_server == dpy->fd);
}

/*
 * CreateDefaultEntry - create a new entry in the connection translation
 *                      table and fill it with "default" values.
 * 
 * CreateDefaultEntry is called in XOpenDisplay. With this we don't need a
 * second version of XOpenDisplay for multiplex connections: we call the
 * "ordinary" version and overwrite the "default" values afterwards.  On
 * success, we return True; on failure False.
 */

int
CreateDefaultEntry (dpy)
  Display *dpy;
{
  register int connection, i;
  register MUXServerPtr server;
  register Atom atom;

  /* nearly impossible, but ... */
  if (dpy == NULL) {
    XmuXErrorF ("Your display pointer is invalid!\n");
    return (False);
  }
  connection = dpy->fd;

  if ((server = (MUXServerPtr) Xmalloc (sizeof (MUXServerRec))) ==
      NullServer) {
    /* we've failed... */
    XmuXErrorF ("CreateDefaultEntry:Out of memory (conn %d)!\n",
		connection);
    return (False);
  }
  /* fill in the default values */
  server->xdpy = dpy;
  server->mustMux = False;

  /*
   * At the beginning (in XOpenDisplay) we store the "default resources"
   * for every connection.  When we return from XOpenDisplay for a
   * multiplex'ed connection we free these "useless" resource list and set
   * this flag to false to avoid further storing for this connection.
   * NOTE:
   * We must duplicate the client's default resources, since they may be
   * used -especially changed- by him (e.g. bitmap "uses" the default GC)
   * and since there may be differences concerning the number of defaults
   * for different machines.
   */
  server->storeResources = True;
  server->prim_server = connection;
  server->all_depths =
    server->vis_depths = 0L;
  CLEARBITS (server->other_servers);
  ConnectionTranslation[connection] = server;
  /* -- create a default application context -- */
  XmuXCreateApplCtx (server);
  return (True);
}

void
XmuXFreeEntry (dpy)
  Display *dpy;
{
  if (ConnectionTranslation[dpy->fd] != NullServer) {
    register MUXServerPtr prim_server;

    XmuXFreeMaps (dpy);
    XmuXFreeClientResources (dpy);
    prim_server =
      ConnectionTranslation[ConnectionTranslation[dpy->fd]->prim_server];
    BITCLEAR (prim_server->other_servers, dpy->fd);

    /*
     * If we close the primary connection, we'll have to free the
     * application context
     */
    if (IsClientDisplay (dpy))
      XmuXFreeApplCtx (dpy);
    Xfree ((char *) ConnectionTranslation[dpy->fd]);
    ConnectionTranslation[dpy->fd] = NullServer;
  }
}

int
XmuXIsAtom (dpy, atom, i)
  Display *dpy;
  Atom atom;
  int i;
{
  return (ConnectionTranslation[dpy->fd]->atoms[i] == atom);
}

Atom
XmuXServerAtom (dpy, i)
  Display *dpy;
  int i;
{
  return (ConnectionTranslation[dpy->fd]->atoms[i]);
}

void
XmuXDefineAtoms (dpy)
  Display *dpy;
{
  register MUXServerPtr server;
  char *hostname[1];
  int fds[1];

#ifdef DEBUG
  CheckDisplay (dpy, "XmuXDefineAtoms");
#endif

  server = ConnectionTranslation[dpy->fd];

  if (!(server->atoms[_MUX_CPORT] =
	XInternAtom (dpy, XMUX_CPORT_NAME, False)))
    XmuXFatalError ("Can't create atom XMUX_CPORT (conn %d)!\n",
		    dpy->fd);

  if (!(server->atoms[_MUX_MESSAGE] =
	XInternAtom (dpy, XMUX_MESSAGE_NAME, False)))
    XmuXFatalError ("Can't create atom XMUX_MESSAGE (conn %d)!\n",
		    dpy->fd);
  if (!(server->atoms[_MUX_HOSTS] =
	XInternAtom (dpy, XMUX_HOSTS_NAME, False)))
    XmuXFatalError ("Can't create atom XMUX_HOSTS (conn %d)!\n",
		    dpy->fd);

  if (!(server->atoms[_MUX_CHALKHOLDER] =
	XInternAtom (dpy, XMUX_CHALKHOLDER_NAME, False)))
    XmuXFatalError ("Can't create atom XMUX_CHALKHOLDER (conn %d)!\n",
		    dpy->fd);

  /* -- Add this display_name to the internal host list -- */
  hostname[0] = server->xdpy->display_name;
  fds[0] = server->xdpy->fd;
  XmuXAddHosts (server->xdpy, hostname, fds, 1);
}

MUXServerPtr
XmuXServerPtr (dpy)
  Display *dpy;
{
  MUXServerPtr server;

  server = ConnectionTranslation[dpy->fd];
  return (server);
}

int
XmuXHasChalk (dpy)
  Display *dpy;
{
#ifdef DEBUG
  CheckDisplay (dpy, "XmuXChalkHolder");
#endif

  if (XmuXCommunicationMode (dpy) == AnarchyMode)
    return (True);
  else
    return (dpy->fd == XmuXChalkHolder (dpy));
}

Display *
XmuXChalkDisplay (dpy)
  Display *dpy;
{
  register Display *chalk_dpy;

#ifdef DEBUG
  CheckDisplay (dpy, "XmuXChalkDisplay(Param)");
#endif

  chalk_dpy = ConnectionTranslation[XmuXChalkHolder (dpy)]->xdpy;

#ifdef DEBUG
  CheckDisplay (chalk_dpy, "XmuXChalkDisplay(Chalk)");
#endif

  return (chalk_dpy);
}

#define NUM_VIS_CLASSES 6

static int class_order[NUM_VIS_CLASSES] = {
  StaticGray, GrayScale,
  StaticColor, TrueColor,
  PseudoColor, DirectColor
  };

int
CompareVisClass (visA, visB)
  Visual *visA, *visB;
{
  if (class_order[visA->class] > class_order[visB->class])
    return (1);
  else if (class_order[visA->class] < class_order[visB->class])
    return (-1);
  else
    return (0);
}

XmuXSortVisuals (dpy, visuals, n, depth)
  Display *dpy;
  Visual *visuals;
  int n;
  unsigned int depth;
{
  if (n > 0)
    ConnectionTranslation[dpy->fd]->vis_depths |=
      (1L << (depth - 1));
  ConnectionTranslation[dpy->fd]->all_depths |= (1L << (depth - 1));
  if (n > 1) {
    qsort (visuals, n, sizeof (Visual), CompareVisClass);
  }
  return (True);
}

void
XmuXSetLastError (dpy, error)
  Display *dpy;
  XErrorEvent *error;
{

#ifdef DEBUG
  CheckDisplay (dpy, "XmuXSetLastError");
#endif

  ConnectionTranslation[dpy->fd]->last_error_code = error->error_code;
  ConnectionTranslation[dpy->fd]->last_request_code = error->request_code;
  ConnectionTranslation[dpy->fd]->last_minor_code = error->minor_code;
}

int
XmuXCheckLastError (dpy, ErrorCode, error_request)
  Display *dpy;
  unsigned char ErrorCode, *error_request;
{
  register unsigned char error_code;

#ifdef DEBUG
  CheckDisplay (dpy, "XmuXSetLastError");
#endif
  error_code = ConnectionTranslation[dpy->fd]->last_error_code;
  *error_request =
    ConnectionTranslation[dpy->fd]->last_request_code;

  /*
   * reset the values
   */
  ConnectionTranslation[dpy->fd]->last_error_code = Success;
  ConnectionTranslation[dpy->fd]->last_request_code = 0;
  return (error_code == ErrorCode);
}

int
XmuXDepthFromVisualID (dpy, visualID, depth)
  Display *dpy;
  XID visualID;
  int *depth;
{
  int i;

  /*
   * first, loop over all screens of dpy
   */
  for (i = 0; i < ScreenCount (dpy); i++) {
    Screen *sp = (Screen *) (&dpy->screens[i]);
    Depth *dp;

    /* loop through depths */
    for (dp = sp->depths; dp < (sp->depths + sp->ndepths); dp++) {
      Visual *vp;

      /* loop through visuals */
      for (vp = dp->visuals;
	   vp < (dp->visuals + dp->nvisuals); vp++)
	if (vp->visualid == visualID)
	  /* found it, return depth */
	{
	  *depth = dp->depth;
	  return (True);
	}
    }
  }
  /* we're trough, found nothing */
  return (False);
}
