/* initapp.c - initialise X Server connection, X application, and widget library
   Copyright (C) 1996-2000 Paul Sheer

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program 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 General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307, USA.
 */

/* setup application */

/*
   Colormap allocation:

   A colormap is allocated as follows into the array color_pixels:
   ('i' refers to color_pixels[i])

   These are allocated in sequential palette cells, hence
   (for pseudocolor only) color_pixels[j + i] = color_pixels[j] + i,
   for j = 0,16,43. Obviously in TrueColor this is not true.

   ,----------+------------------------------------------.
   |    i     |   colors                                 |
   +----------+------------------------------------------+
   |  0-15    | 16 levels of the widget colors that      |
   |          | make up button bevels etc. Starting from |
   |          | (i=0) black (for shadowed bevels), up    |
   |          | to (i=15) bright highlighted bevels      |
   |          | those facing up-to-the-left. These       |
   |          | are sequential (see next block).         |
   +----------+------------------------------------------+
   |  16-42   | 3^3 combinations of RGB, vis. (0,0,0),   |
   |          | (0,0,127), (0,0,255), (0,127,0), ...     |
   |          | ... (255,255,255).                       |
   +----------+------------------------------------------+
   |  43-106  | 64 levels of grey. (optional)            |
   +----------+------------------------------------------+
   |  107->   | For other colors. (Not used at present)  |
   `----------+------------------------------------------'
 */

/* Thence macros are defined in coolwidgets.h for color lookup */


#include <config.h>
#include <stdio.h>
#include <my_string.h>
#include <stdlib.h>

#include <pwd.h>
#include <sys/types.h>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/Xresource.h>

#define  DEF_APP_GLOB		/* so that globals get defined not externed */

#include "coolwidget.h"
#include "xim.h"
#include "stringtools.h"

#include <signal.h>
#include <sys/types.h>
#if HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif

#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif

#include "mad.h"

static int verbose_operation = 0;
CInitData *given = 0;


/* defaults */
#define DEFAULT_DISPLAY			NULL
#define DEFAULT_GEOM			NULL
#define DEFAULT_FONT			"-*-fixed-bold-r-*--13-120-*-*-*-80-*"
#define FONT_FALLBACK			"-*"
#define DEFAULT_BG_COLOR		0 /* not used *************/ 
#define DEFAULT_WIDGET_COLOR_R		"0.9"
#define DEFAULT_WIDGET_COLOR_G		"1.1"
#define DEFAULT_WIDGET_COLOR_B		"1.4"

#define DEFAULT_BDWIDTH         1

struct look *look = 0;
#ifdef NEXT_LOOK
extern struct look look_next;
#endif
extern struct look look_cool;
extern struct look look_gtk;

struct resource_param {
    char *name;
    char **value;
};

static char *init_display = DEFAULT_DISPLAY;
char *init_geometry = DEFAULT_GEOM;
char *init_font = DEFAULT_FONT;
char *init_widget_font = 0;
char *init_bg_color = DEFAULT_BG_COLOR;
char *init_fg_color_red = DEFAULT_WIDGET_COLOR_R;
char *init_fg_color_green = DEFAULT_WIDGET_COLOR_G;
char *init_fg_color_blue = DEFAULT_WIDGET_COLOR_B;
#ifndef NEXT_LOOK
char *init_look = "gtk";
#else
char *init_look = "next";
#endif

Atom ATOM_WM_PROTOCOLS, ATOM_WM_DELETE_WINDOW;
Atom ATOM_WM_NAME, ATOM_WM_NORMAL_HINTS, ATOM_WM_TAKE_FOCUS;

/* Resources */

struct resource_param resources[] =
{
    {"display", &init_display},
    {"geometry", &init_geometry},
    {"font", &init_font},
    {"widget_font", &init_widget_font},
    {"fg_red", &init_fg_color_red},
    {"fg_blue", &init_fg_color_blue},
    {"fg_green", &init_fg_color_green},
    {0, 0}
};

static void alloccolorerror (void)
{
/* Translations in initapp.c are of a lower priority than other files, since they only output when cooledit is run in verbose mode */
    fprintf (stderr, _ ("Cannot allocate colors. Could be to many applications\ntrying to use the colormap. If closing other apps doesn't\nhelp, then your graphics hardware may be inadequite.\n"));
    exit (1);
}

static void init_widgets (void)
{
    int i;
    last_widget = 1;		/*widget[0] is never used since index 0 is used
				   to indicate an error message */
    for (i = 0; i < MAX_NUMBER_OF_WIDGETS; i++)
	CIndex (i) = NULL;	/*initialise */
}

static void open_display (char *app_name, int wait_for_display)
{
    if (wait_for_display) {
	CDisplay = 0;
	while (!(CDisplay = XOpenDisplay (init_display)))
	    sleep (1);
    } else {
	if ((CDisplay = XOpenDisplay (init_display)) == NULL) {
	    fprintf (stderr, _ ("%s: can't open display named \"%s\"\n"),
		     app_name, XDisplayName (init_display));
	    exit (1);
	}
    }
    CRoot = DefaultRootWindow (CDisplay);
    if (verbose_operation)
	printf (_ ("Opened display \"%s\"\n"), XDisplayName (init_display));
}

