/****************************************************************************
 * This module is based on Twm, but has been siginificantly modified
 * by Rob Nation
 ****************************************************************************/
/*****************************************************************************/
/**       Copyright 1988 by Evans & Sutherland Computer Corporation,        **/
/**                          Salt Lake City, Utah                           **/
/**  Portions Copyright 1989 by 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  permis-    **/
/**    sion  notice appear in supporting  documentation,  and  that  the    **/
/**    names of Evans & Sutherland and M.I.T. not be used in advertising    **/
/**    in publicity pertaining to distribution of the  software  without    **/
/**    specific, written prior permission.                                  **/
/**                                                                         **/
/**    EVANS & SUTHERLAND AND M.I.T. DISCLAIM ALL WARRANTIES WITH REGARD    **/
/**    TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES  OF  MERCHANT-    **/
/**    ABILITY  AND  FITNESS,  IN  NO  EVENT SHALL EVANS & SUTHERLAND OR    **/
/**    M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL  DAM-    **/
/**    AGES 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.                                     **/
/*****************************************************************************/

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


/**********************************************************************
 *
 * Add a new window, put the titlbar and other stuff around
 * the window
 *
 **********************************************************************/
#include "config.h"

#include <stdio.h>

#include "libs/fvwmlib.h"
#include "libs/FShape.h"
#include "libs/FScreen.h"
#include "fvwm.h"
#include "externs.h"
#include "cursor.h"
#include "functions.h"
#include "commands.h"
#include "bindings.h"
#include "misc.h"
#include "screen.h"
#include "defaults.h"
#include "add_window.h"
#include "events.h"
#include "module_interface.h"
#include "stack.h"
#include "update.h"
#include "style.h"
#include "icons.h"
#include "gnome.h"
#include "focus.h"
#include "placement.h"
#include "geometry.h"
#include "session.h"
#include "move_resize.h"
#include "borders.h"
#include "colormaps.h"
#include "decorations.h"

char NoName[] = "Untitled"; /* name if no name in XA_WM_NAME */
char NoClass[] = "NoClass"; /* Class if no res_class in class hints */
char NoResource[] = "NoResource"; /* Class if no res_name in class hints */
long isIconicState = 0;
Bool PPosOverride = False;
Bool isIconifiedByParent = False;

void FetchWmProtocols(FvwmWindow *);
void GetWindowSizeHints(FvwmWindow *);
/***********************************************************************/

static void delete_client_context(FvwmWindow *fw)
{
  FvwmWindow *cw;

  /* We can not simply delete the context.  X might have reused the
   * window structure so we would delete the context that was established
   * by another FvwmWindow structure in the mean time. */
  if (XFindContext(
	dpy, fw->w, FvwmContext, (caddr_t *)&cw) != XCNOENT && cw == fw)
  {
    XDeleteContext(dpy, fw->w, FvwmContext);
  }

  return;
}

Bool setup_window_structure(
  FvwmWindow **ptmp_win, Window w, FvwmWindow *ReuseWin)
{
  FvwmWindow save_state;
  FvwmWindow *savewin = NULL;

  memset(&save_state, '\0', sizeof(FvwmWindow));

  /*
      Allocate space for the FvwmWindow struct, or reuse an
      old one (on Recapture).
  */
  if (ReuseWin == NULL)
  {
    *ptmp_win = (FvwmWindow *)safemalloc(sizeof(FvwmWindow));
    if (*ptmp_win == (FvwmWindow *)0)
    {
      return False;
    }
  }
  else
  {
    *ptmp_win = ReuseWin;
    savewin = &save_state;
    memcpy(savewin, ReuseWin, sizeof(FvwmWindow));
  }

  /*
    RBW - 1999/05/28 - modify this when we implement the preserving of
    various states across a Recapture. The Destroy function in misc.c may
    also need tweaking, depending on what you want to preserve.
    For now, just zap any old information, except the desk.
  */
  memset(*ptmp_win, '\0', sizeof(FvwmWindow));
  (*ptmp_win)->w = w;
  if (savewin != NULL)
  {
    (*ptmp_win)->Desk = savewin->Desk;
    SET_SHADED(*ptmp_win, IS_SHADED(savewin));
    SET_PLACED_WB3(*ptmp_win,IS_PLACED_WB3(savewin));
    SET_PLACED_BY_FVWM(*ptmp_win, IS_PLACED_BY_FVWM(savewin));
  }

  (*ptmp_win)->cmap_windows = (Window *)NULL;
#ifdef MINI_ICONS
  (*ptmp_win)->mini_pixmap_file = NULL;
  (*ptmp_win)->mini_icon = NULL;
#endif

  return True;
}

#ifdef I18N_MB
#define GET_NAME_PROPERTY(a, b, c, d) get_name_property(a, b, c, d)
static void get_name_property(
  Status (func)(Display *, Window, XTextProperty *), Window w, char **ret_name,
  char ***ret_name_list)
{
  XTextProperty text_prop;
  char **list;
  int num;

  if (func(dpy, w, &text_prop) != 0)
  {
    if (text_prop.value)
    {
      if (text_prop.encoding == XA_STRING)
      {
        /* STRING encoding, use this as it is */
        *ret_name = (char *)text_prop.value;
        *ret_name_list = NULL;
      }
      else
      {
        /* not STRING encoding, try to convert */
        if (XmbTextPropertyToTextList(dpy, &text_prop, &list, &num) >= Success
            && num > 0 && *list)
	{
          /* XXX: does not consider the conversion is REALLY succeeded */
          XFree(text_prop.value); /* return of XGetWM(Icon)Name() */
          *ret_name = *list;
          *ret_name_list = list;
        }
	else
	{
          if (list)
            XFreeStringList(list);
          XFree(text_prop.value); /* return of XGet(Icon)WMName() */
	  if (func(dpy, w, &text_prop))
	  {
	    *ret_name = (char *)text_prop.value;
	    *ret_name_list = NULL;
	  }
	  else
	  {
	    *ret_name = NoName;
	  }
        }
      }
    }
    else
    {
      *ret_name = NoName;
    }
  }
  else
  {
    *ret_name = NoName;
  }

  return;
}
#else /* I18N_MB */
#define GET_NAME_PROPERTY(a, b, c, d) get_name_property(a, b, c)
#ifdef COMPOUND_TEXT
static void get_name_property(
  Status (func)(Display *, Window, XTextProperty *), Window w, char **ret_name)
{
  XTextProperty text_prop;
  int num;
  char **list;

  /* Note: we dump the name because we cannot keep the list in memory
   * without a lot of changes */
  if (func(dpy, w, &text_prop) != 0)
  {
    if (text_prop.value)
    {
      if (text_prop.encoding == XA_STRING)
      {
        /* STRING encoding, use this as it is */
	CopyString(&*ret_name, (char *)text_prop.value);
	XFree(text_prop.value);
      }
      else
      {
        /* not STRING encoding, try to convert */
        if (XmbTextPropertyToTextList(dpy, &text_prop, &list, &num) == Success
            && num > 0 && *list)
	{
          /* XXX: does not consider the conversion is REALLY succeeded */
          XFree(text_prop.value); /* return of XGetWM(Icon)Name() */
	  CopyString(&*ret_name, *list);
	  if (list)
	    XFreeStringList(list);
        }
	else
	{
          if (list)
            XFreeStringList(list);
          XFree(text_prop.value); /* return of XGet(Icon)WMName() */
	  if (func(dpy, w, &text_prop))
	  {
	    CopyString(&*ret_name, (char *)text_prop.value);
	    XFree(text_prop.value);
	  }
	  else
	  {
	    *ret_name = NoName;
	  }
        }
      }
    }
    else
    {
      *ret_name = NoName;
    }
  }
  else
  {
    *ret_name = NoName;
  }

  return;
}
#else /* COMPOUND_TEXT */
static void get_name_property(
  Status (func)(Display *, Window, XTextProperty *), Window w, char **ret_name)
{
  XTextProperty text_prop;

  if (func(dpy, w, &text_prop) != 0)
    *ret_name = (char *)text_prop.value;
  else
    *ret_name = NoName;

  return;
}
#endif /* COMPOUND_TEXT */
#endif /* I18N_MB */

void setup_window_name(FvwmWindow *tmp_win)
{
  GET_NAME_PROPERTY(XGetWMName, tmp_win->w, &(tmp_win->name),
		    &(tmp_win->name_list));
  if (debugging)
    fvwm_msg(DBG,"setup_window_name","Assigned name %s'",tmp_win->name);
}

void setup_class_and_resource(FvwmWindow *tmp_win)
{
  /* removing NoClass change for now... */
  tmp_win->class.res_name = NoResource;
  tmp_win->class.res_class = NoClass;
  XGetClassHint(dpy, tmp_win->w, &tmp_win->class);
  if (tmp_win->class.res_name == NULL)
    tmp_win->class.res_name = NoResource;
  if (tmp_win->class.res_class == NULL)
    tmp_win->class.res_class = NoClass;
  FetchWmProtocols (tmp_win);
  FetchWmColormapWindows (tmp_win);
  if(!(XGetWindowAttributes(dpy, tmp_win->w, &(tmp_win->attr))))
    tmp_win->attr.colormap = Pcmap;
}

void setup_wm_hints(FvwmWindow *tmp_win)
{
  tmp_win->wmhints = XGetWMHints(dpy, tmp_win->w);
  set_focus_model(tmp_win);
}

static void destroy_window_font(FvwmWindow *tmp_win)
{
  if (IS_WINDOW_FONT_LOADED(tmp_win) && !USING_DEFAULT_WINDOW_FONT(tmp_win) &&
      tmp_win->title_font.font != Scr.DefaultFont.font)
  {
    FreeFvwmFont(dpy, &(tmp_win->title_font));
  }
  SET_WINDOW_FONT_LOADED(tmp_win, 0);
  /* Fall back to default font. There are some race conditions when a window
   * is destroyed and recaptured where an invalid font might be accessed
   * otherwise. */
  tmp_win->title_font = Scr.DefaultFont;
  SET_USING_DEFAULT_WINDOW_FONT(tmp_win, 1);
}

void setup_window_font(
  FvwmWindow *tmp_win, window_style *pstyle, Bool do_destroy)
{
  int height;

  /* get rid of old font */
  if (do_destroy)
  {
    destroy_window_font(tmp_win);
    /* destroy_window_font resets the IS_WINDOW_FONT_LOADED flag */
  }
  /* load new font */
  if (!IS_WINDOW_FONT_LOADED(tmp_win))
  {
    if (SFHAS_WINDOW_FONT(*pstyle) && SGET_WINDOW_FONT(*pstyle) &&
	LoadFvwmFont(dpy, SGET_WINDOW_FONT(*pstyle), &(tmp_win->title_font)))
    {
      SET_USING_DEFAULT_WINDOW_FONT(tmp_win, 0);
    }
    else
    {
      /* no explicit font or failed to load, use default font instead */
      tmp_win->title_font = Scr.DefaultFont;
      SET_USING_DEFAULT_WINDOW_FONT(tmp_win, 1);
    }
    SET_WINDOW_FONT_LOADED(tmp_win, 1);
  }

  /* adjust font offset according to height specified in title style */
  if (tmp_win->decor->title_height)
  {
    height = tmp_win->decor->title_height;
    tmp_win->title_font.y =
      tmp_win->title_font.font->ascent +
      (height - (tmp_win->title_font.height + EXTRA_TITLE_FONT_HEIGHT)) / 2;
    if (tmp_win->title_font.y < tmp_win->title_font.font->ascent)
      tmp_win->title_font.y = tmp_win->title_font.font->ascent;
    tmp_win->title_g.height = height;
  }
  else
  {
    height = tmp_win->title_font.height;
    tmp_win->title_font.y = tmp_win->title_font.font->ascent;
    tmp_win->title_g.height =
      tmp_win->title_font.height + EXTRA_TITLE_FONT_HEIGHT;
  }
    tmp_win->corner_width = tmp_win->title_g.height + tmp_win->boundary_width;
  if (!HAS_TITLE(tmp_win))
  {
    tmp_win->title_g.height = 0;
  }
  tmp_win->title_top_height =
    (HAS_BOTTOM_TITLE(tmp_win)) ? 0 : tmp_win->title_g.height;

  return;
}

