/*
  Around 12/20/99 we did the 3rd rewrite of the shadow/hilite stuff.
  (That I know about (dje).
  The first stuff I saw just applied a percentage.
  Then we got some code from SCWM.
  This stuff comes from "Visual.c" which is part of Lesstif.
  Here's their copyright:

 * Copyright (C) 1995 Free Software Foundation, Inc.
 *
 * This file is part of the GNU LessTif Library.
 *
 * 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.
 *

 The routine at the bottom "pixel_to_color_string" was not from Lesstif.

 Port by Dan Espen, no additional copyright
*/

#include "config.h"                     /* must be first */

#include <stdio.h>
#include <X11/Xproto.h>                 /* for X functions in general */
#include "fvwmlib.h"                    /* prototype GetShadow GetHilit */

#define	PCT_BRIGHTNESS			(6 * 0xffff / 100)

/* How much lighter/darker to make things in default routine */

#define	PCT_DARK_BOTTOM		70	/* lighter (less dark, actually) */
#define	PCT_DARK_TOP		50	/* lighter */
#define	PCT_LIGHT_BOTTOM	55	/* darker */
#define	PCT_LIGHT_TOP		80	/* darker */
#define	PCT_MEDIUM_BOTTOM_BASE	40	/* darker */
#define	PCT_MEDIUM_BOTTOM_RANGE	25
#define	PCT_MEDIUM_TOP_BASE	60	/* lighter */
#define	PCT_MEDIUM_TOP_RANGE	-30

/* The "brightness" of an RGB color.  The "right" way seems to use
 * empirical values like the default thresholds below, but it boils down
 * to red is twice as bright as blue and green is thrice blue.
 */

#define	BRIGHTNESS(r,g,b) (2*(int)(r) + 3*(int)(g) + 1*(int)(b))

/* From Xm.h on Solaris */
#define XmDEFAULT_DARK_THRESHOLD        15
#define XmDEFAULT_LIGHT_THRESHOLD       85
extern Colormap Pcmap;
extern Display *Pdpy;

static XColor color;

/**** This part is the old fvwm way to calculate colours. Still used for
 **** 'medium' brigness colours. */
#define DARKNESS_FACTOR 0.5
#define BRIGHTNESS_FACTOR 1.4
#define SCALE 65535.0
#define HALF_SCALE (SCALE / 2)
typedef enum {
  R_MAX_G_MIN, R_MAX_B_MIN,
  G_MAX_B_MIN, G_MAX_R_MIN,
  B_MAX_R_MIN, B_MAX_G_MIN
} MinMaxState;
static void
color_mult (unsigned short *red,
	    unsigned short *green,
	    unsigned short *blue, double k)
{
  if (*red == *green && *red == *blue) {
    double temp;
    /* A shade of gray */
    temp = k * (double) (*red);
    if (temp > SCALE) {
      temp = SCALE;
    }
    *red = (unsigned short)(temp);
    *green = *red;
    *blue = *red;
  } else {
    /* Non-zero saturation */
    double r, g, b;
    double min, max;
    double a, l, s;
    double delta;
    double middle;
    MinMaxState min_max_state;

    r = (double) *red;
    g = (double) *green;
    b = (double) *blue;

    if (r > g) {
      if (r > b) {
	max = r;
	if (g < b) {
	  min = g;
	  min_max_state = R_MAX_G_MIN;
	  a = b - g;
	} else {
	  min = b;
	  min_max_state = R_MAX_B_MIN;
	  a = g - b;
	}
      } else {
	max = b;
	min = g;
	min_max_state = B_MAX_G_MIN;
	a = r - g;
      }
    } else {
      if (g > b) {
	max = g;
	if (b < r) {
	  min = b;
	  min_max_state = G_MAX_B_MIN;
	  a = r - b;
	} else {
	  min = r;
	  min_max_state = G_MAX_R_MIN;
	  a = b - r;
	}
      } else {
	max = b;
	min = r;
	min_max_state = B_MAX_R_MIN;
	a = g - r;
      }
    }

    delta = max - min;
    a = a / delta;

    l = (max + min) / 2;
    if (l <= HALF_SCALE) {
      s = max + min;
    } else {
      s = 2.0 * SCALE - (max + min);
    }
    s = delta/s;

    l *= k;
    if (l > SCALE) {
      l = SCALE;
    }
    s *= k;
    if (s > 1.0) {
      s = 1.0;
    }

    if (l <= HALF_SCALE) {
      max = l * (1 + s);
    } else {
      max = s * SCALE + l - s * l;
    }

    min = 2 * l - max;
    delta = max - min;
    middle = min + delta * a;

    switch (min_max_state) {
    case R_MAX_G_MIN:
      r = max;
      g = min;
      b = middle;
      break;
    case R_MAX_B_MIN:
      r = max;
      g = middle;
      b = min;
      break;
    case G_MAX_B_MIN:
      r = middle;
      g = max;
      b = min;
      break;
    case G_MAX_R_MIN:
      r = min;
      g = max;
      b = middle;
      break;
    case B_MAX_G_MIN:
      r = middle;
      g = min;
      b = max;
      break;
    case B_MAX_R_MIN:
      r = min;
      g = middle;
      b = max;
      break;
    }

    *red = (unsigned short) r;
    *green = (unsigned short) g;
    *blue = (unsigned short) b;
  }
}
/**** End of original fvwm code. ****/