static void get_resources (void)
{
    int i;
    char *type;
    XrmValue value;
    XrmDatabase rdb;
    XrmInitialize ();
    rdb = XrmGetFileDatabase (catstrs (getenv ("HOME"), "/.Xdefaults", 0));
    if (rdb != NULL) {
	for (i = 0; resources[i].name; i++) {
	    char *rname = catstrs (CAppName, "*", resources[i].name, 0);
	    if (XrmGetResource (rdb, rname, rname,
				&type, &value)) {
		*resources[i].value = value.addr;
	    }
	}
    }
}

static void load_font (void)
{
    char f[256];
    if (CPushFont ("editor", init_font))
	exit (1);
    sprintf (f, init_widget_font, FONT_HEIGHT - 1);
    if (CPushFont ("widget", f)) {
	fprintf (stderr, _ ("%s: falling back to font %s\n"), CAppName, FONT_FALLBACK);
	if (CPushFont ("widget", FONT_FALLBACK))
	    exit (1);
    }
}

static void visual_comments (int class)
{
    switch (class) {
    case PseudoColor:
	printf ("PseudoColor");
	if (CDepth >= 7)
/* 'Depth' is the number of color bits per graphics pixel */
	    printf (_ (" - depth ok, this will work.\n"));
	else
/* 'Depth' is the number of color bits per graphics pixel */
	    printf (_ (" - depth low, this may not work.\n"));
	break;
    case GrayScale:
	printf ("Grayscale -\n");
/* 'Visual' is the hardware method of displaying graphics */
	printf (_ ("Mmmmh, haven't tried this visual class, let's see what happens.\n"));
	break;
    case DirectColor:
	printf ("DirectColor -\n");
/* 'Visual' is the hardware method of displaying graphics */
	printf (_ ("Mmmmh, haven't tried this visual class, let's see what happens.\n"));
	break;
    case StaticColor:
	printf ("StaticColor - ");
/* "Let us attempt to use this Visual even though it may not work" */
	printf (_ ("lets give it a try.\n"));
	break;
    case StaticGray:
	printf ("StaticGray - ");
/* "Let us attempt to use this Visual even though it may not work" */
	printf (_ ("lets give it a try.\n"));
	break;
    case TrueColor:
	printf ("TrueColor - ");
/* "Adequite" (with sarcasm) : i.e. it is actually the best kind of Visual " */
	printf (_ ("fine.\n"));
	break;
    default:
/* 'Visual' is the method hardware method of displaying graphics */
	CError (_ ("?\nVisual class unknown.\n"));
	break;
    }
}

/* must be free'd */
XColor *get_cells (Colormap cmap, int *size)
{
    int i;
    XColor *c;
    *size = DisplayCells (CDisplay, DefaultScreen (CDisplay));
    c = CMalloc (*size * sizeof (XColor));
    for (i = 0; i < *size; i++)
	c[i].pixel = i;
    XQueryColors (CDisplay, cmap, c, *size);
    return c;
}

#define BitsPerRGBofVisual(v) (v->bits_per_rgb)


/* find the closest color without allocating it */
int CGetCloseColor (XColor * cells, int ncells, XColor color, long *error)
{
    unsigned long merror = (unsigned long) -1;
    unsigned long e;
    int min = 0, i;
    unsigned long mask = 0xFFFF0000UL;

    mask >>= min (BitsPerRGBofVisual (CVisual), 5);
    for (i = 0; i < ncells; i++) {
	e = 8 * abs ((int) (color.red & mask) - (cells[i].red & mask)) + 10 * abs ((int) (color.green & mask) - (cells[i].green & mask)) + 5 * abs ((int) (color.blue & mask) - (cells[i].blue & mask));
	if (e < merror) {
	    merror = e;
	    min = i;
	}
    }
    merror = 8 * abs ((int) (color.red & mask) - (cells[min].red & mask)) + 10 * abs ((int) (color.green & mask) - (cells[min].green & mask)) + 5 * abs ((int) (color.blue & mask) - (cells[min].blue & mask));
    if (error)
	*error = (long) merror;
    return min;
}

#define grey_intense(i) (i * 65535 / 63)

/* return -1 if not found. Meaning that another coolwidget app is not running */
int find_coolwidget_grey_scale (XColor * c, int ncells)
{
    unsigned long mask = 0xFFFF0000UL;
    int i, j;
    mask >>= BitsPerRGBofVisual (CVisual);

    for (i = 0; i < ncells; i++) {
	for (j = 0; j < 64; j++)
	    if (!((c[i + j].green & mask) == (grey_intense (j) & mask)
		  && c[i + j].red == c[i + j].green && c[i + j].green == c[i + j].blue))
		goto cont;
	return i;
      cont:;
    }
    return -1;
}


void CAllocColorCells (Colormap colormap, Bool contig,
		       unsigned long plane_masks[], unsigned int nplanes,
		       unsigned long pixels[], unsigned int npixels)
{
    if (!XAllocColorCells (CDisplay, colormap, contig,
			   plane_masks, nplanes, pixels, npixels))
	alloccolorerror ();
}

void CAllocColor (Colormap cmap, XColor * c)
{
    if (!XAllocColor (CDisplay, cmap, c))
	alloccolorerror ();
}

static void get_grey_colors (XColor * color, int i)
{
    color->red = color->green = grey_intense (i);
    color->blue = grey_intense (i);
    color->flags = DoRed | DoBlue | DoGreen;
}

static void get_button_color (XColor * color, int i)
{
    (*look->get_button_color) (color, i);
}

int option_invert_colors = 0;
int option_invert_crome = 0;
int option_invert_red_green = 0;
int option_invert_green_blue = 0;
int option_invert_red_blue = 0;