static void destroy_icon_font(FvwmWindow *tmp_win)
{
  if (IS_ICON_FONT_LOADED(tmp_win) && !USING_DEFAULT_ICON_FONT(tmp_win) &&
      tmp_win->icon_font.font != Scr.DefaultFont.font)
  {
    FreeFvwmFont(dpy, &(tmp_win->icon_font));
  }
  SET_ICON_FONT_LOADED(tmp_win, 0);
  /* Fall back to default font (see comment above). */
  tmp_win->icon_font = Scr.DefaultFont;
  SET_USING_DEFAULT_ICON_FONT(tmp_win, 1);
}

void setup_icon_font(
  FvwmWindow *tmp_win, window_style *pstyle, Bool do_destroy)
{
  int height;

  height = (IS_ICON_FONT_LOADED(tmp_win)) ? tmp_win->icon_font.height : 0;
  if (IS_ICON_SUPPRESSED(tmp_win) || HAS_NO_ICON_TITLE(tmp_win))
  {
    if (IS_ICON_FONT_LOADED(tmp_win))
    {
      destroy_icon_font(tmp_win);
      /* destroy_icon_font resets the IS_ICON_FONT_LOADED flag */
    }
    return;
  }
  /* get rid of old font */
  if (do_destroy && IS_ICON_FONT_LOADED(tmp_win))
  {
    destroy_icon_font(tmp_win);
    /* destroy_icon_font resets the IS_ICON_FONT_LOADED flag */
  }
  /* load new font */
  if (!IS_ICON_FONT_LOADED(tmp_win))
  {
    if (SFHAS_ICON_FONT(*pstyle) && SGET_ICON_FONT(*pstyle) &&
	LoadFvwmFont(dpy, SGET_ICON_FONT(*pstyle), &(tmp_win->icon_font)))
    {
      SET_USING_DEFAULT_ICON_FONT(tmp_win, 0);
    }
    else
    {
      /* no explicit font or failed to load, use default font instead */
      tmp_win->icon_font = Scr.DefaultFont;
      SET_USING_DEFAULT_ICON_FONT(tmp_win, 1);
    }
    SET_ICON_FONT_LOADED(tmp_win, 1);
  }
  /* adjust y position of existing icons */
  if (height)
  {
    tmp_win->icon_g.y += height - tmp_win->icon_font.height;
    /* this repositions the icon even if the window is not iconified */
    DrawIconWindow(tmp_win);
  }

  return;
}

void setup_style_and_decor(
  FvwmWindow *tmp_win, window_style *pstyle, short *buttons)
{
  /* first copy the static styles into the window struct */
  memcpy(&(FW_COMMON_FLAGS(tmp_win)), &(pstyle->flags.common),
         sizeof(common_flags_type));
  tmp_win->wShaped = None;
  if (FShapesSupported)
  {
    int i;
    unsigned int u;
    Bool b;
    int boundingShaped;

    /* suppress compiler warnings w/o shape extension */
    i = 0;
    u = 0;
    b = False;

    FShapeSelectInput(dpy, tmp_win->w, FShapeNotifyMask);
    if (FShapeQueryExtents(
      dpy, tmp_win->w, &boundingShaped, &i, &i, &u, &u, &b, &i, &i, &u, &u))
    {
      tmp_win->wShaped = boundingShaped;
    }
  }

  /*  Assume that we'll decorate */
  SET_HAS_BORDER(tmp_win, 1);

  SET_HAS_TITLE(tmp_win, 1);

#ifdef USEDECOR
  /* search for a UseDecor tag in the style */
  if (!IS_DECOR_CHANGED(tmp_win))
  {
    FvwmDecor *decor = &Scr.DefaultDecor;

    for (; decor; decor = decor->next)
    {
      if (StrEquals(SGET_DECOR_NAME(*pstyle), decor->tag))
      {
        tmp_win->decor = decor;
        break;
      }
    }
  }
  if (tmp_win->decor == NULL)
  {
    tmp_win->decor = &Scr.DefaultDecor;
  }
#endif

  GetMwmHints(tmp_win);
  GetOlHints(tmp_win);

  tmp_win->buttons = SIS_BUTTON_DISABLED(&pstyle->flags);
  SelectDecor(tmp_win, pstyle, buttons);

  if (IS_TRANSIENT(tmp_win) && !pstyle->flags.do_decorate_transient)
  {
      SET_HAS_BORDER(tmp_win, 0);
      SET_HAS_TITLE(tmp_win, 0);
  }
  /* set boundary width to zero for shaped windows */
  if (FHaveShapeExtension)
  {
    if (tmp_win->wShaped)
      tmp_win->boundary_width = 0;
  }

  /****** window colors ******/
  update_window_color_style(tmp_win, pstyle);
  update_window_color_hi_style(tmp_win, pstyle);

  /****** window shading ******/
  tmp_win->shade_anim_steps = pstyle->shade_anim_steps;
}

void setup_icon_boxes(FvwmWindow *tmp_win, window_style *pstyle)
{
  icon_boxes *ib;

  /* copy iconboxes ptr (if any) */
  if (SHAS_ICON_BOXES(&pstyle->flags))
  {
    tmp_win->IconBoxes = SGET_ICON_BOXES(*pstyle);
    for (ib = tmp_win->IconBoxes; ib; ib = ib->next)
    {
      ib->use_count++;
    }
  }
  else
  {
    tmp_win->IconBoxes = NULL;
  }
}

void destroy_icon_boxes(FvwmWindow *tmp_win)
{
  if (tmp_win->IconBoxes)
  {
    tmp_win->IconBoxes->use_count--;
    if (tmp_win->IconBoxes->use_count == 0 && tmp_win->IconBoxes->is_orphan)
    {
      /* finally destroy the icon box */
      free_icon_boxes(tmp_win->IconBoxes);
      tmp_win->IconBoxes = NULL;
    }
  }
}

void change_icon_boxes(FvwmWindow *tmp_win, window_style *pstyle)
{
  destroy_icon_boxes(tmp_win);
  setup_icon_boxes(tmp_win, pstyle);
}

void setup_layer(FvwmWindow *tmp_win, window_style *pstyle)
{
  FvwmWindow *tf;
  int layer;

  if (SUSE_LAYER(&pstyle->flags))
  {
    /* use layer from style */
    layer = SGET_LAYER(*pstyle);
  }
  else if ((tf = get_transientfor_fvwmwindow(tmp_win)) != NULL)
  {
    /* inherit layer from transientfor window */
    layer = get_layer(tf);
  }
  else
  {
    /* use default layer */
    layer = Scr.DefaultLayer;
  }
  set_default_layer(tmp_win, layer);
  set_layer(tmp_win, layer);

  return;
}

void setup_frame_size_limits(FvwmWindow *tmp_win, window_style *pstyle)
{
  if (SHAS_MAX_WINDOW_SIZE(&pstyle->flags))
  {
    tmp_win->max_window_width = SGET_MAX_WINDOW_WIDTH(*pstyle);
    tmp_win->max_window_height = SGET_MAX_WINDOW_HEIGHT(*pstyle);
  }
  else
  {
    tmp_win->max_window_width = DEFAULT_MAX_MAX_WINDOW_WIDTH;
    tmp_win->max_window_height = DEFAULT_MAX_MAX_WINDOW_HEIGHT;
  }
}

Bool setup_window_placement(FvwmWindow *tmp_win, window_style *pstyle)
{
  int client_argc = 0;
  char **client_argv = NULL;
  XrmValue rm_value;
  int tmpno1 = -1, tmpno2 = -1, tmpno3 = -1, spargs = 0;
  /* Used to parse command line of clients for specific desk requests. */
  /* Todo: check for multiple desks. */
  XrmDatabase db = NULL;
  static XrmOptionDescRec table [] = {
    /* Want to accept "-workspace N" or -xrm "fvwm*desk:N" as options
     * to specify the desktop. I have to include dummy options that
     * are meaningless since Xrm seems to allow -w to match -workspace
     * if there would be no ambiguity. */
    {"-workspacf",      "*junk",        XrmoptionSepArg, (caddr_t) NULL},
    {"-workspace",	"*desk",	XrmoptionSepArg, (caddr_t) NULL},
    {"-xrn",		NULL,		XrmoptionResArg, (caddr_t) NULL},
    {"-xrm",		NULL,		XrmoptionResArg, (caddr_t) NULL},
  };

  /* Find out if the client requested a specific desk on the command line. */
  /*  RBW - 11/20/1998 - allow a desk of -1 to work.  */

  if (XGetCommand(dpy, tmp_win->w, &client_argv, &client_argc) &&
      client_argc > 0 && client_argv != NULL)
  {
    /* Get global X resources */
    MergeXResources(dpy, &db, False);

    /* command line takes precedence over all */
    MergeCmdLineResources(
      &db, table, 4, client_argv[0], &client_argc, client_argv, True);

    /* Now parse the database values: */
    if (GetResourceString(db, "desk", client_argv[0], &rm_value) &&
	rm_value.size != 0)
    {
      SGET_START_DESK(*pstyle) = atoi(rm_value.addr);
      /*  RBW - 11/20/1998  */
      if (SGET_START_DESK(*pstyle) > -1)
      {
	SSET_START_DESK(*pstyle, SGET_START_DESK(*pstyle) + 1);
      }
      pstyle->flags.use_start_on_desk = 1;
    }
    if (GetResourceString(db, "fvwmscreen", client_argv[0], &rm_value) &&
	rm_value.size != 0)
    {
      SSET_START_SCREEN(*pstyle, FScreenGetScreenArgument(rm_value.addr, 'c'));
      pstyle->flags.use_start_on_screen = 1;
    }
    if (GetResourceString(db, "page", client_argv[0], &rm_value) &&
	rm_value.size != 0)
    {
      spargs = sscanf (rm_value.addr, "%d %d %d", &tmpno1, &tmpno2, &tmpno3);
      switch (spargs)
      {
      case 1:
	pstyle->flags.use_start_on_desk = 1;
	SSET_START_DESK(*pstyle, (tmpno1 > -1) ? tmpno1 + 1 : tmpno1);
	break;
      case 2:
	pstyle->flags.use_start_on_desk = 1;
	SSET_START_PAGE_X(*pstyle, (tmpno1 > -1) ? tmpno1 + 1 : tmpno1);
	SSET_START_PAGE_Y(*pstyle, (tmpno2 > -1) ? tmpno2 + 1 : tmpno2);
	break;
      case 3:
	pstyle->flags.use_start_on_desk = 1;
	SSET_START_DESK(*pstyle, (tmpno1 > -1) ? tmpno1 + 1 : tmpno1);
	SSET_START_PAGE_X(*pstyle, (tmpno2 > -1) ? tmpno2 + 1 : tmpno2);
	SSET_START_PAGE_Y(*pstyle, (tmpno3 > -1) ? tmpno3 + 1 : tmpno3);
	break;
      default:
	break;
      }
    }

    XFreeStringList(client_argv);
    XrmDestroyDatabase(db);
  }

  return PlaceWindow(
      tmp_win, &pstyle->flags, SGET_START_DESK(*pstyle),
      SGET_START_PAGE_X(*pstyle), SGET_START_PAGE_Y(*pstyle),
      SGET_START_SCREEN(*pstyle), PLACE_INITIAL);
}

void get_default_window_attributes(
  FvwmWindow *tmp_win, unsigned long *pvaluemask,
  XSetWindowAttributes *pattributes)
{
  if(Pdepth < 2)
  {
    *pvaluemask = CWBackPixmap;
    if(IS_STICKY(tmp_win))
      pattributes->background_pixmap = Scr.sticky_gray_pixmap;
    else
      pattributes->background_pixmap = Scr.light_gray_pixmap;
  }
  else
  {
    *pvaluemask = CWBackPixel;
    pattributes->background_pixel = tmp_win->colors.back;
    pattributes->background_pixmap = None;
  }

  *pvaluemask |= CWBackingStore | CWCursor | CWSaveUnder;
  pattributes->backing_store = NotUseful;
  pattributes->cursor = Scr.FvwmCursors[CRS_DEFAULT];
  pattributes->save_under = False;
}