static XColor *GetShadowOrHiliteColor(
  Pixel background, float light, float dark, float factor)
{
  long brightness;
  unsigned int red, green, blue;

  memset(&color, 0, sizeof(color));
  color.pixel = background;
  XQueryColor(Pdpy, Pcmap, &color);
  red = color.red;
  green = color.green;
  blue = color.blue;

  brightness = BRIGHTNESS(red, green, blue);
  /* For "dark" backgrounds, make everything a fixed %age lighter */
  if (brightness < XmDEFAULT_DARK_THRESHOLD * PCT_BRIGHTNESS)
  {
    color.red =
      (unsigned short)(0xffff - ((0xffff - red) * dark + 50) / 100);
    color.green =
      (unsigned short)(0xffff - ((0xffff - green) * dark + 50) / 100);
    color.blue =
      (unsigned short)(0xffff - ((0xffff - blue) * dark + 50) / 100);
  }
  /* For "light" background, make everything a fixed %age darker */
  else if (brightness > XmDEFAULT_LIGHT_THRESHOLD * PCT_BRIGHTNESS)
  {
    color.red =
      (unsigned short)((red * light + 50) / 100);
    color.green =
      (unsigned short)((green * light + 50) / 100);
    color.blue =
      (unsigned short)((blue * light + 50) / 100);
  }
  /* For "medium" background, select is a fixed %age darker;
   * top (lighter) and bottom (darker) are a variable %age
   * based on the background's brightness
   */
  else
  {
    color_mult(&color.red, &color.green, &color.blue, factor);
  }
  return &color;
}


XColor *GetShadowColor(Pixel background)
{
  return GetShadowOrHiliteColor(
    background, PCT_LIGHT_BOTTOM, PCT_DARK_BOTTOM, DARKNESS_FACTOR);
}

Pixel GetShadow(Pixel background)
{
  XColor *colorp;

  colorp = GetShadowColor(background);
  XAllocColor (Pdpy, Pcmap, colorp);
  return colorp->pixel;
}

XColor *GetHiliteColor(Pixel background)
{
  return GetShadowOrHiliteColor(
    background, PCT_LIGHT_TOP, PCT_DARK_TOP, BRIGHTNESS_FACTOR);
}

Pixel GetHilite(Pixel background)
{
  XColor *colorp;

  colorp = GetHiliteColor(background);
  XAllocColor (Pdpy, Pcmap, colorp);
  return colorp->pixel;
}

/* This function converts the colour stored in a colorcell (pixel) into the
 * string representation of a colour.  The output is printed at the
 * address 'output'.  It is either in rgb format ("rgb:rrrr/gggg/bbbb") if
 * use_hash is False or in hash notation ("#rrrrggggbbbb") if use_hash is true.
 * The return value is the number of characters used by the string.  The
 * rgb values of the output are undefined if the colorcell is invalid.  The
 * memory area pointed at by 'output' must be at least 64 bytes (in case of
 * future extensions and multibyte characters).*/
int pixel_to_color_string(
  Display *dpy, Colormap cmap, Pixel pixel, char *output, Bool use_hash)
{
  XColor color;
  int n;

  color.pixel = pixel;
  color.red = 0;
  color.green = 0;
  color.blue = 0;

  XQueryColor(dpy, cmap, &color);
  if (!use_hash)
  {
    sprintf(output, "rgb:%04x/%04x/%04x%n", (int)color.red, (int)color.green,
	    (int)color.blue, &n);
  }
  else
  {
    sprintf(output, "#%04x%04x%04x%n", (int)color.red, (int)color.green,
	    (int)color.blue, &n);
  }

  return n;
}


syntax highlighted by Code2HTML, v. 0.9.1