#define clip(x,a,b) ((x) >= (b) ? (b) : ((x) <= (a) ? (a) : (x)))
#define cswap(a,b) {float t; t = (a); (a) = (b); (b) = t;}

/* inverts the cromiance - this is for editor background colors that are very light */
static int transform (int color)
{
    float r, g, b, y, y_max, c1, c1_max, c2, c2_max;
    r = (float) ((color >> 16) & 0xFF);
    g = (float) ((color >> 8) & 0xFF);
    b = (float) ((color >> 0) & 0xFF);
    y_max = 0.3 * 240.0 + 0.6 * 240.0 + 0.1 * 240.0;
    if (option_invert_red_green)
	cswap (r, g)
	    if (option_invert_green_blue)
	    cswap (g, b)
		if (option_invert_red_blue)
		cswap (r, b)
		    y = 0.3000 * r + 0.6000 * g + 0.1000 * b;
    c1 = -0.1500 * r - 0.3000 * g + 0.4500 * b;
    c2 = 0.4375 * r - 0.3750 * g - 0.0625 * b;
    c1_max = -0.1500 * 255.0 - 0.3000 * 255.0 + 0.4500 * 255.0;
    c2_max = 0.4375 * 255.0 - 0.3750 * 255.0 - 0.0625 * 255.0;
    if (option_invert_crome) {
	c1 = c1_max - c1;
	c2 = c2_max - c2;
    }
    if (option_invert_colors)
	y = y_max - y;
    r = 1.0 * y + 0.0000 * c1 + 1.6 * c2;
    g = 1.0 * y - 0.3333 * c1 - 0.8 * c2;
    b = 1.0 * y + 2.0000 * c1 + 0.0 * c2;
    r = clip (r, 0.0, 255.0);
    g = clip (g, 0.0, 255.0);
    b = clip (b, 0.0, 255.0);
    return (int) (((int) r) << 16) | (((int) g) << 8) | (((int) b) << 0);
}

/* colours */
int option_color_0 = 0x080808;
int option_color_1 = 0x000065;
int option_color_2 = 0x0000FF;
int option_color_3 = 0x008B00;
int option_color_4 = 0x008B8B;
int option_color_5 = 0x009ACD;
int option_color_6 = 0x00FF00;
int option_color_7 = 0x00FA9A;
int option_color_8 = 0x00FFFF;
int option_color_9 = 0x8B2500;
int option_color_10 = 0x8B008B;
int option_color_11 = 0x7D26CD;
int option_color_12 = 0x8B7500;
int option_color_13 = 0x7F7F7F;
int option_color_14 = 0x7B68EE;
int option_color_15 = 0x7FFF00;
int option_color_16 = 0x87CEEB;
int option_color_17 = 0x7FFFD4;
int option_color_18 = 0xEE0000;
int option_color_19 = 0xEE1289;
int option_color_20 = 0xEE00EE;
int option_color_21 = 0xCD6600;
int option_color_22 = 0xF8B7B7;
int option_color_23 = 0xE066FF;
int option_color_24 = 0xEEEE00;
int option_color_25 = 0xEEE685;
int option_color_26 = 0xF8F8FF;

/* takes 0-26 and converts it to RGB */
static void get_general_colors (XColor * color, int i)
{
    unsigned long c = 0;
    switch (i) {
    case 0:
	c = transform (option_color_0);
	break;
    case 1:
	c = transform (option_color_1);
	break;
    case 2:
	c = transform (option_color_2);
	break;
    case 3:
	c = transform (option_color_3);
	break;
    case 4:
	c = transform (option_color_4);
	break;
    case 5:
	c = transform (option_color_5);
	break;
    case 6:
	c = transform (option_color_6);
	break;
    case 7:
	c = transform (option_color_7);
	break;
    case 8:
	c = transform (option_color_8);
	break;
    case 9:
	c = transform (option_color_9);
	break;
    case 10:
	c = transform (option_color_10);
	break;
    case 11:
	c = transform (option_color_11);
	break;
    case 12:
	c = transform (option_color_12);
	break;
    case 13:
	c = transform (option_color_13);
	break;
    case 14:
	c = transform (option_color_14);
	break;
    case 15:
	c = transform (option_color_15);
	break;
    case 16:
	c = transform (option_color_16);
	break;
    case 17:
	c = transform (option_color_17);
	break;
    case 18:
	c = transform (option_color_18);
	break;
    case 19:
	c = transform (option_color_19);
	break;
    case 20:
	c = transform (option_color_20);
	break;
    case 21:
	c = transform (option_color_21);
	break;
    case 22:
	c = transform (option_color_22);
	break;
    case 23:
	c = transform (option_color_23);
	break;
    case 24:
	c = transform (option_color_24);
	break;
    case 25:
	c = transform (option_color_25);
	break;
    case 26:
	c = transform (option_color_26);
	break;
    }
    color->red = ((c >> 16) & 0xFF) << 8;
    color->green = ((c >> 8) & 0xFF) << 8;
    color->blue = ((c >> 0) & 0xFF) << 8;
    color->flags = DoRed | DoBlue | DoGreen;
}

void alloc_grey_scale (Colormap cmap)
{
    XColor color;
    int i;

    if (option_using_grey_scale) {
	for (i = 0; i < 64; i++) {
	    get_grey_colors (&color, i);
	    CAllocColor (cmap, &color);
	    color_pixels[i + 43] = color.pixel;
	}
    }
}