void setup_frame_window(
  FvwmWindow *tmp_win)
{
  XSetWindowAttributes attributes;
  int valuemask = CWBackingStore | CWBackPixmap | CWEventMask | CWSaveUnder;

  attributes.backing_store = NotUseful;
  attributes.background_pixmap = None;
  attributes.event_mask = XEVMASK_FRAMEW;
  attributes.save_under = False;

  /* create the frame window, child of root, grandparent of client */
  tmp_win->frame = XCreateWindow(
      dpy, Scr.Root, tmp_win->frame_g.x, tmp_win->frame_g.y,
      tmp_win->frame_g.width, tmp_win->frame_g.height, 0, CopyFromParent,
      InputOutput, CopyFromParent, valuemask, &attributes);
  XSaveContext(dpy, tmp_win->w, FvwmContext, (caddr_t) tmp_win);
  XSaveContext(dpy, tmp_win->frame, FvwmContext, (caddr_t) tmp_win);
}

void setup_decor_window(
  FvwmWindow *tmp_win, int valuemask, XSetWindowAttributes *pattributes)
{
  /* stash attribute bits in case BorderStyle TiledPixmap overwrites */
  Pixmap TexturePixmap, TexturePixmapSave = pattributes->background_pixmap;

  valuemask |= CWBorderPixel | CWColormap | CWEventMask;

  pattributes->border_pixel = 0;
  pattributes->colormap = Pcmap;
  pattributes->event_mask = XEVMASK_DECORW;

  if (DFS_FACE_TYPE(GetDecor(tmp_win, BorderStyle.inactive.style)) ==
      TiledPixmapButton)
  {
    TexturePixmap = GetDecor(tmp_win,BorderStyle.inactive.u.p->picture);
    if (TexturePixmap)
    {
      pattributes->background_pixmap = TexturePixmap;
      valuemask = (valuemask & ~CWBackPixel) | CWBackPixmap;
    }
  }

  /* decor window, parent of all decorative subwindows */
  tmp_win->decor_w =
    XCreateWindow(
      dpy, tmp_win->frame, 0, 0, tmp_win->frame_g.width,
      tmp_win->frame_g.height, 0, Pdepth, InputOutput, Pvisual, valuemask,
      pattributes);
  XSaveContext(dpy, tmp_win->decor_w, FvwmContext, (caddr_t) tmp_win);

  /* restore background */
  pattributes->background_pixmap = TexturePixmapSave;
}

void setup_title_window(
  FvwmWindow *tmp_win, int valuemask, XSetWindowAttributes *pattributes)
{
  valuemask |= CWCursor | CWEventMask;
  pattributes->cursor = Scr.FvwmCursors[CRS_TITLE];
  pattributes->event_mask = XEVMASK_TITLEW;

  /* set up the title geometry - the height is already known from the decor */
  tmp_win->title_g.width = tmp_win->frame_g.width - 2*tmp_win->boundary_width;
  if(tmp_win->title_g.width < 1)
    tmp_win->title_g.width = 1;
  tmp_win->title_g.x = tmp_win->boundary_width + tmp_win->title_g.height + 1;
  tmp_win->title_g.y = tmp_win->boundary_width;

  tmp_win->title_w =
    XCreateWindow(
      dpy, tmp_win->decor_w, tmp_win->title_g.x, tmp_win->title_g.y,
      tmp_win->title_g.width, tmp_win->title_g.height, 0, CopyFromParent,
      InputOutput, CopyFromParent, valuemask, pattributes);
  XSaveContext(dpy, tmp_win->title_w, FvwmContext, (caddr_t) tmp_win);
}

void destroy_title_window(FvwmWindow *tmp_win, Bool do_only_delete_context)
{
  if (!do_only_delete_context)
  {
    XDestroyWindow(dpy, tmp_win->title_w);
    tmp_win->title_w = None;
  }
  XDeleteContext(dpy, tmp_win->title_w, FvwmContext);
  tmp_win->title_w = None;
}

void change_title_window(
  FvwmWindow *tmp_win, int valuemask, XSetWindowAttributes *pattributes)
{
  if (HAS_TITLE(tmp_win) && tmp_win->title_w == None)
    setup_title_window(tmp_win, valuemask, pattributes);
  else if (!HAS_TITLE(tmp_win) && tmp_win->title_w != None)
    destroy_title_window(tmp_win, False);
}

void setup_button_windows(
  FvwmWindow *tmp_win, int valuemask, XSetWindowAttributes *pattributes,
  short buttons)
{
  int i;
  Bool has_button;

  valuemask |= CWCursor | CWEventMask;
  pattributes->cursor = Scr.FvwmCursors[CRS_SYS];
  pattributes->event_mask = XEVMASK_BUTTONW;

  for (i = 0; i < NUMBER_OF_BUTTONS; i++)
  {
    has_button = (((!(i & 1) && i / 2 < Scr.nr_left_buttons) ||
		   ( (i & 1) && i / 2 < Scr.nr_right_buttons)) &&
		  (buttons & (1 << i)));
    if (tmp_win->button_w[i] == None && has_button)
    {
      tmp_win->button_w[i] =
	XCreateWindow(
	  dpy, tmp_win->decor_w, (i < NR_LEFT_BUTTONS) ?
	  (tmp_win->title_g.height * i) :
	  (tmp_win->title_g.width - tmp_win->title_g.height * (i+1)),
	  0, tmp_win->title_g.height, tmp_win->title_g.height, 0,
	  CopyFromParent, InputOutput, CopyFromParent, valuemask, pattributes);
      XSaveContext(dpy, tmp_win->button_w[i], FvwmContext, (caddr_t) tmp_win);
    }
    else if (tmp_win->button_w[i] != None && !has_button)
    {
      /* destroy the current button window */
      XDestroyWindow(dpy, tmp_win->button_w[i]);
      XDeleteContext(dpy, tmp_win->button_w[i], FvwmContext);
      tmp_win->button_w[i] = None;
    }
  }
}

void destroy_button_windows(FvwmWindow *tmp_win, Bool do_only_delete_context)
{
  int i;

  for(i = 0; i < NUMBER_OF_BUTTONS; i++)
  {
    if(tmp_win->button_w[i] != None)
    {
      if (!do_only_delete_context)
      {
	XDestroyWindow(dpy, tmp_win->button_w[i]);
	tmp_win->button_w[i] = None;
      }
      XDeleteContext(dpy, tmp_win->button_w[i], FvwmContext);
      tmp_win->button_w[i] = None;
    }
  }
}

void change_button_windows(
  FvwmWindow *tmp_win, int valuemask, XSetWindowAttributes *pattributes,
  short buttons)
{
  if (HAS_TITLE(tmp_win))
    setup_button_windows(
      tmp_win, valuemask, pattributes, buttons);
  else
    destroy_button_windows(tmp_win, False);
}


void setup_parent_window(FvwmWindow *tmp_win)
{
  XSetWindowAttributes attributes;
  int valuemask = CWBackingStore | CWBackPixmap | CWCursor | CWEventMask
  		  | CWSaveUnder;

  attributes.backing_store = NotUseful;
  attributes.background_pixmap = None;
  attributes.cursor = Scr.FvwmCursors[CRS_DEFAULT];
  attributes.event_mask = XEVMASK_PARENTW;
  attributes.save_under = False;

  /* This window is exactly the same size as the client for the
     benefit of some clients */
  tmp_win->Parent = XCreateWindow(
    dpy, tmp_win->frame, tmp_win->boundary_width,
    tmp_win->boundary_width + tmp_win->title_g.height,
    (tmp_win->frame_g.width - 2 * tmp_win->boundary_width),
    (tmp_win->frame_g.height - 2 * tmp_win->boundary_width -
     tmp_win->title_g.height),
    0, CopyFromParent, InputOutput, CopyFromParent, valuemask, &attributes);

  XSaveContext(dpy, tmp_win->Parent, FvwmContext, (caddr_t) tmp_win);
}

void setup_resize_handle_windows(FvwmWindow *tmp_win)
{
  unsigned long valuemask;
  XSetWindowAttributes attributes;
  int i;

  /* sides and corners are input only */
  /* title and buttons maybe one day */
  valuemask = CWCursor | CWEventMask;
  attributes.event_mask = XEVMASK_BORDERW;
  if (HAS_BORDER(tmp_win))
  {
    /* Just dump the windows any old place and let SetupFrame take
     * care of the mess */
    for(i = 0; i < 4; i++)
    {
      attributes.cursor = Scr.FvwmCursors[CRS_TOP_LEFT+i];
      tmp_win->corners[i] =
	XCreateWindow(
	  dpy, tmp_win->decor_w, 0, 0, tmp_win->corner_width,
	  tmp_win->corner_width, 0, 0, InputOnly,
	  DefaultVisual(dpy, Scr.screen), valuemask, &attributes);
      attributes.cursor = Scr.FvwmCursors[CRS_TOP+i];
      tmp_win->sides[i] =
	XCreateWindow(
	  dpy, tmp_win->decor_w, 0, 0, tmp_win->boundary_width,
	  tmp_win->boundary_width, 0, 0, InputOnly,
	  DefaultVisual(dpy, Scr.screen), valuemask, &attributes);
      XSaveContext(dpy, tmp_win->sides[i], FvwmContext, (caddr_t) tmp_win);
      XSaveContext(dpy, tmp_win->corners[i], FvwmContext, (caddr_t) tmp_win);
    }
  }
}

void destroy_resize_handle_windows(
  FvwmWindow *tmp_win, Bool do_only_delete_context)
{
  int i;

  for(i = 0; i < 4 ; i++)
  {
    if (!do_only_delete_context)
    {
      XDestroyWindow(dpy, tmp_win->sides[i]);
      XDestroyWindow(dpy, tmp_win->corners[i]);
      tmp_win->sides[i] = None;
      tmp_win->corners[i] = None;
    }
    XDeleteContext(dpy, tmp_win->sides[i], FvwmContext);
    XDeleteContext(dpy, tmp_win->corners[i], FvwmContext);
  }
}

static void resize_resize_handle_windows(FvwmWindow *tmp_win)
{
        int i;

        tmp_win->corner_width =
                tmp_win->title_g.height + tmp_win->boundary_width;

        if (HAS_BORDER(tmp_win))
        {
                /* Just dump the windows any old place and let SetupFrame take
                 * care of the mess */
                for (i = 0; i < 4; i++)
                {
                        XResizeWindow(
                                dpy, tmp_win->corners[i],
                                tmp_win->corner_width, tmp_win->corner_width);
                }
        }

        return;
}

void change_resize_handle_windows(FvwmWindow *tmp_win)
{
  if (HAS_BORDER(tmp_win) && tmp_win->sides[0] == None)
    setup_resize_handle_windows(tmp_win);
  else if (!HAS_BORDER(tmp_win) && tmp_win->sides[0] != None)
    destroy_resize_handle_windows(tmp_win, False);
  else if (tmp_win->corner_width !=
           tmp_win->title_g.height + tmp_win->boundary_width)
  {
    resize_resize_handle_windows(tmp_win);
  }
}

void setup_frame_stacking(FvwmWindow *tmp_win)
{
  int i;

  XMapSubwindows(dpy, tmp_win->decor_w);
  XLowerWindow(dpy, tmp_win->decor_w);
  /* Must lower all four corners since they may overlap the (sibling) title */
  for (i = 0; i < 4; i++)
  {
    if (tmp_win->corners[i] != None)
      XLowerWindow(dpy, tmp_win->corners[i]);
  }
}

