/*  gxfce
 *
 *  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.
*/

/****************************************************************************
 * This module is from original code
 * by Rob Nation 
 *
 * This code handles colormaps for xfwm.
 *
 * Copyright 1994 Robert Nation. No restrictions are placed on this code,
 * as long as the copyright notice is preserved . No guarantees or
 * warrantees of any sort whatsoever are given or implied or anything.
 ****************************************************************************/

#include "configure.h"

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>

#include "xfwm.h"
#include "menus.h"
#include "misc.h"
#include "parse.h"
#include "screen.h"
#include "module.h"

#ifdef DMALLOC
#  include "dmalloc.h"
#endif

XfwmWindow *colormap_win;
Colormap last_cmap = None;
extern XfwmWindow *Tmp_win;

/***********************************************************************
 *
 *  Procedure:
 *	HandleColormapNotify - colormap notify event handler
 *
 * This procedure handles both a client changing its own colormap, and
 * a client explicitly installing its colormap itself (only the window
 * manager should do that, so we must set it correctly).
 *
 ***********************************************************************/
void
HandleColormapNotify (void)
{
  XColormapEvent *cevent = (XColormapEvent *) & Event;
  Bool ReInstall = False;


  if (!Tmp_win)
  {
    return;
  }
  if (cevent->new)
  {
    XGetWindowAttributes (dpy, Tmp_win->w, &(Tmp_win->attr));
    if ((Tmp_win == colormap_win) && (Tmp_win->number_cmap_windows == 0))
      last_cmap = Tmp_win->attr.colormap;
    ReInstall = True;
  }
  else if ((cevent->state == ColormapUninstalled) && (last_cmap == cevent->colormap))
  {
    /* Some window installed its colormap, change it back */
    ReInstall = True;
  }

  while (XCheckTypedEvent (dpy, ColormapNotify, &Event))
  {
#ifdef REQUIRES_STASHEVENT
    StashEventTime (&Event);
#endif
    if (XFindContext (dpy, cevent->window, XfwmContext, (caddr_t *) & Tmp_win) == XCNOENT)
      Tmp_win = NULL;
    if ((Tmp_win) && (cevent->new))
    {
      XGetWindowAttributes (dpy, Tmp_win->w, &(Tmp_win->attr));
      if ((Tmp_win == colormap_win) && (Tmp_win->number_cmap_windows == 0))
	last_cmap = Tmp_win->attr.colormap;
      ReInstall = True;
    }
    else if ((Tmp_win) && (cevent->state == ColormapUninstalled) && (last_cmap == cevent->colormap))
    {
      /* Some window installed its colormap, change it back */
      ReInstall = True;
    }
    else if ((Tmp_win) && (cevent->state == ColormapInstalled) && (last_cmap == cevent->colormap))
    {
      /* The last color map installed was the correct one. Don't
       * change anything */
      ReInstall = False;
    }
  }

  /* Reinstall the colormap that we think should be installed,
   * UNLESS and unrecognized window has the focus - it might be
   * an override-redirect window that has its own colormap. */
  if ((ReInstall) && (Scr.UnknownWinFocused == None))
  {
    XInstallColormap (dpy, last_cmap);
  }
}

/************************************************************************
 *
 * Re-Install the active colormap 
 *
 *************************************************************************/
void
ReInstallActiveColormap (void)
{
  InstallWindowColormaps (colormap_win);
}

/***********************************************************************
 *
 *  Procedure:
 *	InstallWindowColormaps - install the colormaps for one xfwm window
 *
 *  Inputs:
 *	type	- type of event that caused the installation
 *	tmp	- for a subset of event types, the address of the
 *		  window structure, whose colormaps are to be installed.
 *
 ************************************************************************/

void
InstallWindowColormaps (XfwmWindow * tmp)
{
  int i;
  XWindowAttributes attributes;
  Window w;
  Bool ThisWinInstalled = False;


  /* If no window, then install root colormap */
  if (!tmp)
    tmp = &Scr.XfwmRoot;

  colormap_win = tmp;
  /* Save the colormap to be loaded for when force loading of
   * root colormap(s) ends.
   */
  Scr.pushed_window = tmp;
  /* Don't load any new colormap if root colormap(s) has been
   * force loaded.
   */
  if (Scr.root_pushes)
  {
    return;
  }

  if (tmp->number_cmap_windows > 0)
  {
    for (i = tmp->number_cmap_windows - 1; i >= 0; i--)
    {
      w = tmp->cmap_windows[i];
      if (w == tmp->w)
	ThisWinInstalled = True;
      XGetWindowAttributes (dpy, w, &attributes);

      if (last_cmap != attributes.colormap)
      {
	last_cmap = attributes.colormap;
	XInstallColormap (dpy, attributes.colormap);
      }
    }
  }

  if (!ThisWinInstalled)
  {
    if (last_cmap != tmp->attr.colormap)
    {
      last_cmap = tmp->attr.colormap;
      XInstallColormap (dpy, tmp->attr.colormap);
    }
  }
}


/***********************************************************************
 *
 *  Procedures:
 *	<Uni/I>nstallRootColormap - Force (un)loads root colormap(s)
 *
 *	   These matching routines provide a mechanism to insure that
 *	   the root colormap(s) is installed during operations like
 *	   rubber banding or menu display that require colors from
 *	   that colormap.  Calls may be nested arbitrarily deeply,
 *	   as long as there is one UninstallRootColormap call per
 *	   InstallRootColormap call.
 *
 *	   The final UninstallRootColormap will cause the colormap list
 *	   which would otherwise have be loaded to be loaded, unless
 *	   Enter or Leave Notify events are queued, indicating some
 *	   other colormap list would potentially be loaded anyway.
 ***********************************************************************/
void
InstallRootColormap ()
{
  XfwmWindow *tmp;
  if (Scr.root_pushes == 0)
  {
    tmp = Scr.pushed_window;
    InstallWindowColormaps (&Scr.XfwmRoot);
    Scr.pushed_window = tmp;
  }
  Scr.root_pushes++;
  return;
}

/***************************************************************************
 * 
 * Unstacks one layer of root colormap pushing 
 * If we peel off the last layer, re-install th e application colormap
 * 
 ***************************************************************************/
void
UninstallRootColormap ()
{
  if (Scr.root_pushes)
    Scr.root_pushes--;

  if (!Scr.root_pushes)
  {
    InstallWindowColormaps (Scr.pushed_window);
  }

  return;
}



/*****************************************************************************
 *
 * Gets the WM_COLORMAP_WINDOWS property from the window
 *   This property typically doesn't exist, but a few applications
 *   use it. These seem to occur mostly on SGI machines.
 *
 ****************************************************************************/
void
FetchWmColormapWindows (XfwmWindow * tmp)
{
  if (tmp->cmap_windows != (Window *) NULL)
    XFree ((void *) tmp->cmap_windows);

  if (!XGetWMColormapWindows (dpy, tmp->w, &(tmp->cmap_windows), &(tmp->number_cmap_windows)))
  {
    tmp->number_cmap_windows = 0;
    tmp->cmap_windows = NULL;
  }
}