/*
   This sets up static color, but tries to be more intelligent about the
   way it handles grey scales. This allows resonable color display on 16
   color VGA servers.
 */
static void setup_staticcolor (void)
{
    XColor *c;
    unsigned short *grey_levels;
    XColor color;
    int size, i, j, k, n, m = 0, num_greys, grey;

    c = get_cells (CColormap, &size);
    grey_levels = CMalloc ((size + 2) * sizeof (unsigned short));
    num_greys = 0;

/* we are probably not going to find our coolwwidget colors here,
   so use greyscale for the buttons. first count how many greys,
   and sort them: */

    grey = 0;
    for (i = 0; i < size; i++) {
	if (c[i].red == c[i].green && c[i].green == c[i].blue) {
	    if (grey) {
		for (n = 0; n < grey; n++)
		    if (c[i].green == grey_levels[n])
			goto cont;
		for (n = grey - 1; n >= 0; n--)
		    if (grey_levels[n] > c[i].green) {
			memmove (&(grey_levels[n + 1]), &(grey_levels[n]), (grey - n) * sizeof (unsigned short));
			grey_levels[n] = c[i].green;
			grey++;
			goto cont;
		    }
		grey_levels[grey] = c[i].green;
	    } else
		grey_levels[grey] = c[i].green;
	    grey++;
	  cont:;
	}
    }
    num_greys = grey;

    if (num_greys <= 2) {	/* there's just no hope  :(   */
	if (verbose_operation)
	    printf (_ ("This will work, but it may look terrible.\n"));
	for (grey = 0; grey < 16; grey++) {
	    color.flags = DoRed | DoGreen | DoBlue;
	    color.red = grey * 65535 / 15;
	    color.green = grey * 65535 / 15;
	    color.blue = grey * 65535 / 15;
	    if (!XAllocColor (CDisplay, CColormap, &color))
		alloccolorerror ();
	    color_pixels[grey] = color.pixel;
	}
	alloc_grey_scale (CColormap);
    } else {
	j = 0;
	k = 0;
	for (grey = 0; grey < num_greys; grey++) {
/* button colors */
	    color.red = color.green = grey_levels[grey];
	    color.blue = grey_levels[grey];
	    color.flags = DoRed | DoGreen | DoBlue;

	    for (; j < (grey + 1) * 16 / num_greys; j++) {
		CAllocColor (CColormap, &color);
		color_pixels[j] = color.pixel;
	    }
/* grey scale */
	    if (option_using_grey_scale) {
		for (; k < (grey + 1) * 64 / num_greys; k++) {
		    CAllocColor (CColormap, &color);
		    color_pixels[k + 43] = color.pixel;
		}
	    }
	}
    }

    for (i = 0; i < 27; i++) {
	get_general_colors (&color, i);
	m = CGetCloseColor (c, size, color, 0);
	CAllocColor (CColormap, &(c[m]));
	color_pixels[16 + i] = c[m].pixel;
    }

    free (grey_levels);
    free (c);
}

static void make_grey (XColor * color)
{
    long g;

    g = ((long) color->red) * 8L + ((long) color->green) * 10L + ((long) color->blue) * 5L;
    g /= (8l + 10L + 5L);
    color->red = color->green = g;
    color->blue = g;
}

/*
   For TrueColor displays, colors can always be found. Hence we
   need not find the "closest" matching color.
 */
static void setup_alloc_colors (int force_grey)
{
    int i;
    XColor color;

    color.flags = DoRed | DoGreen | DoBlue;

    for (i = 0; i < 16; i++) {
	get_button_color (&color, i);
	if (force_grey)
	    make_grey (&color);
	CAllocColor (CColormap, &color);
	color_pixels[i] = color.pixel;
    }

    for (i = 0; i < 27; i++) {
	get_general_colors (&color, i);
	if (force_grey)
	    make_grey (&color);
	CAllocColor (CColormap, &color);
	color_pixels[16 + i] = color.pixel;
    }

    alloc_grey_scale (CColormap);
}

void store_grey_scale (Colormap cmap)
{
    XColor color;
    int i;
    if (verbose_operation)
/* "Grey scale" is a list of increasingly bright grey levels of color */
	printf (_ ("Storing grey scale.\n"));
    if (!XAllocColorCells (CDisplay, cmap, 1, color_planes, 6, color_pixels + 43, 1))
	alloccolorerror ();
    for (i = 0; i < 64; i++) {
	color_pixels[43 + i] = color_pixels[43] + i;
	color.pixel = color_pixels[43 + i];
	get_grey_colors (&color, i);
	XStoreColor (CDisplay, cmap, &color);
    }
}

void try_color (Colormap cmap, XColor * c, int size, XColor color, int i)
{
    int x;
    long error;
    XColor closest;

    x = CGetCloseColor (c, size, color, &error);
    closest = c[x];

    if (error) {
	if (XAllocColorCells (CDisplay, cmap, 0, color_planes, 0, color_pixels + i, 1)) {
	    color.pixel = color_pixels[i];
	    XStoreColor (CDisplay, cmap, &color);
	    if (verbose_operation)
/* "Assign color" */
		printf (_ ("Store,"));
	    return;
	}
    }
    if (!XAllocColor (CDisplay, cmap, &closest))
	if (verbose_operation)
/* "Ignoring" means that the program will continue regardless of this error */
	    printf (_ ("\nerror allocating this color - ignoring;"));	/* this should never happen since closest comes from the colormap */
    color_pixels[i] = closest.pixel;
    if (verbose_operation)
	printf ("%ld,", ((error == 0) ? 0 : 1) + ((error / (8 + 10 + 5)) >> (16 - BitsPerRGBofVisual (CVisual))));
}