void setup_auxiliary_windows(
  FvwmWindow *tmp_win, Bool setup_frame_and_parent, short buttons)
{
  unsigned long valuemask_save = 0;
  XSetWindowAttributes attributes;

  get_default_window_attributes(tmp_win, &valuemask_save, &attributes);

  /****** frame window ******/
  if (setup_frame_and_parent)
  {
    setup_frame_window(tmp_win);
    setup_decor_window(tmp_win, valuemask_save, &attributes);
    setup_parent_window(tmp_win);
  }

  /****** resize handle windows ******/
  setup_resize_handle_windows(tmp_win);

  /****** title window ******/
  if (HAS_TITLE(tmp_win))
  {
    setup_title_window(tmp_win, valuemask_save, &attributes);
    setup_button_windows(tmp_win, valuemask_save, &attributes, buttons);
  }

  /****** setup frame stacking order ******/
  setup_frame_stacking(tmp_win);
  XMapSubwindows (dpy, tmp_win->frame);
}

void setup_frame_attributes(
  FvwmWindow *tmp_win, window_style *pstyle)
{
  int i;
  XSetWindowAttributes xswa;

  /* Backing_store is controlled on the client, decor_w, title & buttons */
  switch (pstyle->flags.use_backing_store)
  {
  case BACKINGSTORE_DEFAULT:
	  xswa.backing_store = tmp_win->initial_backing_store;
	  break;
  case BACKINGSTORE_ON:
	  xswa.backing_store = Scr.use_backing_store;
	  break;
  case BACKINGSTORE_OFF:
  default:
	  xswa.backing_store = NotUseful;
	  break;
  }
  XChangeWindowAttributes(dpy, tmp_win->w, CWBackingStore, &xswa);
  XChangeWindowAttributes(dpy, tmp_win->decor_w, CWBackingStore, &xswa);
  if (HAS_TITLE(tmp_win))
  {
    XChangeWindowAttributes(dpy, tmp_win->title_w, CWBackingStore, &xswa);
    for (i = 0; i < NUMBER_OF_BUTTONS; i++)
    {
      if (tmp_win->button_w[i])
	XChangeWindowAttributes(dpy, tmp_win->button_w[i], CWBackingStore,
				&xswa);
    }
  }
  /* parent_relative is applied to the frame and the parent */
  xswa.background_pixmap = pstyle->flags.use_parent_relative
			   ? ParentRelative : None;
  /* Save_under is only useful on the frame */
  xswa.save_under = pstyle->flags.do_save_under
		    ? Scr.flags.do_save_under : NotUseful;
  XChangeWindowAttributes(
	  dpy, tmp_win->Parent, CWBackPixmap | CWBackingStore, &xswa);
  XChangeWindowAttributes(
	  dpy, tmp_win->frame, CWBackPixmap | CWBackingStore | CWSaveUnder,
	  &xswa);
}

void destroy_auxiliary_windows(FvwmWindow *tmp_win,
			       Bool destroy_frame_and_parent)
{
  if (destroy_frame_and_parent)
  {
    XDestroyWindow(dpy, tmp_win->frame);
    XDeleteContext(dpy, tmp_win->frame, FvwmContext);
    XDeleteContext(dpy, tmp_win->decor_w, FvwmContext);
    XDeleteContext(dpy, tmp_win->Parent, FvwmContext);
    delete_client_context(tmp_win);
  }

  if (HAS_TITLE(tmp_win))
    destroy_title_window(tmp_win, True);
  if (HAS_TITLE(tmp_win))
    destroy_button_windows(tmp_win, True);
  if (HAS_BORDER(tmp_win))
    destroy_resize_handle_windows(tmp_win, True);
}

void change_auxiliary_windows(FvwmWindow *tmp_win, short buttons)
{
  unsigned long valuemask_save = 0;
  XSetWindowAttributes attributes;

  get_default_window_attributes(tmp_win, &valuemask_save, &attributes);
  change_title_window(tmp_win, valuemask_save, &attributes);
  change_button_windows(tmp_win, valuemask_save, &attributes, buttons);
  change_resize_handle_windows(tmp_win);
  setup_frame_stacking(tmp_win);
}

void increase_icon_hint_count(FvwmWindow *tmp_win)
{
  if (tmp_win->wmhints &&
      (tmp_win->wmhints->flags & (IconWindowHint | IconPixmapHint)))
  {
    switch (WAS_ICON_HINT_PROVIDED(tmp_win))
    {
    case ICON_HINT_NEVER:
      SET_WAS_ICON_HINT_PROVIDED(tmp_win, ICON_HINT_ONCE);
      break;
    case ICON_HINT_ONCE:
      SET_WAS_ICON_HINT_PROVIDED(tmp_win, ICON_HINT_MULTIPLE);
      break;
    case ICON_HINT_MULTIPLE:
    default:
      break;
    }
ICON_DBG((stderr,"icon hint count++ (%d) '%s'\n", (int)WAS_ICON_HINT_PROVIDED(tmp_win), tmp_win->name));
  }

  return;
}

static void setup_icon(FvwmWindow *tmp_win, window_style *pstyle)
{
  increase_icon_hint_count(tmp_win);
  /* find a suitable icon pixmap */
  if((tmp_win->wmhints) && (tmp_win->wmhints->flags & IconWindowHint))
  {
    if (SHAS_ICON(&pstyle->flags) &&
	SICON_OVERRIDE(pstyle->flags) == ICON_OVERRIDE)
    {
ICON_DBG((stderr,"si: iwh ignored '%s'\n", tmp_win->name));
      tmp_win->icon_bitmap_file = SGET_ICON_NAME(*pstyle);
    }
    else
    {
ICON_DBG((stderr,"si: using iwh '%s'\n", tmp_win->name));
      tmp_win->icon_bitmap_file = NULL;
    }
  }
  else if((tmp_win->wmhints) && (tmp_win->wmhints->flags & IconPixmapHint))
  {
    if (SHAS_ICON(&pstyle->flags) &&
	SICON_OVERRIDE(pstyle->flags) != NO_ICON_OVERRIDE)
    {
ICON_DBG((stderr,"si: iph ignored '%s'\n", tmp_win->name));
      tmp_win->icon_bitmap_file = SGET_ICON_NAME(*pstyle);
    }
    else
    {
ICON_DBG((stderr,"si: using iph '%s'\n", tmp_win->name));
      tmp_win->icon_bitmap_file = NULL;
    }
  }
  else if(SHAS_ICON(&pstyle->flags))
  {
    /* an icon was specified */
ICON_DBG((stderr,"si: using style '%s'\n", tmp_win->name));
    tmp_win->icon_bitmap_file = SGET_ICON_NAME(*pstyle);
  }
  else
  {
    /* use default icon */
ICON_DBG((stderr,"si: using default '%s'\n", tmp_win->name));
    tmp_win->icon_bitmap_file = Scr.DefaultIcon;
  }

  /* icon name */
  GET_NAME_PROPERTY(XGetWMIconName, tmp_win->w, &(tmp_win->icon_name),
		    &(tmp_win->icon_name_list));
  if (tmp_win->icon_name == NoName)
  {
    tmp_win->icon_name = tmp_win->name;
    SET_WAS_ICON_NAME_PROVIDED(tmp_win, 0);
  }

  /* wait until the window is iconified and the icon window is mapped
   * before creating the icon window
   */
  tmp_win->icon_w = None;

  BroadcastName(M_ICON_NAME,tmp_win->w,tmp_win->frame,
		(unsigned long)tmp_win,tmp_win->icon_name);
  if (tmp_win->icon_bitmap_file != NULL &&
      tmp_win->icon_bitmap_file != Scr.DefaultIcon)
    BroadcastName(M_ICON_FILE,tmp_win->w,tmp_win->frame,
		  (unsigned long)tmp_win,tmp_win->icon_bitmap_file);
}

static void destroy_icon(FvwmWindow *tmp_win)
{
  free_window_names(tmp_win, False, True);
  if (tmp_win->icon_w)
  {
    if (IS_PIXMAP_OURS(tmp_win))
    {
      XFreePixmap(dpy, tmp_win->iconPixmap);
      if (tmp_win->icon_maskPixmap != None)
      {
	XFreePixmap(dpy, tmp_win->icon_maskPixmap);
      }
    }
    XDestroyWindow(dpy, tmp_win->icon_w);
    XDeleteContext(dpy, tmp_win->icon_w, FvwmContext);
  }
  if (tmp_win->icon_pixmap_w != None)
  {
    if (IS_ICON_OURS(tmp_win))
    {
      XDestroyWindow(dpy, tmp_win->icon_pixmap_w);
    }
    else
    {
      XUnmapWindow(dpy, tmp_win->icon_pixmap_w);
    }
    XDeleteContext(dpy, tmp_win->icon_pixmap_w, FvwmContext);
  }
  tmp_win->icon_pixmap_w = None;
  tmp_win->iconPixmap = None;
  tmp_win->icon_maskPixmap = None;
  tmp_win->icon_g.width = 0;
  tmp_win->icon_g.height = 0;
  tmp_win->icon_t_width = 0;
  tmp_win->icon_p_width = 0;
  tmp_win->icon_p_height = 0;
}

void change_icon(FvwmWindow *tmp_win, window_style *pstyle)
{
  destroy_icon(tmp_win);
  setup_icon(tmp_win, pstyle);
}

#ifdef MINI_ICONS
void setup_mini_icon(FvwmWindow *tmp_win, window_style *pstyle)
{
  if (SHAS_MINI_ICON(&pstyle->flags))
  {
    tmp_win->mini_pixmap_file = SGET_MINI_ICON_NAME(*pstyle);
  }
  else
  {
    tmp_win->mini_pixmap_file = NULL;
  }

  if (tmp_win->mini_pixmap_file)
  {
    tmp_win->mini_icon = CachePicture(dpy, Scr.NoFocusWin, NULL,
				      tmp_win->mini_pixmap_file,
				      Scr.ColorLimit);
    if (tmp_win->mini_icon != NULL)
    {
      BroadcastMiniIcon(M_MINI_ICON,
			tmp_win->w, tmp_win->frame, (unsigned long)tmp_win,
			tmp_win->mini_icon->width,
			tmp_win->mini_icon->height,
			tmp_win->mini_icon->depth,
			tmp_win->mini_icon->picture,
			tmp_win->mini_icon->mask,
			tmp_win->mini_pixmap_file);
    }
  }
  else
  {
    tmp_win->mini_icon = NULL;
  }

}

void destroy_mini_icon(FvwmWindow *tmp_win)
{
  if (tmp_win->mini_icon)
  {
    DestroyPicture(dpy, tmp_win->mini_icon);
    tmp_win->mini_icon = 0;
  }
}

void change_mini_icon(FvwmWindow *tmp_win, window_style *pstyle)
{
  Picture *old_mi = tmp_win->mini_icon;
  destroy_mini_icon(tmp_win);
  setup_mini_icon(tmp_win, pstyle);
  if (old_mi != NULL && tmp_win->mini_icon == 0)
  {
    /* this case is not handled in setup_mini_icon, so we must broadcast here
     * explicitly */
    BroadcastMiniIcon(M_MINI_ICON,
		      tmp_win->w, tmp_win->frame, (unsigned long)tmp_win,
			0, 0, 0, 0, 0, "");
  }
}
#endif

void setup_focus_policy(FvwmWindow *tmp_win)
{
  focus_grab_buttons(tmp_win, False);
}


void setup_key_and_button_grabs(FvwmWindow *tmp_win)
{
#ifdef BUGS_ARE_COOL
  /* dv (29-May-2001): If keys are grabbed separately for C_WINDOW and the other
   * contexts, new windows have problems wen bindings are removed.  Therefore,
   * grab all keys in a single pass through the list. */
  GrabAllWindowKeysAndButtons(dpy, tmp_win->Parent, Scr.AllBindings,
			      C_WINDOW, GetUnusedModifiers(), None, True);
  GrabAllWindowKeys(dpy, tmp_win->frame, Scr.AllBindings,
		    C_TITLE|C_RALL|C_LALL|C_SIDEBAR,
		    GetUnusedModifiers(), True);
#endif
  GrabAllWindowButtons(dpy, tmp_win->Parent, Scr.AllBindings,
		       C_WINDOW, GetUnusedModifiers(), None, True);
  GrabAllWindowKeys(dpy, tmp_win->frame, Scr.AllBindings,
		    C_TITLE|C_RALL|C_LALL|C_SIDEBAR|C_WINDOW,
		    GetUnusedModifiers(), True);
  setup_focus_policy(tmp_win);
  /* Special handling for MouseFocusClickRaises *only*. If a
   * MouseFocusClickRaises window was so raised, it is now ungrabbed. If it's
   * no longer on top of its layer because of the new window we just added, we
   * have to restore the grab so it can be raised again. */
  if (Scr.Ungrabbed && DO_RAISE_MOUSE_FOCUS_CLICK(Scr.Ungrabbed)
      && (HAS_SLOPPY_FOCUS(Scr.Ungrabbed) || HAS_MOUSE_FOCUS(Scr.Ungrabbed))
      && !is_on_top_of_layer(Scr.Ungrabbed))
  {
    focus_grab_buttons(Scr.Ungrabbed, False);
  }
}


/***********************************************************************
 *
 *  Procedure:
 *	AddWindow - add a new window to the fvwm list
 *
 ***********************************************************************/
FvwmWindow *AddWindow(Window w, FvwmWindow *ReuseWin)
{
  /* new fvwm window structure */
  register FvwmWindow *tmp_win = NULL;
  FvwmWindow *tmptmp_win = NULL;
  /* mask for create windows */
  unsigned long valuemask;
  /* attributes for create windows */
  XSetWindowAttributes attributes;
  /* area for merged styles */
  window_style style;
  /* used for faster access */
  style_flags *sflags;
  short buttons;
  extern FvwmWindow *colormap_win;
  extern Bool PPosOverride;
  int do_shade = 0;
  int do_maximize = 0;
  Bool used_sm = False;
  Bool do_resize_too = False;

  /****** init window structure ******/
  if (!setup_window_structure(&tmptmp_win, w, ReuseWin)) {
    fvwm_msg(ERR,"AddWindow","Bad return code from setup_window_structure");
    return NULL;
  }
  tmp_win = tmptmp_win;

  /****** safety check, window might disappear before we get to it ******/
  if(!PPosOverride &&
     XGetGeometry(dpy, tmp_win->w, &JunkRoot, &JunkX, &JunkY,
                  &JunkWidth, &JunkHeight, &JunkBW, &JunkDepth) == 0)
  {
    free((char *)tmp_win);
    return NULL;
  }

  /****** Make sure the client window still exists.  We don't want to leave an
   * orphan frame window if it doesn't.  Since we now have the server
   * grabbed, the window can't disappear later without having been
   * reparented, so we'll get a DestroyNotify for it.  We won't have
   * gotten one for anything up to here, however. ******/
  MyXGrabServer(dpy);
  if (XGetGeometry(dpy, w, &JunkRoot, &JunkX, &JunkY,
                   &JunkWidth, &JunkHeight,
                   &JunkBW,  &JunkDepth) == 0)
  {
    free((char *)tmp_win);
    MyXUngrabServer(dpy);
    return NULL;
  }

  /****** window name ******/
  setup_window_name(tmp_win);
  setup_class_and_resource(tmp_win);
  setup_wm_hints(tmp_win);

  /* save a copy of the backing store attribute */
  tmp_win->initial_backing_store = tmp_win->attr.backing_store;

  /****** basic style and decor ******/
  /* If the window is in the NoTitle list, or is a transient, dont decorate it.
   * If its a transient, and DecorateTransients was specified, decorate anyway.
   */
  /* get merged styles */
  lookup_style(tmp_win, &style);
  sflags = SGET_FLAGS_POINTER(style);
  SET_TRANSIENT(
    tmp_win, !!XGetTransientForHint(dpy, tmp_win->w, &tmp_win->transientfor));
  tmp_win->decor = NULL;
  setup_style_and_decor(tmp_win, &style, &buttons);

  /****** fonts ******/
  setup_window_font(tmp_win, &style, False);
  setup_icon_font(tmp_win, &style, False);

  /****** state setup ******/
  setup_icon_boxes(tmp_win, &style);
  SET_ICONIFIED(tmp_win, 0);
  SET_ICON_UNMAPPED(tmp_win, 0);
  SET_MAXIMIZED(tmp_win, 0);
  /*
   * Reparenting generates an UnmapNotify event, followed by a MapNotify.
   * Set the map state to FALSE to prevent a transition back to
   * WithdrawnState in HandleUnmapNotify.  Map state gets set corrected
   * again in HandleMapNotify.
   */
  SET_MAPPED(tmp_win, 0);

  /****** window list and stack ring ******/
  /* add the window to the end of the fvwm list */
  tmp_win->next = Scr.FvwmRoot.next;
  tmp_win->prev = &Scr.FvwmRoot;
  while (tmp_win->next != NULL)
  {
    tmp_win->prev = tmp_win->next;
    tmp_win->next = tmp_win->next->next;
  }
  /* tmp_win->prev points to the last window in the list, tmp_win->next is
   * NULL. Now fix the last window to point to tmp_win */
  tmp_win->prev->next = tmp_win;
  /*
   * RBW - 11/13/1998 - add it into the stacking order chain also.
   * This chain is anchored at both ends on Scr.FvwmRoot, there are
   * no null pointers.
   */
  add_window_to_stack_ring_after(tmp_win, &Scr.FvwmRoot);

  /****** calculate frame size ******/
  tmp_win->hints.win_gravity = NorthWestGravity;
  GetWindowSizeHints(tmp_win);

  /****** border width ******/
  tmp_win->old_bw = tmp_win->attr.border_width;
  XSetWindowBorderWidth(dpy, tmp_win->w, 0);

  /*
   * MatchWinToSM changes tmp_win->attr and the stacking order.
   * Thus it is important have this call *after* PlaceWindow and the
   * stacking order initialization.
   */
  used_sm = MatchWinToSM(tmp_win, &do_shade, &do_maximize);
  if (used_sm)
  {
    /* read the requested absolute geometry */
    gravity_translate_to_northwest_geometry_no_bw(
      tmp_win->hints.win_gravity, tmp_win, &tmp_win->normal_g,
      &tmp_win->normal_g);
    gravity_resize(
      tmp_win->hints.win_gravity, &tmp_win->normal_g,
      2 * tmp_win->boundary_width,
      2 * tmp_win->boundary_width + tmp_win->title_g.height);
    tmp_win->frame_g = tmp_win->normal_g;
    tmp_win->frame_g.x -= Scr.Vx;
    tmp_win->frame_g.y -= Scr.Vy;

    /****** calculate frame size ******/
    setup_frame_size_limits(tmp_win, &style);
    constrain_size(
      tmp_win, (unsigned int *)&tmp_win->frame_g.width,
      (unsigned int *)&tmp_win->frame_g.height, 0, 0, 0);

    /****** maximize ******/
    if (do_maximize)
    {
      SET_MAXIMIZED(tmp_win, 1);
      constrain_size(
	tmp_win, (unsigned int *)&tmp_win->max_g.width,
	(unsigned int *)&tmp_win->max_g.height, 0, 0, CS_UPDATE_MAX_DEFECT);
      get_relative_geometry(&tmp_win->frame_g, &tmp_win->max_g);
    }
    else
    {
      get_relative_geometry(&tmp_win->frame_g, &tmp_win->normal_g);
    }
  }
  else
  {
    if (IS_SHADED(tmp_win))
    {
      do_shade = 1;
      SET_SHADED(tmp_win, 0);
    }
    /* Tentative size estimate */
    tmp_win->frame_g.width = tmp_win->attr.width + 2 * tmp_win->boundary_width;
    tmp_win->frame_g.height = tmp_win->attr.height + tmp_win->title_g.height +
      2 * tmp_win->boundary_width;

    /****** calculate frame size ******/
    setup_frame_size_limits(tmp_win, &style);

    /****** layer ******/
    setup_layer(tmp_win, &style);

    /****** window placement ******/
    do_resize_too = setup_window_placement(tmp_win, &style);

    /* set up geometry */
    tmp_win->frame_g.x = tmp_win->attr.x;
    tmp_win->frame_g.y = tmp_win->attr.y;
    tmp_win->frame_g.width = tmp_win->attr.width + 2 * tmp_win->boundary_width;
    tmp_win->frame_g.height = tmp_win->attr.height + tmp_win->title_g.height
      + 2 * tmp_win->boundary_width;
    gravity_constrain_size(
      tmp_win->hints.win_gravity, tmp_win, &tmp_win->frame_g, 0);

    update_absolute_geometry(tmp_win);
  }

  /****** auxiliary window setup ******/
  setup_auxiliary_windows(tmp_win, True, buttons);

  /****** 'backing store' and 'save under' window setup ******/
  setup_frame_attributes(tmp_win, &style);

  /****** reparent the window ******/
  XReparentWindow(dpy, tmp_win->w, tmp_win->Parent, 0, 0);

  /****** select events ******/
  valuemask = CWEventMask | CWDontPropagate;
  attributes.event_mask = XEVMASK_CLIENTW;
  attributes.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask;
  XChangeWindowAttributes(dpy, tmp_win->w, valuemask, &attributes);
  /******  ******/
  XAddToSaveSet(dpy, tmp_win->w);

  /****** arrange the frame ******/
  ForceSetupFrame(tmp_win, tmp_win->frame_g.x, tmp_win->frame_g.y,
		  tmp_win->frame_g.width, tmp_win->frame_g.height,
		  True);

  /****** grab keys and buttons ******/
  setup_key_and_button_grabs(tmp_win);

  /****** now we can sefely ungrab the server ******/
  MyXUngrabServer(dpy);

  /****** inform modules of new window ******/
  BroadcastConfig(M_ADD_WINDOW,tmp_win);
  BroadcastName(M_WINDOW_NAME,tmp_win->w,tmp_win->frame,
		(unsigned long)tmp_win,tmp_win->name);

  /****** place the window in the stack ring ******/
  if (!position_new_window_in_stack_ring(tmp_win, SDO_START_LOWERED(sflags)))
  {
    XWindowChanges xwc;
    xwc.sibling = get_next_window_in_stack_ring(tmp_win)->frame;
    xwc.stack_mode = Above;
    XConfigureWindow(dpy, tmp_win->frame, CWSibling|CWStackMode, &xwc);
  }

  /* these are sent and broadcast before res_{class,name} for the benefit
   * of FvwmIconBox which can't handle M_ICON_FILE after M_RES_NAME */
  /****** icon and mini icon ******/
  /* migo (20-Jan-2000): the logic is to unset this flag on NULL values */
  SET_WAS_ICON_NAME_PROVIDED(tmp_win, 1);
  setup_icon(tmp_win, &style);
#ifdef MINI_ICONS
  setup_mini_icon(tmp_win, &style);
#endif

  BroadcastName(M_RES_CLASS,tmp_win->w,tmp_win->frame,
		(unsigned long)tmp_win,tmp_win->class.res_class);
  BroadcastName(M_RES_NAME,tmp_win->w,tmp_win->frame,
		(unsigned long)tmp_win,tmp_win->class.res_name);

  /****** stick window ******/
  if (IS_STICKY(tmp_win) && (!(tmp_win->hints.flags & USPosition) || used_sm))
  {
    /* If it's sticky and the user didn't ask for an explicit position, force
     * it on screen now.  Don't do that with USPosition because we have to
     * assume the user knows what he is doing.  This is necessary e.g. if we
     * want a sticky 'panel' in FvwmButtons but don't want to see when it's
     * mapped in the void. */
    if (!IsRectangleOnThisPage(&tmp_win->frame_g, Scr.CurrentDesk))
    {
      SET_STICKY(tmp_win, 0);
      handle_stick(&Event, tmp_win->frame, tmp_win, C_FRAME, "", 0, 1);
    }
  }

  /****** resize window ******/
  if(do_resize_too)
  {
    XWarpPointer(dpy, Scr.Root, Scr.Root, 0, 0, Scr.MyDisplayWidth,
		 Scr.MyDisplayHeight,
		 tmp_win->frame_g.x + (tmp_win->frame_g.width>>1),
		 tmp_win->frame_g.y + (tmp_win->frame_g.height>>1));
    Event.xany.type = ButtonPress;
    Event.xbutton.button = 1;
    Event.xbutton.x_root = tmp_win->frame_g.x + (tmp_win->frame_g.width>>1);
    Event.xbutton.y_root = tmp_win->frame_g.y + (tmp_win->frame_g.height>>1);
    Event.xbutton.x = (tmp_win->frame_g.width>>1);
    Event.xbutton.y = (tmp_win->frame_g.height>>1);
    Event.xbutton.subwindow = None;
    Event.xany.window = tmp_win->w;
    CMD_Resize(&Event , tmp_win->w, tmp_win, C_WINDOW, "", 0);
  }

  /****** window colormap ******/
  InstallWindowColormaps(colormap_win);

  /****** gnome setup ******/
  /* set GNOME hints on the window from flags set on tmp_win */
  GNOME_SetHints(tmp_win);
  GNOME_SetLayer(tmp_win);
  GNOME_SetDesk(tmp_win);
  GNOME_SetWinArea(tmp_win);

  /****** windowshade ******/
  if (do_shade)
  {
    rectangle big_g;

    big_g = (IS_MAXIMIZED(tmp_win)) ? tmp_win->max_g : tmp_win->frame_g;
    get_shaded_geometry(tmp_win, &tmp_win->frame_g, &tmp_win->frame_g);
    XLowerWindow(dpy, tmp_win->Parent);
    SET_SHADED(tmp_win ,1);
    SetupFrame(
      tmp_win, tmp_win->frame_g.x, tmp_win->frame_g.y,
      tmp_win->frame_g.width, tmp_win->frame_g.height, False);
  }
  if (!IS_SHADED(tmp_win) && !IS_ICONIFIED(tmp_win))
  {
    /* TK always wants some special treatment: If the window is simply mapped,
     * the tk menus come up at funny Y coordinates.  Tell it its geometry
     * *again* to work around this problem. */
    SendConfigureNotify(
      tmp_win, tmp_win->frame_g.x, tmp_win->frame_g.y, tmp_win->frame_g.width,
      tmp_win->frame_g.height, 0, False);
  }
  if (!XGetGeometry(dpy, tmp_win->w, &JunkRoot, &JunkX, &JunkY, &JunkWidth,
		    &JunkHeight, &JunkBW,  &JunkDepth))
  {
    /* The window has disappeared somehow.  For some reason we do not always
     * get a DestroyNotify on the window, so make sure it is destroyed. */
    destroy_window(tmp_win);
    tmp_win = NULL;
  }

  return tmp_win;
}