/*
   for PseudoColor displays. This tries to be conservative in the number
   of entries its going to store, while still keeping the colors exact.
   first it looks for an entry in the default colormap and only stores
   in the map if no match is found. Multiple coolwidget applications can
   therefore share the same map. At worst 16 + 27 of the palette are used
   plus another 64 if you are using greyscale.
 */
static void setup_store_colors (void)
{
    int i, size;
    XColor *c;
    XColor color;

    c = get_cells (CColormap, &size);

    color.flags = DoRed | DoGreen | DoBlue;

/* grey scale has to be contigous to be fast so store a 64 colors */
    if (option_using_grey_scale) {
#if 0
	i = find_coolwidget_grey_scale (c, size);
	if (i >= 0) {
	    if (verbose_operation)
/* Not essential to translate */
		printf (_ ("found grey scale\n"));
	    alloc_grey_scale (CColormap);
	} else {
#endif
	    store_grey_scale (CColormap);
#if 0
	}
#endif
    }
    if (verbose_operation)
/* This isn't very important, run cooledit -verbose to see how this works */
	printf (_ ("Trying colors...\n( 'Store'=store my own color, Number=integer error with existing color )\n"));

    for (i = 0; i < 16; i++) {
	get_button_color (&color, i);
	try_color (CColormap, c, size, color, i);
    }

    for (i = 0; i < 27; i++) {
	get_general_colors (&color, i);
	try_color (CColormap, c, size, color, i + 16);
    }
    if (verbose_operation)
	printf ("\n");
    free (c);
}


static void setup_colormap (int class)
{
    switch (class) {
    case StaticColor:
    case StaticGray:
	setup_staticcolor ();
	break;
    case GrayScale:
	setup_alloc_colors (1);
	return;
    case DirectColor:
    case TrueColor:
	setup_alloc_colors (0);
	return;
    case PseudoColor:
	setup_store_colors ();
	break;
    }
}

int CSendEvent (XEvent * e);
static XEvent xevent;

static int cursor_blink_rate;

/*
   Aim1: Get the cursor to flash all the time:

   Aim2: Have coolwidgets send an alarm event, just like
   any other event. For the application to use.

   Requirements: XNextEvent must still be the blocker
   so that the process doesn't hog when idle.

   Problems: If the alarm handler sends an event using
   XSendEvent it may hang the program.

   To solve, we put a pause() before XNextEvent so that it waits for 
   an alarm, and also define our own CSendEvent routine with
   its own queue. So that things don't slow down, we pause() only
   if no events are pending. Also make the alarm rate high (100 X per sec).
   (I hope this is the easiest way to do this  :|   )
 */

void CSetCursorBlinkRate (int b)
{
    if (b < 1)
	b = 1;
    cursor_blink_rate = b;
}

int CGetCursorBlinkRate (void)
{
    return cursor_blink_rate;
}

/* does nothing and calls nothing for t seconds, resolution is ALRM_PER_SECOND */
void CSleep (double t)
{
    float i;
    for (i = 0; i < t; i += 1.0 / ALRM_PER_SECOND)
	pause ();
}


static struct itimerval alarm_every =
{
    {
	0, 0
    },
    {
	0, 1000000 / ALRM_PER_SECOND
    }
};

static struct itimerval alarm_off =
{
    {
	0, 0
    },
    {
	0, 0
    }
};

/*
   This flag goes non-zero during the CSendEvent procedure. This
   prevents the small chance that an alarm event might occur during
   a CSendEvent.
 */
int block_push_event = 0;
int got_alarm = 0;

void _alarmhandler (void)
{
    static int count = ALRM_PER_SECOND;
    got_alarm = 0;
    if (count) {
	count--;
	if (CQueueSize () < 16 && !block_push_event) {
	    CSendEvent (&xevent);
	}
    } else {
	xevent.type = AlarmEvent;
	if (CQueueSize () < 128 && !block_push_event) {	/* say */
	    CSendEvent (&xevent);
	}
	xevent.type = TickEvent;
	count = ALRM_PER_SECOND / cursor_blink_rate;
    }
}

static RETSIGTYPE alarmhandler (int x)
{
    if (!got_alarm)
	got_alarm = 1;
    signal (SIGALRM, alarmhandler);
    setitimer (ITIMER_REAL, &alarm_every, NULL);
#if (RETSIGTYPE==void)
    return;
#else
    return 1;			/* :guess --- I don't know what to return here */
#endif
}

#define CHILD_EXITED_MAX	256

static struct {
    pid_t pid;
    int status;
} children_exitted[CHILD_EXITED_MAX];

static unsigned char children_exitted_leader = 0;
static unsigned char children_exitted_trailer = 0;

#if (RETSIGTYPE==void)
#define handler_return return
#else
#define handler_return return 0
#endif

static RETSIGTYPE childhandler (int x)
{
    int save_errno = errno;
    pid_t pid;
    pid = waitpid (-1, &children_exitted[children_exitted_leader].status, WNOHANG);
    if (pid > 0) {
	if ((unsigned char) (children_exitted_leader - children_exitted_trailer) <
	    (unsigned char) ((int) CHILD_EXITED_MAX - 2)) {
	    children_exitted[children_exitted_leader].pid = pid;
	    children_exitted_leader++;
	}
    }
    errno = save_errno;
    signal (SIGCHLD, childhandler);
    handler_return;
}

struct child_exitted_item {
    struct child_exitted_item *next;
    pid_t pid;
    int status;
};

struct child_exitted_list {
    struct child_exitted_item *next;
};

static struct child_exitted_list child_list = {NULL};

void childhandler_ (void)
{
    while (children_exitted_trailer != children_exitted_leader) {
        struct child_exitted_item *c;
        c = malloc (sizeof (struct child_exitted_item));
        memset (c, '\0', sizeof (*c));
        c->pid = children_exitted[children_exitted_trailer].pid;
        c->status = children_exitted[children_exitted_trailer].status;
        c->next = child_list.next;
        child_list.next = c;
        children_exitted_trailer++;
    }
}

#if 0
/* returns non-zero on child exit */
int CChildExitted (pid_t p, int *status)
{
    unsigned char i;
    for (i = children_exitted_trailer; i != children_exitted_leader; i++)
        if (p && children_exitted[i].pid == p) {
            if (status)
                *status = children_exitted[i].status;
            children_exitted[i].pid = 0;
            children_exitted[i].status = 0;
            return 1;
        }
    return 0;
}
#endif

/* returns non-zero on child exit */
int CChildExitted (pid_t p, int *status)
{
    struct child_exitted_item *c;
    if (!p)
        return 0;
    for (c = (struct child_exitted_item *) &child_list; c->next;) {
        if (c->next->pid == p) {
            struct child_exitted_item *t;
            t = c->next;
            c->next = c->next->next;
	    if (status)
        	*status = t->status;
            free (t);
            return 1;
        } else {
            c = c->next;
        }
    }
    return 0;
}

void CChildWait (pid_t p)
{
    while (!CChildExitted (p, NULL)) {
        struct timeval tv;
        childhandler_ ();
        tv.tv_sec = 0;
        tv.tv_usec = 50000;
        select (0, NULL, NULL, NULL, &tv);
    }
}

static void set_child_handler (void)
{
    memset (children_exitted, 0, sizeof (children_exitted));
    signal (SIGCHLD, childhandler);
}

static void set_alarm (void)
{
    memset (&xevent, 0, sizeof (XEvent));
    xevent.type = 0;
    xevent.xany.display = CDisplay;
    xevent.xany.send_event = 1;

    CSetCursorBlinkRate (7);	/* theta rhythms ? */

    signal (SIGALRM, alarmhandler);
    setitimer (ITIMER_REAL, &alarm_every, NULL);
}

void CEnableAlarm (void)
{
    set_alarm ();
}

void CDisableAlarm (void)
{
    setitimer (ITIMER_REAL, &alarm_off, NULL);
    signal (SIGALRM, 0);
}

void get_temp_dir (void)
{
    if (temp_dir)
	return;
    temp_dir = getenv ("TEMP");
    if (temp_dir)
	if (*temp_dir) {
	    temp_dir = (char *) strdup (temp_dir);
	    return;
	}
    temp_dir = getenv ("TMP");
    if (temp_dir)
	if (*temp_dir) {
	    temp_dir = (char *) strdup (temp_dir);
	    return;
	}
    temp_dir = (char *) strdup ("/tmp");
}

void get_home_dir (void)
{
    if (home_dir)		/* already been set */
	return;
    home_dir = getenv ("HOME");
    if (home_dir)
	if (*home_dir) {
	    home_dir = (char *) strdup (home_dir);
	    return;
	}
    home_dir = (getpwuid (geteuid ()))->pw_dir;
    if (home_dir)
	if (*home_dir) {
	    home_dir = (char *) strdup (home_dir);
	    return;
	}
    fprintf (stderr, _ ("%s: HOME environment variable not set and no passwd entry - aborting\n"), CAppName);
    abort ();
}

void get_dir (void)
{
    if (!get_current_wd (current_dir, MAX_PATH_LEN))
	*current_dir = 0;
    get_temp_dir ();
    get_home_dir ();
}

void wm_interaction_init (void)
{
    ATOM_WM_PROTOCOLS = XInternAtom (CDisplay, "WM_PROTOCOLS", False);
    ATOM_WM_DELETE_WINDOW = XInternAtom (CDisplay, "WM_DELETE_WINDOW", False);
    ATOM_WM_NAME = XInternAtom (CDisplay, "WM_NAME", False);
    ATOM_WM_TAKE_FOCUS = XInternAtom (CDisplay, "WM_TAKE_FOCUS", False);
}

#ifdef GUESS_VISUAL

char visual_name[16][16];

void make_visual_list (void)
{
    memset (visual_name, 0, sizeof (visual_name));
    strcpy (visual_name[StaticGray], "StaticGray");
    strcpy (visual_name[GrayScale], "GrayScale");
    strcpy (visual_name[StaticColor], "StaticColor");
    strcpy (visual_name[PseudoColor], "PseudoColor");
    strcpy (visual_name[TrueColor], "TrueColor");
    strcpy (visual_name[DirectColor], "DirectColor");
}