/***********************************************************************
 *
 *  Procedure:
 *	FetchWMProtocols - finds out which protocols the window supports
 *
 *  Inputs:
 *	tmp - the fvwm window structure to use
 *
 ***********************************************************************/
void FetchWmProtocols (FvwmWindow *tmp)
{
  Atom *protocols = NULL, *ap;
  int i, n;
  Atom atype;
  int aformat;
  unsigned long bytes_remain,nitems;

  if(tmp == NULL) return;
  /* First, try the Xlib function to read the protocols.
   * This is what Twm uses. */
  if (XGetWMProtocols (dpy, tmp->w, &protocols, &n))
  {
    for (i = 0, ap = protocols; i < n; i++, ap++)
    {
      if (*ap == (Atom)_XA_WM_TAKE_FOCUS)
      {
	SET_WM_TAKES_FOCUS(tmp, 1);
	set_focus_model(tmp);
      }
      if (*ap == (Atom)_XA_WM_DELETE_WINDOW)
	SET_WM_DELETES_WINDOW(tmp, 1);
    }
    if (protocols)
      XFree((char *)protocols);
  }
  else
  {
    /* Next, read it the hard way. mosaic from Coreldraw needs to
     * be read in this way. */
    if ((XGetWindowProperty(dpy, tmp->w, _XA_WM_PROTOCOLS, 0L, 10L, False,
			    _XA_WM_PROTOCOLS, &atype, &aformat, &nitems,
			    &bytes_remain,
			    (unsigned char **)&protocols))==Success)
    {
      for (i = 0, ap = protocols; i < nitems; i++, ap++)
      {
	if (*ap == (Atom)_XA_WM_TAKE_FOCUS)
	{
	  SET_WM_TAKES_FOCUS(tmp, 1);
	  set_focus_model(tmp);
	}
	if (*ap == (Atom)_XA_WM_DELETE_WINDOW)
	  SET_WM_DELETES_WINDOW(tmp, 1);
      }
      if (protocols)
	XFree((char *)protocols);
    }
  }
  return;
}



/***********************************************************************
 *
 *  Procedure:
 *	GetWindowSizeHints - gets application supplied size info
 *
 *  Inputs:
 *	tmp - the fvwm window structure to use
 *
 ***********************************************************************/

void GetWindowSizeHints(FvwmWindow *tmp)
{
  long supplied = 0;
  char *broken_cause ="";
  XSizeHints orig_hints;

  if (HAS_OVERRIDE_SIZE_HINTS(tmp) ||
      !XGetWMNormalHints(dpy, tmp->w, &tmp->hints, &supplied))
  {
    tmp->hints.flags = 0;
    memset(&orig_hints, 0, sizeof(orig_hints));
  }
  else
  {
    memcpy(&orig_hints, &tmp->hints, sizeof(orig_hints));
  }

  /* Beat up our copy of the hints, so that all important field are
   * filled in! */
  if (tmp->hints.flags & PResizeInc)
  {
    SET_SIZE_INC_SET(tmp, 1);
    if (tmp->hints.width_inc <= 0)
    {
      if (tmp->hints.width_inc < 0 ||
	  (tmp->hints.width_inc == 0 &&
	   (tmp->hints.flags & PMinSize) && (tmp->hints.flags & PMaxSize) &&
	   tmp->hints.min_width != tmp->hints.max_width))
      {
	broken_cause = "width_inc";
      }
      tmp->hints.width_inc = 1;
      SET_SIZE_INC_SET(tmp, 0);
    }
    if (tmp->hints.height_inc <= 0)
    {
      if (tmp->hints.height_inc < 0 ||
	  (tmp->hints.height_inc == 0 &&
	   (tmp->hints.flags & PMinSize) && (tmp->hints.flags & PMaxSize) &&
	   tmp->hints.min_height != tmp->hints.max_height))
      {
	if (!*broken_cause)
	  broken_cause = "height_inc";
      }
      tmp->hints.height_inc = 1;
      SET_SIZE_INC_SET(tmp, 0);
    }
  }
  else
  {
    SET_SIZE_INC_SET(tmp, 0);
    tmp->hints.width_inc = 1;
    tmp->hints.height_inc = 1;
  }

  if(tmp->hints.flags & PMinSize)
  {
    if (tmp->hints.min_width < 0 && !*broken_cause)
      broken_cause = "min_width";
    if (tmp->hints.min_height < 0 && !*broken_cause)
      broken_cause = "min_height";
  }
  else
  {
    if(tmp->hints.flags & PBaseSize)
    {
      tmp->hints.min_width = tmp->hints.base_width;
      tmp->hints.min_height = tmp->hints.base_height;
    }
    else
    {
      tmp->hints.min_width = 1;
      tmp->hints.min_height = 1;
    }
  }
  if (tmp->hints.min_width <= 0)
    tmp->hints.min_width = 1;
  if (tmp->hints.min_height <= 0)
    tmp->hints.min_height = 1;

  if(tmp->hints.flags & PMaxSize)
  {
    if(tmp->hints.max_width < tmp->hints.min_width)
    {
      tmp->hints.max_width = DEFAULT_MAX_MAX_WINDOW_WIDTH;
      if (!*broken_cause)
	broken_cause = "max_width";
    }
    if(tmp->hints.max_height < tmp->hints.min_height)
    {
      tmp->hints.max_height = DEFAULT_MAX_MAX_WINDOW_HEIGHT;
      if (!*broken_cause)
	broken_cause = "max_height";
    }
  }
  else
  {
    tmp->hints.max_width = DEFAULT_MAX_MAX_WINDOW_WIDTH;
    tmp->hints.max_height = DEFAULT_MAX_MAX_WINDOW_HEIGHT;
  }

  /*
   * ICCCM says that PMinSize is the default if no PBaseSize is given,
   * and vice-versa.
   */

  if(tmp->hints.flags & PBaseSize)
  {
    if (tmp->hints.base_width < 0)
    {
      tmp->hints.base_width = 0;
      if (!*broken_cause)
	broken_cause = "base_width";
    }
    if (tmp->hints.base_height < 0)
    {
      tmp->hints.base_height = 0;
      if (!*broken_cause)
	broken_cause = "base_height";
    }
    if ((tmp->hints.base_width > tmp->hints.min_width) ||
	(tmp->hints.base_height > tmp->hints.min_height))
    {
      /* In this case, doing the aspect ratio calculation
	 for window_size - base_size as prescribed by the
	 ICCCM is going to fail.
	 Resetting the flag disables the use of base_size
	 in aspect ratio calculation while it is still used
	 for grid sizing.
      */
      tmp->hints.flags &= ~PBaseSize;
#if 0
      /* Keep silent about this, since the Xlib manual actually
	 recommends making min <= base <= max ! */
      broken_cause = "";
#endif
    }
  }
  else
  {
    if(tmp->hints.flags & PMinSize)
    {
      tmp->hints.base_width = tmp->hints.min_width;
      tmp->hints.base_height = tmp->hints.min_height;
    }
    else
    {
      tmp->hints.base_width = 0;
      tmp->hints.base_height = 0;
    }
  }

  if(!(tmp->hints.flags & PWinGravity))
  {
    tmp->hints.win_gravity = NorthWestGravity;
  }

  if (tmp->hints.flags & PAspect)
  {
    /*
    ** check to make sure min/max aspect ratios look valid
    */
#define maxAspectX tmp->hints.max_aspect.x
#define maxAspectY tmp->hints.max_aspect.y
#define minAspectX tmp->hints.min_aspect.x
#define minAspectY tmp->hints.min_aspect.y


    /*
    ** The math looks like this:
    **
    **   minAspectX    maxAspectX
    **   ---------- <= ----------
    **   minAspectY    maxAspectY
    **
    ** If that is multiplied out, this must be satisfied:
    **
    **   minAspectX * maxAspectY <=  maxAspectX * minAspectY
    **
    ** So, what to do if this isn't met?  Ignoring it entirely
    ** seems safest.
    **
    */

    /* We also ignore people who put negative entries into
     * their aspect ratios. They deserve it.
     *
     * We cast to double here, since the values may be large.
     */
    if ((maxAspectX < 0) || (maxAspectY < 0) ||
        (minAspectX < 0) || (minAspectY < 0) ||
        (((double)minAspectX * (double)maxAspectY) >
         ((double)maxAspectX * (double)minAspectY)))
    {
      if (!*broken_cause)
	broken_cause = "aspect ratio";
      tmp->hints.flags &= ~PAspect;
      fvwm_msg (WARN, "GetWindowSizeHints",
		"The applicaton window \"%s\"\n"
		"(window id %#lx)"
		" has broken aspect ratio: %d/%d - %d/%d\n"
		"Fvwm is ignoring the aspect ratio.  "
		"If you are having a problem\n"
		"with the application, send this message to"
		" the application owner.\n"
		"There is no need to notify "
		"fvwm-workers@fvwm.org.\n",
		tmp->name, tmp->w, minAspectX, minAspectY, maxAspectX,
	  	maxAspectY);
    }
    else
    {
      /* protect against overflow */
      if ((maxAspectX > 65536) || (maxAspectY > 65536))
      {
	double ratio = (double) maxAspectX / (double) maxAspectY;
	if (ratio > 1.0)
	{
	  maxAspectX = 65536;
	  maxAspectY = 65536 / ratio;
	}
	else
	{
	  maxAspectX = 65536 * ratio;
	  maxAspectY = 65536;
	}
      }
      if ((minAspectX > 65536) || (minAspectY > 65536))
      {
	double ratio = (double) minAspectX / (double) minAspectY;
	if (ratio > 1.0)
	{
	  minAspectX = 65536;
	  minAspectY = 65536 / ratio;
	}
	else
	{
	  minAspectX = 65536 * ratio;
	  minAspectY = 65536;
	}
      }
    }
  }

  if (*broken_cause != 0)
  {
    fvwm_msg(WARN, "GetWindowSizeHints",
	     "The application window \"%s\"\n(window id %#lx) "
	     "has broken size hints (%s).\n"
	     "Fvwm is ignoring those hints.  "
	     "If you are having a problem\n"
	     "with the application, send this message to"
	     " the application owner.\n"
	     "There is no need to notify fvwm-workers@fvwm.org.\n"
	     "hint override = %d\n"
	     "flags = %lx\n"
	     "min_width = %d, min_height = %d, "
	     "max_width = %d, max_height = %d\n"
	     "width_inc = %d, height_inc = %d\n"
	     "min_aspect = %d/%d, max_aspect = %d/%d\n"
	     "base_width = %d, base_height = %d\n"
	     "win_gravity = %d\n",
	     tmp->name, tmp->w, broken_cause,
	     HAS_OVERRIDE_SIZE_HINTS(tmp),
	     orig_hints.flags,
	     orig_hints.min_width, orig_hints.min_height,
	     orig_hints.max_width, orig_hints.max_height,
	     orig_hints.width_inc, orig_hints.height_inc,
	     orig_hints.min_aspect.x, orig_hints.min_aspect.y,
	     orig_hints.max_aspect.x, orig_hints.max_aspect.y,
	     orig_hints.base_width, orig_hints.base_height,
	     orig_hints.win_gravity);
  }
}

#ifdef I18N_MB
#define FREE_TEXT_PROPERTY(a, b) free_text_property(a, b)
static void free_text_property(char **ptext, char ***ptext_list)
{
  if (*ptext_list != NULL)
  {
    XFreeStringList(*ptext_list);
    *ptext_list = NULL;
  }
  else
  {
    XFree(*ptext);
  }
  *ptext = NULL;

  return;
}
#else
#define FREE_TEXT_PROPERTY(a, b) free_text_property(a)
static void free_text_property(char **ptext)
{
#ifdef COMPOUND_TEXT
  free(*ptext);
#else
  XFree(*ptext);
#endif
  *ptext = NULL;
}
#endif

/**************************************************************************
 *
 * Releases dynamically allocated space used to store window/icon names
 *
 **************************************************************************/
void free_window_names(FvwmWindow *tmp, Bool nukename, Bool nukeicon)
{
  if (!tmp)
    return;

  if (nukename && tmp->name)
  {
    if (tmp->name != tmp->icon_name && tmp->name != NoName)
      FREE_TEXT_PROPERTY(&(tmp->name), &(tmp->name_list));
  }
  if (nukeicon && tmp->icon_name)
  {
    if ((tmp->name != tmp->icon_name || nukename) && tmp->icon_name != NoName)
      FREE_TEXT_PROPERTY(&(tmp->icon_name), &(tmp->icon_name_list));
  }

  return;
}


static void adjust_fvwm_internal_windows(FvwmWindow *tmp_win)
{
  extern FvwmWindow *ButtonWindow;

  if (tmp_win == Scr.Hilite)
    Scr.Hilite = NULL;
  update_last_screen_focus_window(tmp_win);
  if (ButtonWindow == tmp_win)
    ButtonWindow = NULL;
  restore_focus_after_unmap(tmp_win, False);

  return;
}


/***************************************************************************
 *
 * Handles destruction of a window
 *
 ****************************************************************************/
void destroy_window(FvwmWindow *tmp_win)
{
  extern Bool PPosOverride;

  /*
   * Warning, this is also called by HandleUnmapNotify; if it ever needs to
   * look at the event, HandleUnmapNotify will have to mash the UnmapNotify
   * into a DestroyNotify.
   */
  if(!tmp_win)
    return;

  /****** remove from window list ******/

  if (!IS_SCHEDULED_FOR_DESTROY(tmp_win))
  {
    /* first of all, remove the window from the list of all windows! */
    if (tmp_win->prev != NULL)
    {
      tmp_win->prev->next = tmp_win->next;
    }
    if (tmp_win->next != NULL)
    {
      tmp_win->next->prev = tmp_win->prev;
    }
    tmp_win->prev = NULL;
    tmp_win->next = NULL;
  }

  /****** also remove it from the stack ring ******/

  /*
   * RBW - 11/13/1998 - new: have to unhook the stacking order chain also.
   * There's always a prev and next, since this is a ring anchored on
   * Scr.FvwmRoot
   */
  remove_window_from_stack_ring(tmp_win);

  /****** check if we have to delay window destruction ******/

  if (Scr.flags.is_executing_complex_function && !DO_REUSE_DESTROYED(tmp_win))
  {
    if (IS_SCHEDULED_FOR_DESTROY(tmp_win))
    {
      return;
    }
    /* mark window for destruction */
    SET_SCHEDULED_FOR_DESTROY(tmp_win, 1);
    Scr.flags.is_window_scheduled_for_destroy = 1;
    /* this is necessary in case the application destroys the client window and
     * a new window is created with the saem window id */
    delete_client_context(tmp_win);
    /* unmap the the window to fake that it was already removed */
    if (IS_ICONIFIED(tmp_win))
    {
      if (tmp_win->icon_w)
	XUnmapWindow(dpy, tmp_win->icon_w);
      if(tmp_win->icon_pixmap_w != None)
	XUnmapWindow(dpy, tmp_win->icon_pixmap_w);
    }
    else
    {
      XUnmapWindow(dpy, tmp_win->frame);
    }
    adjust_fvwm_internal_windows(tmp_win);
    BroadcastPacket(M_DESTROY_WINDOW, 3,
		    tmp_win->w, tmp_win->frame, (unsigned long)tmp_win);

    return;
  }

  /****** unmap the frame ******/

  XUnmapWindow(dpy, tmp_win->frame);

  if(!PPosOverride)
    XSync(dpy,0);

  /* already done above? */
  if (!IS_SCHEDULED_FOR_DESTROY(tmp_win))
  {
    SET_SCHEDULED_FOR_DESTROY(tmp_win, 1);
    /****** adjust fvwm internal windows and the focus ******/

    adjust_fvwm_internal_windows(tmp_win);

    /****** broadcast ******/

    BroadcastPacket(M_DESTROY_WINDOW, 3,
		    tmp_win->w, tmp_win->frame, (unsigned long)tmp_win);
  }

  /****** adjust fvwm internal windows II ******/

  /* restore_focus_after_unmap takes care of Scr.pushed_window and colormap_win
   */
  if (tmp_win == Scr.Ungrabbed)
    Scr.Ungrabbed = NULL;

  /****** destroy auxiliary windows ******/

  destroy_auxiliary_windows(tmp_win, True);

  /****** destroy icon ******/

  destroy_icon_boxes(tmp_win);
  destroy_icon(tmp_win);

  /****** destroy mini icon ******/

#ifdef MINI_ICON
  destroy_mini_icon(tmp_win);
#endif

  /****** free strings ******/

  free_window_names(tmp_win, True, True);

  if (tmp_win->class.res_name && tmp_win->class.res_name != NoResource)
  {
    XFree ((char *)tmp_win->class.res_name);
    tmp_win->class.res_name = NoResource;
  }
  if (tmp_win->class.res_class && tmp_win->class.res_class != NoClass)
  {
    XFree ((char *)tmp_win->class.res_class);
    tmp_win->class.res_class = NoClass;
  }
  if (tmp_win->mwm_hints)
  {
    XFree((char *)tmp_win->mwm_hints);
    tmp_win->mwm_hints = NULL;
  }

  /****** free fonts ******/

  destroy_window_font(tmp_win);
  destroy_icon_font(tmp_win);

  /****** free wmhints ******/

  if (tmp_win->wmhints)
  {
    XFree ((char *)tmp_win->wmhints);
    tmp_win->wmhints = NULL;
  }

  /****** free colormap windows ******/

  if (tmp_win->cmap_windows != (Window *)NULL)
  {
    XFree((void *)tmp_win->cmap_windows);
    tmp_win->cmap_windows = NULL;
  }

  /****** throw away the structure ******/

  /*  Recapture reuses this struct, so don't free it.  */
  if (!DO_REUSE_DESTROYED(tmp_win))
  {
    free((char *)tmp_win);
  }

  /****** cleanup ******/

  if(!PPosOverride)
    XSync(dpy,0);

  return;
}




/***********************************************************************
 *
 *  Procedure:
 *	RestoreWithdrawnLocation
 *
 *  Puts windows back where they were before fvwm took over
 *
 ************************************************************************/
void RestoreWithdrawnLocation(
  FvwmWindow *tmp, Bool is_restart_or_recapture, Window parent)
{
  int w2,h2;
  unsigned int mask;
  XWindowChanges xwc;
  rectangle naked_g;
  rectangle unshaded_g;
  XSetWindowAttributes xswa;

  if(!tmp)
    return;

  /* always get the latest size hints in case the application changed the
   * gravity and we do not yet know about it */
  XSync(dpy, 0);
  GetWindowSizeHints(tmp);
  get_unshaded_geometry(tmp, &unshaded_g);
  gravity_get_naked_geometry(
    tmp->hints.win_gravity, tmp, &naked_g, &unshaded_g);
  gravity_translate_to_northwest_geometry(
    tmp->hints.win_gravity, tmp, &naked_g, &naked_g);
  xwc.x = naked_g.x;
  xwc.y = naked_g.y;
  xwc.width = naked_g.width;
  xwc.height = naked_g.height;
  xwc.border_width = tmp->old_bw;
  mask = (CWX | CWY | CWWidth | CWHeight | CWBorderWidth);

  /* We can not assume that the window is currently on the screen.
   * Although this is normally the case, it is not always true.  The
   * most common example is when the user does something in an
   * application which will, after some amount of computational delay,
   * cause the window to be unmapped, but then switches screens before
   * this happens.  The XTranslateCoordinates call above will set the
   * window coordinates to either be larger than the screen, or negative.
   * This will result in the window being placed in odd, or even
   * unviewable locations when the window is remapped.  The following code
   * forces the "relative" location to be within the bounds of the display.
   *
   * gpw -- 11/11/93
   *
   * Unfortunately, this does horrendous things during re-starts,
   * hence the "if(restart)" clause (RN)
   *
   * Also, fixed so that it only does this stuff if a window is more than
   * half off the screen. (RN)
   */

  if (!is_restart_or_recapture)
  {
    /* Don't mess with it if its partially on the screen now */
    if (unshaded_g.x < 0 || unshaded_g.y < 0 ||
	unshaded_g.x >= Scr.MyDisplayWidth ||
	unshaded_g.y >= Scr.MyDisplayHeight)
    {
      w2 = (unshaded_g.width >> 1);
      h2 = (unshaded_g.height >> 1);
      if ( xwc.x < -w2 || xwc.x > Scr.MyDisplayWidth - w2)
      {
	xwc.x = xwc.x % Scr.MyDisplayWidth;
	if (xwc.x < -w2)
	  xwc.x += Scr.MyDisplayWidth;
      }
      if (xwc.y < -h2 || xwc.y > Scr.MyDisplayHeight - h2)
      {
	xwc.y = xwc.y % Scr.MyDisplayHeight;
	if (xwc.y < -h2)
	  xwc.y += Scr.MyDisplayHeight;
      }
    }
  }

  /* restore initial backing store setting on window */
  xswa.backing_store = tmp->initial_backing_store;
  XChangeWindowAttributes(dpy, tmp->w, CWBackingStore, &xswa);
  /* reparent to root window */
  XReparentWindow(
    dpy, tmp->w, (parent == None) ? Scr.Root : parent, xwc.x, xwc.y);

  if (IS_ICONIFIED(tmp) && !IS_ICON_SUPPRESSED(tmp))
  {
    if (tmp->icon_w)
      XUnmapWindow(dpy, tmp->icon_w);
    if (tmp->icon_pixmap_w)
      XUnmapWindow(dpy, tmp->icon_pixmap_w);
  }

  XConfigureWindow(dpy, tmp->w, mask, &xwc);
  if (!is_restart_or_recapture)
  {
    /* must be initial capture */
    XSync(dpy,0);
  }
}