struct visual_priority {
    int class;
    int depth_low;
    int depth_high;
} visual_priority[] = {

    {
	TrueColor, 15, 16
    },
    {
	TrueColor, 12, 14
    },
    {
	PseudoColor, 6, 999
    },
    {
	DirectColor, 12, 999
    },
    {
	TrueColor, 17, 999
    },

    {
	DirectColor, 8, 11
    },
    {
	TrueColor, 8, 11
    },
    {
	StaticColor, 8, 999
    },

    {
	PseudoColor, 4, 5
    },
    {
	DirectColor, 6, 7
    },
    {
	TrueColor, 6, 7
    },
    {
	StaticColor, 6, 7
    },

    {
	DirectColor, 4, 5
    },
    {
	TrueColor, 4, 5
    },
    {
	StaticColor, 4, 5
    },

    {
	GrayScale, 6, 999
    },
    {
	StaticGray, 6, 999
    },
    {
	GrayScale, 4, 5
    },
    {
	StaticGray, 4, 5
    },
};

#endif

char *option_preferred_visual = 0;
int option_force_own_colormap = 0;
int option_force_default_colormap = 0;

void get_preferred (XVisualInfo * v, int n, Visual ** vis, int *depth)
{
#ifndef GUESS_VISUAL
    *vis = DefaultVisual (CDisplay, DefaultScreen (CDisplay));
    *depth = DefaultDepth (CDisplay, DefaultScreen (CDisplay));
#else
    int i, j;
    Visual *def = 0;
    int def_depth = 0;

    if (option_preferred_visual)
	if (!*option_preferred_visual)
	    option_preferred_visual = 0;

/* NLS ? */
    if (option_preferred_visual)
	if (!strcasecmp (option_preferred_visual, "help")
	    || !strcasecmp (option_preferred_visual, "h")) {
	    printf (_ ("%s:\n  The <visual-class> is the hardware technique used by the\n" \
		       "computer to draw pixels to the screen. _Usually_ only one\n" \
		 "visual is truly supported. This option is provided\n" \
	     "if you would rather use a TrueColor than a PseudoColor\n" \
		 "visual where you are short of color palette space.\n" \
		       "The depth is the number of bits per pixel used by the hardware.\n" \
		   "It is automatically selected using heuristics.\n"), \
		    CAppName);
	    printf (_ ("Available visuals on this system are:\n"));
	    for (i = 0; i < n; i++)
		printf ("        class %s, depth %d\n", visual_name[v[i].class], v[i].depth);
	    exit (1);
	}
    if (option_preferred_visual) {	/* first check if the user wants the default visual */
	int default_name;
	default_name = ClassOfVisual (DefaultVisual (CDisplay, DefaultScreen (CDisplay)));
	if (visual_name[default_name])
	    if (!strcasecmp (visual_name[default_name], option_preferred_visual)) {
		*vis = DefaultVisual (CDisplay, DefaultScreen (CDisplay));
		*depth = DefaultDepth (CDisplay, DefaultScreen (CDisplay));
		return;
	    }
    }
    for (j = 0; j < sizeof (visual_priority) / sizeof (struct visual_priority); j++)
	for (i = 0; i < n; i++)
	    if (v[i].class == visual_priority[j].class)
		if (v[i].depth >= visual_priority[j].depth_low && v[i].depth <= visual_priority[j].depth_high) {
		    if (option_preferred_visual) {
			if (!strcasecmp (visual_name[v[i].class], option_preferred_visual)) {
			    *vis = v[i].visual;
			    *depth = v[i].depth;
			    return;
			}
		    }
		    if (!def) {
			def = v[i].visual;
			def_depth = v[i].depth;
		    }
		}
    if (option_preferred_visual)
/* We will select a visual in place of the one you specified, since yours is unavailable */
	fprintf (stderr, _ ("%s: preferred visual not found, selecting...\n"), CAppName);

    if (def) {
	*vis = def;
	*depth = def_depth;
    } else {
/* We will select the default visual, since the list didn't have a matching visual */
	fprintf (stderr, _ ("%s: no known visuals found, using default...\n"), CAppName);
	*vis = DefaultVisual (CDisplay, DefaultScreen (CDisplay));
	*depth = DefaultDepth (CDisplay, DefaultScreen (CDisplay));
    }
    option_preferred_visual = 0;
#endif
}

static void get_preferred_visual_and_depth (void)
{
    XVisualInfo *v, t;
    int n;

#ifdef GUESS_VISUAL
    make_visual_list ();
#endif
    t.screen = DefaultScreen (CDisplay);

    v = XGetVisualInfo (CDisplay, VisualScreenMask, &t, &n);

    get_preferred (v, n, &CVisual, &CDepth);
}

static void assign_default_cmap (void)
{
    if (verbose_operation)
	printf (_ ("Using DefaultColormap()\n"));
    CColormap = DefaultColormap (CDisplay, DefaultScreen (CDisplay));
}

static void assign_own_cmap (void)
{
    if (verbose_operation)
	printf (_ ("Creating own colormap\n"));
    CColormap = XCreateColormap (CDisplay,
			 RootWindow (CDisplay, DefaultScreen (CDisplay)),
				 CVisual, AllocNone);
}

static void assign_check_colormap (void)
{
#if 0				/* What do I do here ? */
    switch (ClassOfVisual (CVisual)) {
    case PseudoColor:
    case GrayScale:
    case DirectColor:
	assign_default_cmap ();
	return;
    }
#endif
    assign_own_cmap ();
}

/* #define TRY_WM_COLORMAP 1 */

#define COLORMAP_PROPERTY "RGB_DEFAULT_MAP"