/***********************************************************************
 *
 *  Procedure:
 *	Reborder - Removes fvwm border windows
 *
 ************************************************************************/
void Reborder(void)
{
  FvwmWindow *tmp;

  /* put a border back around all windows */
  MyXGrabServer (dpy);

  /* force reinstall */
  InstallWindowColormaps (&Scr.FvwmRoot);

  /* RBW - 05/15/1998
   * Grab the last window and work backwards: preserve stacking order on
   * restart. */
  for (tmp = get_prev_window_in_stack_ring(&Scr.FvwmRoot); tmp != &Scr.FvwmRoot;
       tmp = get_prev_window_in_stack_ring(tmp))
  {
    if (!IS_ICONIFIED(tmp) && Scr.CurrentDesk != tmp->Desk)
    {
      XMapWindow(dpy, tmp->w);
      SetMapStateProp(tmp, NormalState);
    }
    RestoreWithdrawnLocation (tmp, True, Scr.Root);
    XUnmapWindow(dpy,tmp->frame);
    XDestroyWindow(dpy,tmp->frame);
  }

  MyXUngrabServer (dpy);
  FOCUS_RESET();
  XSync(dpy,0);
}

/***********************************************************************
 *
 *  Procedure:
 *      CaptureOneWindow
 *      CaptureAllWindows
 *
 *   Decorates windows at start-up and during recaptures
 *
 ***********************************************************************/

void CaptureOneWindow(
  FvwmWindow *fw, Window window, Window keep_on_top_win, Window parent_win,
  Bool is_recapture)
{
  Window w;
  unsigned long data[1];

  isIconicState = DontCareState;
  if (fw == NULL)
  {
    return;
  }
  if (IS_SCHEDULED_FOR_DESTROY(fw))
  {
    /* Fvwm might crash in complex functions if we really try to the dying
     * window here because AddWindow() may fail and leave a destroyed window
     * in Tmp_win.  Any, by the way, it is pretty useless to recapture a
     * window that will vanish in a moment. */
    return;
  }
  /* Grab the server to make sure the window does not die during the recapture.
   */
  MyXGrabServer(dpy);
  if (!XGetGeometry(dpy, fw->w, &JunkRoot, &JunkX, &JunkY, &JunkWidth,
		   &JunkHeight, &JunkBW,  &JunkDepth))
  {
    /* The window has already died, do not recapture it! */
    MyXUngrabServer(dpy);
    return;
  }
  if (XFindContext(dpy, window, FvwmContext, (caddr_t *)&fw) != XCNOENT)
  {
    Bool f = PPosOverride;
    Bool is_mapped = IS_MAPPED(fw);

    PPosOverride = True;
    if (IS_ICONIFIED(fw))
    {
      isIconicState = IconicState;
      isIconifiedByParent = IS_ICONIFIED_BY_PARENT(fw);
    }
    else
    {
      isIconicState = NormalState;
      isIconifiedByParent = 0;
      if (Scr.CurrentDesk != fw->Desk)
	SetMapStateProp(fw, NormalState);
    }
    data[0] = (unsigned long) fw->Desk;
    XChangeProperty (dpy, fw->w, _XA_WM_DESKTOP, _XA_WM_DESKTOP, 32,
                     PropModeReplace, (unsigned char *) data, 1);

    GNOME_SetHints(fw);
    GNOME_SetDesk(fw);
    GNOME_SetLayer(fw);
    GNOME_SetWinArea(fw);

    XSelectInput(dpy, fw->w, NoEventMask);
    w = fw->w;
    XUnmapWindow(dpy, fw->frame);
    RestoreWithdrawnLocation(fw, is_recapture, parent_win);
    SET_DO_REUSE_DESTROYED(fw, 1); /* RBW - 1999/03/20 */
    destroy_window(fw);
    Event.xmaprequest.window = w;
    HandleMapRequestKeepRaised(keep_on_top_win, fw);
    /* HandleMapRequestKeepRaised may have destroyed the fw if the window
     * vanished while in AddWindow(), so don't access fw anymore before
     * checking if it is a valid window. */
    if (check_if_fvwm_window_exists(fw))
    {
      if (!fFvwmInStartup)
      {
	SET_MAP_PENDING(fw, 0);
	SET_MAPPED(fw, is_mapped);
      }
    }
    /* Clean out isIconicState here, otherwise all new windos may start
     * iconified. */
    isIconicState = DontCareState;
    /* restore previous value */
    PPosOverride = f;
  }
  MyXUngrabServer(dpy);

  return;
}

/* Put a transparent window all over the screen to hide what happens below. */
static void hide_screen(
  Bool do_hide, Window *ret_hide_win, Window *ret_parent_win)
{
  static Bool is_hidden = False;
  static Window hide_win = None;
  static Window parent_win = None;
  XSetWindowAttributes xswa;
  unsigned long valuemask;

  if (do_hide == is_hidden)
  {
    /* nothing to do */
    if (ret_hide_win)
      *ret_hide_win = hide_win;
    if (ret_parent_win)
      *ret_parent_win = parent_win;

    return;
  }
  is_hidden = do_hide;
  if (do_hide)
  {
    xswa.override_redirect = True;
    xswa.cursor = Scr.FvwmCursors[CRS_WAIT];
    valuemask = CWOverrideRedirect|CWCursor;
    hide_win = XCreateWindow(
      dpy, Scr.Root, 0, 0, Scr.MyDisplayWidth, Scr.MyDisplayHeight, 0,
      CopyFromParent, InputOutput, CopyFromParent, valuemask, &xswa);
    if (hide_win)
    {
      /* When recapturing, all windows are reparented to this window. If they
       * are reparented to the root window, they will flash over the hide_win
       * with XFree.  So reparent them to an unmapped window that looks like
       * the root window. */
      parent_win = XCreateWindow(
	dpy, Scr.Root, 0, 0, Scr.MyDisplayWidth, Scr.MyDisplayHeight, 0,
	CopyFromParent, InputOutput, CopyFromParent, valuemask, &xswa);
      if (!parent_win)
      {
	XDestroyWindow(dpy, hide_win);
	hide_win = None;
      }
      else
      {
	XMapWindow(dpy, hide_win);
	XSync(dpy, 0);
      }
    }
  }
  else
  {
    if (hide_win != None)
      XDestroyWindow(dpy, hide_win);
    if (parent_win != None)
      XDestroyWindow(dpy, parent_win);
    XSync(dpy,0);
    hide_win = None;
    parent_win = None;
  }
  if (ret_hide_win)
    *ret_hide_win = hide_win;
  if (ret_parent_win)
    *ret_parent_win = parent_win;

  return;
}

/***********************************************************************
 *
 *  Procedure:
 *	MappedNotOverride - checks to see if we should really
 *		put a fvwm frame on the window
 *
 *  Returned Value:
 *	TRUE	- go ahead and frame the window
 *	FALSE	- don't frame the window
 *
 *  Inputs:
 *	w	- the window to check
 *
 ***********************************************************************/

static int MappedNotOverride(Window w)
{
  XWindowAttributes wa;
  Atom atype;
  int aformat;
  unsigned long nitems, bytes_remain;
  unsigned char *prop;

  isIconicState = DontCareState;

  if((w==Scr.NoFocusWin)||(!XGetWindowAttributes(dpy, w, &wa)))
    return False;

  if(XGetWindowProperty(dpy,w,_XA_WM_STATE,0L,3L,False,_XA_WM_STATE,
			&atype,&aformat,&nitems,&bytes_remain,&prop)==Success)
  {
    if(prop != NULL)
    {
      isIconicState = *(long *)prop;
      XFree(prop);
    }
  }
  if (wa.override_redirect == True)
  {
    XSelectInput(dpy, w, XEVMASK_ORW);
  }
  return (((isIconicState == IconicState)||(wa.map_state != IsUnmapped)) &&
	  (wa.override_redirect != True));
}

void CaptureAllWindows(Bool is_recapture)
{
  int i,j;
  unsigned int nchildren;
  Window root, parent, *children;
  FvwmWindow *tmp;		/* temp fvwm window structure */

  MyXGrabServer(dpy);

  if(!XQueryTree(dpy, Scr.Root, &root, &parent, &children, &nchildren))
  {
    MyXUngrabServer(dpy);
    return;
  }

  PPosOverride = True;
  if (!(Scr.flags.windows_captured)) /* initial capture? */
  {
    /*
    ** weed out icon windows
    */
    for (i=0;i<nchildren;i++)
    {
      if (children[i])
      {
        XWMHints *wmhintsp = XGetWMHints (dpy, children[i]);
        if (wmhintsp)
        {
          if (wmhintsp->flags & IconWindowHint)
          {
            for (j = 0; j < nchildren; j++)
            {
              if (children[j] == wmhintsp->icon_window)
              {
                children[j] = None;
                break;
              }
            }
          }
          XFree ((char *) wmhintsp);
        }
      }
    }
    /*
    ** map all of the non-override, non-icon windows
    */
    for (i=0;i<nchildren;i++)
    {
      if (children[i] && MappedNotOverride(children[i]))
      {
        XUnmapWindow(dpy, children[i]);
        Event.xmaprequest.window = children[i];
        HandleMapRequestKeepRaised(None, NULL);
      }
    }
    Scr.flags.windows_captured = 1;
  }
  else /* must be recapture */
  {
    Window keep_on_top_win;
    Window parent_win;
    Window focus_w;
    FvwmWindow *t;

    t = get_focus_window();
    focus_w = (t) ? t->w : None;
    hide_screen(True, &keep_on_top_win, &parent_win);
    /* reborder all windows */
    for (i=0;i<nchildren;i++)
    {
      if (XFindContext(dpy, children[i], FvwmContext, (caddr_t *)&tmp)!=XCNOENT)
      {
        CaptureOneWindow(
	  tmp, children[i], keep_on_top_win, parent_win, is_recapture);
      }
    }
    hide_screen(False, NULL, NULL);
    /* find the window that had the focus and focus it again */
    if (focus_w)
    {
      for (t = Scr.FvwmRoot.next; t && t->w != focus_w; t = t->next)
	;
      if (t)
      {
	SetFocusWindow(t, 0);
      }
    }
  }

  isIconicState = DontCareState;

  if(nchildren > 0)
    XFree((char *)children);

  /* after the windows already on the screen are in place,
   * don't use PPosition */
  PPosOverride = False;
  MyXUngrabServer(dpy);
}

static void do_recapture(F_CMD_ARGS, Bool fSingle)
{
  if (fSingle)
  {
    if (DeferExecution(eventp,&w,&tmp_win,&context, CRS_SELECT,ButtonRelease))
      return;
  }
  MyXGrabServer(dpy);
  if (fSingle)
    CaptureOneWindow(tmp_win, tmp_win->w, None, None, True);
  else
    CaptureAllWindows(True);
  /* Throw away queued up events. We don't want user input during a
   * recapture. The window the user clicks in might disapper at the very same
   * moment and the click goes through to the root window. Not good */
  XAllowEvents(dpy, AsyncPointer, CurrentTime);
  discard_events(
    ButtonPressMask|ButtonReleaseMask|ButtonMotionMask|PointerMotionMask|
    EnterWindowMask|LeaveWindowMask|KeyPressMask|KeyReleaseMask);
#ifdef DEBUG_STACK_RING
  verify_stack_ring_consistency();
#endif
  MyXUngrabServer(dpy);
}

void CMD_Recapture(F_CMD_ARGS)
{
  do_recapture(F_PASS_ARGS, False);
}

void CMD_RecaptureWindow(F_CMD_ARGS)
{
  do_recapture(F_PASS_ARGS, True);
}


syntax highlighted by Code2HTML, v. 0.9.1