static void get_colormap (void)
{
#ifdef TRY_WM_COLORMAP
    Atom DEFAULT_CMAPS;
#endif

    if (option_force_default_colormap) {
	assign_default_cmap ();
	return;
    }
    if (option_force_own_colormap) {
	assign_own_cmap ();
	return;
    }
    if (XVisualIDFromVisual (CVisual)
	== XVisualIDFromVisual (DefaultVisual (CDisplay, DefaultScreen (CDisplay)))) {
	if (verbose_operation)
	    printf (_ ("Default visual ID found\n"));
	assign_default_cmap ();
	return;
    }
#ifdef TRY_WM_COLORMAP
/* NLS ? */
    if (verbose_operation)
	printf ("I don't really know what I'm doing here, so feel free to help - paul\n");

    DEFAULT_CMAPS = XInternAtom (CDisplay, COLORMAP_PROPERTY, True);

    if (DEFAULT_CMAPS == None) {
	if (verbose_operation)
/* "An Atom of name %s could not be found". 'Atom' is X terminology */
	    printf (_ ("No Atom %s \n"), COLORMAP_PROPERTY);
	assign_check_colormap ();
	return;
    } else {
	int i, n;
	XStandardColormap *cmap;
	if (!XGetRGBColormaps (CDisplay, CRoot, &cmap, &n,
			       DEFAULT_CMAPS)) {
	    if (verbose_operation)
		printf (_ ("XGetRGBColormaps(%s) failed\n"), COLORMAP_PROPERTY);
	    assign_check_colormap ();
	    return;
	}
	if (verbose_operation)
	    printf (_ ("Choosing from %d 'XGetRGBColormaps' colormaps\n"), n);
	for (i = 0; i < n; i++) {
	    if (XVisualIDFromVisual (CVisual) == cmap[i].visualid) {
		if (verbose_operation)
		    printf (_ ("Colormap %d matches visual ID\n"), i);
		CColormap = cmap[i].colormap;
		return;
	    }
	}
	if (verbose_operation)
	    printf (_ ("No colormap found matching our visual ID\n"));
    }
#endif

    assign_check_colormap ();
}

int ignore_handler (Display * c, XErrorEvent * e)
{
    return 0;
}

void init_cursors (void);

/*-------------------------------------------------------------*/
void CInitialise (CInitData * config_start)
{
    if (!config_start->look)
	config_start->look = init_look;

    if (!strncmp (config_start->look, "gtk", 3)) {
	look = &look_gtk;
    } else if (!strncmp (config_start->look, "next", 4)) {
#ifdef NEXT_LOOK
	look = &look_next;
#else
	fprintf (stderr, _ ("%s: NeXT look was not compiled into this binary\n"), config_start->name);
	exit (1);
#endif
    } else if (!strncmp (config_start->look, "cool", 4)) {
	look = &look_cool;
    } else {
	look = &look_gtk;
    }

    option_interwidget_spacing = (*look->get_default_interwidget_spacing) ();
    init_widget_font = (*look->get_default_widget_font) ();

    given = config_start;
    verbose_operation = (given->options & CINIT_OPTION_VERBOSE);

    if (verbose_operation)
	printf ("sizeof(CWidget) = %d\n", (int) sizeof (CWidget));

    CAppName = given->name;

    option_using_grey_scale = (given->options & CINIT_OPTION_USE_GREY);

/* Initialise the widget library */
    init_widgets ();

/* get home dir directory into home_dir and current directory into current_dir */
    get_dir ();

/* Get resources from the resource file */
    get_resources ();
    if (given->display)
	init_display = given->display;
    if (given->geometry)
	init_geometry = given->geometry;
    if (given->font)
	init_font = given->font;
    if (given->widget_font)
	init_widget_font = given->widget_font;
    if (given->bg)
	init_bg_color = given->bg;
    if (given->fg_red)
	init_fg_color_red = given->fg_red;
    if (given->fg_green)
	init_fg_color_green = given->fg_green;
    if (given->fg_blue)
	init_fg_color_blue = given->fg_blue;

/*  Open connection to display selected by user */
    open_display (CAppName, given->options & CINIT_OPTION_WAIT_FOR_DISPLAY);
    XSetErrorHandler (ignore_handler);

/*  Initialise window manager atoms to detect a user close */
    wm_interaction_init ();

/* Now set up the visual and colors */
    get_preferred_visual_and_depth ();

    if (verbose_operation) {
	printf (_ ("Found a visual, depth = %d,\n       visual class = "), CDepth);
	visual_comments (ClassOfVisual (CVisual));
    }
    get_colormap ();

/* Now setup that color map discribed above */
    setup_colormap (ClassOfVisual (CVisual));

/* Set up font */
    load_font ();

#ifdef USE_XIM
/* set the XIM locale */
    init_xlocale ();
#endif

/* some special cursors */
    init_cursors ();

#ifdef HAVE_DND
/*  Initialise drag and drop capabilities cursius dnd protocol version 1 */
    initialise_drag_n_drop ();
#else
/* Initialise drag and drop capabilities xdnd protocol */
    xdnd_init (CDndClass, CDisplay);
    mouse_init ();
#endif

    XAaInit (CDisplay, CVisual, CDepth, CRoot);

/* set child handler */
    set_child_handler ();

/* an alarm handler generates xevent of tyoe AlarmEvent every 1/4 second to flash the cursor */
    set_alarm ();
}
