Dear fvwm developers,

I am using fvwm2 since a couple of months and I am very pleased with it.
I liked the vector buttons very much and I was a little sad
because a vector button can only have 31 lines and because
these lines must always be connected to one another.
Therefore I would like to propose a new fwwm-feature.

This is the first open source project that I want to contribute,
so please be patient with me.
It would be a great honor if this feature could be introduced
in one of the next versions.
I am using it in version 2.4.6 and it works fine.

Yours,
Johannes Gajdosik

------------------------------------------------------

Feature description:

the man-page says:

...,num  is  a  number  of
point  specifications  of  the form [EMAIL PROTECTED]
...  C specifies a line color
(0 - the shadow color, 1 - the highlight color, 2 -
the  background  color,  3 - the foreground color).
... You can use up to 32 points in a line pattern.

The new feature is:
 1) You can use up to 1000 (or more?) points in a line pattern.
 2) special color C=-1: no line is drawn.
    This is different from color C=2, because a line of color C=2
    makes that all pixels of this line get the background color
    and so are erased.

The implementation affects 5 files.

-----------------------------------------------------

1) the man page:
The new text should be:
 ...  C specifies a line color
 (0 - the shadow color, 1 - the highlight color,
 2 - the  background  color,  3 - the foreground color,
 -1 - special meaning: this line is not drawn).
 ... You can use up to 1000 points in a line pattern.

------------------------------------------------------

2) screen.h
Instead of

   struct vector_coords
   {
     int num;
     int *x;
     int *y;
     unsigned long line_style;
     unsigned long use_fgbg;
   } vector;

I propose

    struct vector_coords
    {
      int num;
      struct vector_vertex {
        unsigned char x;
        unsigned char y;
        unsigned short color;
      } *vertices;
      unsigned long use_fgbg;
    } vector;

Please observe that in the old code every point needs 8 bytes,
in the new code only 4 bytes. Furthermore the color is not any more stored
in the bits of two unsigned long variables(line_style,use_fgbg),
but instead in just on variable(color), so the code becomes more readable.
The variable use_fgbg remains just because of an optimization when
drawing the button.


----------------------------------------------------------

3) fvwm.c

The functions LoadDefaultLeftButton and LoadDefaultRightButton
need to be adapted:

for example, instead of

    v->line_style = 0;
    v->x = (int *) safemalloc (sizeof(int) * v->num);
    v->y = (int *) safemalloc (sizeof(int) * v->num);
    v->x[0] = 22;
    v->y[0] = 39;
    v->line_style |= (1 << 0);
    v->x[1] = 78;
    v->y[1] = 39;
    v->line_style |= (1 << 1);
    v->x[2] = 78;
    v->y[2] = 61;
    v->x[3] = 22;
    v->y[3] = 61;
    v->x[4] = 22;
    v->y[4] = 39;
    v->line_style |= (1 << 4);

the code must be

     v->vertices = (struct vector_vertex *)
                   safemalloc (sizeof(struct vector_vertex) * v->num);
     v->vertices[0].x = 22;
     v->vertices[0].y = 39;
     v->vertices[0].color = 1;
     v->vertices[1].x = 78;
     v->vertices[1].y = 39;
     v->vertices[1].color = 1;
     v->vertices[2].x = 78;
     v->vertices[2].y = 61;
     v->vertices[2].color = 0;
     v->vertices[3].x = 22;
     v->vertices[3].y = 61;
     v->vertices[3].color = 0;
     v->vertices[4].x = 22;
     v->vertices[4].y = 39;
     v->vertices[4].color = 1;
     v->use_fgbg = 0;

------------------------------------------------

4) builtins.c

Instead of line 1423:

    if (df->u.vector.x)
      free (df->u.vector.x);
    if (df->u.vector.y)
      free (df->u.vector.y);

it must be

     if (df->u.vector.vertices)
       free (df->u.vector.vertices);

Instead of line 1515:

   else if (strncasecmp(style,"Vector",6)==0 ||
            (strlen(style)<=2 && isdigit(*style)))
    {
     /* normal coordinate list button style */
     int i, num_coords, num, line_style;
     struct vector_coords *vc = &df->u.vector;

     /* get number of points */
     if (strncasecmp(style,"Vector",6)==0)
     {
       num = sscanf(s,"%d%n",&num_coords,&offset);
       s += offset;
     } else
       num = sscanf(style,"%d",&num_coords);

     if((num != 1)||(num_coords>32)||(num_coords<2))
     {
       if (verbose)
       {
         fvwm_msg(
ERR,"ReadDecorFace", "Bad button style (2) in line: %s",action);
       }
       return False;
     }

     vc->num = num_coords;
     vc->line_style = 0;
     vc->use_fgbg = 0;
     vc->x = (int *) safemalloc (sizeof (int) * num_coords);
     vc->y = (int *) safemalloc (sizeof (int) * num_coords);


     /* get the points */
     for(i = 0; i < vc->num; ++i)
     {
       /* X x Y @ line_style */
       num = sscanf(s,"[EMAIL PROTECTED]",&vc->x[i],&vc->y[i],
                    &line_style, &offset);
       if(num != 3)
       {
         if (verbose)
         {
           fvwm_msg(
ERR, "ReadDecorFace", "Bad button style (3) in line %s", action);
         }
         free(vc->x);
         free(vc->y);
         vc->x = NULL;
         vc->y = NULL;
         return False;
       }
       if (line_style % 2)
       {
         vc->line_style |= (1 << i);
       }
       if (line_style >= 2)
       {
         vc->use_fgbg |= (1 << i);
       }
       s += offset;
     }
     memset(&df->style, 0, sizeof(df->style));
     DFS_FACE_TYPE(df->style) = VectorButton;
   }

I propose

    else if (strncasecmp(style,"Vector",6)==0 ||
             (strlen(style)<=2 && isdigit(*style)))
    {
      /* normal coordinate list button style */
      int i, num_coords, num, x, y, line_style;
      struct vector_coords *vc = &df->u.vector;

      /* get number of points */
      if (strncasecmp(style,"Vector",6)==0)
      {
        num = sscanf(s,"%d%n",&num_coords,&offset);
        s += offset;
      } else
        num = sscanf(style,"%d",&num_coords);

      if((num != 1)||(num_coords>1000)||(num_coords<2))
      {
        if (verbose)
        {
          fvwm_msg(
ERR,"ReadDecorFace", "Bad button style (2) in line: %s",action);
        }
        return False;
      }

      vc->num = num_coords;
      vc->use_fgbg = 0;
      vc->vertices = (struct vector_vertex *)
safemalloc (sizeof(struct vector_vertex) * num_coords);

      /* get the points */
      for(i = 0; i < vc->num; ++i)
      {
        /* X x Y @ line_style */
        num = sscanf(s,"[EMAIL PROTECTED]", &x, &y, &line_style, &offset);
        vc->vertices[i].x = x;
        vc->vertices[i].y = y;
        vc->vertices[i].color = line_style;
        if(num != 3)
        {
          if (verbose)
          {
            fvwm_msg(
ERR, "ReadDecorFace", "Bad button style (3) in line %s", action);
          }
          free(vc->vertices);
          vc->vertices = NULL;
                      vc->num = 0;
          return False;
        }
        if (line_style >= 2 && line_style < 4)
        {
          vc->use_fgbg = 1;
        }
        s += offset;
      }
      memset(&df->style, 0, sizeof(df->style));
      DFS_FACE_TYPE(df->style) = VectorButton;
    }


----------------------------------------------------

5) borders.c

Instead of

static void DrawLinePattern(
Window win, GC ReliefGC, GC ShadowGC, Pixel fore_color, Pixel back_color,
 struct vector_coords *coords, int w, int h)
{
 int i;

 /* It is rare, so evaluate this only when needed */
 GC fore_GC = NULL, back_GC = NULL;
 if (coords->use_fgbg)
 {
   Globalgcv.foreground = fore_color;
   Globalgcm = GCForeground;
   XChangeGC(dpy, Scr.ScratchGC3, Globalgcm, &Globalgcv);
   fore_GC = Scr.ScratchGC3;
   Globalgcv.foreground = back_color;
   XChangeGC(dpy, Scr.ScratchGC4, Globalgcm, &Globalgcv);
   back_GC = Scr.ScratchGC4;
 }

 for (i = 1; i < coords->num; ++i)
 {
     XDrawLine(dpy,win,
               (coords->use_fgbg & (1 << i))
                 ? (coords->line_style & (1 << i)) ? fore_GC : back_GC
: (coords->line_style & (1 << i)) ? ReliefGC : ShadowGC,
               w * coords->x[i-1]/100,
               h * coords->y[i-1]/100,
               w * coords->x[i]/100,
               h * coords->y[i]/100);
 }
}

I propose

static void DrawLinePattern(
Window win, GC ReliefGC, GC ShadowGC, Pixel fore_color, Pixel back_color,
  struct vector_coords *coords, int w, int h)
{
  int i, prev_x, prev_y;
  GC gcs[4];
  gcs[0] = ShadowGC;
  gcs[1] = ReliefGC;
  gcs[2] = NULL;
  gcs[3] = NULL;
    /* It is rare, so evaluate this only when needed */
  if (coords->use_fgbg)
  {
    Globalgcv.foreground = fore_color;
    Globalgcm = GCForeground;
    XChangeGC(dpy, Scr.ScratchGC3, Globalgcm, &Globalgcv);
    gcs[3] = Scr.ScratchGC3;
    Globalgcv.foreground = back_color;
    XChangeGC(dpy, Scr.ScratchGC4, Globalgcm, &Globalgcv);
    gcs[2] = Scr.ScratchGC4;
  }
    /* safety check */
  if (coords->num <= 0) return;
    prev_x = w * coords->vertices->x/100;
  prev_y = h * coords->vertices->y/100;

  for (i = 1; i < coords->num; ++i)
  {
      struct vector_vertex *v = coords->vertices + i;
      int x = w * v->x/100;
      int y = h * v->y/100;
if (v->color < 4) XDrawLine(dpy,win,gcs[v->color],prev_x,prev_y,x,y);
      prev_x = x;
      prev_y = y;
  }
}

Please notice that the newer version should be slightly faster
than the old one.

/* 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 based on Twm, but has been siginificantly modified
 * by Rob Nation
 ****************************************************************************/
/*
 * Copyright 1989 Massachusetts Institute of Technology
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of M.I.T. not be used in advertising
 * or publicity pertaining to distribution of the software without specific,
 * written prior permission.  M.I.T. makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T.
 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/***********************************************************************
 *
 * fvwm per-screen data include file
 *
 ***********************************************************************/

#ifndef _SCREEN_
#define _SCREEN_

#define SIZE_HINDENT 5
#define SIZE_VINDENT 3

/* colormap focus styes */
#define COLORMAP_FOLLOWS_MOUSE 1 /* default */
#define COLORMAP_FOLLOWS_FOCUS 2


typedef struct
{
  Window win;
  int isMapped;
} PanFrame;

typedef enum
{
    /* button types */
    DefaultVectorButton       ,
    VectorButton              ,
    SimpleButton              ,
    GradientButton            ,
    PixmapButton              ,
    TiledPixmapButton         ,
#ifdef MINI_ICONS
    MiniIconButton            ,
#endif
    SolidButton
} DecorFaceType;

typedef enum
{
  JUST_CENTER = 0,
  JUST_LEFT = 1,
  JUST_TOP = 1,
  JUST_RIGHT = 2,
  JUST_BOTTOM = 2,
  JUST_MASK = 3
} JustificationType;

typedef struct
{
  unsigned face_type : 3; /* was DecorFaceType : 3 */
  struct
  {
    unsigned h_justification : 2; /* was JustificationType : 2 */
    unsigned v_justification : 2; /* was JustificationType : 2 */
#define DFS_BUTTON_IS_UP   0
#define DFS_BUTTON_IS_FLAT 1
#define DFS_BUTTON_IS_SUNK 2
#define DFS_BUTTON_MASK    3
    unsigned int button_relief : 2;
    /* not used in border styles */
    unsigned int use_title_style : 1;
    unsigned int use_border_style : 1;
    /* only used in border styles */
    unsigned int has_hidden_handles : 1;
    unsigned int has_no_inset : 1;
  } flags;
} DecorFaceStyle;

#define DFS_FACE_TYPE(dfs)          ((dfs).face_type)
#define DFS_FLAGS(dfs)              ((dfs).flags)
#define DFS_H_JUSTIFICATION(dfs)    ((dfs).flags.h_justification)
#define DFS_V_JUSTIFICATION(dfs)    ((dfs).flags.v_justification)
#define DFS_BUTTON_RELIEF(dfs)      ((dfs).flags.button_relief)
#define DFS_USE_TITLE_STYLE(dfs)    ((dfs).flags.use_title_style)
#define DFS_USE_BORDER_STYLE(dfs)   ((dfs).flags.use_border_style)
#define DFS_HAS_HIDDEN_HANDLES(dfs) ((dfs).flags.has_hidden_handles)
#define DFS_HAS_NO_INSET(dfs)       ((dfs).flags.has_no_inset)

typedef struct DecorFace
{
  DecorFaceStyle style;
  struct
  {
    Picture *p;
    Pixel back;
    struct
    {
      int npixels;
      Pixel *pixels;
      char gradient_type;
    } grad;
    struct vector_coords
    {
      int num;
      struct vector_vertex {
        unsigned char x;
        unsigned char y;
        unsigned short color;
      } *vertices;
      unsigned long use_fgbg;
    } vector;
  } u;

#ifdef MULTISTYLE
  struct DecorFace *next;
#endif

  struct
  {
    unsigned has_changed : 1;
  } flags;
} DecorFace;

enum ButtonState
{
  BS_All = -1,
  BS_ActiveUp,
  BS_ActiveDown,
  BS_Inactive,
  BS_ToggledActiveUp,
  BS_ToggledActiveDown,
  BS_ToggledInactive,
  BS_MaxButtonState
};

typedef enum
{
  /* The first five are used in title buttons.  These can't be renumbered
   * without extending the mwm_decor_flags member below and adapting the style
   * structure. */
  MWM_DECOR_MENU     = 0x1,
  MWM_DECOR_MINIMIZE = 0x2,
  MWM_DECOR_MAXIMIZE = 0x4,
  MWM_DECOR_SHADE    = 0x8,
  MWM_DECOR_STICK    = 0x10,
  /* --- */
  MWM_DECOR_BORDER   = 0x20,
  MWM_DECOR_RESIZEH  = 0x40,
  MWM_DECOR_TITLE    = 0x80,
  MWM_DECOR_ALL      = 0x100,
  MWM_DECOR_EVERYTHING = 0xff
} mwm_flags;

typedef struct
{
  unsigned just : 2; /* was JustificationType : 2 */
  struct
  {
    unsigned has_changed : 1;
    mwm_flags mwm_decor_flags : 9;
  } flags;
  DecorFace state[BS_MaxButtonState];
} TitleButton;

#define TB_FLAGS(tb)              ((tb).flags)
#define TB_STATE(tb)              ((tb).state)
#define TB_JUSTIFICATION(tb)      ((tb).just)
#define TB_MWM_DECOR_FLAGS(tb)    ((tb).flags.mwm_decor_flags)
#define TB_HAS_CHANGED(tb)     \
  (!!((tb).flags.has_changed))
#define TB_HAS_MWM_DECOR_MENU(tb)     \
  (!!((tb).flags.mwm_decor_flags & MWM_DECOR_MENU))
#define TB_HAS_MWM_DECOR_MINIMIZE(tb) \
  (!!((tb).flags.mwm_decor_flags & MWM_DECOR_MINIMIZE))
#define TB_HAS_MWM_DECOR_MAXIMIZE(tb) \
  (!!((tb).flags.mwm_decor_flags & MWM_DECOR_MAXIMIZE))
#define TB_HAS_MWM_DECOR_SHADE(tb)    \
  (!!((tb).flags.mwm_decor_flags & MWM_DECOR_SHADE))
#define TB_HAS_MWM_DECOR_STICK(tb)    \
  (!!((tb).flags.mwm_decor_flags & MWM_DECOR_STICK))

typedef struct FvwmDecor
{
#ifdef USEDECOR
  char *tag;			/* general style tag */
#endif
  int title_height;           /* explicitly specified title bar height */
  /* titlebar buttons */
  TitleButton buttons[NUMBER_OF_BUTTONS];
  TitleButton titlebar;
  struct BorderStyle
  {
    DecorFace active, inactive;
  } BorderStyle;
#ifdef USEDECOR
  struct FvwmDecor *next;	/* additional user-defined styles */
#endif
  struct
  {
    unsigned has_changed : 1;
    unsigned has_title_height_changed : 1;
  } flags;
} FvwmDecor;


typedef struct ScreenInfo
{
  unsigned long screen;
  Screen *pscreen;
  int NumberOfScreens;          /* number of screens on display */
  int MyDisplayWidth;		/* my copy of DisplayWidth(dpy, screen) */
  int MyDisplayHeight;	        /* my copy of DisplayHeight(dpy, screen) */

  FvwmWindow FvwmRoot;		/* the head of the fvwm window list */
  Window Root;		        /* the root window */
  Window SizeWindow;		/* the resize dimensions window */
  Window NoFocusWin;            /* Window which will own focus when no other
				 * windows have it */
  PanFrame PanFrameTop;
  PanFrame PanFrameLeft;
  PanFrame PanFrameRight;
  PanFrame PanFrameBottom;

  Pixmap gray_bitmap;           /*dark gray pattern for shaded out menu items*/
  Pixmap gray_pixmap;           /* dark gray pattern for inactive borders */
  Pixmap light_gray_pixmap;     /* light gray pattern for inactive borders */
  Pixmap sticky_gray_pixmap;     /* light gray pattern for sticky borders */

  Binding *AllBindings;

  int root_pushes;		/* current push level to install root
				   colormap windows */
  int fvwm_pushes;		/* current push level to install fvwm
				   colormap windows */
  FvwmWindow *pushed_window;	/* saved window to install when pushes drops
				   to zero */
  Cursor *FvwmCursors;
  int BusyCursor;               /* context where we display the busy cursor */
  char *DefaultIcon;            /* Icon to use when no other icons are found */

  int TopLayer;
  int DefaultLayer;
  int BottomLayer;

  FvwmFunction *functions;

  FvwmFont DefaultFont;     	/* font structure */

  GC TransMaskGC;               /* GC for transparency masks */
  Pixel StdFore, StdBack, StdHilite, StdShadow; /* don't change the order */
  GC StdGC;
  GC StdReliefGC;
  GC StdShadowGC;

  Pixmap ScratchMonoPixmap;     /* A scratch 1x1x1 pixmap */
  GC MonoGC;                    /* GC for drawing into depth 1 drawables */

  GC XorGC;			/* GC to draw lines for move and resize */
  GC ScratchGC1;
  GC ScratchGC2;
  GC ScratchGC3;
  GC ScratchGC4;
  GC TitleGC;
  int SizeStringWidth;	        /* minimum width of size window */

  FvwmDecor DefaultDecor;	/* decoration style(s) */
  FvwmDecor *cur_decor;

  int nr_left_buttons;		/* number of left-side title-bar buttons */
  int nr_right_buttons;		/* number of right-side title-bar buttons */

  FvwmWindow *Hilite;		/* the fvwm window that is highlighted
				 * except for networking delays, this is the
				 * window which REALLY has the focus */
  Window UnknownWinFocused;      /* None, if the focus is nowhere or on an fvwm
				 * managed window. Set to id of otherwindow
				 * with focus otherwise */
  FvwmWindow *Ungrabbed;
  int EdgeScrollX;              /* #pixels to scroll on screen edge */
  int EdgeScrollY;              /* #pixels to scroll on screen edge */
  unsigned char buttons2grab;   /* buttons to grab in click to focus mode */
  int NumBoxes;
  int randomx;                  /* values used for randomPlacement */
  int randomy;
  int VxMax;                    /* Max location for top left of virt desk*/
  int VyMax;
  int Vx;                       /* Current loc for top left of virt desk */
  int Vy;

  int ClickTime;               /*Max button-click delay for Function built-in*/
  int ScrollResistance;        /* resistance to scrolling in desktop */
  int MoveResistance;          /* res to moving windows over viewport edge */
  int XiMoveResistance;        /* the same for edges of xinerama screens */
  int MoveThreshold;           /* number of pixels of mouse motion to decide
				* it's a move operation */
  int SnapAttraction;          /* attractiveness of window edges */
  int SnapMode;                /* mode of snap attraction */
  int SnapGridX;               /* snap grid X size */
  int SnapGridY;               /* snap grid Y size */
  int OpaqueSize;
  int CurrentDesk;             /* The current desktop number */
  int ColormapFocus;           /* colormap focus style */
  int ColorLimit;              /* Limit on colors used in pixmaps */
  int DefaultColorset;         /* Default Colorset used by feedback window */

  int use_backing_store;

  /*
  ** some additional global options which will probably become window
  ** specific options later on:
  */
#if 0
  /* Scr.go.ModifyUSP was always 1.  How is it supposed to be set? */
  struct
  {
    unsigned ModifyUSP : 1;                          /* - RBW - 11/02/1998  */
  } go; /* global options */
#endif
  struct
  {
    unsigned ModalityIsEvil : 1;
    unsigned RaiseHackNeeded : 1;
    unsigned DisableConfigureNotify : 1;
    unsigned InstallRootCmap : 1;
    unsigned RaiseOverUnmanaged : 1;
    unsigned FlickeringQtDialogsWorkaround : 1;
  } bo; /* bug workaround control options */
  struct
  {
    unsigned EmulateMWM : 1;
    unsigned EmulateWIN : 1;
    unsigned use_active_down_buttons : 1;
    unsigned use_inactive_buttons : 1;
    unsigned do_hide_position_window : 1;
    unsigned do_hide_resize_window : 1;
  } gs; /* global style structure */
  struct
  {
    Bool do_save_under : 1;
    unsigned do_need_window_update : 1;
    unsigned do_need_style_list_update : 1;
    unsigned has_default_font_changed : 1;
    unsigned has_default_color_changed : 1;
    unsigned has_mouse_binding_changed : 1;
    unsigned has_nr_buttons_changed : 1;
    unsigned has_xinerama_state_changed : 1;
    unsigned is_executing_complex_function : 1;
    unsigned is_map_desk_in_progress : 1;
    unsigned is_pointer_on_this_screen : 1;
    unsigned is_single_screen : 1;
    unsigned is_window_scheduled_for_destroy : 1;
    unsigned is_wire_frame_displayed : 1;
    unsigned silent_functions : 1;
    unsigned windows_captured : 1;
    unsigned edge_wrap_x : 1;
    unsigned edge_wrap_y : 1;
  } flags;

  struct
  {
    last_added_item_type type;
    void *item;
  } last_added_item;
} ScreenInfo;

/*
   Macro which gets specific decor or default decor.
   This saves an indirection in case you don't want
   the UseDecor mechanism.
 */
#ifdef USEDECOR
#define GetDecor(window,part) ((window)->decor->part)
#else
#define GetDecor(window,part) (Scr.DefaultDecor.part)
#endif

/* some protos for the decoration structures */
void LoadDefaultButton(DecorFace *bf, int i);
void ResetAllButtons(FvwmDecor *decor);
void DestroyAllButtons(FvwmDecor *decor);

void simplify_style_list(void);

/*
 * Diverts a style definition to an FvwmDecor structure ([EMAIL PROTECTED])
 */
void AddToDecor(FvwmDecor *decor, char *s);

extern ScreenInfo Scr;

#endif /* _SCREEN_ */
/* 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 all original code
 * by Rob Nation
 * Copyright 1993, Robert Nation
 *     You may use this code for any purpose, as long as the original
 *     copyright remains in the source code and all documentation
 ****************************************************************************/

/***********************************************************************
 * fvwm - "F? Virtual Window Manager"
 ***********************************************************************/

#include "config.h"

#include <stdio.h>
#include <signal.h>
#include <sys/wait.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif

#ifdef HAVE_GETPWUID
#  include <pwd.h>
#endif

#include "libs/fvwmlib.h"
#include "libs/FScreen.h"
#include "libs/FShape.h"
#include "fvwm.h"
#include "externs.h"
#include "cursor.h"
#include "functions.h"
#include "bindings.h"
#include "misc.h"
#include "screen.h"
#include "defaults.h"
#include "builtins.h"
#include "menus.h"
#include "module_interface.h"
#include "libs/Colorset.h"
#include "icccm2.h"
#include "gnome.h"
#include "icons.h"
#include "add_window.h"
#include "fvwmsignal.h"
#include "colormaps.h"
#include "stack.h"
#include "virtual.h"
#include "session.h"
#include "events.h"
#include "read.h"
#include "colors.h"
#include "focus.h"
#include "update.h"
#include "window_flags.h"
#include "move_resize.h"

#include <X11/Xproto.h>
#include <X11/Xatom.h>
/* need to get prototype for XrmUniqueQuark for XUniqueContext call */
#include <X11/Xresource.h>

#if defined(I18N_MB) || defined(COMPOUND_TEXT)
#include <X11/Xlocale.h>
#endif

#if HAVE_SYS_SYSTEMINFO_H
/* Solaris has sysinfo instead of gethostname.  */
#include <sys/systeminfo.h>
#endif

#define MAXHOSTNAME 255

#ifndef lint
static char sccsid[] __attribute__((__unused__))
    = "@(#)fvwm.c " VERSION " " __DATE__ " fvwm";
#endif

int master_pid;			/* process number of 1st fvwm process */

ScreenInfo Scr;		        /* structures for the screen */
MenuInfo Menus;                 /* structures for menus */
Display *dpy = NULL;		/* which display are we talking to */

Bool fFvwmInStartup = True;     /* Set to False when startup has finished */
Bool DoingCommandLine = False;	/* Set True before each cmd line arg */

/* Grab pointer state. Set by GrabEm, UngrabEm, menus.c and StartupStuff */
#define MAX_CFG_CMDS 10
static char *config_commands[MAX_CFG_CMDS];
static int num_config_commands=0;

/* Is this definition of mkdir portable? */
int mkdir(const char *, mode_t);
int FvwmErrorHandler(Display *, XErrorEvent *);
int CatchFatal(Display *);
int CatchRedirectError(Display *, XErrorEvent *);
void InstallSignals(void);
void SaveDesktopState(void);
void SetRCDefaults(void);
void StartupStuff(void);
static void catch_exit(void);
static RETSIGTYPE SigDone(int);
static RETSIGTYPE Restart(int);
static RETSIGTYPE ReapChildren(int);
static int parseCommandArgs(
  const char *command, char **argv, int maxArgc, const char **errorMsg);
static void InternUsefulAtoms(void);
static void InitVariables(void);
static void usage(void);
static void setVersionInfo(void);

XContext FvwmContext;		/* context for fvwm windows */
XContext MenuContext;		/* context for fvwm menus */

int JunkX = 0, JunkY = 0;
Window JunkRoot, JunkChild;		/* junk window */
unsigned int JunkWidth, JunkHeight, JunkBW, JunkDepth, JunkMask;

Bool debugging = False;
Bool debugging_stack_ring = False;

Window bad_window = None;

char **g_argv;
int g_argc;

char *state_filename = NULL;
char *restart_state_filename = NULL;  /* $HOME/.fs-restart */

/* assorted gray bitmaps for decorative borders */
#define g_width 2
#define g_height 2
static char g_bits[] = {0x02, 0x01};

#define l_g_width 4
#define l_g_height 2
static char l_g_bits[] = {0x08, 0x02};

#define s_g_width 4
#define s_g_height 4
static char s_g_bits[] = {0x01, 0x02, 0x04, 0x08};

extern XEvent Event;
Bool Restarting = False;
int fd_width, x_fd;
char *display_name = NULL;
char *fvwm_userdir;
static char *home_dir;
char const *Fvwm_VersionInfo;
char const *Fvwm_SupportInfo;

typedef enum { FVWM_RUNNING=0, FVWM_DONE, FVWM_RESTART } FVWM_STATE;

static volatile sig_atomic_t fvwmRunState = FVWM_RUNNING;

/***********************************************************************
 *
 *  Procedure:
 *	main - start of fvwm
 *
 ***********************************************************************
 */
int main(int argc, char **argv)
{
  unsigned long valuemask;	/* mask for create windows */
  XSetWindowAttributes attributes;	/* attributes for create windows */
  int i;
  extern int x_fd;
  int len;
  char *display_string;
  char message[255];
  Bool do_force_single_screen = False;
  Bool replace_wm = False;
  Bool option_error = False;
  int visualClass = -1;
  int visualId = -1;

  /* for use on restart */
  g_argv = (char **)safemalloc((argc + 4) * sizeof(char *));
  g_argc = argc;
  for (i = 0; i < argc; i++)
    g_argv[i] = argv[i];
  g_argv[g_argc] = NULL;

  DBUG("main","Entered, about to parse args");

  setVersionInfo();

#if defined(I18N_MB) || defined(COMPOUND_TEXT)
  {
    char *s;

    if ((s = setlocale(LC_CTYPE, "")) == NULL)
      fvwm_msg(ERR, "main","Can't set locale. Check your $LC_CTYPE or $LANG.\n");
    else if (!XSupportsLocale())
    {
      fvwm_msg(WARN, "main", "X does not support current locale: %s\n", s);
    }
  }
#endif

  /* Put the default module directory into the environment so it can be used
     later by the config file, etc.  */
  putenv("FVWM_MODULEDIR=" FVWM_MODULEDIR);

  /* Figure out user's home directory */
  home_dir = getenv("HOME");
#ifdef HAVE_GETPWUID
  if (home_dir == NULL) {
    struct passwd* pw = getpwuid(getuid());
    if (pw != NULL)
      home_dir = safestrdup(pw->pw_dir);
  }
#endif
  if (home_dir == NULL)
    home_dir = "/"; /* give up and use root dir */

  /* Figure out where to read and write user's data files. */
  fvwm_userdir = getenv("FVWM_USERDIR");
  if (fvwm_userdir == NULL)
  {
    fvwm_userdir = safestrdup(CatString2(home_dir, "/.fvwm"));
    /* Put the user directory into the environment so it can be used
       later everywhere. */
    putenv(safestrdup(CatString2("FVWM_USERDIR=", fvwm_userdir)));
  }

  /* Create FVWM_USERDIR directory if needed */
  if (access(fvwm_userdir, F_OK) != 0)
    mkdir(fvwm_userdir, 0777);
  if (access(fvwm_userdir, W_OK) != 0)
    fvwm_msg(ERR, "main", "No write permissions in `%s/'.\n", fvwm_userdir);

  for (i = 1; i < argc; i++)
  {
    if (strncasecmp(argv[i],"-debug_stack_ring", 17)==0)
    {
      debugging_stack_ring = True;
    }
    else if (strncasecmp(argv[i],"-debug",6)==0)
    {
      debugging = True;
    }
    else if (strncasecmp(argv[i], "-clientId", 9) == 0)
    {
      if (++i >= argc)
	usage();
      SetClientID(argv[i]);
    }
    else if (strncasecmp(argv[i], "-restore", 8) == 0)
    {
      if (++i >= argc)
	usage();
      state_filename = argv[i];
    }
    else if (strncasecmp(argv[i],"-s",2)==0)
    {
      do_force_single_screen = True;
    }
    else if (strncasecmp(argv[i],"-d",2)==0)
    {
      if (++i >= argc)
        usage();
      display_name = argv[i];
    }
    else if (strncasecmp(argv[i],"-f",2)==0)
    {
      if (++i >= argc)
        usage();
      if (num_config_commands < MAX_CFG_CMDS)
      {
        config_commands[num_config_commands] =
          (char *)malloc(6+strlen(argv[i]));
        strcpy(config_commands[num_config_commands],"Read ");
        strcat(config_commands[num_config_commands],argv[i]);
        num_config_commands++;
      }
      else
      {
        fvwm_msg(ERR,"main","only %d -f and -cmd parms allowed!",MAX_CFG_CMDS);
      }
    }
    else if (strncasecmp(argv[i],"-cmd",4)==0)
    {
      if (++i >= argc)
        usage();
      if (num_config_commands < MAX_CFG_CMDS)
      {
        config_commands[num_config_commands] = safestrdup(argv[i]);
        num_config_commands++;
      }
      else
      {
        fvwm_msg(ERR,"main","only %d -f and -cmd parms allowed!",MAX_CFG_CMDS);
      }
    }
    else if (strncasecmp(argv[i],"-h",2)==0)
    {
      usage();
      exit(0);
    }
    else if (strncasecmp(argv[i],"-blackout",9)==0)
    {
      /* obsolete option */
      fvwm_msg(WARN, "main", "The -blackout option is obsolete, it may be "
	       "removed in the future.",
              VERSION,__DATE__,__TIME__);
    }
    else if (strncasecmp(argv[i], "-replace", 8) == 0)
    {
      replace_wm = True;
    }
    /* check for visualId before visual to remove ambiguity */
    else if (strncasecmp(argv[i],"-visualId",9)==0) {
      visualClass = -1;
      if (++i >= argc)
        usage();
      if (sscanf(argv[i], "0x%x", &visualId) == 0)
        if (sscanf(argv[i], "%d", &visualId) == 0)
          usage();
    }
    else if (strncasecmp(argv[i],"-visual",7)==0) {
      visualId = None;
      if (++i >= argc)
        usage();
      if (strncasecmp(argv[i], "staticg", 7) == 0)
        visualClass = StaticGray;
      else if (strncasecmp(argv[i], "g", 1) == 0)
        visualClass = GrayScale;
      else if (strncasecmp(argv[i], "staticc", 7) == 0)
        visualClass = StaticColor;
      else if (strncasecmp(argv[i], "p", 1) == 0)
        visualClass = PseudoColor;
      else if (strncasecmp(argv[i], "t", 1) == 0)
        visualClass = TrueColor;
      else if (strncasecmp(argv[i], "d", 1) == 0)
        visualClass = DirectColor;
      else
        usage();
    }
    else if (strncasecmp(argv[i], "-version", 8) == 0)
    {
      printf("%s\n%s\n", Fvwm_VersionInfo, Fvwm_SupportInfo);
      exit(0);
    }
    else
    {
      fvwm_msg(ERR,"main","Unknown option: `%s'", argv[i]);
      option_error = TRUE;
    }
  }

  DBUG("main","Done parsing args");

  if (option_error)
  {
    usage();
  }

  DBUG("main","Installing signal handlers");
  InstallSignals();

  if (!(Pdpy = dpy = XOpenDisplay(display_name)))
  {
    fvwm_msg(ERR,"main","can't open display %s", XDisplayName(display_name));
    exit (1);
  }
  atexit(catch_exit);
  Scr.screen= DefaultScreen(dpy);
  Scr.NumberOfScreens = ScreenCount(dpy);

  master_pid = getpid();

  if(!do_force_single_screen)
  {
    int	myscreen = 0;
    char *cp;

    strcpy(message, XDisplayString(dpy));

    for(i=0;i<Scr.NumberOfScreens;i++)
    {
      if (i != Scr.screen && fork() == 0)
      {
        myscreen = i;

        /*
         * Truncate the string 'whatever:n.n' to 'whatever:n',
         * and then append the screen number.
         */
        cp = strchr(message, ':');
        if (cp != NULL)
        {
          cp = strchr(cp, '.');
          if (cp != NULL)
            *cp = '\0';		/* truncate at display part */
        }
        sprintf(message + strlen(message), ".%d", myscreen);
        Pdpy = dpy = XOpenDisplay(message);
        Scr.screen = myscreen;
        Scr.NumberOfScreens = ScreenCount(dpy);

        break;
      }
    }

    g_argv[argc++] = "-s";
/*
    g_argv[argc++] = "-d";
    g_argv[argc++] = safestrdup(XDisplayString(dpy));
*/
    g_argv[argc] = NULL;
  }

  FScreenInit(dpy);

  x_fd = XConnectionNumber(dpy);
  fd_width = GetFdWidth();

#ifdef HAVE_X11_FD
  if (fcntl(x_fd, F_SETFD, 1) == -1)
  {
    fvwm_msg(ERR,"main","close-on-exec failed");
    exit (1);
  }
#endif

  /*  Add a DISPLAY entry to the environment, incase we were started
   * with fvwm -display term:0.0
   */
  len = strlen(XDisplayString(dpy));
  display_string = safemalloc(len+10);
  sprintf(display_string,"DISPLAY=%s",XDisplayString(dpy));
  putenv(display_string);
  /* Add a HOSTDISPLAY environment variable, which is the same as
   * DISPLAY, unless display = :0.0 or unix:0.0, in which case the full
   * host name will be used for ease in networking . */
  /* Note: Can't free the rdisplay_string after putenv, because it
   * becomes part of the environment! */
  if(strncmp(display_string,"DISPLAY=:",9)==0)
  {
    char client[MAXHOSTNAME], *rdisplay_string;
    gethostname(client,MAXHOSTNAME);
    rdisplay_string = safemalloc(len+14 + strlen(client));
    sprintf(rdisplay_string,"HOSTDISPLAY=%s:%s",client,&display_string[9]);
    putenv(rdisplay_string);
  }
  else if(strncmp(display_string,"DISPLAY=unix:",13)==0)
  {
    char client[MAXHOSTNAME], *rdisplay_string;
    gethostname(client,MAXHOSTNAME);
    rdisplay_string = safemalloc(len+14 + strlen(client));
    sprintf(rdisplay_string,"HOSTDISPLAY=%s:%s",client,
            &display_string[13]);
    putenv(rdisplay_string);
  }
  else
  {
    char *rdisplay_string;
    rdisplay_string = safemalloc(len+14);
    sprintf(rdisplay_string,"HOSTDISPLAY=%s",XDisplayString(dpy));
    putenv(rdisplay_string);
  }

  Scr.Root = RootWindow(dpy, Scr.screen);
  if(Scr.Root == None)
  {
    fvwm_msg(ERR,"main","Screen %d is not a valid screen",(char *)Scr.screen);
    exit(1);
  }

  if (visualClass != -1) {
    /* grab the best visual of the required class */
    XVisualInfo template, *vizinfo;
    int total, i;

    Pdepth = 0;
    template.screen = Scr.screen;
    template.class = visualClass;
    vizinfo = XGetVisualInfo(dpy, VisualScreenMask | VisualClassMask, &template,
                             &total);
    if (total) {
      for (i = 0; i < total; i++) {
        if (vizinfo[i].depth > Pdepth) {
          Pvisual = vizinfo[i].visual;
          Pdepth = vizinfo[i].depth;
        }
      }
      XFree(vizinfo);
      /* have to have a colormap for non-default visual windows */
      Pcmap = XCreateColormap(dpy, Scr.Root, Pvisual, AllocNone);
      Pdefault = False;
    } else {
      fvwm_msg(ERR, "main","Cannot find visual class %d", visualClass);
      visualClass = -1;
    }
  } else if (visualId != -1) {
    /* grab visual id asked for */
    XVisualInfo template, *vizinfo;
    int total;

    Pdepth = 0;
    template.screen = Scr.screen;
    template.visualid = visualId;
    vizinfo = XGetVisualInfo(dpy, VisualScreenMask | VisualIDMask, &template,
                             &total);
    if (total) {
      /* visualID's are unique so there will only be one */
      Pvisual = vizinfo[0].visual;
      Pdepth = vizinfo[0].depth;
      XFree(vizinfo);
      /* have to have a colormap for non-default visual windows */
      Pcmap = XCreateColormap(dpy, Scr.Root, Pvisual, AllocNone);
      Pdefault = False;
    } else {
      fvwm_msg(ERR, "main", "VisualId 0x%x is not valid ", visualId);
      visualId = -1;
    }
  }

  /* use default visuals if none found so far */
  if (visualClass == -1 && visualId == -1) {
    Pvisual = DefaultVisual(dpy, Scr.screen);
    Pdepth = DefaultDepth(dpy, Scr.screen);
    Pcmap = DefaultColormap(dpy, Scr.screen);
    Pdefault = True;
  }

  /* make a copy of the visual stuff so that XorPixmap can swap with root */
  SaveFvwmVisual();

  FShapeInit(dpy);
  Scr.pscreen = XScreenOfDisplay(dpy, Scr.screen);
  Scr.use_backing_store = DoesBackingStore(Scr.pscreen);
  Scr.flags.do_save_under = DoesSaveUnders(Scr.pscreen);

  InternUsefulAtoms ();

  /* Make sure property priority colors is empty */
  XChangeProperty(dpy, Scr.Root, _XA_MIT_PRIORITY_COLORS,
		  XA_CARDINAL, 32, PropModeReplace, NULL, 0);

  /* create a window which will accept the keyboard focus when no other
     windows have it */
  /* do this before any RC parsing as some GC's are created from this window
   * rather than the root window */
  attributes.event_mask = XEVMASK_NOFOCUSW;
  attributes.override_redirect = True;
  attributes.colormap = Pcmap;
  attributes.background_pixmap = None;
  attributes.border_pixel = 0;
  Scr.NoFocusWin=XCreateWindow(dpy, Scr.Root, -10, -10, 10, 10, 0, Pdepth,
                               InputOutput, Pvisual,
                               CWEventMask | CWOverrideRedirect | CWColormap
                               | CWBackPixmap | CWBorderPixel, &attributes);
  XMapWindow(dpy, Scr.NoFocusWin);

  SetMWM_INFO(Scr.NoFocusWin);
  FOCUS_SET(Scr.NoFocusWin);

  XSync(dpy, 0);
  if(debugging)
    XSynchronize(dpy,1);

  SetupICCCM2 (replace_wm);
  XSetErrorHandler(CatchRedirectError);
  XSetIOErrorHandler(CatchFatal);
  XSelectInput(dpy, Scr.Root, XEVMASK_ROOTW);
  XSync(dpy, 0);

  XSetErrorHandler(FvwmErrorHandler);

  {
    /* do not grab the pointer earlier because if fvwm exits with the pointer
     * grabbed while a different display is visible, XFree 4.0 freezes. */
    Cursor cursor = XCreateFontCursor(dpy, XC_watch);
    XGrabPointer(dpy, Scr.Root, 0, 0, GrabModeAsync, GrabModeAsync,
		 None, cursor, CurrentTime);
  }

  {
    Atom atype;
    int aformat;
    unsigned long nitems, bytes_remain;
    unsigned char *prop;

    if (
      XGetWindowProperty(
        dpy, Scr.Root, _XA_WM_DESKTOP, 0L, 1L, True, _XA_WM_DESKTOP,
        &atype, &aformat, &nitems, &bytes_remain, &prop
      ) == Success
    ) {
      if (prop != NULL) {
        Restarting = True;
        /* do_force_single_screen = True; */
      }
    }
  }

  restart_state_filename =
    safestrdup(
      CatString3(fvwm_userdir, "/.fs-restart-", getenv("HOSTDISPLAY")));
  if (!state_filename && Restarting)
    state_filename = restart_state_filename;

  /*
     This should be done early enough to have the window states loaded
     before the first call to AddWindow.
   */
  LoadWindowStates(state_filename);

  Scr.FvwmCursors = CreateCursors(dpy);
  InitVariables();
  if (visualClass != -1 || visualId != -1) {
    /* this is so that menus use the (non-default) fvwm colormap */
    Scr.FvwmRoot.w = Scr.NoFocusWin;
    Scr.FvwmRoot.number_cmap_windows = 1;
    Scr.FvwmRoot.cmap_windows = &Scr.NoFocusWin;
  }
  InitEventHandlerJumpTable();
  initModules();

  Scr.gray_bitmap =
    XCreateBitmapFromData(dpy,Scr.Root,g_bits, g_width,g_height);


  DBUG("main","Setting up rc file defaults...");
  SetRCDefaults();
  flush_window_updates();
  simplify_style_list();

  DBUG("main","Running config_commands...");
  if (num_config_commands > 0)
  {
    int i;
    for(i=0;i<num_config_commands;i++)
    {
      DoingCommandLine = True;
      old_execute_function(
	config_commands[i], NULL, &Event, C_ROOT, 1, 0, NULL);
      free(config_commands[i]);
    }
    DoingCommandLine = False;
  } else {
    /* Run startup command file in 5 places: FVWM_USERDIR, FVWM_DATADIR,
       and for compatibility: ~/.fvwm2rc, $sysconfdir/system.fvwm2rc */
    if (
      !run_command_file( CatString3(fvwm_userdir, "/",       FVWMRC),
        &Event, NULL, C_ROOT, 1 ) &&
      !run_command_file( CatString3(home_dir,     "/",       FVWMRC),
        &Event, NULL, C_ROOT, 1 ) &&
      !run_command_file( CatString3(FVWM_DATADIR, "/",       FVWMRC),
        &Event, NULL, C_ROOT, 1 ) &&
      !run_command_file( CatString3(FVWM_DATADIR, "/system", FVWMRC),
        &Event, NULL, C_ROOT, 1 ) &&
      !run_command_file( CatString3(FVWM_CONFDIR, "/system", FVWMRC),
        &Event, NULL, C_ROOT, 1 ) )
    {
      fvwm_msg( ERR, "main", "Cannot read startup file, tried: "
        "\n\t%s/%s\n\t%s/%s\n\t%s/%s\n\t%s/system%s\n\t%s/system%s",
          fvwm_userdir, FVWMRC,
          home_dir,     FVWMRC,
          FVWM_DATADIR, FVWMRC,
          FVWM_DATADIR, FVWMRC,
          FVWM_CONFDIR, FVWMRC);
    }
  }

  DBUG("main","Done running config_commands");

  if(Pdepth<2)
  {
    Scr.gray_pixmap =
      XCreatePixmapFromBitmapData(dpy,Scr.NoFocusWin, g_bits, g_width,
				  g_height, BlackPixel(dpy, Scr.screen),
				  WhitePixel(dpy, Scr.screen), Pdepth);
    Scr.light_gray_pixmap =
      XCreatePixmapFromBitmapData(dpy, Scr.NoFocusWin, l_g_bits, l_g_width,
				  l_g_height, BlackPixel(dpy, Scr.screen),
				  WhitePixel(dpy, Scr.screen), Pdepth);
    Scr.sticky_gray_pixmap =
      XCreatePixmapFromBitmapData(dpy, Scr.NoFocusWin, s_g_bits, s_g_width,
				  s_g_height, BlackPixel(dpy, Scr.screen),
				  WhitePixel(dpy, Scr.screen), Pdepth);
  }

  attributes.background_pixel = Scr.StdBack;
  attributes.colormap = Pcmap;
  attributes.border_pixel = 0;
  valuemask = CWBackPixel | CWColormap | CWBorderPixel;

  Scr.SizeWindow = XCreateWindow(
    dpy, Scr.Root, 0, 0, 1, 1, (unsigned int)0, Pdepth, InputOutput, Pvisual,
    valuemask, &attributes);
  resize_geometry_window();
  initPanFrames();

  MyXGrabServer(dpy);
  checkPanFrames();
  MyXUngrabServer(dpy);

  CoerceEnterNotifyOnCurrentWindow();

  SessionInit();

  GNOME_Init();

  DBUG("main","Entering HandleEvents loop...");

  HandleEvents();

  switch( fvwmRunState )
  {
  case FVWM_DONE:
    Done(0, NULL);     /* does not return */

  case FVWM_RESTART:
    Done(1, "");       /* does not return */

  default:
    DBUG("main","Unknown FVWM run-state");
  }

  exit(0);
}


/* exit handler that will try to release any grabs */
static void catch_exit(void)
{
  if (dpy != NULL)
  {
    /* Don't care if this is called from an X error handler. We *have* to try
     * this, whatever happens. XFree 4.0 may freeze if we don't do this. */
    XUngrabServer(dpy);
    XUngrabPointer(dpy, CurrentTime);
    XUngrabKeyboard(dpy, CurrentTime);
  }

  return;
}

/*
** StartupStuff
**
** Does initial window captures and runs init/restart function
*/
void StartupStuff(void)
{
#define startFuncName "StartFunction"
  const char *initFuncName;

  CaptureAllWindows(False);
  /* Turn off the SM stuff after the initial capture so that new windows will
   * not be matched by accident. */
  if (Restarting)
    DisableRestoringState();
  /* Have to do this here too because preprocessor modules have not run to the
   * end when HandleEvents is entered from the main loop. */
  checkPanFrames();

  fFvwmInStartup = False;

  /* Make sure we have the correct click time now. */
  if (Scr.ClickTime < 0)
    Scr.ClickTime = -Scr.ClickTime;

  /* It is safe to Ungrab here: if not and one of the init functions not
   * finish we've got a complete freeze ! */
  UngrabEm(GRAB_STARTUP);
  XUngrabPointer(dpy, CurrentTime);

  /* migo (04-Sep-1999): execute StartFunction */
  if (FindFunction(startFuncName)) {
    char *action = "Function " startFuncName;
    old_execute_function(action, NULL, &Event, C_ROOT, 1, 0, NULL);
  }

  /* migo (03-Jul-1999): execute [Session]{Init|Restart}Function */
  initFuncName = getInitFunctionName(Restarting == True);
  if (FindFunction(initFuncName)) {
    char *action = safestrdup(CatString2("Function ", initFuncName));
    old_execute_function(action, NULL, &Event, C_ROOT, 1, 0, NULL);
    free(action);
  }

  /*
     This should be done after the initialization is finished, since
     it directly changes the global state.
   */
  LoadGlobalState(state_filename);

  /*
  ** migo (20-Jun-1999): Remove state file after usage.
  ** migo (09-Jul-1999): but only on restart, otherwise it can be reused.
  */
  if (Restarting)
    unlink(state_filename);

} /* StartupStuff */



/*
** SetRCDefaults
**
** Sets some initial style values & such
*/
void SetRCDefaults(void)
{
  /* set up default colors, fonts, etc */
  char *defaults[] = {
    "XORValue 0",
    "DefaultFont fixed",
    "DefaultColors black grey",
    DEFAULT_MENU_STYLE,
    "TitleStyle Centered -- Raised",
    "Style * Color lightgrey/dimgrey",
    "Style * HilightFore black, HilightBack grey",
    "AddToMenu MenuFvwmRoot \"Builtin Menu\" Title",
    "+ \"&1. XTerm\" Exec xterm",
    "+ \"&2. Setup Form\" Module FvwmForm FvwmForm-Setup",
    "+ \"&3. Setup 95 Script\" Module FvwmScript FvwmScript-Setup95",
    "+ \"&4. Issue FVWM commands\" Module FvwmConsole",
    "+ \"&R. Restart FVWM\" Restart",
    "+ \"&X. Exit FVWM\" Quit",
    "Mouse 0 R N Menu MenuFvwmRoot",
    "Read "FVWM_DATADIR"/ConfigFvwmDefaults",
    NULL
  };
  int i=0;

  while (defaults[i])
  {
    old_execute_function(defaults[i], NULL, &Event, C_ROOT, 1, 0, NULL);
    i++;
  }
} /* SetRCDefaults */


/***********************************************************************
 *
 *  Procedure:
 *	InternUsefulAtoms:
 *            Dont really know what it does
 *
 ***********************************************************************
 */
Atom _XA_MIT_PRIORITY_COLORS;
Atom _XA_WM_CHANGE_STATE;
Atom _XA_WM_STATE;
Atom _XA_WM_COLORMAP_WINDOWS;
extern Atom _XA_WM_PROTOCOLS;
Atom _XA_WM_TAKE_FOCUS;
Atom _XA_WM_DELETE_WINDOW;
Atom _XA_WM_DESKTOP;
Atom _XA_MwmAtom;
Atom _XA_MOTIF_WM;

Atom _XA_OL_WIN_ATTR;
Atom _XA_OL_WT_BASE;
Atom _XA_OL_WT_CMD;
Atom _XA_OL_WT_HELP;
Atom _XA_OL_WT_NOTICE;
Atom _XA_OL_WT_OTHER;
Atom _XA_OL_DECOR_ADD;
Atom _XA_OL_DECOR_DEL;
Atom _XA_OL_DECOR_CLOSE;
Atom _XA_OL_DECOR_RESIZE;
Atom _XA_OL_DECOR_HEADER;
Atom _XA_OL_DECOR_ICON_NAME;

Atom _XA_WM_WINDOW_ROLE;
Atom _XA_WM_CLIENT_LEADER;
Atom _XA_SM_CLIENT_ID;

static void InternUsefulAtoms (void)
{
  /*
   * Create priority colors if necessary.
   */
  _XA_MIT_PRIORITY_COLORS = XInternAtom(dpy, "_MIT_PRIORITY_COLORS", False);
  _XA_WM_CHANGE_STATE = XInternAtom (dpy, "WM_CHANGE_STATE", False);
  _XA_WM_STATE = XInternAtom (dpy, "WM_STATE", False);
  _XA_WM_COLORMAP_WINDOWS = XInternAtom (dpy, "WM_COLORMAP_WINDOWS", False);
  _XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);
  _XA_WM_TAKE_FOCUS = XInternAtom (dpy, "WM_TAKE_FOCUS", False);
  _XA_WM_DELETE_WINDOW = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
  _XA_WM_DESKTOP = XInternAtom (dpy, "WM_DESKTOP", False);
  _XA_MwmAtom=XInternAtom(dpy,"_MOTIF_WM_HINTS",False);
  _XA_MOTIF_WM=XInternAtom(dpy,"_MOTIF_WM_INFO",False);

  _XA_OL_WIN_ATTR=XInternAtom(dpy,"_OL_WIN_ATTR",False);
  _XA_OL_WT_BASE=XInternAtom(dpy,"_OL_WT_BASE",False);
  _XA_OL_WT_CMD=XInternAtom(dpy,"_OL_WT_CMD",False);
  _XA_OL_WT_HELP=XInternAtom(dpy,"_OL_WT_HELP",False);
  _XA_OL_WT_NOTICE=XInternAtom(dpy,"_OL_WT_NOTICE",False);
  _XA_OL_WT_OTHER=XInternAtom(dpy,"_OL_WT_OTHER",False);
  _XA_OL_DECOR_ADD=XInternAtom(dpy,"_OL_DECOR_ADD",False);
  _XA_OL_DECOR_DEL=XInternAtom(dpy,"_OL_DECOR_DEL",False);
  _XA_OL_DECOR_CLOSE=XInternAtom(dpy,"_OL_DECOR_CLOSE",False);
  _XA_OL_DECOR_RESIZE=XInternAtom(dpy,"_OL_DECOR_RESIZE",False);
  _XA_OL_DECOR_HEADER=XInternAtom(dpy,"_OL_DECOR_HEADER",False);
  _XA_OL_DECOR_ICON_NAME=XInternAtom(dpy,"_OL_DECOR_ICON_NAME",False);

  _XA_WM_WINDOW_ROLE=XInternAtom(dpy, "WM_WINDOW_ROLE",False);
  _XA_WM_CLIENT_LEADER=XInternAtom(dpy, "WM_CLIENT_LEADER",False);
  _XA_SM_CLIENT_ID=XInternAtom(dpy, "SM_CLIENT_ID",False);
  return;
}


/***********************************************************************
 *
 *  Procedure:
 *	InstallSignals: install the signal handlers, using whatever
 *         means we have at our disposal. The more POSIXy, the better
 *
 ************************************************************************/
void
InstallSignals(void)
{
#ifdef HAVE_SIGACTION
  struct sigaction  sigact;

  /*
   * All signals whose handlers call fvwmSetTerminate()
   * must be mutually exclusive - we mustn't receive one
   * while processing any of the others ...
   */
  sigemptyset(&sigact.sa_mask);
  sigaddset(&sigact.sa_mask, SIGINT);
  sigaddset(&sigact.sa_mask, SIGHUP);
  sigaddset(&sigact.sa_mask, SIGQUIT);
  sigaddset(&sigact.sa_mask, SIGTERM);
  sigaddset(&sigact.sa_mask, SIGUSR1);

#ifdef SA_RESTART
  sigact.sa_flags = SA_RESTART;
#else
  sigact.sa_flags = 0;
#endif
  sigact.sa_handler = DeadPipe;
  sigaction(SIGPIPE, &sigact, NULL);

  sigact.sa_handler = Restart;
  sigaction(SIGUSR1, &sigact, NULL);

  sigact.sa_handler = SigDone;
  sigaction(SIGINT,  &sigact, NULL);
  sigaction(SIGHUP,  &sigact, NULL);
  sigaction(SIGQUIT, &sigact, NULL);
  sigaction(SIGTERM, &sigact, NULL);

  /*
   * Reap all zombies automatically! This signal handler will
   * only be called if a child process dies, not if someone
   * sends a child a STOP signal. Note that none of our "terminate"
   * signals can be delivered until the SIGCHLD handler completes,
   * and this is a Good Thing because the terminate handlers
   * might exit abruptly via "siglongjmp". This could potentially
   * leave SIGCHLD handler with unfinished business ...
   *
   * NOTE: We could still receive SIGPIPE signals within the
   *       SIGCHLD handler, but the SIGPIPE handler has the
   *       SA_RESTART flag set and so should not affect our
   *       "wait" system call.
   */
  sigact.sa_flags |= SA_NOCLDSTOP;
  sigact.sa_handler = ReapChildren;
  sigaction(SIGCHLD, &sigact, NULL);
#else
#ifdef USE_BSD_SIGNALS
  fvwmSetSignalMask( sigmask(SIGUSR1) |
                     sigmask(SIGINT)  |
                     sigmask(SIGHUP)  |
                     sigmask(SIGQUIT) |
                     sigmask(SIGTERM) );
#endif

  /*
   * We don't have sigaction(), so fall back on
   * less reliable methods ...
   */
  signal(SIGPIPE, DeadPipe);
  signal(SIGUSR1, Restart);
#ifdef HAVE_SIGINTERRUPT
  siginterrupt(SIGUSR1, 0);
#endif

  signal(SIGINT, SigDone);
#ifdef HAVE_SIGINTERRUPT
  siginterrupt(SIGINT, 0);
#endif
  signal(SIGHUP, SigDone);
#ifdef HAVE_SIGINTERRUPT
  siginterrupt(SIGHUP, 0);
#endif
  signal(SIGQUIT, SigDone);
#ifdef HAVE_SIGINTERRUPT
  siginterrupt(SIGQUIT, 0);
#endif
  signal(SIGTERM, SigDone);
#ifdef HAVE_SIGINTERRUPT
  siginterrupt(SIGTERM, 0);
#endif
  signal(SIGCHLD, ReapChildren);
#ifdef HAVE_SIGINTERRUPT
  siginterrupt(SIGCHLD, 0);
#endif
#endif

  /* When FVWM restarts, the SIGCHLD handler is automatically reset
   * to the default handler. This means that Zombies left over from
   * the previous instance of FVWM could still be roaming the process
   * table if they exited while the default handler was in place.
   * We fix this by invoking the SIGCHLD handler NOW, so that they
   * may finally rest in peace. */
  ReapChildren(0);
}


/*************************************************************************
 * Reap child processes, preventing them from becoming zombies.
 * We do this asynchronously within the SIGCHLD handler so that
 * "it just happens".
 ************************************************************************/
static RETSIGTYPE
ReapChildren(int sig)
{
  (void)sig;

  BSD_BLOCK_SIGNALS;

  /*
   * This is a signal handler, AND SO MUST BE REENTRANT!
   * Now the wait() functions are safe here, but please don't
   * add anything unless you're SURE that the new functions
   * (plus EVERYTHING they call) are also reentrant. There
   * are very few functions which are truly safe.
   */
#if HAVE_WAITPID
  while (waitpid(-1, NULL, WNOHANG) > 0)
    ;
#elif HAVE_WAIT3
  while (wait3(NULL, WNOHANG, NULL) > 0)
    ;
#else
# error One of waitpid or wait3 is needed.
#endif

  BSD_UNBLOCK_SIGNALS;
}


/*************************************************************************
 * Restart on a signal
 ************************************************************************/
static RETSIGTYPE
Restart(int sig)
{
  fvwmRunState = FVWM_RESTART;

  /*
   * This function might not return - it could "long-jump"
   * right out, so we need to do everything we need to do
   * BEFORE we call it ...
   */
  fvwmSetTerminate(sig);
}

/***********************************************************************
 *
 *  LoadDefaultLeftButton -- loads default left button # into
 *		assumes associated button memory is already free
 *
 ************************************************************************/
static void LoadDefaultLeftButton(DecorFace *df, int i)
{
  struct vector_coords *v = &df->u.vector;

  memset(&df->style, 0, sizeof(df->style));
  DFS_FACE_TYPE(df->style) = DefaultVectorButton;
  switch (i % 5)
  {
  case 0:
  case 4:
    v->num = 5;
    v->vertices = (struct vector_vertex *)
                  safemalloc (sizeof(struct vector_vertex) * v->num);
    v->vertices[0].x = 22;
    v->vertices[0].y = 39;
    v->vertices[0].color = 1;
    v->vertices[1].x = 78;
    v->vertices[1].y = 39;
    v->vertices[1].color = 1;
    v->vertices[2].x = 78;
    v->vertices[2].y = 61;
    v->vertices[2].color = 0;
    v->vertices[3].x = 22;
    v->vertices[3].y = 61;
    v->vertices[3].color = 0;
    v->vertices[4].x = 22;
    v->vertices[4].y = 39;
    v->vertices[4].color = 1;
    v->use_fgbg = 0;
    break;
  case 1:
    v->num = 5;
    v->vertices = (struct vector_vertex *)
                  safemalloc (sizeof(struct vector_vertex) * v->num);
    v->vertices[0].x = 32;
    v->vertices[0].y = 45;
    v->vertices[0].color = 0;
    v->vertices[1].x = 68;
    v->vertices[1].y = 45;
    v->vertices[1].color = 0;
    v->vertices[2].x = 68;
    v->vertices[2].y = 55;
    v->vertices[2].color = 1;
    v->vertices[3].x = 32;
    v->vertices[3].y = 55;
    v->vertices[3].color = 1;
    v->vertices[4].x = 32;
    v->vertices[4].y = 45;
    v->vertices[4].color = 0;
    v->use_fgbg = 0;
    break;
  case 2:
    v->num = 5;
    v->vertices = (struct vector_vertex *)
                  safemalloc (sizeof(struct vector_vertex) * v->num);
    v->vertices[0].x = 49;
    v->vertices[0].y = 49;
    v->vertices[0].color = 1;
    v->vertices[1].x = 51;
    v->vertices[1].y = 49;
    v->vertices[1].color = 1;
    v->vertices[2].x = 51;
    v->vertices[2].y = 51;
    v->vertices[2].color = 0;
    v->vertices[3].x = 49;
    v->vertices[3].y = 51;
    v->vertices[3].color = 0;
    v->vertices[4].x = 49;
    v->vertices[4].y = 49;
    v->vertices[4].color = 1;
    v->use_fgbg = 0;
    break;
  case 3:
    v->num = 5;
    v->vertices = (struct vector_vertex *)
                  safemalloc (sizeof(struct vector_vertex) * v->num);
    v->vertices[0].x = 32;
    v->vertices[0].y = 45;
    v->vertices[0].color = 1;
    v->vertices[1].x = 68;
    v->vertices[1].y = 45;
    v->vertices[1].color = 1;
    v->vertices[2].x = 68;
    v->vertices[2].y = 55;
    v->vertices[2].color = 0;
    v->vertices[3].x = 32;
    v->vertices[3].y = 55;
    v->vertices[3].color = 0;
    v->vertices[4].x = 32;
    v->vertices[4].y = 45;
    v->vertices[4].color = 1;
    v->use_fgbg = 0;
    break;
  }
}

/***********************************************************************
 *
 *  LoadDefaultRightButton -- loads default left button # into
 *		assumes associated button memory is already free
 *
 ************************************************************************/
static void LoadDefaultRightButton(DecorFace *df, int i)
{
  struct vector_coords *v = &df->u.vector;

  memset(&df->style, 0, sizeof(df->style));
  DFS_FACE_TYPE(df->style) = DefaultVectorButton;
  switch (i % 5)
  {
  case 0:
  case 3:
    v->num = 5;
    v->vertices = (struct vector_vertex *)
                  safemalloc (sizeof(struct vector_vertex) * v->num);
    v->vertices[0].x = 25;
    v->vertices[0].y = 25;
    v->vertices[0].color = 1;
    v->vertices[1].x = 75;
    v->vertices[1].y = 25;
    v->vertices[1].color = 1;
    v->vertices[2].x = 75;
    v->vertices[2].y = 75;
    v->vertices[2].color = 0;
    v->vertices[3].x = 25;
    v->vertices[3].y = 75;
    v->vertices[3].color = 0;
    v->vertices[4].x = 25;
    v->vertices[4].y = 25;
    v->vertices[4].color = 1;
    v->use_fgbg = 0;
    break;
  case 1:
    v->num = 5;
    v->vertices = (struct vector_vertex *)
                  safemalloc (sizeof(struct vector_vertex) * v->num);
    v->vertices[0].x = 39;
    v->vertices[0].y = 39;
    v->vertices[0].color = 1;
    v->vertices[1].x = 61;
    v->vertices[1].y = 39;
    v->vertices[1].color = 1;
    v->vertices[2].x = 61;
    v->vertices[2].y = 61;
    v->vertices[2].color = 0;
    v->vertices[3].x = 39;
    v->vertices[3].y = 61;
    v->vertices[3].color = 0;
    v->vertices[4].x = 39;
    v->vertices[4].y = 39;
    v->vertices[4].color = 1;
    v->use_fgbg = 0;
    break;
  case 2:
    v->num = 5;
    v->vertices = (struct vector_vertex *)
                  safemalloc (sizeof(struct vector_vertex) * v->num);
    v->vertices[0].x = 49;
    v->vertices[0].y = 49;
    v->vertices[0].color = 1;
    v->vertices[1].x = 51;
    v->vertices[1].y = 49;
    v->vertices[1].color = 1;
    v->vertices[2].x = 51;
    v->vertices[2].y = 51;
    v->vertices[2].color = 0;
    v->vertices[3].x = 49;
    v->vertices[3].y = 51;
    v->vertices[3].color = 0;
    v->vertices[4].x = 49;
    v->vertices[4].y = 49;
    v->vertices[4].color = 1;
    v->use_fgbg = 0;
    break;
  case 4:
    v->num = 5;
    v->vertices = (struct vector_vertex *)
                  safemalloc (sizeof(struct vector_vertex) * v->num);
    v->vertices[0].x = 36;
    v->vertices[0].y = 36;
    v->vertices[0].color = 1;
    v->vertices[1].x = 64;
    v->vertices[1].y = 36;
    v->vertices[1].color = 1;
    v->vertices[2].x = 64;
    v->vertices[2].y = 64;
    v->vertices[2].color = 0;
    v->vertices[3].x = 36;
    v->vertices[3].y = 64;
    v->vertices[3].color = 0;
    v->vertices[4].x = 36;
    v->vertices[4].y = 36;
    v->vertices[4].color = 1;
    v->use_fgbg = 0;
    break;
  }
}

/***********************************************************************
 *
 *  LoadDefaultButton -- loads default button # into button structure
 *		assumes associated button memory is already free
 *
 ************************************************************************/
void LoadDefaultButton(DecorFace *df, int i)
{
  if (i & 1)
    LoadDefaultRightButton(df, i / 2);
  else
    LoadDefaultLeftButton(df, i / 2);
}

extern void FreeDecorFace(Display *dpy, DecorFace *df);

/***********************************************************************
 *
 *  ResetOrDestroyAllButtons -- resets all buttons to defaults
 *                              destroys existing buttons
 *
 ************************************************************************/
void DestroyAllButtons(FvwmDecor *decor)
{
  TitleButton *tbp;
  DecorFace *face;
  int i;
  int j;

  for (tbp = decor->buttons, i = 0; i < NUMBER_OF_BUTTONS; i++, tbp++)
  {
    for (j = 0, face = TB_STATE(*tbp); j < BS_MaxButtonState; j++, face++)
    {
      FreeDecorFace(dpy, face);
    }
  }

  return;
}

void ResetAllButtons(FvwmDecor *decor)
{
  TitleButton *tbp;
  DecorFace *face;
  int i;
  int j;

  for (tbp = decor->buttons, i = 0; i < NUMBER_OF_BUTTONS; i++, tbp++)
  {
    memset(&TB_FLAGS(*tbp), 0, sizeof(TB_FLAGS(*tbp)));
    TB_JUSTIFICATION(*tbp) = JUST_CENTER;
    for (face = TB_STATE(*tbp), j = 0; j < BS_MaxButtonState; j++, face++)
    {
      LoadDefaultButton(face, i);
    }
  }

  /* standard MWM decoration hint assignments ([EMAIL PROTECTED])
     [Menu]  - Title Bar - [Minimize] [Maximize] */
  TB_MWM_DECOR_FLAGS(decor->buttons[0]) |= MWM_DECOR_MENU;
  TB_MWM_DECOR_FLAGS(decor->buttons[1]) |= MWM_DECOR_MAXIMIZE;
  TB_MWM_DECOR_FLAGS(decor->buttons[3]) |= MWM_DECOR_MINIMIZE;
}

/***********************************************************************
 *
 *  Procedure:
 *	CreateGCs - open fonts and create all the needed GC's.  I only
 *		    want to do this once, hence the first_time flag.
 *
 ***********************************************************************/
static void CreateGCs(void)
{
  XGCValues gcv;
  unsigned long gcm;
  XColor c;

  /* create scratch GC's */
  gcm = GCFunction|GCLineWidth;
  gcv.function = GXcopy;
  gcv.line_width = 0;

  Scr.ScratchGC1 = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);
  Scr.ScratchGC2 = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);
  Scr.ScratchGC3 = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);
  Scr.ScratchGC4 = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);
  Scr.TitleGC = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);
  Scr.TransMaskGC = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);
  c.pixel = GetColor(DEFAULT_FORE_COLOR);
  Scr.ScratchMonoPixmap = XCreatePixmap(dpy, Scr.Root, 1, 1, 1);
  Scr.MonoGC = fvwmlib_XCreateGC(dpy, Scr.ScratchMonoPixmap, gcm, &gcv);
}

/***********************************************************************
 *
 *  Procedure:
 *	InitVariables - initialize fvwm variables
 *
 ************************************************************************/
static void InitVariables(void)
{
  FvwmContext = XUniqueContext();
  MenuContext = XUniqueContext();

  /* initialize some lists */
  Scr.AllBindings = NULL;

  Scr.functions = NULL;

  Menus.all = NULL;
  Menus.DefaultStyle = NULL;
  Menus.LastStyle = NULL;
  Scr.last_added_item.type = ADDED_NONE;

  Scr.DefaultIcon = NULL;

  Scr.DefaultColorset = -1;
  AllocColorset(0);
  /* set up colorset 0 so that if FvwmTheme fails to start any modules
   * using colorsets don't appear black on black */
  Colorset[0].fg = Scr.StdFore = GetColor(DEFAULT_FORE_COLOR);
  Colorset[0].bg = Scr.StdBack = GetColor(DEFAULT_BACK_COLOR);
  Colorset[0].hilite = Scr.StdHilite = GetHilite(Scr.StdBack);
  Colorset[0].shadow = Scr.StdShadow = GetShadow(Scr.StdBack);
  Scr.StdGC = 0;
  Scr.StdReliefGC = 0;
  Scr.StdShadowGC = 0;
  Scr.XorGC = 0;

  /* zero all flags */
  memset(&Scr.flags, 0, sizeof(Scr.flags));

  /* create graphics contexts */
  CreateGCs();

  if (Pdepth <= 8) {               /* if the color is limited */
    /* a number > than the builtin table! */
    Scr.ColorLimit = 255;
  }
  Scr.FvwmRoot.w = Scr.Root;
  Scr.FvwmRoot.next = 0;

  init_stack_and_layers();

  if (!XGetWindowAttributes(dpy,Scr.Root,&(Scr.FvwmRoot.attr)))
  {
    fvwm_msg(ERR, "InitVariables", "Could not get root window attributes");
    exit(0);
  }
  Scr.root_pushes = 0;
  Scr.fvwm_pushes = 0;
  Scr.pushed_window = &Scr.FvwmRoot;
  Scr.FvwmRoot.number_cmap_windows = 0;


  Scr.MyDisplayWidth = DisplayWidth(dpy, Scr.screen);
  Scr.MyDisplayHeight = DisplayHeight(dpy, Scr.screen);
  Scr.BusyCursor = BUSY_NONE;
  Scr.Hilite = NULL;
  /* this indicates that the root window was never entered since the startup of
   * fvwm. */
  Scr.Ungrabbed = NULL;

  Scr.DefaultFont.font = NULL;

  Scr.VxMax = 2*Scr.MyDisplayWidth;
  Scr.VyMax = 2*Scr.MyDisplayHeight;

  Scr.Vx = Scr.Vy = 0;

  Scr.SizeWindow = None;

  /* Sets the current desktop number to zero */
  /* Multiple desks are available even in non-virtual
   * compilations */
  Scr.CurrentDesk = 0;

  Scr.EdgeScrollX = Scr.EdgeScrollY = DEFAULT_EDGE_SCROLL;
  Scr.ScrollResistance = DEFAULT_SCROLL_RESISTANCE;
  Scr.MoveResistance = DEFAULT_MOVE_RESISTANCE;
  Scr.XiMoveResistance = DEFAULT_XIMOVE_RESISTANCE;
  Scr.SnapAttraction = DEFAULT_SNAP_ATTRACTION;
  Scr.SnapMode = DEFAULT_SNAP_ATTRACTION_MODE;
  Scr.SnapGridX = DEFAULT_SNAP_GRID_X;
  Scr.SnapGridY = DEFAULT_SNAP_GRID_Y;
  Scr.OpaqueSize = DEFAULT_OPAQUE_MOVE_SIZE;
  Scr.MoveThreshold = DEFAULT_MOVE_THRESHOLD;
  /* ClickTime is set to the positive value upon entering the event loop. */
  Scr.ClickTime = -DEFAULT_CLICKTIME;
  Scr.ColormapFocus = COLORMAP_FOLLOWS_MOUSE;

  /* set major operating modes */
  Scr.NumBoxes = 0;

  Scr.randomx = Scr.randomy = 0;
  Scr.buttons2grab = 0;

  InitFvwmDecor(&Scr.DefaultDecor);
#ifdef USEDECOR
  Scr.DefaultDecor.tag = "Default";
#endif

  /* Initialize RaiseHackNeeded by identifying X servers
     possibly running under NT. This is probably not an
     ideal solution, since eg NCD also produces X servers
     which do not run under NT.

     "Hummingbird Communications Ltd."
        is the ServerVendor string of the Exceed X server under NT,

     "Network Computing Devices Inc."
        is the ServerVendor string of the PCXware X server under Windows.

     "WRQ, Inc."
        is the ServerVendor string of the Reflection X server under Windows.
  */
  Scr.bo.RaiseHackNeeded =
    (strcmp (ServerVendor (dpy), "Hummingbird Communications Ltd.") == 0) ||
    (strcmp (ServerVendor (dpy), "Network Computing Devices Inc.") == 0) ||
    (strcmp (ServerVendor (dpy), "WRQ, Inc.") == 0);

  Scr.bo.ModalityIsEvil = 0;
  Scr.bo.DisableConfigureNotify = 0;
  Scr.bo.InstallRootCmap = 0;
  Scr.bo.FlickeringQtDialogsWorkaround = 1;

  Scr.gs.EmulateMWM = DEFAULT_EMULATE_MWM;
  Scr.gs.EmulateWIN = DEFAULT_EMULATE_WIN;
  Scr.gs.use_active_down_buttons = DEFAULT_USE_ACTIVE_DOWN_BUTTONS;
  Scr.gs.use_inactive_buttons = DEFAULT_USE_INACTIVE_BUTTONS;
  /* Not the right place for this, should only be called once somewhere .. */

  return;
}

/***********************************************************************
 *
 *  Procedure:
 *	Done - tells FVWM to clean up and exit
 *
 ***********************************************************************
 */
static RETSIGTYPE
SigDone(int sig)
{
  fvwmRunState = FVWM_DONE;

  /*
   * This function might not return - it could "long-jump"
   * right out, so we need to do everything we need to do
   * BEFORE we call it ...
   */
  fvwmSetTerminate(sig);
}

/* if restart is true, command must not be NULL... */
void Done(int restart, char *command)
{
  const char *exitFuncName;

  /* XFree freeze hack */
  XUngrabPointer(dpy, CurrentTime);
  XUngrabKeyboard(dpy, CurrentTime);
  XUngrabServer(dpy);

  if (!restart)
  {
    MoveViewport(0,0,False);
  }

  /* migo (03/Jul/1999): execute [Session]ExitFunction */
  exitFuncName = getInitFunctionName(2);
  if (FindFunction(exitFuncName))
  {
    char *action = safestrdup(CatString2("Function ", exitFuncName));
    old_execute_function(action, NULL, &Event, C_ROOT, 1, 0, NULL);
    free(action);
  }

  if (!restart)
  {
    Reborder();
  }

  if (restart)
  {
    Bool doPreserveState = True;
    SaveDesktopState();

    if (command)
    {
      while (isspace(command[0])) command++;
      if (strncmp(command, "--dont-preserve-state", 21) == 0)
      {
        doPreserveState = False;
        command += 21;
        while (isspace(command[0])) command++;
      }
    }
    if (command[0] == '\0')
      command = NULL; /* native restart */

    /* won't return under SM on Restart without parameters */
    RestartInSession(restart_state_filename, command == NULL, doPreserveState);

    /*
      RBW - 06/08/1999 - without this, windows will wander to other pages on
      a Restart/Recapture because Restart gets the window position information
      out of sync. There may be a better way to do this (i.e., adjust the
      Restart code), but this works for now.
    */
    MoveViewport(0,0,False);
    Reborder();

    /* Really make sure that the connection is closed and cleared! */
    CloseICCCM2();
    catch_exit();
    XCloseDisplay(dpy);
    dpy = NULL;

    /* really need to destroy all windows, explicitly,
     * not sleep, but this is adequate for now */
    sleep(1);

    if (command)
    {
#define MAX_ARG_SIZE 25
      char *my_argv[MAX_ARG_SIZE];
      const char *errorMsg;
      int n = parseCommandArgs(command, my_argv, MAX_ARG_SIZE, &errorMsg);

      if (n <= 0)
      {
        fvwm_msg(ERR, "Done", "Restart command parsing error in (%s): [%s]",
          command, errorMsg);
      }
      else if (StrEquals(my_argv[0], "--pass-args"))
      {
        if (n != 2)
	{
          fvwm_msg(ERR, "Done",
		   "Restart --pass-args: single name expected. "
		   "(restarting '%s' instead)",
            g_argv[0]);

        }
	else
	{
          int i;
          my_argv[0] = my_argv[1];
          for (i = 1; i < g_argc && i < MAX_ARG_SIZE - 1; i++)
            my_argv[i] = g_argv[i];
          my_argv[i] = NULL;

          execvp(my_argv[0], my_argv);
          fvwm_msg(ERR, "Done",
		   "Call of '%s' failed! (restarting '%s' instead)",
            my_argv[0], g_argv[0]);
          perror("  system error description");
        }

      }
      else
      {
        /* Warn against an old 'Restart fvwm2' usage */
        if (n == 1 && strcmp(my_argv[0], "fvwm2") == 0)
        {
          fvwm_msg(WARN, "Done",
            "`Restart fvwm2' might not do what you want, see the man page.\n\t"
            "Use Restart without parameters if you mean to restart the same WM."
          );
        }
        execvp(my_argv[0], my_argv);
        fvwm_msg(ERR, "Done", "Call of '%s' failed! (restarting '%s' instead)",
          my_argv[0], g_argv[0]);
        perror("  system error description");
      }
    }

    execvp(g_argv[0], g_argv);    /* that _should_ work */
    fvwm_msg(ERR, "Done", "Call of '%s' failed!", g_argv[0]);
    perror("  system error description");
  }
  else
  {
    CloseICCCM2();
    catch_exit();
    XCloseDisplay(dpy);
    dpy = NULL;
  }

  /* Close all my pipes */
  /* domivogt (15-Jan-2000): This must be done after calling CloseICCCM2()!
   * Otherwise fvwm ignores map requests while it still has
   * SubstructureRedirect selected on the root window ==> windows end up in
   * nirvana. This explicitly happened with windows unswallowed by FvwmButtons.
   */
  ClosePipes();

  exit(0);
}

int CatchRedirectError(Display *dpy, XErrorEvent *event)
{
  fvwm_msg(ERR,"CatchRedirectError","another WM is running");
  exit(1);
  /* to make insure happy */
  return 0;
}

/***********************************************************************
 *
 *  Procedure:
 *	CatchFatal - Shuts down if the server connection is lost
 *
 ************************************************************************/
int CatchFatal(Display *dpy)
{
  /* No action is taken because usually this action is caused by someone
     using "xlogout" to be able to switch between multiple window managers
     */
  ClosePipes();
  exit(1);
  /* to make insure happy */
  return 0;
}

/***********************************************************************
 *
 *  Procedure:
 *	FvwmErrorHandler - displays info on internal errors
 *
 ************************************************************************/
int FvwmErrorHandler(Display *dpy, XErrorEvent *event)
{
  extern int last_event_type;

  if (event->error_code == BadWindow)
  {
    bad_window = event->resourceid;
    return 0;
  }
  /* some errors are acceptable, mostly they're caused by
   * trying to update a lost  window or free'ing another modules colors */
  if(event->error_code == BadWindow ||
     event->request_code == X_GetGeometry ||
     event->error_code == BadDrawable ||
     event->request_code == X_ConfigureWindow ||
     event->request_code == X_SetInputFocus||
     event->request_code == X_GrabButton ||
     event->request_code == X_ChangeWindowAttributes ||
     event->request_code == X_InstallColormap ||
     event->request_code == X_FreePixmap ||
     event->request_code == X_FreeColors)
    return 0;

  fvwm_msg(ERR,"FvwmErrorHandler","*** internal error ***");
  fvwm_msg(ERR,"FvwmErrorHandler","Request %d, Error %d, EventType: %d",
           event->request_code,
           event->error_code,
           last_event_type);
  return 0;
}

static void usage(void)
{
  fprintf(stderr,"\nFVWM version %s Usage:\n\n",VERSION);
  fprintf(stderr,"  %s [-d dpy] [-debug] [-f config_file] [-cmd config_cmd] [-s] [-version] [-h] [-replace] [-clientId id] [-restore file] [-visualId id] [-visual class]\n\n",g_argv[0]);
  exit(1);
}

/****************************************************************************
 *
 * Save Desktop State
 *
 ****************************************************************************/
void SaveDesktopState()
{
  FvwmWindow *t;
  unsigned long data[1];

  for (t = Scr.FvwmRoot.next; t != NULL; t = t->next)
  {
    data[0] = (unsigned long) t->Desk;
    XChangeProperty (dpy, t->w, _XA_WM_DESKTOP, _XA_WM_DESKTOP, 32,
                     PropModeReplace, (unsigned char *) data, 1);
  }

  data[0] = (unsigned long) Scr.CurrentDesk;
  XChangeProperty (dpy, Scr.Root, _XA_WM_DESKTOP, _XA_WM_DESKTOP, 32,
                   PropModeReplace, (unsigned char *) data, 1);

  XSync(dpy, 0);
}


void SetMWM_INFO(Window window)
{
  struct mwminfo
  {
    long flags;
    Window win;
  }  motif_wm_info;
  static char set_yorn='n';

  if(set_yorn=='y') {
    return;
  }

  if (Scr.bo.ModalityIsEvil)
  {
    /* Set Motif WM_INFO atom to make motif relinquish
     * broken handling of modal dialogs */
    motif_wm_info.flags = 2;
    motif_wm_info.win = window;
    XChangeProperty(dpy,Scr.Root,_XA_MOTIF_WM,_XA_MOTIF_WM,32,
		    PropModeReplace,(unsigned char *)&motif_wm_info,2);
    set_yorn='y';
  }
}


/*
 * parseCommandArgs - parses a given command string into a given limited
 * argument array suitable for execv*. The parsing is similar to shell's.
 * Returns:
 *   positive number of parsed arguments - on success,
 *   0 - on empty command (only spaces),
 *   negative - on no command or parsing error.
 *
 * Any character can be quoted with a backslash (even inside single quotes).
 * Every command argument is separated by a space/tab/new-line from both sizes
 * or is at the start/end of the command. Sequential spaces are ignored.
 * An argument can be enclosed into single quotes (no further expanding)
 * or double quotes (expending environmental variables $VAR or ${VAR}).
 * The character '~' is expanded into user home directory (if not in quotes).
 *
 * In the current implementation, parsed arguments are stored in one
 * large static string pointed by returned argv[0], so they will be lost
 * on the next function call. This can be changed using dynamic allocation,
 * in this case the caller must free the string pointed by argv[0].
 */
static int parseCommandArgs(
  const char *command, char **argv, int maxArgc, const char **errorMsg)
{
  /* It is impossible to guess the exact length because of expanding */
#define MAX_TOTAL_ARG_LEN 256
  /* char *argString = safemalloc(MAX_TOTAL_ARG_LEN); */
  static char argString[MAX_TOTAL_ARG_LEN];
  int totalArgLen = 0;
  int errorCode = 0;
  int argc;
  char *aptr = argString;
  const char *cptr = command;
#define theChar (*cptr)
#define advChar (cptr++)
#define topChar (*cptr     == '\\'? *(cptr+1): *cptr)
#define popChar (*(cptr++) == '\\'? *(cptr++): *(cptr-1))
#define canAddArgChar (totalArgLen < MAX_TOTAL_ARG_LEN-1)
#define addArgChar(ch) (++totalArgLen, *(aptr++) = ch)
#define canAddArgStr(str) (totalArgLen < MAX_TOTAL_ARG_LEN-strlen(str))
#define addArgStr(str) {const char *tmp = str; while (*tmp) { addArgChar(*(tmp++)); }}

  *errorMsg = "";
  if (!command) { *errorMsg = "No command"; return -1; }
  for (argc = 0; argc < maxArgc - 1; argc++) {
    int sQuote = 0;
    argv[argc] = aptr;
    while (isspace(theChar)) advChar;
    if (theChar == '\0') break;
    while ((sQuote || !isspace(theChar)) && theChar != '\0' && canAddArgChar) {
      if (theChar == '"') {
        if (sQuote) { sQuote = 0; }
        else { sQuote = 1; }
        advChar;
      } else if (!sQuote && theChar == '\'') {
        advChar;
        while (theChar != '\'' && theChar != '\0' && canAddArgChar) {
          addArgChar(popChar);
        }
        if (theChar == '\'') advChar;
        else if (!canAddArgChar) break;
        else { *errorMsg = "No closing single quote"; errorCode = -3; break; }
      } else if (!sQuote && theChar == '~') {
        if (!canAddArgStr(home_dir)) break;
        addArgStr(home_dir);
        advChar;
      } else if (theChar == '$') {
        int beg, len;
        const char *str = getFirstEnv(cptr, &beg, &len);
        if (!str || beg) { addArgChar(theChar); advChar; continue; }
        if (!canAddArgStr(str)) { break; }
        addArgStr(str);
        cptr += len;
      } else {
        if (addArgChar(popChar) == '\0') break;
      }
    }
    if (*(aptr-1) == '\0')
    {
      *errorMsg = "Unexpected last backslash";
      errorCode = -2;
      break;
    }
    if (errorCode)
      break;
    if (theChar == '~' || theChar == '$' || !canAddArgChar)
    {
      *errorMsg = "The command is too long";
      errorCode = -argc - 100;
      break;
    }
    if (sQuote)
    {
      *errorMsg = "No closing double quote";
      errorCode = -4;
      break;
    }
    addArgChar('\0');
  }

#undef theChar
#undef advChar
#undef topChar
#undef popChar
#undef canAddArgChar
#undef addArgChar
#undef canAddArgStr
#undef addArgStr
  argv[argc] = NULL;
  if (argc == 0 && !errorCode) *errorMsg = "Void command";
  return errorCode? errorCode: argc;
}


/*
 * setInitFunctionName - sets one of the init, restart or exit function names
 * getInitFunctionName - gets one of the init, restart or exit function names
 *
 * First parameter defines a function type: 0 - init, 1 - restart, 2 - exit.
 */
static const char *initFunctionNames[4] = {
  "InitFunction", "RestartFunction", "ExitFunction", "Nop"
};

void setInitFunctionName(int n, const char *name)
{
  initFunctionNames[n >= 0 && n < 3? n: 3] = name;
}

const char *getInitFunctionName(int n)
{
  return initFunctionNames[n >= 0 && n < 3? n: 3];
}


static void setVersionInfo(void)
{
  char version_str[256];
  char support_str[512] = "";
  int support_len;

  /* Set version information string */
  sprintf(version_str, "FVWM version %s compiled on %s at %s",
          VERSION, __DATE__, __TIME__);
  Fvwm_VersionInfo = safestrdup(version_str);

#ifdef HAVE_READLINE
  strcat(support_str, " ReadLine,");
#endif
#ifdef HAVE_RPLAY
  strcat(support_str, " RPlay,");
#endif
#ifdef HAVE_STROKE
  strcat(support_str, " Stroke,");
#endif
#ifdef XPM
  strcat(support_str, " XPM,");
#endif
#ifdef GNOME
  strcat(support_str, " GNOME WM hints,");
#endif
  if (FHaveShapeExtension)
    strcat(support_str, " Shape,");
#ifdef SESSION
  strcat(support_str, " SM,");
#endif
#ifdef I18N_MB
  strcat(support_str, " Multibyte,");
#endif
#ifdef HAVE_XINERAMA
  strcat(support_str, " Xinerama,");
#endif

  support_len = strlen(support_str);
  if (support_len > 0)
  {
    /* strip last comma */
    support_str[support_len - 1] = '\0';
    Fvwm_SupportInfo = safestrdup(CatString2("with support for:", support_str));
  }
  else
    Fvwm_SupportInfo = "with no optional feature support";
}
/* 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 all original code
 * by Rob Nation
 * Copyright 1993, Robert Nation
 *     You may use this code for any purpose, as long as the original
 *     copyright remains in the source code and all documentation
 ****************************************************************************/

#include "config.h"

#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <X11/keysym.h>

#include "libs/fvwmlib.h"
#include "libs/fvwmsignal.h"
#include "libs/FShape.h"
#include "fvwm.h"
#include "externs.h"
#include "libs/Colorset.h"
#include "bindings.h"
#include "misc.h"
#include "cursor.h"
#include "functions.h"
#include "commands.h"
#include "screen.h"
#include "defaults.h"
#include "builtins.h"
#include "module_interface.h"
#include "borders.h"
#include "geometry.h"
#include "events.h"
#include "gnome.h"
#include "icons.h"
#include "menus.h"
#include "virtual.h"
#include "decorations.h"
#include "add_window.h"
#include "update.h"
#include "style.h"
#include "focus.h"
#include "stack.h"
#include "move_resize.h"
#ifdef HAVE_STROKE
#include "stroke.h"
#endif /* HAVE_STROKE */

static char *ReadTitleButton(
    char *s, TitleButton *tb, Boolean append, int button);
static void DestroyFvwmDecor(FvwmDecor *decor);

extern float rgpctMovementDefault[32];
extern int cpctMovementDefault;
extern int cmsDelayDefault;

char *ModulePath = FVWM_MODULEDIR;
int moduleTimeout = DEFAULT_MODULE_TIMEOUT;
static char *exec_shell_name="/bin/sh";
/* button state strings must match the enumerated states */
static char *button_states[BS_MaxButtonState + 1] =
{
  "ActiveUp",
  "ActiveDown",
  "Inactive",
  "ToggledActiveUp",
  "ToggledActiveDown",
  "ToggledInactive",
  NULL
};





/***********************************************************************
 *
 *  WindowShade -- shades or unshades a window ([EMAIL PROTECTED])
 *
 *  Args: 1 -- shade, 2 -- unshade  No Arg: toggle
 *
 ***********************************************************************
 *
 *  Animation added.
 *     Based on code from AfterStep-1.0 (CT: [EMAIL PROTECTED])
 *     Additional fixes by Tomas Ogren <[EMAIL PROTECTED]>
 *
 *  Builtin function: WindowShade
 ***********************************************************************/
void CMD_WindowShade(F_CMD_ARGS)
{
  int bwl;
  int bwr;
  int bw;
  int bht;
  int bhb;
  int bh;
  int cw;
  int ch;
  int step = 1;
  int toggle;
  int grav = NorthWestGravity;
  int client_grav = NorthWestGravity;
  int move_parent_too = False;
  rectangle frame_g;
  rectangle parent_g;
  rectangle shape_g;
  rectangle diff;
  rectangle pdiff;
  rectangle sdiff;
  rectangle big_g;
  rectangle small_g;
  Bool do_scroll;
  FvwmWindow *sf;
  static Window shape_w = None;

  if (DeferExecution(eventp,&w,&tmp_win,&context, CRS_SELECT,ButtonRelease))
    return;
  if (tmp_win == NULL || IS_ICONIFIED(tmp_win))
    return;

  if (FShapesSupported && shape_w == None && tmp_win->wShaped)
  {
    XSetWindowAttributes attributes;
    unsigned long valuemask;

    valuemask = CWOverrideRedirect;
    attributes.override_redirect = True;
    shape_w = XCreateWindow(
      dpy, Scr.Root, -32768, -32768, 1, 1, 0, CopyFromParent,
      (unsigned int) CopyFromParent, CopyFromParent, valuemask, &attributes);
  }

  sf = get_focus_window();
  /* parse arguments */
  toggle = ParseToggleArgument(action, NULL, -1, 0);
  if (toggle == -1)
  {
    if (GetIntegerArguments(action, NULL, &toggle, 1) > 0)
    {
      if (toggle == 1)
        toggle = 1;
      else if (toggle == 2)
        toggle = 0;
      else
        toggle = -1;
    }
  }
  if (toggle == -1)
    toggle = (IS_SHADED(tmp_win)) ? 0 : 1;

  /* prepare some convenience variables */
  frame_g = tmp_win->frame_g;
  big_g = (IS_MAXIMIZED(tmp_win)) ? tmp_win->max_g : tmp_win->normal_g;
  get_relative_geometry(&big_g, &big_g);
  bwl = tmp_win->boundary_width;
  bwr = tmp_win->boundary_width;
  bw = bwl + bwr;
  bht = tmp_win->boundary_width + tmp_win->title_top_height;
  bhb = tmp_win->boundary_width +
    tmp_win->title_g.height - tmp_win->title_top_height;;
  bh = bht + bhb;
  cw = big_g.width - bw;
  ch = big_g.height - bh;
  do_scroll = !DO_SHRINK_WINDOWSHADE(tmp_win);
  /* calcuate the step size */
  if (tmp_win->shade_anim_steps > 0)
    step = big_g.height / tmp_win->shade_anim_steps;
  else if (tmp_win->shade_anim_steps < 0) /* if it's -ve it means pixels */
    step = -tmp_win->shade_anim_steps;
  if (step <= 0)  /* We don't want an endless loop, do we? */
    step = 1;
  if (tmp_win->shade_anim_steps)
  {
    grav = (HAS_BOTTOM_TITLE(tmp_win)) ? SouthEastGravity : NorthWestGravity;
    if (!do_scroll)
      client_grav = grav;
    else
      client_grav =
	(HAS_BOTTOM_TITLE(tmp_win)) ? NorthWestGravity : SouthEastGravity;
    set_decor_gravity(tmp_win, grav, grav, client_grav);
    move_parent_too = HAS_BOTTOM_TITLE(tmp_win);
  }

  if (IS_SHADED(tmp_win) && toggle == 0)
  {
    /* unshade window */
    SET_SHADED(tmp_win, 0);

    if (tmp_win->shade_anim_steps != 0)
    {
      XMoveResizeWindow(dpy, tmp_win->Parent, bwl, bht, cw, 1);
      XMoveWindow(dpy, tmp_win->w, 0,
		  (client_grav == SouthEastGravity) ? -ch + 1 : 0);
      XLowerWindow(dpy, tmp_win->decor_w);
      parent_g.x = bwl;
      parent_g.y = bht + (HAS_BOTTOM_TITLE(tmp_win) ? -step : 0);
      parent_g.width = cw;
      parent_g.height = 0;
      pdiff.x = 0;
      pdiff.y = 0;
      pdiff.width = 0;
      pdiff.height = step;
      if (client_grav == SouthEastGravity)
      {
	shape_g.x = bwl;
	shape_g.y = bht - ch;
	sdiff.x = 0;
	sdiff.y = step;
      }
      else
      {
	shape_g.x = bwl;
	shape_g.y = bht;
	sdiff.x = 0;
	sdiff.y = 0;
      }
      diff.x = 0;
      diff.y = HAS_BOTTOM_TITLE(tmp_win) ? -step : 0;
      diff.width = 0;
      diff.height = step;

      /* This causes a ConfigureNotify if the client size has changed while
       * the window was shaded. */
      XResizeWindow(dpy, tmp_win->w, cw, ch);
      /* make the decor window the full sze before the animation unveils it */
      XMoveResizeWindow(dpy, tmp_win->decor_w, 0, HAS_BOTTOM_TITLE(tmp_win)
        ? frame_g.height - big_g.height : 0, big_g.width, big_g.height);
      /* draw the border decoration iff backing store is on */
      if (Scr.use_backing_store != NotUseful)
      { /* eek! fvwm doesn't know the backing_store setting for windows */
        XWindowAttributes xwa;

        if (XGetWindowAttributes(dpy, tmp_win->decor_w, &xwa)
            && xwa.backing_store != NotUseful)
        {
          tmp_win->frame_g.width = big_g.width;
          tmp_win->frame_g.height = big_g.height;
          DrawDecorations(
	    tmp_win, DRAW_FRAME, sf == tmp_win, True, None);
        }
      }
      while (frame_g.height + diff.height < big_g.height)
      {
	parent_g.x += pdiff.x;
	parent_g.y += pdiff.y;
	parent_g.width += pdiff.width;
	parent_g.height += pdiff.height;
	shape_g.x += sdiff.x;
	shape_g.y += sdiff.y;
	frame_g.x += diff.x;
	frame_g.y += diff.y;
	frame_g.width += diff.width;
	frame_g.height += diff.height;
	if (FShapesSupported && tmp_win->wShaped && shape_w)
	{
	  FShapeCombineShape(
	    dpy, shape_w, FShapeBounding, shape_g.x, shape_g.y, tmp_win->w,
	    FShapeBounding, FShapeSet);
	  if (tmp_win->title_w)
	  {
	    XRectangle rect;

	    /* windows w/ titles */
	    rect.x = bwl;
	    rect.y = (HAS_BOTTOM_TITLE(tmp_win)) ? frame_g.height - bhb : 0;
	    rect.width = frame_g.width - bw;
	    rect.height = tmp_win->title_g.height;
	    FShapeCombineRectangles(
	      dpy, shape_w, FShapeBounding, 0, 0, &rect, 1, FShapeUnion,
	      Unsorted);
	  }
	}
	if (move_parent_too)
	{
	  if (FShapesSupported && tmp_win->wShaped && shape_w)
	  {
	    FShapeCombineShape(
	      dpy, tmp_win->frame, FShapeBounding, 0, 0, shape_w,
	      FShapeBounding, FShapeUnion);
	  }
	  XMoveResizeWindow(
	    dpy, tmp_win->Parent, parent_g.x, parent_g.y, parent_g.width,
	    parent_g.height);
	}
	else
	{
	  XResizeWindow(dpy, tmp_win->Parent, parent_g.width, parent_g.height);
	}
	XMoveResizeWindow(
	  dpy, tmp_win->frame, frame_g.x, frame_g.y, frame_g.width,
	  frame_g.height);
	if (FShapesSupported && tmp_win->wShaped && shape_w)
	{
	  FShapeCombineShape(
	    dpy, tmp_win->frame, FShapeBounding, 0, 0, shape_w, FShapeBounding,
	    FShapeSet);
	}
        FlushAllMessageQueues();
        XSync(dpy, 0);
      }
      if (client_grav == SouthEastGravity && move_parent_too)
      {
	/* We must finish above loop to the very end for this special case.
	 * Otherwise there is a visible jump of the client window. */
	XMoveResizeWindow(
	  dpy, tmp_win->Parent, parent_g.x,
	  bht - (big_g.height - frame_g.height), parent_g.width, ch);
	XMoveResizeWindow(
	  dpy, tmp_win->frame, big_g.x, big_g.y, big_g.width, big_g.height);
      }
      else
      {
	XMoveWindow(dpy, tmp_win->w, 0, 0);
      }
      if (sf)
      {
	focus_grab_buttons(sf, True);
      }
    }
    else
    {
      XMoveResizeWindow(dpy, tmp_win->w, 0, 0, cw, ch);
      if (HAS_BOTTOM_TITLE(tmp_win))
      {
	XMoveResizeWindow(dpy, tmp_win->Parent, bwl, bht - ch + 1, cw, ch);
	set_decor_gravity(
	  tmp_win, SouthEastGravity, SouthEastGravity, NorthWestGravity);
      }
      else
      {
	XMoveResizeWindow(dpy, tmp_win->Parent, bwl, bht, cw, ch);
	set_decor_gravity(
	  tmp_win, NorthWestGravity, NorthWestGravity, NorthWestGravity);
      }
      XLowerWindow(dpy, tmp_win->decor_w);
      XMoveResizeWindow(
	dpy, tmp_win->frame, big_g.x, big_g.y, big_g.width, big_g.height);
    }
    set_decor_gravity(
      tmp_win, NorthWestGravity, NorthWestGravity, NorthWestGravity);
    tmp_win->frame_g.height = big_g.height + 1;
    SetupFrame(
      tmp_win, big_g.x, big_g.y, big_g.width, big_g.height, True);
    tmp_win->frame_g = big_g;
    update_absolute_geometry(tmp_win);
    BroadcastConfig(M_CONFIGURE_WINDOW, tmp_win);
    BroadcastPacket(M_DEWINDOWSHADE, 3, tmp_win->w, tmp_win->frame,
                    (unsigned long)tmp_win);
  }
  else if (!IS_SHADED(tmp_win) && toggle == 1)
  {
    /* shade window */
    SET_SHADED(tmp_win, 1);
    get_shaded_geometry(tmp_win, &small_g, &big_g);
    if (small_g.width < 1)
    {
      small_g.width = 1;
    }
    if (small_g.height < 1)
    {
      small_g.height = 1;
    }
    if (tmp_win->shade_anim_steps != 0)
    {
      parent_g.x = bwl;
      parent_g.y = bht;
      parent_g.width = cw;
      parent_g.height = frame_g.height - bh;
      pdiff.x = 0;
      pdiff.y = 0;
      pdiff.width = 0;
      pdiff.height = -step;
      shape_g = parent_g;
      sdiff.x = 0;
      sdiff.y = (client_grav == SouthEastGravity) ? -step : 0;
      diff.x = 0;
      diff.y = HAS_BOTTOM_TITLE(tmp_win) ? step : 0;
      diff.width = 0;
      diff.height = -step;

      while (frame_g.height + diff.height > small_g.height)
      {
	parent_g.x += pdiff.x;
	parent_g.y += pdiff.y;
	parent_g.width += pdiff.width;
	parent_g.height += pdiff.height;
	shape_g.x += sdiff.x;
	shape_g.y += sdiff.y;
	frame_g.x += diff.x;
	frame_g.y += diff.y;
	frame_g.width += diff.width;
	frame_g.height += diff.height;
	if (FShapesSupported && tmp_win->wShaped && shape_w)
	{
	  FShapeCombineShape(
	    dpy, shape_w, FShapeBounding, shape_g.x, shape_g.y, tmp_win->w,
	    FShapeBounding, FShapeSet);
	  if (tmp_win->title_w)
	  {
	    XRectangle rect;

	    /* windows w/ titles */
	    rect.x = bwl;
	    rect.y = (HAS_BOTTOM_TITLE(tmp_win)) ? frame_g.height - bhb : 0;
	    rect.width = big_g.width - bw;
	    rect.height = tmp_win->title_g.height;
	    FShapeCombineRectangles(
	      dpy, shape_w, FShapeBounding, 0, 0, &rect, 1, FShapeUnion,
	      Unsorted);
	  }
	}
	if (move_parent_too)
	{
	  if (FShapesSupported && tmp_win->wShaped && shape_w)
	  {
	    FShapeCombineShape(
	      dpy, tmp_win->frame, FShapeBounding, 0, 0, shape_w,
	      FShapeBounding, FShapeUnion);
	  }
	  XMoveResizeWindow(
	    dpy, tmp_win->frame, frame_g.x, frame_g.y, frame_g.width,
	    frame_g.height);
	  XMoveResizeWindow(
	    dpy, tmp_win->Parent, parent_g.x, parent_g.y, parent_g.width,
	    parent_g.height);
	}
	else
	{
	  XResizeWindow(dpy, tmp_win->Parent, parent_g.width, parent_g.height);
	  XMoveResizeWindow(
	    dpy, tmp_win->frame, frame_g.x, frame_g.y, frame_g.width,
	    frame_g.height);
	}
	if (FShapesSupported && tmp_win->wShaped && shape_w)
	{
	  FShapeCombineShape(
	    dpy, tmp_win->frame, FShapeBounding, 0, 0, shape_w, FShapeBounding,
	    FShapeSet);
	}
        FlushAllMessageQueues();
        XSync(dpy, 0);
      }
    }
    /* All this stuff is necessary to prevent flickering */
    set_decor_gravity(tmp_win, grav, UnmapGravity, client_grav);
    XMoveResizeWindow(
      dpy, tmp_win->frame, small_g.x, small_g.y, small_g.width,
      small_g.height);
    XResizeWindow(dpy, tmp_win->Parent, cw, 1);
    XMoveWindow(dpy, tmp_win->decor_w, 0, 0);
    XRaiseWindow(dpy, tmp_win->decor_w);
    XMapWindow(dpy, tmp_win->Parent);

    if (HAS_BOTTOM_TITLE(tmp_win))
    {
      XMoveWindow(dpy, tmp_win->w, 0, 1 - ch);
    }
    else
    {
      XMoveWindow(dpy, tmp_win->w, 0, 0);
    }

    /* domivogt (28-Dec-1999): For some reason the XMoveResize() on the frame
     * window removes the input focus from the client window.  I have no idea
     * why, but if we explicitly restore the focus here everything works fine.
     */
    if (sf == tmp_win)
      FOCUS_SET(tmp_win->w);
    set_decor_gravity(
      tmp_win, NorthWestGravity, NorthWestGravity, NorthWestGravity);
    /* Finally let SetupFrame take care of the window */
    SetupFrame(
      tmp_win, small_g.x, small_g.y, small_g.width, small_g.height, False);
    tmp_win->frame_g = small_g;
    update_absolute_geometry(tmp_win);
    BroadcastConfig(M_CONFIGURE_WINDOW, tmp_win);
    BroadcastPacket(
      M_WINDOWSHADE, 3, tmp_win->w, tmp_win->frame, (unsigned long)tmp_win);
  }
  DrawDecorations(
    tmp_win, DRAW_FRAME | DRAW_BUTTONS, (Scr.Hilite == tmp_win), True, None);
  FlushAllMessageQueues();
  XSync(dpy, 0);

  GNOME_SetHints(tmp_win);
  GNOME_SetWinArea(tmp_win);
}

void CMD_Beep(F_CMD_ARGS)
{
  XBell(dpy, 0);
}

void CMD_Plus(F_CMD_ARGS)
{
  if (Scr.last_added_item.type == ADDED_MENU)
    add_another_menu_item(action);
  else if (Scr.last_added_item.type == ADDED_FUNCTION)
    AddToFunction(Scr.last_added_item.item, action);
#ifdef USEDECOR
  else if (Scr.last_added_item.type == ADDED_DECOR) {
    FvwmDecor *tmp = &Scr.DefaultDecor;
    for (; tmp; tmp = tmp->next)
      if (tmp == Scr.last_added_item.item)
	break;
    if (!tmp)
      return;
    AddToDecor(tmp, action);
  }
#endif /* USEDECOR */
}

void CMD_DestroyFunc(F_CMD_ARGS)
{
  FvwmFunction *func;
  char *token;

  GetNextToken(action,&token);
  if (!token)
    return;
  func = FindFunction(token);
  free(token);
  if (!func)
    return;
  if (Scr.last_added_item.type == ADDED_FUNCTION)
    set_last_added_item(ADDED_NONE, NULL);
  DestroyFunction(func);

  return;
}

void CMD_AddToFunc(F_CMD_ARGS)
{
  FvwmFunction *func;
  char *token;

  action = GetNextToken(action,&token);
  if (!token)
    return;
  func = FindFunction(token);
  if(func == NULL)
    func = NewFvwmFunction(token);

  /* Set + state to last function */
  set_last_added_item(ADDED_FUNCTION, func);

  free(token);
  AddToFunction(func, action);

  return;
}


void CMD_Nop(F_CMD_ARGS)
{
}

void CMD_EscapeFunc(F_CMD_ARGS)
{
}


void CMD_CursorMove(F_CMD_ARGS)
{
  int x = 0, y = 0;
  int val1, val2, val1_unit, val2_unit;
  int virtual_x, virtual_y;
  int pan_x, pan_y;
  int x_pages, y_pages;

  if (GetTwoArguments(action, &val1, &val2, &val1_unit, &val2_unit) != 2)
  {
    fvwm_msg(ERR, "movecursor", "CursorMove needs 2 arguments");
    return;
  }

  XQueryPointer( dpy, Scr.Root, &JunkRoot, &JunkChild,
                 &x, &y, &JunkX, &JunkY, &JunkMask);

  x = x + val1 * val1_unit / 100;
  y = y + val2 * val2_unit / 100;

  virtual_x = Scr.Vx;
  virtual_y = Scr.Vy;
  if (x >= 0)
    x_pages = x / Scr.MyDisplayWidth;
  else
    x_pages = ((x + 1) / Scr.MyDisplayWidth) - 1;
  virtual_x += x_pages * Scr.MyDisplayWidth;
  x -= x_pages * Scr.MyDisplayWidth;
  if (virtual_x < 0)
  {
    x += virtual_x;
    virtual_x = 0;
  }
  else if (virtual_x > Scr.VxMax)
  {
    x += virtual_x - Scr.VxMax;
    virtual_x = Scr.VxMax;
  }

  if (y >= 0)
    y_pages = y / Scr.MyDisplayHeight;
  else
    y_pages = ((y + 1) / Scr.MyDisplayHeight) - 1;
  virtual_y += y_pages * Scr.MyDisplayHeight;
  y -= y_pages * Scr.MyDisplayHeight;
  if (virtual_y < 0)
  {
    y += virtual_y;
    virtual_y = 0;
  }
  else if (virtual_y > Scr.VyMax)
  {
    y += virtual_y - Scr.VyMax;
    virtual_y = Scr.VyMax;
  }
  if (virtual_x != Scr.Vx || virtual_y != Scr.Vy)
    MoveViewport(virtual_x, virtual_y, True);
  pan_x = (Scr.EdgeScrollX != 0) ? 2 : 0;
  pan_y = (Scr.EdgeScrollY != 0) ? 2 : 0;
  /* prevent paging if EdgeScroll is active */
  if (x >= Scr.MyDisplayWidth - pan_x)
    x = Scr.MyDisplayWidth - pan_x -1;
  else if (x < pan_x)
    x = pan_x;
  if (y >= Scr.MyDisplayHeight - pan_y)
    y = Scr.MyDisplayHeight - pan_y - 1;
  else if (y < pan_y)
    y = pan_y;

  XWarpPointer(dpy, None, Scr.Root, 0, 0, Scr.MyDisplayWidth,
	       Scr.MyDisplayHeight, x, y);
  return;
}


void CMD_Destroy(F_CMD_ARGS)
{
  if (DeferExecution(eventp,&w,&tmp_win,&context, CRS_DESTROY, ButtonRelease))
    return;

  if(check_if_function_allowed(F_DESTROY,tmp_win,True,NULL) == 0)
  {
    XBell(dpy, 0);
    return;
  }

  if (XGetGeometry(dpy, tmp_win->w, &JunkRoot, &JunkX, &JunkY,
		   &JunkWidth, &JunkHeight, &JunkBW, &JunkDepth) == 0)
  {
    destroy_window(tmp_win);
  }
  else
    XKillClient(dpy, tmp_win->w);
  XSync(dpy,0);
}

void CMD_Delete(F_CMD_ARGS)
{
  if (DeferExecution(eventp,&w,&tmp_win,&context, CRS_DESTROY,ButtonRelease))
    return;

  if(check_if_function_allowed(F_DELETE,tmp_win,True,NULL) == 0)
  {
    XBell(dpy, 0);
    return;
  }

  if (WM_DELETES_WINDOW(tmp_win))
  {
    send_clientmessage (dpy, tmp_win->w, _XA_WM_DELETE_WINDOW, CurrentTime);
    return;
  }
  else
  {
    XBell (dpy, 0);
  }
  XSync(dpy,0);
}

void CMD_Close(F_CMD_ARGS)
{
  if (DeferExecution(eventp,&w,&tmp_win,&context, CRS_DESTROY,ButtonRelease))
    return;

  if(check_if_function_allowed(F_CLOSE,tmp_win,True,NULL) == 0)
  {
    XBell(dpy, 0);
    return;
  }

  if (WM_DELETES_WINDOW(tmp_win))
  {
    send_clientmessage (dpy, tmp_win->w, _XA_WM_DELETE_WINDOW, CurrentTime);
    return;
  }
  else if (XGetGeometry(dpy, tmp_win->w, &JunkRoot, &JunkX, &JunkY,
			&JunkWidth, &JunkHeight, &JunkBW, &JunkDepth) == 0)
  {
    destroy_window(tmp_win);
  }
  else
  {
    XKillClient(dpy, tmp_win->w);
  }
  XSync(dpy,0);
}

void CMD_Restart(F_CMD_ARGS)
{
  Done(1, action);
}

void CMD_ExecUseShell(F_CMD_ARGS)
{
  char *arg=NULL;
  static char shell_set = 0;

  if (shell_set)
    free(exec_shell_name);
  shell_set = 1;
  action = GetNextToken(action,&arg);
  if (arg) /* specific shell was specified */
  {
    exec_shell_name = arg;
  }
  else /* no arg, so use $SHELL -- not working??? */
  {
    if (getenv("SHELL"))
      exec_shell_name = safestrdup(getenv("SHELL"));
    else
      /* if $SHELL not set, use default */
      exec_shell_name = safestrdup("/bin/sh");
  }
}

void CMD_Exec(F_CMD_ARGS)
{
  char *cmd=NULL;

  /* if it doesn't already have an 'exec' as the first word, add that
   * to keep down number of procs started */
  /* need to parse string better to do this right though, so not doing this
     for now... */
#if 0
  if (strncasecmp(action,"exec",4)!=0)
  {
    cmd = (char *)safemalloc(strlen(action)+6);
    strcpy(cmd,"exec ");
    strcat(cmd,action);
  }
  else
#endif
  {
    cmd = safestrdup(action);
  }
  if (!cmd)
    return;
  /* Use to grab the pointer here, but the fork guarantees that
   * we wont be held up waiting for the function to finish,
   * so the pointer-gram just caused needless delay and flashing
   * on the screen */
  /* Thought I'd try vfork and _exit() instead of regular fork().
   * The man page says that its better. */
  /* Not everyone has vfork! */
  if (!(fork())) /* child process */
  {
    if (execl(exec_shell_name, exec_shell_name, "-c", cmd, NULL)==-1)
    {
      fvwm_msg(ERR,"exec_function","execl failed (%s)",strerror(errno));
      exit(100);
    }
  }
  free(cmd);
  return;
}

static void refresh_window(Window w)
{
  XSetWindowAttributes attributes;
  unsigned long valuemask;

  valuemask = CWOverrideRedirect | CWBackingStore | CWSaveUnder | CWBackPixmap;
  attributes.override_redirect = True;
  attributes.save_under = False;
  attributes.background_pixmap = None;
  attributes.backing_store = NotUseful;
  w = XCreateWindow(dpy,
		    w,
		    0, 0,
		    (unsigned int) Scr.MyDisplayWidth,
		    (unsigned int) Scr.MyDisplayHeight,
		    (unsigned int) 0,
		    CopyFromParent, (unsigned int) CopyFromParent,
		    CopyFromParent, valuemask,
		    &attributes);
  XMapWindow(dpy, w);
  if (Scr.flags.do_need_window_update)
  {
    flush_window_updates();
  }
  XDestroyWindow(dpy, w);
  XSync(dpy, 0);
  usleep(1);
  XSync(dpy, 0);
  handle_all_expose();

  return;
}

void CMD_Refresh(F_CMD_ARGS)
{
  refresh_window(Scr.Root);
}

void CMD_RefreshWindow(F_CMD_ARGS)
{
  if (DeferExecution(eventp,&w,&tmp_win,&context,CRS_SELECT,ButtonRelease))
    return;
  refresh_window((context == C_ICON)? tmp_win->icon_w : tmp_win->frame);
}


void CMD_Wait(F_CMD_ARGS)
{
  Bool done = False;
  Bool redefine_cursor = False;
  Bool is_ungrabbed;
  char *escape;
  Window nonewin = None;
  extern FvwmWindow *Tmp_win;
  char *wait_string, *rest;
  FvwmWindow *s_Tmp_win = Tmp_win;

  /* try to get a single token */
  rest = GetNextToken(action, &wait_string);
  if (wait_string)
  {
    while (*rest && isspace((unsigned char)*rest))
      rest++;
    if (*rest)
    {
      int i;
      char *temp;

      /* nope, multiple tokens - try old syntax */

      /* strip leading and trailing whitespace */
      temp = action;
      while (*temp && isspace((unsigned char)*temp))
        temp++;
      wait_string = safestrdup(temp);
      for (i = strlen(wait_string) - 1; i >= 0 && isspace(wait_string[i]); i--)
      {
        wait_string[i] = 0;
      }
    }
  }
  else
  {
    wait_string = safestrdup("");
  }

  is_ungrabbed = UngrabEm(GRAB_NORMAL);
  while (!done && !isTerminated)
  {
    if (BUSY_WAIT & Scr.BusyCursor)
    {
      XDefineCursor(dpy, Scr.Root, Scr.FvwmCursors[CRS_WAIT]);
      redefine_cursor = True;
    }
    if (My_XNextEvent(dpy, &Event))
    {
      DispatchEvent(False);
      if(Event.type == MapNotify)
      {
        if (!*wait_string)
          done = True;
        if((Tmp_win)&&(matchWildcards(wait_string, Tmp_win->name)==True))
          done = True;
        else if((Tmp_win)&&(Tmp_win->class.res_class)&&
           (matchWildcards(wait_string, Tmp_win->class.res_class)==True))
          done = True;
        else if((Tmp_win)&&(Tmp_win->class.res_name)&&
           (matchWildcards(wait_string, Tmp_win->class.res_name)==True))
          done = True;
      }
      else if (Event.type == KeyPress)
      {
        escape = CheckBinding(Scr.AllBindings,
                              STROKE_ARG(0)
                              Event.xkey.keycode,
                              Event.xkey.state, GetUnusedModifiers(),
                              GetContext(Tmp_win,&Event,&nonewin),
                              KEY_BINDING);
        if (escape != NULL)
        {
	  if (!strcasecmp(escape,"escapefunc"))
            done = True;
        }
      }
    }
  }
  if (redefine_cursor)
    XDefineCursor(dpy, Scr.Root, Scr.FvwmCursors[CRS_ROOT]);
  if (is_ungrabbed)
  {
    GrabEm(CRS_NONE, GRAB_NORMAL);
  }
  Tmp_win = (check_if_fvwm_window_exists(s_Tmp_win)) ? s_Tmp_win : NULL;
  free(wait_string);

  return;
}

void CMD_Quit(F_CMD_ARGS)
{
  if (master_pid != getpid())
    kill(master_pid, SIGTERM);
  Done(0,NULL);
}

void CMD_QuitScreen(F_CMD_ARGS)
{
  Done(0,NULL);
}

void CMD_Echo(F_CMD_ARGS)
{
  unsigned int len;

  if (!action)
    action = "";
  len = strlen(action);
  if (len != 0)
  {
    if (action[len-1]=='\n')
      action[len-1]='\0';
  }
  fvwm_msg(ECHO,"Echo",action);
}

void CMD_ColormapFocus(F_CMD_ARGS)
{
  if (MatchToken(action,"FollowsFocus"))
  {
    Scr.ColormapFocus = COLORMAP_FOLLOWS_FOCUS;
  }
  else if (MatchToken(action,"FollowsMouse"))
  {
    Scr.ColormapFocus = COLORMAP_FOLLOWS_MOUSE;
  }
  else
  {
    fvwm_msg(ERR,"SetColormapFocus",
             "ColormapFocus requires 1 arg: FollowsFocus or FollowsMouse");
    return;
  }
}

void CMD_ClickTime(F_CMD_ARGS)
{
  int val;

  if(GetIntegerArguments(action, NULL, &val, 1) != 1)
  {
    Scr.ClickTime = DEFAULT_CLICKTIME;
  }
  else
  {
    Scr.ClickTime = (val < 0)? 0 : val;
  }

  /* Use a negative value during startup and change sign afterwards. This
   * speeds things up quite a bit. */
  if (fFvwmInStartup)
    Scr.ClickTime = -Scr.ClickTime;
}


void CMD_ImagePath(F_CMD_ARGS)
{
    SetImagePath( action );
}

/** Prepend rather than replace the image path.
    Used for obsolete PixmapPath and IconPath **/
static void obsolete_imagepaths( const char* pre_path )
{
    char* tmp = stripcpy( pre_path );
    char* path = alloca( strlen( tmp ) + strlen( GetImagePath() ) + 2 );

    strcpy( path, tmp );
    free( tmp );

    strcat( path, ":" );
    strcat( path, GetImagePath() );

    SetImagePath( path );
}


void CMD_IconPath(F_CMD_ARGS)
{
    fvwm_msg(ERR, "iconPath_function",
	     "IconPath is deprecated since 2.3.0; use ImagePath instead." );
    obsolete_imagepaths( action );
}

void CMD_PixmapPath(F_CMD_ARGS)
{
    fvwm_msg(ERR, "pixmapPath_function",
	     "PixmapPath is deprecated since 2.3.0; use ImagePath instead." );
    obsolete_imagepaths( action );
}


void CMD_ModulePath(F_CMD_ARGS)
{
    static int need_to_free = 0;
    setPath( &ModulePath, action, need_to_free );
    need_to_free = 1;
}


void CMD_ModuleTimeout(F_CMD_ARGS)
{
  int timeout;

  moduleTimeout = DEFAULT_MODULE_TIMEOUT;
  if (
       (GetIntegerArguments(action, NULL, &timeout, 1) == 1) &&
       (timeout > 0)
     )
  {
    moduleTimeout = timeout;
  }
}


void CMD_HilightColor(F_CMD_ARGS)
{
  char *fore;
  char *back;
#ifdef USEDECOR
  if (Scr.cur_decor && Scr.cur_decor != &Scr.DefaultDecor)
  {
    fvwm_msg(
      ERR, "SetHiColor",
      "Decors do not support the HilightColor command anymore."
      " Please use 'Style <stylename> HilightFore <forecolor>' and"
      " 'Style <stylename> HilightBack <backcolor>' instead."
      " Sorry for the inconvenience.");
    return;
  }
#endif
  action = GetNextToken(action, &fore);
  GetNextToken(action, &back);
  if (fore && back)
  {
    action = safemalloc(strlen(fore) + strlen(back) + 29);
    sprintf(action, "* HilightFore %s, HilightBack %s", fore, back);
    CMD_Style(F_PASS_ARGS);
  }
  if (fore)
    free(fore);
  if (back)
    free(back);
}


void CMD_HilightColorset(F_CMD_ARGS)
{
  char *newaction;

#ifdef USEDECOR
  if (Scr.cur_decor && Scr.cur_decor != &Scr.DefaultDecor)
  {
    fvwm_msg(
      ERR, "SetHiColorset",
      "Decors do not support the HilightColorset command anymore."
      " Please use 'Style <stylename> HilightColorset <colorset>' instead."
      " Sorry for the inconvenience.");
    return;
  }
#endif
  if (action)
  {
    newaction = safemalloc(strlen(action) + 32);
    sprintf(newaction, "* HilightColorset %s", action);
    action = newaction;
    CMD_Style(F_PASS_ARGS);
    free(newaction);
  }
}


void do_title_style(F_CMD_ARGS, Bool do_add)
{

  char *parm;
  char *prev;
#ifdef USEDECOR
  FvwmDecor *decor = Scr.cur_decor ? Scr.cur_decor : &Scr.DefaultDecor;
#else
  FvwmDecor *decor = &Scr.DefaultDecor;
#endif

  Scr.flags.do_need_window_update = 1;
  decor->flags.has_changed = 1;
  decor->titlebar.flags.has_changed = 1;

  for (prev = action ; (parm = PeekToken(action, &action)); prev = action)
  {
    if (!do_add && StrEquals(parm,"centered"))
    {
      TB_JUSTIFICATION(decor->titlebar) = JUST_CENTER;
    }
    else if (!do_add && StrEquals(parm,"leftjustified"))
    {
      TB_JUSTIFICATION(decor->titlebar) = JUST_LEFT;
    }
    else if (!do_add && StrEquals(parm,"rightjustified"))
    {
      TB_JUSTIFICATION(decor->titlebar) = JUST_RIGHT;
    }
    else if (!do_add && StrEquals(parm,"height"))
    {
      int height = 0;
      int next = 0;

      if (!action || sscanf(action, "%d%n", &height, &next) <= 0 ||
	  height < MIN_FONT_HEIGHT || height > MAX_FONT_HEIGHT)
      {
	if (height != 0)
	{
	  fvwm_msg(ERR, "do_title_style",
		   "bad height argument (height must be from 5 to 256)");
	  height = 0;
	}
      }
      if (decor->title_height != height)
      {
	decor->title_height = height;
	decor->flags.has_title_height_changed = 1;
      }
      if (action)
	action += next;
    }
    else
    {
      action = ReadTitleButton(prev, &decor->titlebar, do_add, -1);
    }
  }
}

void CMD_TitleStyle(F_CMD_ARGS)
{
  do_title_style(F_PASS_ARGS, False);
} /* SetTitleStyle */

#if defined(MULTISTYLE)
/*****************************************************************************
 *
 * Appends a titlestyle ([EMAIL PROTECTED])
 *
 ****************************************************************************/
void CMD_AddTitleStyle(F_CMD_ARGS)
{
  do_title_style(F_PASS_ARGS, True);
}
#endif /* MULTISTYLE */

void ApplyDefaultFontAndColors(void)
{
  XGCValues gcv;
  unsigned long gcm;
  int cset = Scr.DefaultColorset;

  /* make GC's */
  gcm = GCFunction|GCFont|GCLineWidth|GCForeground|GCBackground;
  gcv.function = GXcopy;
  gcv.font = Scr.DefaultFont.font->fid;
  gcv.line_width = 0;
  if (cset >= 0) {
    gcv.foreground = Colorset[cset].fg;
    gcv.background = Colorset[cset].bg;
  } else {
    gcv.foreground = Scr.StdFore;
    gcv.background = Scr.StdBack;
  }
  if(Scr.StdGC)
    XChangeGC(dpy, Scr.StdGC, gcm, &gcv);
  else
    Scr.StdGC = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);

  gcm = GCFunction|GCLineWidth|GCForeground;
  if (cset >= 0)
    gcv.foreground = Colorset[cset].hilite;
  else
    gcv.foreground = Scr.StdHilite;
  if(Scr.StdReliefGC)
    XChangeGC(dpy, Scr.StdReliefGC, gcm, &gcv);
  else
    Scr.StdReliefGC = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);

  if (cset >= 0)
    gcv.foreground = Colorset[cset].shadow;
  else
    gcv.foreground = Scr.StdShadow;
  if(Scr.StdShadowGC)
    XChangeGC(dpy, Scr.StdShadowGC, gcm, &gcv);
  else
    Scr.StdShadowGC = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);

  /* update the geometry window for move/resize */
  if(Scr.SizeWindow != None)
  {
    resize_geometry_window();
  }

  UpdateAllMenuStyles();
}

void CMD_Colorset(F_CMD_ARGS)
{
  int n = -1, ret;
  char *token;

  token = PeekToken(action, NULL);
  if (token == NULL)
    return;
  ret = sscanf(token, "%x", &n);

  if ((ret == 0) || (n < 0))
    return;

  LoadColorset(action);
  BroadcastColorset(n);

  if (n == Scr.DefaultColorset)
  {
    Scr.flags.do_need_window_update = 1;
    Scr.flags.has_default_color_changed = 1;
  }

  UpdateMenuColorset(n);
  update_style_colorset(n);
}


void CMD_DefaultIcon(F_CMD_ARGS)
{
  if (Scr.DefaultIcon)
    free(Scr.DefaultIcon);
  GetNextToken(action, &Scr.DefaultIcon);
}

void CMD_DefaultColorset(F_CMD_ARGS)
{
  int cset;

  if (GetIntegerArguments(action, NULL, &cset, 1) != 1)
    return;

  Scr.DefaultColorset = cset;
  if (Scr.DefaultColorset < 0)
    Scr.DefaultColorset = -1;
  AllocColorset(Scr.DefaultColorset);
  Scr.flags.do_need_window_update = 1;
  Scr.flags.has_default_color_changed = 1;
}

void CMD_DefaultColors(F_CMD_ARGS)
{
  char *fore = NULL;
  char *back = NULL;

  action = GetNextToken(action, &fore);
  if (action)
    action = GetNextToken(action, &back);
  if (!back)
    back = safestrdup(DEFAULT_BACK_COLOR);
  if (!fore)
    fore = safestrdup(DEFAULT_FORE_COLOR);

  if (!StrEquals(fore, "-"))
  {
    XFreeColors(dpy, Pcmap, &Scr.StdFore, 1, 0);
    Scr.StdFore = GetColor(fore);
  }
  if (!StrEquals(back, "-"))
  {
    XFreeColors(dpy, Pcmap, &Scr.StdBack, 3, 0);
    Scr.StdBack = GetColor(back);
    Scr.StdHilite = GetHilite(Scr.StdBack);
    Scr.StdShadow = GetShadow(Scr.StdBack);
  }
  free(fore);
  free(back);

  Scr.DefaultColorset = -1;
  Scr.flags.do_need_window_update = 1;
  Scr.flags.has_default_color_changed = 1;
}

void CMD_DefaultFont(F_CMD_ARGS)
{
  char *font;
  FvwmFont new_font;

  font = PeekToken(action, &action);
  if (!font)
  {
    /* Try 'fixed', pass NULL font name */
  }

  if (!LoadFvwmFont(dpy, font, &new_font))
  {
    if (Scr.DefaultFont.font == NULL)
      exit(1);
    else
      return;
  }

  FreeFvwmFont(dpy, &Scr.DefaultFont);
  Scr.DefaultFont = new_font;
  /* set flags to indicate that the font has changed */
  Scr.flags.do_need_window_update = 1;
  Scr.flags.has_default_font_changed = 1;

  return;
}

void CMD_IconFont(F_CMD_ARGS)
{
  char *newaction;

#ifdef USEDECOR
  if (Scr.cur_decor && Scr.cur_decor != &Scr.DefaultDecor)
  {
    fvwm_msg(
      ERR, "LoadIconFont",
      "Decors do not support the IconFont command anymore."
      " Please use 'Style <stylename> IconFont <fontname>' instead."
      " Sorry for the inconvenience.");
    return;
  }
#endif
  if (action)
  {
    newaction = safemalloc(strlen(action) + 16);
    sprintf(newaction, "* IconFont %s", action);
    action = newaction;
    CMD_Style(F_PASS_ARGS);
    free(newaction);
  }
}

void CMD_WindowFont(F_CMD_ARGS)
{
  char *newaction;

#ifdef USEDECOR
  if (Scr.cur_decor && Scr.cur_decor != &Scr.DefaultDecor)
  {
    fvwm_msg(
      ERR, "LoadWindowFont",
      "Decors do not support the WindowFont command anymore."
      " Please use 'Style <stylename> Font <fontname>' instead."
      " Sorry for the inconvenience.");
    return;
  }
#endif
  if (action)
  {
    newaction = safemalloc(strlen(action) + 16);
    sprintf(newaction, "* Font %s", action);
    action = newaction;
    CMD_Style(F_PASS_ARGS);
    free(newaction);
  }
}

void FreeDecorFace(Display *dpy, DecorFace *df)
{
  switch (DFS_FACE_TYPE(df->style))
  {
  case GradientButton:
    /* - should we check visual is not TrueColor before doing this?

       XFreeColors(dpy, PictureCMap,
       df->u.grad.pixels, df->u.grad.npixels,
       AllPlanes); */
    free(df->u.grad.pixels);
    break;

  case PixmapButton:
  case TiledPixmapButton:
    if (df->u.p)
      DestroyPicture(dpy, df->u.p);
    break;

  case VectorButton:
  case DefaultVectorButton:
    if (df->u.vector.vertices)
      free (df->u.vector.vertices);
  default:
    /* see below */
    break;
  }
#ifdef MULTISTYLE
  /* delete any compound styles */
  if (df->next)
  {
    FreeDecorFace(dpy, df->next);
    free(df->next);
  }
  df->next = NULL;
#endif
  memset(&df->style, 0, sizeof(df->style));
  memset(&df->u, 0, sizeof(df->u));
  DFS_FACE_TYPE(df->style) = SimpleButton;
}

/*****************************************************************************
 *
 * Reads a button face line into a structure ([EMAIL PROTECTED])
 *
 ****************************************************************************/
Bool ReadDecorFace(char *s, DecorFace *df, int button, int verbose)
{
  int offset;
  char style[256], *file;
  char *action = s;

  /* some variants of scanf do not increase the assign count when %n is used,
   * so a return value of 1 is no error. */
  if (sscanf(s, "%256s%n", style, &offset) < 1)
  {
    if (verbose)
    {
      fvwm_msg(ERR, "ReadDecorFace", "error in face `%s'", s);
    }
    return False;
  }
  style[255] = 0;

  if (strncasecmp(style, "--", 2) != 0)
  {
    s += offset;

    FreeDecorFace(dpy, df);

    /* determine button style */
    if (strncasecmp(style,"Simple",6)==0)
    {
      memset(&df->style, 0, sizeof(df->style));
      DFS_FACE_TYPE(df->style) = SimpleButton;
    }
    else if (strncasecmp(style,"Default",7)==0)
    {
      int b = -1, n = sscanf(s, "%d%n", &b, &offset);

      if (n < 1)
      {
	if (button == -1)
	{
	  if (verbose)
	  {
	    fvwm_msg(ERR,"ReadDecorFace", "need default button number to load");
	  }
	  return False;
	}
	b = button;
      }
      else
      {
	b = BUTTON_INDEX(b);
	s += offset;
      }
      if (b >= 0 && b < NUMBER_OF_BUTTONS)
	LoadDefaultButton(df, b);
      else
      {
	if (verbose)
	{
	  fvwm_msg(ERR,"ReadDecorFace", "button number out of range: %d", b);
	}
	return False;
      }
    }
    else if (strncasecmp(style,"Vector",6)==0 ||
	     (strlen(style)<=2 && isdigit(*style)))
    {
      /* normal coordinate list button style */
      int i, num_coords, num, x, y, line_style;
      struct vector_coords *vc = &df->u.vector;

      /* get number of points */
      if (strncasecmp(style,"Vector",6)==0)
      {
	num = sscanf(s,"%d%n",&num_coords,&offset);
	s += offset;
      } else
	num = sscanf(style,"%d",&num_coords);

      if((num != 1)||(num_coords>1000)||(num_coords<2))
      {
	if (verbose)
	{
	  fvwm_msg(
	    ERR,"ReadDecorFace", "Bad button style (2) in line: %s",action);
	}
	return False;
      }

      vc->num = num_coords;
      vc->use_fgbg = 0;
      vc->vertices = (struct vector_vertex *)
                     safemalloc (sizeof(struct vector_vertex) * num_coords);

      /* get the points */
      for(i = 0; i < vc->num; ++i)
      {
	/* X x Y @ line_style */
	num = sscanf(s,"[EMAIL PROTECTED]", &x, &y, &line_style, &offset);
	vc->vertices[i].x = x;
	vc->vertices[i].y = y;
	vc->vertices[i].color = line_style;
	if(num != 3)
	{
	  if (verbose)
	  {
	    fvwm_msg(
	      ERR, "ReadDecorFace", "Bad button style (3) in line %s", action);
	  }
	  free(vc->vertices);
	  vc->vertices = NULL;
                      vc->num = 0;
	  return False;
	}
	if (line_style >= 2 && line_style < 4)
	{
	  vc->use_fgbg = 1;
	}
	s += offset;
      }
      memset(&df->style, 0, sizeof(df->style));
      DFS_FACE_TYPE(df->style) = VectorButton;
    }
    else if (strncasecmp(style,"Solid",5)==0)
    {
      s = GetNextToken(s, &file);
      if (file)
      {
	memset(&df->style, 0, sizeof(df->style));
	DFS_FACE_TYPE(df->style) = SolidButton;
	df->u.back = GetColor(file);
	free(file);
      }
      else
      {
	if (verbose)
	{
	  fvwm_msg(
	    ERR, "ReadDecorFace", "no color given for Solid face type: %s",
	    action);
	}
	return False;
      }
    }
    else if (strncasecmp(style+1, "Gradient", 8)==0)
    {
      char **s_colors;
      int npixels, nsegs, *perc;
      Pixel *pixels;

      if (!IsGradientTypeSupported(style[0]))
	return False;

      /* translate the gradient string into an array of colors etc */
      npixels = ParseGradient(s, &s, &s_colors, &perc, &nsegs);
      while (*s && isspace(*s))
	s++;
      if (npixels <= 0)
	return False;
      /* grab the colors */
      pixels = AllocAllGradientColors(s_colors, perc, nsegs, npixels);
      if (pixels == None)
	return False;

      df->u.grad.pixels = pixels;
      df->u.grad.npixels = npixels;
      memset(&df->style, 0, sizeof(df->style));
      DFS_FACE_TYPE(df->style) = GradientButton;
      df->u.grad.gradient_type = toupper(style[0]);
    }
    else if (strncasecmp(style,"Pixmap",6)==0
	     || strncasecmp(style,"TiledPixmap",11)==0)
    {
      s = GetNextToken(s, &file);
      df->u.p = CachePicture(dpy, Scr.NoFocusWin, NULL,
			     file,Scr.ColorLimit);
      if (df->u.p == NULL)
      {
	if (file)
	{
	  if (verbose)
	  {
	    fvwm_msg(ERR,"ReadDecorFace", "couldn't load pixmap %s", file);
	  }
	  free(file);
	}
	return False;
      }
      if (file)
      {
	free(file);
	file = NULL;
      }

      memset(&df->style, 0, sizeof(df->style));
      if (strncasecmp(style,"Tiled",5)==0)
	DFS_FACE_TYPE(df->style) = TiledPixmapButton;
      else
	DFS_FACE_TYPE(df->style) = PixmapButton;
    }
#ifdef MINI_ICONS
    else if (strncasecmp (style, "MiniIcon", 8) == 0)
    {
      memset(&df->style, 0, sizeof(df->style));
      DFS_FACE_TYPE(df->style) = MiniIconButton;
#if 0
/* Have to remove this again. This is all so badly written there is no chance
 * to prevent a coredump and a memory leak the same time without a rewrite of
 * large parts of the code. */
      if (df->u.p)
	DestroyPicture(dpy, df->u.p);
#endif
      df->u.p = NULL; /* pixmap read in when the window is created */
    }
#endif
    else
    {
      if (verbose)
      {
	fvwm_msg(ERR,"ReadDecorFace", "unknown style %s: %s", style, action);
      }
      return False;
    }
  }

  /* Process button flags ("--" signals start of flags,
     it is also checked for above) */
  s = GetNextToken(s, &file);
  if (file && (strcmp(file,"--")==0))
  {
    char *tok;
    s = GetNextToken(s, &tok);
    while (tok && *tok)
    {
      int set = 1;
      char *old_tok = NULL;

      if (*tok == '!')
      { /* flag negate */
	set = 0;
	old_tok = tok;
	tok++;
      }
      if (StrEquals(tok,"Clear"))
      {
	memset(&DFS_FLAGS(df->style), (set) ? 0 : 0xff,
	       sizeof(DFS_FLAGS(df->style)));
	/* ? what is set == 0 good for ? */
      }
      else if (StrEquals(tok,"Left"))
      {
	if (set)
	  DFS_H_JUSTIFICATION(df->style) = JUST_LEFT;
	else
	  DFS_H_JUSTIFICATION(df->style) = JUST_RIGHT;
      }
      else if (StrEquals(tok,"Right"))
      {
	if (set)
	  DFS_H_JUSTIFICATION(df->style) = JUST_RIGHT;
	else
	  DFS_H_JUSTIFICATION(df->style) = JUST_LEFT;
      }
      else if (StrEquals(tok,"Centered"))
      {
	DFS_H_JUSTIFICATION(df->style) = JUST_CENTER;
	DFS_V_JUSTIFICATION(df->style) = JUST_CENTER;
      }
      else if (StrEquals(tok,"Top"))
      {
	if (set)
	  DFS_V_JUSTIFICATION(df->style) = JUST_TOP;
	else
	  DFS_V_JUSTIFICATION(df->style) = JUST_BOTTOM;
      }
      else if (StrEquals(tok,"Bottom"))
      {
	if (set)
	  DFS_V_JUSTIFICATION(df->style) = JUST_BOTTOM;
	else
	  DFS_V_JUSTIFICATION(df->style) = JUST_TOP;
      }
      else if (StrEquals(tok,"Flat"))
      {
	if (set)
	  DFS_BUTTON_RELIEF(df->style) = DFS_BUTTON_IS_FLAT;
	else if (DFS_BUTTON_RELIEF(df->style) == DFS_BUTTON_IS_FLAT)
	  DFS_BUTTON_RELIEF(df->style) = DFS_BUTTON_IS_UP;
      }
      else if (StrEquals(tok,"Sunk"))
      {
	if (set)
	  DFS_BUTTON_RELIEF(df->style) = DFS_BUTTON_IS_SUNK;
	else if (DFS_BUTTON_RELIEF(df->style) == DFS_BUTTON_IS_SUNK)
	  DFS_BUTTON_RELIEF(df->style) = DFS_BUTTON_IS_UP;
      }
      else if (StrEquals(tok,"Raised"))
      {
	if (set)
	  DFS_BUTTON_RELIEF(df->style) = DFS_BUTTON_IS_UP;
	else
	  DFS_BUTTON_RELIEF(df->style) = DFS_BUTTON_IS_SUNK;
      }
      else if (StrEquals(tok,"UseTitleStyle"))
      {
	if (set)
	{
	  DFS_USE_TITLE_STYLE(df->style) = 1;
	  DFS_USE_BORDER_STYLE(df->style) = 0;
	}
	else
	  DFS_USE_TITLE_STYLE(df->style) = 0;
      }
      else if (StrEquals(tok,"HiddenHandles"))
      {
	DFS_HAS_HIDDEN_HANDLES(df->style) = !!set;
      }
      else if (StrEquals(tok,"NoInset"))
      {
	DFS_HAS_NO_INSET(df->style) = !!set;
      }
      else if (StrEquals(tok,"UseBorderStyle"))
      {
	if (set)
	{
	  DFS_USE_BORDER_STYLE(df->style) = 1;
	  DFS_USE_TITLE_STYLE(df->style) = 0;
	}
	else
	  DFS_USE_BORDER_STYLE(df->style) = 0;
      }
      else
	if (verbose)
	{
	  fvwm_msg(
	    ERR, "ReadDecorFace", "unknown button face flag '%s' -- line: %s",
	    tok, action);
	}
      if (set)
	free(tok);
      else
	free(old_tok);
      s = GetNextToken(s, &tok);
    }
  }
  if (file)
    free(file);
  return True;
}


/*****************************************************************************
 *
 * Reads a title button description ([EMAIL PROTECTED])
 *
 ****************************************************************************/
static char *ReadTitleButton(
    char *s, TitleButton *tb, Boolean append, int button)
{
  char *end = NULL;
  char *spec;
  char *t;
  int i;
  int bs;
  int pstyle = 0;
  DecorFace tmpdf;
  Bool do_set_all = False;

  s = SkipSpaces(s, NULL, 0);
  t = GetNextTokenIndex(s, button_states, 0, &bs);
  if (bs != BS_All)
  {
    s = SkipSpaces(t, NULL, 0);
  }
  else
  {
    do_set_all = True;
  }

  if (*s == '(')
  {
    int len;
    pstyle = 1;
    if (!(end = strchr(++s, ')')))
    {
      fvwm_msg(ERR, "ReadTitleButton", "missing parenthesis: %s", s);
      return NULL;
    }
    s = SkipSpaces(s, NULL, 0);
    len = end - s + 1;
    spec = safemalloc(len);
    strncpy(spec, s, len - 1);
    spec[len - 1] = 0;
  }
  else
  {
    spec = s;
  }

  spec = SkipSpaces(spec, NULL, 0);
  /* setup temporary in case button read fails */
  memset(&tmpdf, 0, sizeof(DecorFace));
  DFS_FACE_TYPE(tmpdf.style) = SimpleButton;

  if (strncmp(spec, "--",2)==0)
  {
    /* only change flags */
    if (do_set_all)
    {
      for (i = 0; i < BS_MaxButtonState; ++i)
      {
	ReadDecorFace(spec, &TB_STATE(*tb)[i], BS_All, !i);
      }
    }
    else
    {
      ReadDecorFace(spec, &TB_STATE(*tb)[bs], button, True);
    }
  }
  else if (ReadDecorFace(spec, &tmpdf, button, True))
  {
    int b = (do_set_all) ? 0 : bs;
#ifdef MULTISTYLE
    if (append)
    {
      DecorFace *head = &TB_STATE(*tb)[b];
      DecorFace *tail = head;
      DecorFace *next;

      while (tail->next)
      {
	tail = tail->next;
      }
      tail->next = (DecorFace *)safemalloc(sizeof(DecorFace));
      memcpy(tail->next, &tmpdf, sizeof(DecorFace));
      if (DFS_FACE_TYPE(tail->next->style) == VectorButton &&
	  DFS_FACE_TYPE((&TB_STATE(*tb)[b])->style) == DefaultVectorButton)
      {
	/* override the default vector style */
	memcpy(&tail->next->style, &head->style, sizeof(DecorFaceStyle));
	DFS_FACE_TYPE(tail->next->style) = VectorButton;
	next = head->next;
	head->next = NULL;
	FreeDecorFace(dpy, head);
	memcpy(head, next, sizeof(DecorFace));
	free(next);
      }
      if (do_set_all)
      {
	for (i = 1; i < BS_MaxButtonState; ++i)
	{
	  if (i == b)
	  {
	    /* already done above */
	    continue;
	  }
	  head = &TB_STATE(*tb)[i];
	  tail = head;
	  while (tail->next)
	  {
	    tail = tail->next;
	  }
	  tail->next = (DecorFace *)safemalloc(sizeof(DecorFace));
	  memset(&DFS_FLAGS(tail->next->style), 0,
		 sizeof(DFS_FLAGS(tail->next->style)));
	  DFS_FACE_TYPE(tail->next->style) = SimpleButton;
	  tail->next->next = NULL;
	  ReadDecorFace(spec, tail->next, button, False);
	  if (DFS_FACE_TYPE(tail->next->style) == VectorButton &&
	      DFS_FACE_TYPE((&TB_STATE(*tb)[i])->style) == DefaultVectorButton)
	  {
	    /* override the default vector style */
	    memcpy(&tail->next->style, &head->style, sizeof(DecorFaceStyle));
	    DFS_FACE_TYPE(tail->next->style) = VectorButton;
	    next = head->next;
	    head->next = NULL;
	    FreeDecorFace(dpy, head);
	    memcpy(head, next, sizeof(DecorFace));
	    free(next);
	  }
	}
      }
    }
    else
#endif
    {
      FreeDecorFace(dpy, &TB_STATE(*tb)[b]);
      memcpy(&(TB_STATE(*tb)[b]), &tmpdf, sizeof(DecorFace));
      if (do_set_all)
      {
	for (i = 1; i < BS_MaxButtonState; ++i)
	{
	  ReadDecorFace(spec, &TB_STATE(*tb)[i], button, False);
	}
      }
    }
  }
  if (pstyle)
  {
    free(spec);
    end++;
    end = SkipSpaces(end, NULL, 0);
  }

  return end;
}


#ifdef USEDECOR
/*****************************************************************************
 *
 * Diverts a style definition to an FvwmDecor structure ([EMAIL PROTECTED])
 *
 ****************************************************************************/
void AddToDecor(FvwmDecor *decor, char *s)
{
  if (!s)
    return;
  while (*s && isspace((unsigned char)*s))
    ++s;
  if (!*s)
    return;
  Scr.cur_decor = decor;
  old_execute_function(s, NULL, &Event, C_ROOT, -1, 0, NULL);
  Scr.cur_decor = NULL;
}

/*****************************************************************************
 *
 * Changes the window's FvwmDecor pointer ([EMAIL PROTECTED])
 *
 ****************************************************************************/
void CMD_ChangeDecor(F_CMD_ARGS)
{
  char *item;
  int old_height;
  int extra_height;
  FvwmDecor *decor = &Scr.DefaultDecor;
  FvwmDecor *found = NULL;

  if (DeferExecution(eventp,&w,&tmp_win,&context, CRS_SELECT,ButtonRelease))
    return;
  item = PeekToken(action, &action);
  if (!action || !item)
    return;
  /* search for tag */
  for (; decor; decor = decor->next)
  {
    if (decor->tag)
    {
      if (StrEquals(item, decor->tag))
      {
	found = decor;
	break;
      }
    }
  }
  if (!found)
  {
    XBell(dpy, 0);
    return;
  }
  SET_DECOR_CHANGED(tmp_win, 1);
  old_height = (tmp_win->decor) ? tmp_win->decor->title_height : 0;
  tmp_win->decor = found;
  apply_decor_change(tmp_win);
  extra_height = (HAS_TITLE(tmp_win) && old_height) ?
    (old_height - tmp_win->decor->title_height) : 0;
  ForceSetupFrame(
    tmp_win, tmp_win->frame_g.x, tmp_win->frame_g.y, tmp_win->frame_g.width,
    tmp_win->frame_g.height - extra_height, True);
  DrawDecorations(tmp_win, DRAW_ALL, (Scr.Hilite == tmp_win), 2, None);
}

/*****************************************************************************
 *
 * Destroys an FvwmDecor ([EMAIL PROTECTED])
 *
 ****************************************************************************/
void CMD_DestroyDecor(F_CMD_ARGS)
{
  char *item;
  FvwmDecor *decor = Scr.DefaultDecor.next;
  FvwmDecor *prev = &Scr.DefaultDecor, *found = NULL;
  Bool do_recreate = False;

  item = PeekToken(action, &action);
  if (!item)
  {
    return;
  }
  if (StrEquals(item, "recreate"))
  {
    do_recreate = True;
    item = PeekToken(action, NULL);
  }
  if (!item)
  {
    return;
  }

  /* search for tag */
  for (; decor; decor = decor->next)
  {
    if (decor->tag)
    {
      if (StrEquals(item, decor->tag))
      {
	found = decor;
	break;
      }
    }
    prev = decor;
  }

  if (found && (found != &Scr.DefaultDecor || do_recreate))
  {
    FvwmWindow *fw;

    if (!do_recreate)
    {
      for (fw = Scr.FvwmRoot.next; fw; fw = fw->next)
      {
	if (fw->decor == found)
	{
	  /* remove the extra title height now because we delete the current
	   * decor before calling ChangeDecor(). */
	  fw->frame_g.height -= fw->decor->title_height;
	  fw->decor = NULL;
	  old_execute_function(
	    "ChangeDecor Default", fw, eventp, C_WINDOW, *Module, 0, NULL);
	}
      }
    }
    DestroyFvwmDecor(found);
    if (do_recreate)
    {
      int i;

      InitFvwmDecor(found);
      found->tag = safestrdup(item);
      Scr.flags.do_need_window_update = 1;
      found->flags.has_changed = 1;
      found->flags.has_title_height_changed = 0;
      found->titlebar.flags.has_changed = 1;
      for (i = 0; i < NUMBER_OF_BUTTONS; ++i)
      {
	TB_FLAGS(found->buttons[i]).has_changed = 1;
      }
    }
    else
    {
      prev->next = found->next;
      free(found);
    }
  }
}

/***********************************************************************
 *
 *  InitFvwmDecor -- initializes an FvwmDecor structure to defaults
 *
 ************************************************************************/
void InitFvwmDecor(FvwmDecor *decor)
{
  int i;
  DecorFace tmpdf;

  /* zero out the structures */
  memset(decor, 0, sizeof (FvwmDecor));
  memset(&tmpdf, 0, sizeof(DecorFace));

  /* initialize title-bar button styles */
  DFS_FACE_TYPE(tmpdf.style) = SimpleButton;
  for (i = 0; i < NUMBER_OF_BUTTONS; ++i)
  {
    int j = 0;
    for (; j < BS_MaxButtonState; ++j)
    {
      TB_STATE(decor->buttons[i])[j] = tmpdf;
    }
  }
  /* reset to default button set */
  ResetAllButtons(decor);
  /* initialize title-bar styles */
  for (i = 0; i < BS_MaxButtonState; ++i)
  {
    DFS_FACE_TYPE(TB_STATE(decor->titlebar)[i].style) = SimpleButton;
  }

  /* initialize border texture styles */
  DFS_FACE_TYPE(decor->BorderStyle.active.style) = SimpleButton;
  DFS_FACE_TYPE(decor->BorderStyle.inactive.style) = SimpleButton;

  return;
}

/***********************************************************************
 *
 *  DestroyFvwmDecor -- frees all memory assocated with an FvwmDecor
 *	structure, but does not free the FvwmDecor itself
 *
 ************************************************************************/
static void DestroyFvwmDecor(FvwmDecor *decor)
{
  int i;
  /* reset to default button set (frees allocated mem) */
  DestroyAllButtons(decor);
  for (i = 0; i < BS_MaxButtonState; ++i)
  {
    FreeDecorFace(dpy, &TB_STATE(decor->titlebar)[i]);
  }
  FreeDecorFace(dpy, &decor->BorderStyle.active);
  FreeDecorFace(dpy, &decor->BorderStyle.inactive);
#ifdef USEDECOR
  if (decor->tag)
  {
    free(decor->tag);
    decor->tag = NULL;
  }
#endif
}

/*****************************************************************************
 *
 * Initiates an AddToDecor ([EMAIL PROTECTED])
 *
 ****************************************************************************/
void CMD_AddToDecor(F_CMD_ARGS)
{
  FvwmDecor *decor, *found = NULL;
  char *item = NULL, *s = action;

  s = GetNextToken(s, &item);

  if (!item)
    return;
  if (!s)
  {
    free(item);
    return;
  }
  /* search for tag */
  for (decor = &Scr.DefaultDecor; decor; decor = decor->next)
  {
    if (decor->tag)
    {
      if (StrEquals(item, decor->tag))
      {
	found = decor;
	break;
      }
    }
  }
  if (!found)
  { /* then make a new one */
    found = (FvwmDecor *)safemalloc(sizeof( FvwmDecor ));
    InitFvwmDecor(found);
    found->tag = item; /* tag it */
    /* add it to list */
    for (decor = &Scr.DefaultDecor; decor->next; decor = decor->next)
    {
      /* nop */
    }
    decor->next = found;
  }
  else
  {
    free(item);
  }

  if (found)
  {
    AddToDecor(found, s);
    /* Set + state to last decor */
    set_last_added_item(ADDED_DECOR, found);
  }
}
#endif /* USEDECOR */


/*****************************************************************************
 *
 * Updates window decoration styles ([EMAIL PROTECTED])
 *
 ****************************************************************************/
void CMD_UpdateDecor(F_CMD_ARGS)
{
  FvwmWindow *fw;
#ifdef USEDECOR
  FvwmDecor *decor, *found = NULL;
  FvwmWindow *hilight = Scr.Hilite;
  char *item = NULL;
  action = GetNextToken(action, &item);
  if (item)
  {
    /* search for tag */
    for (decor = &Scr.DefaultDecor; decor; decor = decor->next)
    {
      if (decor->tag)
      {
	if (strcasecmp(item, decor->tag)==0)
	{
	  found = decor;
	  break;
	}
      }
    }
    free(item);
  }
#endif

  for (fw = Scr.FvwmRoot.next; fw; fw = fw->next)
  {
#ifdef USEDECOR
    /* update specific decor, or all */
    if (found)
    {
      if (fw->decor == found)
      {
	DrawDecorations(fw, DRAW_ALL, True, True, None);
	DrawDecorations(fw, DRAW_ALL, False, True, None);
      }
    }
    else
#endif
    {
      DrawDecorations(fw, DRAW_ALL, True, True, None);
      DrawDecorations(fw, DRAW_ALL, False, True, None);
    }
  }
  DrawDecorations(hilight, DRAW_ALL, True, True, None);
}

void reset_decor_changes(void)
{
#ifndef USEDECOR
  Scr.DefaultDecor.flags.has_changed = 0;
  Scr.DefaultDecor.flags.has_title_height_changed = 0;
#else
  FvwmDecor *decor;
  for (decor = &Scr.DefaultDecor; decor; decor = decor->next)
  {
    decor->flags.has_changed = 0;
    decor->flags.has_title_height_changed = 0;
  }
  /* todo: must reset individual change flags too */
#endif
}

/*****************************************************************************
 *
 * Changes a button decoration style (changes by [EMAIL PROTECTED])
 *
 ****************************************************************************/
static void SetMWMButtonFlag(
  mwm_flags flag, int multi, int set, FvwmDecor *decor, TitleButton *tb)
{
  int i;
  int start = 0;
  int add = 2;

  if (multi)
  {
    if (multi == 2)
      start = 1;
    else if (multi == 3)
      add = 1;
    for (i = start; i < NUMBER_OF_BUTTONS; i += add)
    {
      if (set)
	TB_MWM_DECOR_FLAGS(decor->buttons[i]) |= flag;
      else
	TB_MWM_DECOR_FLAGS(decor->buttons[i]) &= ~flag;
    }
  }
  else
  {
    if (set)
      TB_MWM_DECOR_FLAGS(*tb) |= flag;
    else
      TB_MWM_DECOR_FLAGS(*tb) &= ~flag;
  }

  return;
}

static void do_button_style(F_CMD_ARGS, Bool do_add)
{
  int i;
  int multi = 0;
  int button = 0;
  char *text = NULL;
  char *prev = NULL;
  char *parm = NULL;
  TitleButton *tb = NULL;
#ifdef USEDECOR
  FvwmDecor *decor = Scr.cur_decor ? Scr.cur_decor : &Scr.DefaultDecor;
#else
  FvwmDecor *decor = &Scr.DefaultDecor;
#endif

  parm = PeekToken(action, &text);
  if (parm && isdigit(*parm))
  {
    button = atoi(parm);
    button = BUTTON_INDEX(button);
  }

  if (parm == NULL || button >= NUMBER_OF_BUTTONS || button < 0)
  {
    fvwm_msg(ERR,"ButtonStyle","Bad button style (1) in line %s",action);
    return;
  }

  Scr.flags.do_need_window_update = 1;

  if (!isdigit(*parm))
  {
    if (StrEquals(parm,"left"))
      multi = 1; /* affect all left buttons */
    else if (StrEquals(parm,"right"))
      multi = 2; /* affect all right buttons */
    else if (StrEquals(parm,"all"))
      multi = 3; /* affect all buttons */
    else
    {
      /* we're either resetting buttons or an invalid button set was specified
       */
      if (StrEquals(parm,"reset"))
	ResetAllButtons(decor);
      else
	fvwm_msg(ERR,"ButtonStyle","Bad button style (2) in line %s", action);
      return;
    }
  }

  /* mark button style and decor as changed */
  decor->flags.has_changed = 1;

  if (multi == 0)
  {
    /* a single button was specified */
    tb = &decor->buttons[button];
    TB_FLAGS(*tb).has_changed = 1;
  }
  else
  {
    for (i = 0; i < NUMBER_OF_BUTTONS; ++i)
    {
      if (((multi & 1) && !(i & 1)) || ((multi & 2) && (i & 1)))
      {
	TB_FLAGS(decor->buttons[i]).has_changed = 1;
      }
    }
  }

  for (prev = text; (parm = PeekToken(text, &text)); prev = text)
  {
    if (!do_add && strcmp(parm,"-") == 0)
    {
      char *tok;
      text = GetNextToken(text, &tok);
      while (tok)
      {
	int set = 1;
	char *old_tok = NULL;

	if (*tok == '!')
	{
	  /* flag negate */
	  set = 0;
	  old_tok = tok;
	  tok++;
	}
	if (StrEquals(tok,"Clear"))
	{
	  if (multi)
	  {
	    for (i = 0; i < NUMBER_OF_BUTTONS; ++i)
	    {
	      if (((multi & 1) && !(i & 1)) ||
		  ((multi & 2) && (i & 1)))
	      {
		TB_JUSTIFICATION(decor->buttons[i]) =
		  (set) ? JUST_CENTER : JUST_RIGHT;
		memset(&TB_FLAGS(decor->buttons[i]), (set) ? 0 : 0xff,
		       sizeof(TB_FLAGS(decor->buttons[i])));
		/* ? not very useful if set == 0 ? */
	      }
	    }
	  }
	  else
	  {
	    TB_JUSTIFICATION(*tb) = (set) ? JUST_CENTER : JUST_RIGHT;
	    memset(&TB_FLAGS(*tb), (set) ? 0 : 0xff, sizeof(TB_FLAGS(*tb)));
	    /* ? not very useful if set == 0 ? */
	  }
	}
	else if (StrEquals(tok, "MWMDecorMenu"))
	{
	  SetMWMButtonFlag(MWM_DECOR_MENU, multi, set, decor, tb);
	}
	else if (StrEquals(tok, "MWMDecorMin"))
	{
	  SetMWMButtonFlag(MWM_DECOR_MINIMIZE, multi, set, decor, tb);
	}
	else if (StrEquals(tok, "MWMDecorMax"))
	{
	  SetMWMButtonFlag(MWM_DECOR_MAXIMIZE, multi, set, decor, tb);
	}
	else if (StrEquals(tok, "MWMDecorShade"))
	{
	  SetMWMButtonFlag(MWM_DECOR_SHADE, multi, set, decor, tb);
	}
	else if (StrEquals(tok, "MWMDecorStick"))
	{
	  SetMWMButtonFlag(MWM_DECOR_STICK, multi, set, decor, tb);
	}
	else
	{
	  fvwm_msg(ERR, "ButtonStyle",
		   "unknown title button flag %s -- line: %s",
		   tok, text);
	}
	if (set)
	  free(tok);
	else
	  free(old_tok);
	text = GetNextToken(text, &tok);
      }
      break;
    }
    else
    {
      if (multi)
      {
	for (i = 0; i < NUMBER_OF_BUTTONS; ++i)
	{
	  if (((multi & 1) && !(i & 1)) ||
	      ((multi & 2) && (i & 1)))
	  {
	    text = ReadTitleButton(prev, &decor->buttons[i], do_add, i);
	  }
	}
      }
      else if (!(text = ReadTitleButton(prev, tb, do_add, button)))
      {
	break;
      }
    }
  }
}

void CMD_ButtonStyle(F_CMD_ARGS)
{
  do_button_style(F_PASS_ARGS, False);
}

#ifdef MULTISTYLE
/*****************************************************************************
 *
 * Appends a button decoration style ([EMAIL PROTECTED])
 *
 ****************************************************************************/
void CMD_AddButtonStyle(F_CMD_ARGS)
{
  do_button_style(F_PASS_ARGS, True);
}
#endif /* MULTISTYLE */


/* add_to_env_list
 *   This function keeps a list of all strings that were set in the environment
 *   via SetEnv or UnsetEnv. If a variable is written again, the old memory is
 *   freed. */
typedef struct
{
  char *var;
  char *env;
} env_list_item;
#define ENV_LIST_INC 10
static void add_to_env_list(char *var, char *env)
{
  static env_list_item *env_list = NULL;
  static unsigned int env_len = 0;
  unsigned int i;

  /* find string in list */
  if (env_list && env_len)
  {
    for (i = 0; i < env_len; i++)
    {
      if (strcmp(var, env_list[i].var) == 0)
      {
	/* found it - replace old string */
	free(env_list[i].var);
	free(env_list[i].env);
	env_list[i].var = var;
	env_list[i].env = env;
	return;
      }
    }
    /* not found, add to list */
    if (env_len % ENV_LIST_INC == 0)
    {
      /* need more memory */
      env_list = (env_list_item *)saferealloc(
	(void *)env_list, (env_len + ENV_LIST_INC) * sizeof(env_list_item));
    }
  }
  else if (env_list == NULL)
  {
    /* list is still empty */
    env_list = (env_list_item *)safecalloc(sizeof(env_list_item), ENV_LIST_INC);
  }
  env_list[env_len].var = var;
  env_list[env_len].env = env;
  env_len++;

  return;
}

void CMD_SetEnv(F_CMD_ARGS)
{
  char *szVar = NULL;
  char *szValue = NULL;
  char *szPutenv = NULL;

  action = GetNextToken(action,&szVar);
  if (!szVar)
    return;
  action = GetNextToken(action,&szValue);
  if (!szValue)
  {
    /* no value, treat as unset */
    CMD_UnsetEnv(F_PASS_ARGS);
    free(szVar);
    return;
  }

  szPutenv = safemalloc(strlen(szVar)+strlen(szValue)+2);
  sprintf(szPutenv,"%s=%s",szVar,szValue);
  putenv(szPutenv);
  add_to_env_list(szVar, szPutenv);
  /* szVar is stored in the env list. do not free it */
  free(szValue);
}

void CMD_UnsetEnv(F_CMD_ARGS)
{
  char *szVar = NULL;
  char *szPutenv = NULL;

  action = GetNextToken(action,&szVar);
  if (!szVar)
    return;

  szPutenv = (char *)safemalloc(strlen(szVar) + 2);
  sprintf(szPutenv, "%s=", szVar);
  putenv(szPutenv);
  add_to_env_list(szVar, szPutenv);
  /* szVar is stored in the env list. do not free it */
  /*free(szVar);*/
}

void CMD_GlobalOpts(F_CMD_ARGS)
{
  char *opt;
  char *replace;
  char buf[64];
  int i;
  Bool is_bugopt;
  char *optlist[] = {
    "WindowShadeShrinks",
    "WindowShadeScrolls",
    "SmartPlacementIsReallySmart",
    "SmartPlacementIsNormal",
    "ClickToFocusDoesntPassClick",
    "ClickToFocusPassesClick",
    "ClickToFocusDoesntRaise",
    "ClickToFocusRaises",
    "MouseFocusClickDoesntRaise",
    "MouseFocusClickRaises",
    "NoStipledTitles",
    "StipledTitles",
    "CaptureHonorsStartsOnPage",
    "CaptureIgnoresStartsOnPage",
    "RecaptureHonorsStartsOnPage",
    "RecaptureIgnoresStartsOnPage",
    "ActivePlacementHonorsStartsOnPage",
    "ActivePlacementIgnoresStartsOnPage",
    "RaiseOverNativeWindows",
    "IgnoreNativeWindows",
    NULL
  };
  char *replacelist[] = {
    /* These options are mapped to the Style * command */
    NULL, /* NULL means to use "Style * <optionname>" */
    NULL,
    "* MinOverlapPlacement",
    "* TileCascadePlacement",
    "* ClickToFocusPassesClickOff",
    "* ClickToFocusPassesClick",
    "* ClickToFocusRaisesOff",
    "* ClickToFocusRaises",
    "* MouseFocusClickRaisesOff",
    "* MouseFocusClickRaises",
    "* StippledTitleOff",
    "* StippledTitle",
    NULL,
    NULL,
    NULL,
    NULL,
    "* ManualPlacementHonorsStartsOnPage",
    "* ManualPlacementIgnoresStartsOnPage",
    /* These options are mapped to the BugOpts command */
    "RaiseOverNativeWindows on",
    "RaiseOverNativeWindows off"
  };

  fvwm_msg(ERR, "SetGlobalOptions", "The GlobalOpts command is obsolete.");
  for (action = GetNextSimpleOption(action, &opt); opt;
       action = GetNextSimpleOption(action, &opt))
  {
    replace = NULL;
    is_bugopt = False;

    i = GetTokenIndex(opt, optlist, 0, NULL);
    if (i > -1)
    {
      char *cmd;
      char *tmp;

      replace = replacelist[i];
      if (replace == NULL)
      {
	replace = &(buf[0]);
	sprintf(buf, "* %s", opt);
      }
      else if (*replace != '*')
      {
	is_bugopt = True;
      }
      tmp = action;
      action = replace;
      if (!is_bugopt)
      {
        CMD_Style(F_PASS_ARGS);
        cmd = "Style";
      }
      else
      {
        CMD_BugOpts(F_PASS_ARGS);
        cmd = "BugOpts";
      }
      action = tmp;
      fvwm_msg(
        ERR, "SetGlobalOptions",
        "Please replace 'GlobalOpts %s' with '%s %s'.", opt, cmd, replace);
    }
    else
    {
      fvwm_msg(ERR, "SetGlobalOptions", "Unknown Global Option '%s'", opt);
    }
    /* should never be null, but checking anyways... */
    if (opt)
      free(opt);
  }
  if (opt)
    free(opt);
}

void CMD_BugOpts(F_CMD_ARGS)
{
  char *opt;
  char delim;
  int toggle;

  /* fvwm_msg(DBG,"SetGlobalOptions","init action == '%s'\n",action); */
  while (action)
  {
    action = DoGetNextToken(action, &opt, NULL, ",", &delim);
    if (!opt)
    {
      /* no more options */
      return;
    }
    if (delim == '\n' || delim == ',')
    {
      /* missing toggle argument */
      toggle = 2;
    }
    else
    {
      toggle = ParseToggleArgument(action, &action, 1, False);
    }

    if (StrEquals(opt, "FlickeringMoveWorkaround"))
    {
      switch (toggle)
      {
      case -1:
	Scr.bo.DisableConfigureNotify ^= 1;
	break;
      case 0:
      case 1:
	Scr.bo.DisableConfigureNotify = toggle;
	break;
      default:
	Scr.bo.DisableConfigureNotify = 0;
	break;
      }
    }
    else if (StrEquals(opt, "MixedVisualWorkaround"))
    {
      switch (toggle)
      {
      case -1:
	Scr.bo.InstallRootCmap ^= 1;
	break;
      case 0:
      case 1:
	Scr.bo.InstallRootCmap = toggle;
	break;
      default:
	Scr.bo.InstallRootCmap = 0;
	break;
      }
    }
    else if (StrEquals(opt, "ModalityIsEvil"))
    {
      switch (toggle)
      {
      case -1:
	Scr.bo.ModalityIsEvil ^= 1;
	break;
      case 0:
      case 1:
	Scr.bo.ModalityIsEvil = toggle;
	break;
      default:
	Scr.bo.ModalityIsEvil = 0;
	break;
      }
      if (Scr.bo.ModalityIsEvil)
      {
        SetMWM_INFO(Scr.NoFocusWin);
      }
    }
    else if (StrEquals(opt, "RaiseOverNativeWindows"))
    {
      switch (toggle)
      {
      case -1:
	Scr.bo.RaiseHackNeeded ^= 1;
	break;
      case 0:
      case 1:
	Scr.bo.RaiseHackNeeded = toggle;
	break;
      default:
	Scr.bo.RaiseHackNeeded = 0;
	break;
      }
    }
    else if (StrEquals(opt, "RaiseOverUnmanaged"))
    {
      switch (toggle)
      {
      case -1:
	Scr.bo.RaiseOverUnmanaged ^= 1;
	break;
      case 0:
      case 1:
	Scr.bo.RaiseOverUnmanaged = toggle;
	break;
      default:
	Scr.bo.RaiseOverUnmanaged = 0;
	break;
      }
    }
    else if (StrEquals(opt, "FlickeringQtDialogsWorkaround"))
    {
      switch (toggle)
      {
      case -1:
	Scr.bo.FlickeringQtDialogsWorkaround ^= 1;
	break;
      case 0:
      case 1:
	Scr.bo.FlickeringQtDialogsWorkaround = toggle;
	break;
      default:
	Scr.bo.FlickeringQtDialogsWorkaround = 0;
	break;
      }
    }
    else
    {
      fvwm_msg(ERR, "SetBugOptions", "Unknown Bug Option '%s'", opt);
    }
    free(opt);
  }
}

void CMD_Emulate(F_CMD_ARGS)
{
  char *style;

  style = PeekToken(action, NULL);
  if (!style || StrEquals(style, "fvwm"))
  {
    Scr.gs.EmulateMWM = False;
    Scr.gs.EmulateWIN = False;
  }
  else if (StrEquals(style, "mwm"))
  {
    Scr.gs.EmulateMWM = True;
    Scr.gs.EmulateWIN = False;
  }
  else if (StrEquals(style, "win"))
  {
    Scr.gs.EmulateMWM = False;
    Scr.gs.EmulateWIN = True;
  }
  else
  {
    fvwm_msg(ERR, "Emulate", "Unknown style '%s'", style);
    return;
  }
  Scr.flags.do_need_window_update = 1;
  Scr.flags.has_default_font_changed = 1;
  Scr.flags.has_default_color_changed = 1;

  return;
}
/*
 * The ColorLimit command is  ignored if the  user has no reason to limit
 * color.  This is so the   same configuration will work on  colorlimited
 * and   non-colorlimited  displays  without    resorting   to using    a
 * preprocessor.
 *
 * Lets assume  the display is  no more  than  2000x1000 pixels. Ie.  the
 * display can display no more than 2,000,000 million  pixels at once.  A
 * display depth of 21 will display 2  million colors at once.  Hence the
 * logic below.
 *
 * dje 03/22/99
 */
 /* It is also ignored if the colormap is static i.e you can't run out */
void CMD_ColorLimit(F_CMD_ARGS)
{
  int val;

  /* from X.h:
   * Note that the statically allocated ones are even numbered and the
   * dynamically changeable ones are odd numbered */
  if (!(Pvisual->class & 1))
    return;
  if (Pdepth > 20) {               /* if more than 20 bit color */
    return;                             /* ignore the limit */
  }
  if (GetIntegerArguments(action, NULL, &val, 1) != 1)
  {
    fvwm_msg(ERR,"SetColorLimit","ColorLimit requires one argument");
    return;
  }

  Scr.ColorLimit = (long)val;
}


/* set animation parameters */
void CMD_SetAnimation(F_CMD_ARGS)
{
  char *opt;
  int delay;
  float pct;
  int i = 0;

  action = GetNextToken(action, &opt);
  if (!opt || sscanf(opt,"%d",&delay) != 1)
  {
    fvwm_msg(ERR,"SetAnimation",
	     "Improper milli-second delay as first argument");
    if (opt)
      free(opt);
    return;
  }
  free(opt);
  if (delay > 500)
  {
    fvwm_msg(WARN,"SetAnimation",
	     "Using longer than .5 seconds as between frame animation delay");
  }
  cmsDelayDefault = delay;
  for (action = GetNextToken(action, &opt); opt;
       free(opt), action = GetNextToken(action, &opt))
  {
    if (sscanf(opt,"%f",&pct) != 1)
    {
      fvwm_msg(ERR,"SetAnimation",
	       "Use fractional values ending in 1.0 as args 2 and on");
      free(opt);
      return;
    }
    rgpctMovementDefault[i++] = pct;
  }
  /* No pct entries means don't change them at all */
  if (i>0 && rgpctMovementDefault[i-1] != 1.0)
  {
    rgpctMovementDefault[i++] = 1.0;
  }
}

/* set the number or size of shade animation steps, N => steps, Np => pixels */
void CMD_WindowShadeAnimate(F_CMD_ARGS)
{
  char *buf;

  if (!action)
    action = "";
  fvwm_msg(
    ERR, "setShadeAnim", "The WindowshadeAnimate command is obsolete. "
    "Please use 'Style * WindowShadeSteps %s' instead.", action);
  buf = safemalloc(strlen(action) + 32);
  sprintf(buf, "* WindowShadeSteps %s", action);
  action = buf;
  CMD_Style(F_PASS_ARGS);
  free(buf);
  return;
}

void CMD_FakeClick(F_CMD_ARGS)
{
  char *token;
  char *optlist[] =
  {
    "press", "p",
    "release", "r",
    "wait", "w",
    "modifiers", "m",
    "depth", "d",
    NULL
  };
  unsigned int mask = 0;
  Window root = Scr.Root;
  int maxdepth = 0;

  /* get the mask of pressed/released buttons */
  XQueryPointer(
    dpy, Scr.Root, &root, &JunkRoot, &JunkX, &JunkY, &JunkX, &JunkY, &mask);
  token = PeekToken(action, &action);
  while (token && action)
  {
    int index = GetTokenIndex(token, optlist, 0, NULL);
    int val;
    XEvent e;
    Window w;
    Window child_w;
    int x = 0;
    int y = 0;
    int rx = 0;
    int ry = 0;
    Bool do_unset;
    long add_mask = 0;

    XSync(dpy, 0);
    if (GetIntegerArguments(action, &action, &val, 1) != 1)
    {
      /* error */
      return;
    }
    do_unset = True;
    switch (index)
    {
    case 0:
    case 1:
      do_unset = False;
      /* fall through */
    case 2:
    case 3:
      /* button press or release */
      if (val >= 1 && val <= NUMBER_OF_MOUSE_BUTTONS)
      {
	int depth = 1;

	w = None;
	child_w = root;
	for (depth = 1; depth != maxdepth && w != child_w && child_w != None;
	     depth++)
	{
	  w = child_w;
	  XQueryPointer(dpy, w, &root, &child_w, &rx, &ry, &x, &y, &JunkMask);
	}
	if (do_unset)
	{
	  e.type = ButtonRelease;
	  add_mask = ButtonPressMask;
	}
	else
	{
	  e.type = ButtonPress;
	  add_mask = ButtonPressMask | ButtonReleaseMask;
	}
	e.xbutton.display = dpy;
	e.xbutton.window = w;
	e.xbutton.subwindow = None;
	e.xbutton.root = root;
	e.xbutton.time = lastTimestamp;
	e.xbutton.x = x;
	e.xbutton.y = y;
	e.xbutton.x_root = rx;
	e.xbutton.y_root = ry;
	e.xbutton.button = val;
	e.xbutton.state = mask;
	e.xbutton.same_screen = (Scr.Root == root);
	XSendEvent(
	  dpy, PointerWindow, True, SubstructureNotifyMask | add_mask, &e);
	XSync(dpy, 0);
	if (do_unset)
	  mask &= ~(Button1Mask << (val - 1));
	else
	  mask |= (Button1Mask << (val - 1));
      }
      else
      {
	/* error */
	return;
      }
      break;
    case 4:
    case 5:
      /* wait */
      if (val > 0 && val <= 1000000)
      {
	usleep(1000 * val);
	XQueryPointer(
	  dpy, Scr.Root, &root, &JunkRoot, &JunkX, &JunkY, &JunkX, &JunkY,
	  &mask);
      }
      else
      {
	/* error */
	return;
      }
      break;
    case 6:
    case 7:
      /* set modifier */
      do_unset = False;
      if (val < 0)
      {
	do_unset = True;
	val = -val;
      }
      if (val == 6)
	val = ShiftMask;
      else if (val == 7)
	val = LockMask;
      else if (val == 8)
	val = ControlMask;
      else if (val >=1 && val <= 5)
	val = (Mod1Mask << (val - 1));
      else
	/* error */
	return;
      if (do_unset)
	mask &= ~val;
      else
	mask |= val;
      break;
    case 8:
    case 9:
      /* new max depth */
      maxdepth = val;
      break;
    default:
      /* error */
      return;
    }
    if (action)
      token = PeekToken(action, &action);
  }
}

/* A function to handle stroke (olicha Nov 11, 1999) */
#ifdef HAVE_STROKE
void CMD_StrokeFunc(F_CMD_ARGS)
{
  int finished = 0;
  int abort = 0;
  int modifiers = eventp->xbutton.state;
  int start_event_type = eventp->type;
  char sequence[STROKE_MAX_SEQUENCE + 1];
  char *stroke_action;
  char *opt = NULL;
  Bool finish_on_release = True;
  KeySym keysym;
  Bool restor_repeat = False;
  Bool echo_sequence = False;
  Bool draw_motion = False;
  int i = 0;

  int* x = (int*)0;
  int* y = (int*)0;
  const int STROKE_CHUNK_SIZE = 0xff;
  int coords_size = STROKE_CHUNK_SIZE;

  Window JunkRoot, JunkChild;
  int JunkX, JunkY;
  int tmpx, tmpy;
  unsigned int JunkMask;
  Bool feed_back = False;
  int stroke_width = 1;

  x = (int*)safemalloc(coords_size * sizeof(int));
  y = (int*)safemalloc(coords_size * sizeof(int));

  if(!GrabEm(CRS_STROKE, GRAB_NORMAL))
  {
    XBell(dpy, 0);
    return;
  }

  /* set the default option */
  if (eventp->type == KeyPress || eventp->type == ButtonPress)
    finish_on_release = True;
  else
    finish_on_release = False;

  /* parse the option */
  for (action = GetNextSimpleOption(action, &opt); opt;
       action = GetNextSimpleOption(action, &opt))
  {
    if (StrEquals("NotStayPressed",opt))
      finish_on_release = False;
    else if (StrEquals("EchoSequence",opt))
      echo_sequence = True;
    else if (StrEquals("DrawMotion",opt))
      draw_motion = True;
    else if (StrEquals("FeedBack",opt))
      feed_back = True;
    else if (StrEquals("StrokeWidth",opt))
    {
      /* stroke width takes a positive integer argument */
      if (opt)
        free(opt);
      action = GetNextToken(action, &opt);
      if (!opt)
	fvwm_msg(WARN,"StrokeWidth","needs an integer argument");
      /* we allow stroke_width == 0 which means drawing a `fast' line
       * of width 1; the upper level of 100 is arbitrary */
      else if (!sscanf(opt, "%d", &stroke_width) || stroke_width < 0 ||
	       stroke_width > 100)
      {
	fvwm_msg(WARN,"StrokeWidth","Bad integer argument %d", stroke_width);
	stroke_width = 1;
      }
    }
    else
      fvwm_msg(WARN,"StrokeFunc","Unknown option %s", opt);
    if (opt)
      free(opt);
  }
  if (opt)
    free(opt);

  /* Force auto repeat off and grab the Keyboard to get proper
   * KeyRelease events if we need it.
   * Some computers do not support keyRelease events, can we
   * check this here ? No ? */
  if (start_event_type == KeyPress && finish_on_release)
  {
    XKeyboardState kstate;

    XGetKeyboardControl(dpy, &kstate);
    if (kstate.global_auto_repeat == AutoRepeatModeOn)
    {
      XAutoRepeatOff(dpy);
      restor_repeat = True;
    }
    MyXGrabKeyboard(dpy);
  }

  /* be ready to get a stroke sequence */
  stroke_init();

  if (draw_motion)
  {
    MyXGrabServer(dpy);
    XQueryPointer(dpy, Scr.Root, &JunkRoot, &JunkChild, &x[0], &y[0],
		&JunkX, &JunkY, &JunkMask);
    XSetLineAttributes(dpy,Scr.XorGC,stroke_width,LineSolid,CapButt,JoinMiter);
  }

  while (!finished && !abort)
  {
    /* block until there is an event */
    XMaskEvent(dpy,  ButtonPressMask | ButtonReleaseMask | KeyPressMask |
	       KeyReleaseMask | ButtonMotionMask | PointerMotionMask, eventp);
    /* Records the time */
    StashEventTime(eventp);

    switch (eventp->type)
    {
    case MotionNotify:
      if (eventp->xany.window != Scr.Root)
      {
	XQueryPointer(dpy, Scr.Root, &JunkRoot, &JunkChild, &tmpx, &tmpy,
		&JunkX, &JunkY, &JunkMask);
      }
      else
      {
	tmpx = eventp->xmotion.x;
	tmpy = eventp->xmotion.y;
      }
      stroke_record(tmpx,tmpy);
      if (draw_motion)
      {
	if ((x[i] != tmpx || y[i] != tmpy))
	{
	  i++;
	  if (i >= coords_size) {
	    coords_size += STROKE_CHUNK_SIZE;
	    x = (int*)saferealloc((void *)x, coords_size * sizeof(int));
	    y = (int*)saferealloc((void *)y, coords_size * sizeof(int));
	    if (!x || !y)
	    {
	      /* unlikely */
	      fvwm_msg(WARN,
		       "strokeFunc","unable to allocate %d bytes ... aborted.",
		       coords_size * sizeof(int));
	      abort = 1;
	      i = -1; /* no undrawing possible since either x or y == 0 */
	      break;
	    }
	  }
	  x[i] = tmpx;
	  y[i] = tmpy;
	  XDrawLine(dpy, Scr.Root, Scr.XorGC, x[i-1], y[i-1], x[i], y[i]);
	}
      }
      break;
    case ButtonRelease:
      if (finish_on_release && start_event_type == ButtonPress)
	finished = 1;
      break;
    case KeyRelease:
      if (finish_on_release &&  start_event_type == KeyPress)
	finished = 1;
      break;
    case KeyPress:
      keysym = XLookupKeysym(&eventp->xkey,0);
      /* abort if Escape or Delete is pressed (as in menus.c) */
      if (keysym == XK_Escape || keysym == XK_Delete ||
	  keysym == XK_KP_Separator)
	abort = 1;
      /* finish on enter or space (as in menus.c) */
       if (keysym == XK_Return || keysym == XK_KP_Enter ||
	   keysym ==  XK_space)
	 finished = 1;
       break;
    case ButtonPress:
     if (!finish_on_release)
       finished = 1;
     break;
    default:
      break;
    }
  }

  if (draw_motion)
  {
    while (i > 0)
    {
      XDrawLine(dpy, Scr.Root, Scr.XorGC, x[i-1], y[i-1], x[i], y[i]);
      i--;
    }
    XSetLineAttributes(dpy,Scr.XorGC,0,LineSolid,CapButt,JoinMiter);
    MyXUngrabServer(dpy);
    free(x);
    free(y);
  }
  if (start_event_type == KeyPress && finish_on_release)
    MyXUngrabKeyboard(dpy);
  UngrabEm(GRAB_NORMAL);

  if (restor_repeat)
    XAutoRepeatOn(dpy);

  /* get the stroke sequence */
  stroke_trans(sequence);

  if (echo_sequence)
  {
    char num_seq[STROKE_MAX_SEQUENCE + 1];

    for(i=0;sequence[i] != '\0';i++)
    {
      /* Telephone to numeric pad */
      if ('7' <= sequence[i] && sequence[i] <= '9')
	num_seq[i] = sequence[i]-6;
      else if ('1' <= sequence[i] && sequence[i] <= '3')
	num_seq[i] = sequence[i]+6;
      else
	num_seq[i] = sequence[i];
    }
    num_seq[i++] = '\0';
    fvwm_msg(INFO, "StrokeFunc", "stroke sequence: %s (N%s)",
	     sequence, num_seq);
  }

  if (abort) return;

  /* check for a binding */
  stroke_action = CheckBinding(Scr.AllBindings, sequence, 0, modifiers,
			       GetUnusedModifiers(), context, STROKE_BINDING);

  /* execute the action */
  if (stroke_action != NULL)
  {
    if (feed_back && atoi(sequence) != 0)
    {
      GrabEm(CRS_WAIT, GRAB_BUSY);
      usleep(200000);
      UngrabEm(GRAB_BUSY);
    }
    old_execute_function(stroke_action, tmp_win, eventp, context, -1, 0, NULL);
  }

}
#endif /* HAVE_STROKE */
/* 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 all original code
 * by Rob Nation
 * Copyright 1993, Robert Nation
 *     You may use this code for any purpose, as long as the original
 *     copyright remains in the source code and all documentation
 ****************************************************************************/


/***********************************************************************
 *
 * fvwm window border drawing code
 *
 ***********************************************************************/

#include "config.h"

#include <stdio.h>
#include <signal.h>
#include <string.h>

#include "libs/fvwmlib.h"
#include "libs/FShape.h"
#include "fvwm.h"
#include "externs.h"
#include "events.h"
#include "cursor.h"
#include "functions.h"
#include "bindings.h"
#include "misc.h"
#include "screen.h"
#include "defaults.h"
#include "geometry.h"
#include "borders.h"
#include "builtins.h"
#include "icons.h"
#include "module_interface.h"
#include "libs/Colorset.h"

typedef struct
{
  int relief_width;
  GC relief_gc;
  GC shadow_gc;
  Pixel fore_color;
  Pixel back_color;
  Pixmap back_pixmap;
  XSetWindowAttributes attributes;
  unsigned long valuemask;
  Pixmap texture_pixmap;
  XSetWindowAttributes notex_attributes;
  unsigned long notex_valuemask;

  struct
  {
    unsigned has_color_changed : 1;
  } flags;
} common_decorations_type;

XGCValues Globalgcv;
unsigned long Globalgcm;

extern Window PressedW;

/* used by several drawing functions */
static void ClipClear(Window win, XRectangle *rclip, Bool do_flush_expose)
{
  if (rclip)
  {
    XClearArea(
      dpy, win, rclip->x, rclip->y, rclip->width, rclip->height, False);
  }
  else
  {
    if (do_flush_expose)
    {
      flush_expose(win);
    }
    XClearWindow(dpy, win);
  }

  return;
}

/***********************************************************************
 * change by [EMAIL PROTECTED] to correct popups off title buttons
 *
 *  Procedure:
 *ButtonPosition - find the actual position of the button
 *                 since some buttons may be disabled
 *
 *  Returned Value:
 *The button count from left or right taking in to account
 *that some buttons may not be enabled for this window
 *
 *  Inputs:
 *      context - context as per the global Context
 *      t       - the window (FvwmWindow) to test against
 *
 ***********************************************************************/
int ButtonPosition(int context, FvwmWindow *t)
{
  int i = 0;
  int buttons = -1;
  int end = Scr.nr_left_buttons;

  if (context & C_RALL)
  {
    i  = 1;
    end = Scr.nr_right_buttons;
  }
  {
    for (; i / 2 < end; i += 2)
    {
      if (t->button_w[i])
      {
	buttons++;
      }
      /* is this the button ? */
      if (((1 << i) * C_L1) & context)
	return buttons;
    }
  }
  /* you never know... */
  return 0;
}

static void change_window_background(
  Window w, unsigned long valuemask, XSetWindowAttributes *attributes)
{
  XChangeWindowAttributes(dpy, w, valuemask, attributes);
  XClearWindow(dpy, w);
}

/****************************************************************************
 *
 *  Draws a little pattern within a window (more complex)
 *
 ****************************************************************************/
static void DrawLinePattern(
  Window win, GC ReliefGC, GC ShadowGC, Pixel fore_color, Pixel back_color,
  struct vector_coords *coords, int w, int h)
{
  int i, prev_x, prev_y;
  GC gcs[4];
  gcs[0] = ShadowGC;
  gcs[1] = ReliefGC;
  gcs[2] = NULL;
  gcs[3] = NULL;
  
  /* It is rare, so evaluate this only when needed */
  if (coords->use_fgbg)
  {
    Globalgcv.foreground = fore_color;
    Globalgcm = GCForeground;
    XChangeGC(dpy, Scr.ScratchGC3, Globalgcm, &Globalgcv);
    gcs[3] = Scr.ScratchGC3;
    Globalgcv.foreground = back_color;
    XChangeGC(dpy, Scr.ScratchGC4, Globalgcm, &Globalgcv);
    gcs[2] = Scr.ScratchGC4;
  }
  
  /* safety check */
  if (coords->num <= 0) return;
  
  prev_x = w * coords->vertices->x/100;
  prev_y = h * coords->vertices->y/100;

  for (i = 1; i < coords->num; ++i)
  {
      struct vector_vertex *v = coords->vertices + i;
      int x = w * v->x/100;
      int y = h * v->y/100;
      if (v->color < 4) XDrawLine(dpy,win,gcs[v->color],prev_x,prev_y,x,y);
      prev_x = x;
      prev_y = y;
  }
}

/* used by DrawButton() */
static void clip_button_pixmap(
  int *ox, int *oy, int *cw, int *ch, int*cx, int *cy, XRectangle *rclip)
{
  if (rclip)
  {
    if (rclip->x > *cx)
    {
      *ox = (rclip->x - *cx);
      *cx += *ox;
      *cw -= *ox;
    }
    if (*cx + *cw > rclip->x + rclip->width)
    {
      *cw = rclip->x - *cx + rclip->width;
    }
    if (rclip->y > *cy)
    {
      *oy = (rclip->y - *cy);
      *cy += *oy;
      *ch -= *oy;
    }
    if (*cy + *ch > rclip->y + rclip->height)
    {
      *ch = rclip->y - *cy + rclip->height;
    }
  }

  return;
}

/****************************************************************************
 *
 *  Redraws buttons ([EMAIL PROTECTED])
 *
 ****************************************************************************/
static void DrawButton(
  FvwmWindow *t, Window win, int w, int h, DecorFace *df, GC ReliefGC,
  GC ShadowGC, Pixel fore_color, Pixel back_color, Bool is_lowest,
  mwm_flags stateflags, int left1right0, XRectangle *rclip,
  Pixmap *pbutton_background_pixmap)
{
  register DecorFaceType type = DFS_FACE_TYPE(df->style);
  Picture *p;
  int border = 0;
  int x;
  int y;
  int width;
  int height;

  /* Note: it is assumed that ReliefGC and ShadowGC are already clipped with
   * the rclip rectangle when this function is called. */
  switch (type)
  {
  case SimpleButton:
    break;

  case SolidButton:
    if (pbutton_background_pixmap)
    {
      XSetWindowBackground(dpy, win, df->u.back);
      *pbutton_background_pixmap = None;
    }
    ClipClear(win, rclip, True);
    break;

  case VectorButton:
  case DefaultVectorButton:
    if(HAS_MWM_BUTTONS(t) &&
       ((stateflags & MWM_DECOR_MAXIMIZE && IS_MAXIMIZED(t)) ||
	(stateflags & MWM_DECOR_SHADE && IS_SHADED(t)) ||
	(stateflags & MWM_DECOR_STICK && IS_STICKY(t))))
    {
      DrawLinePattern(win, ShadowGC, ReliefGC, fore_color, back_color,
        &df->u.vector, w, h);
    }
    else
    {
      DrawLinePattern(win, ReliefGC, ShadowGC, fore_color, back_color,
        &df->u.vector, w, h);
    }
    break;

#ifdef MINI_ICONS
  case MiniIconButton:
  case PixmapButton:
  case TiledPixmapButton:
    if (type == PixmapButton || type == TiledPixmapButton)
    {
      p = df->u.p;
    }
    else
    {
      if (!t->mini_icon)
	break;
      p = t->mini_icon;
    }
#else
  case PixmapButton:
  case TiledPixmapButton:
    p = df->u.p;
#endif /* MINI_ICONS */
    if (DFS_BUTTON_RELIEF(df->style) == DFS_BUTTON_IS_FLAT)
      border = 0;
    else
      border = HAS_MWM_BORDER(t) ? 1 : 2;
    x = border;
    y = border;
    width = w - border * 2;
    height = h - border * 2;

    if (is_lowest && !p->mask && pbutton_background_pixmap)
    {
      if (type == TiledPixmapButton ||
	  (p && p->width >= width && p->height >= height))
      {
	/* better performance: set a background pixmap if possible, i.e. if the
	 * decoration is the lowest one in the button and it has no transparent
	 * parts and it is tiled or bigger than the button */
	if (*pbutton_background_pixmap != p->picture)
	{
	  *pbutton_background_pixmap = p->picture;
	  XSetWindowBackgroundPixmap(dpy, win, p->picture);
	  ClipClear(win, rclip, True);
	}
	else
	{
	  ClipClear(win, rclip, True);
	}
	break;
      }
    }
    else if (pbutton_background_pixmap && *pbutton_background_pixmap != None)
    {
      XSetWindowBackgroundPixmap(dpy, win, None);
      if (pbutton_background_pixmap)
      {
	*pbutton_background_pixmap = None;
	flush_expose(win);
	XClearWindow(dpy, win);
      }
    }
    if (type != TiledPixmapButton)
    {
      if (DFS_H_JUSTIFICATION(df->style) == JUST_RIGHT)
	x += (int)(width - p->width);
      else if (DFS_H_JUSTIFICATION(df->style) == JUST_CENTER)
	/* round up for left buttons, down for right buttons */
	x += (int)(width - p->width + left1right0) / 2;
      if (DFS_V_JUSTIFICATION(df->style) == JUST_BOTTOM)
	y += (int)(height - p->height);
      else if (DFS_V_JUSTIFICATION(df->style) == JUST_CENTER)
	/* round up */
	y += (int)(height - p->height + 1) / 2;
      if (x < border)
	x = border;
      if (y < border)
	y = border;
      if (width > p->width)
	width = p->width;
      if (height > p->height)
	height = p->height;
      if (width > w - x - border)
	width = w - x - border;
      if (height > h - y - border)
	height = h - y - border;
    }

    XSetClipMask(dpy, Scr.TransMaskGC, p->mask);
    XSetClipOrigin(dpy, Scr.TransMaskGC, x, y);
    if (type != TiledPixmapButton)
    {
      int cx = x;
      int cy = y;
      int cw = width;
      int ch = height;
      int ox = 0;
      int oy = 0;

      clip_button_pixmap(&ox, &oy, &cw, &ch, &cx, &cy, rclip);
      if (cw > 0 && ch > 0)
      {
	XCopyArea(
	  dpy, p->picture, win, Scr.TransMaskGC, ox, oy, cw, ch, cx, cy);
      }
    }
    else
    {
      int xi;
      int yi;

      for (yi = border; yi < height; yi += p->height)
      {
	for (xi = border; xi < width; xi += p->width)
	{
	  int lw = width - xi;
	  int lh = height - yi;
	  int cx;
	  int cy;
	  int cw;
	  int ch;
	  int ox;
	  int oy;

	  if (lw > p->width)
	    lw = p->width;
	  if (lh > p->height)
	    lh = p->height;
	  if (rclip)
	  {
	    if (xi + lw <= rclip->x || xi >= rclip->x + rclip->width ||
		yi + lh <= rclip->y || yi >= rclip->y + rclip->height)
	    {
	      continue;
	    }
	  }
	  cx = xi;
	  cy = yi;
	  cw = lw;
	  ch = lh;
	  ox = 0;
	  oy = 0;
	  clip_button_pixmap(&ox, &oy, &cw, &ch, &cx, &cy, rclip);
	  if (cw > 0 && ch > 0)
	  {
	    XSetClipOrigin(dpy, Scr.TransMaskGC, xi, yi);
	    XCopyArea(dpy, p->picture, win, Scr.TransMaskGC,
		      ox, oy, cw, ch, cx, cy);
	  }
	}
      }
    }
    XSetClipMask(dpy, Scr.TransMaskGC, None);
    break;

  case GradientButton:
  {
    unsigned int g_width;
    unsigned int g_height;

    if (!rclip)
    {
      flush_expose(win);
    }
    XSetClipMask(dpy, Scr.TransMaskGC, None);
    /* find out the size the pixmap should be */
    CalculateGradientDimensions(
      dpy, win, df->u.grad.npixels, df->u.grad.gradient_type,
      &g_width, &g_height);
    /* draw the gradient directly into the window */
    CreateGradientPixmap(
      dpy, win, Scr.TransMaskGC, df->u.grad.gradient_type, g_width, g_height,
      df->u.grad.npixels, df->u.grad.pixels, win, 0, 0, w, h, rclip);
  }
  break;

  default:
    fvwm_msg(ERR, "DrawButton", "unknown button type");
    break;
  }

  return;
}

/* rules to get button state */
static enum ButtonState get_button_state(
  Bool has_focus, Bool toggled, Window w)
{
  if (Scr.gs.use_active_down_buttons)
  {
    if (Scr.gs.use_inactive_buttons && !has_focus)
    {
      return (toggled) ? BS_ToggledInactive : BS_Inactive;
    }
    else
    {
      if (PressedW == w)
      {
	return (toggled) ? BS_ToggledActiveDown : BS_ActiveDown;
      }
      else
      {
	return (toggled) ? BS_ToggledActiveUp : BS_ActiveUp;
      }
    }
  }
  else
  {
    if (Scr.gs.use_inactive_buttons && !has_focus)
    {
      return (toggled) ? BS_ToggledInactive : BS_Inactive;
    }
    else
    {
      return (toggled) ? BS_ToggledActiveUp : BS_ActiveUp;
    }
  }
}

static const char ulgc[] = { 1, 0, 0, 0x7f, 2, 1, 1 };
static const char brgc[] = { 1, 1, 2, 0x7f, 0, 0, 3 };

/* called twice by RedrawBorder */
static void draw_frame_relief(
  FvwmWindow *t, GC rgc, GC sgc, GC tgc, GC dgc, int w_dout, int w_hiout,
  int w_trout, int w_c, int w_trin, int w_shin, int w_din)
{
  int i;
  int offset = 0;
  int width = t->frame_g.width - 1;
  int height = t->frame_g.height - 1;
  int w[7];
  GC gc[4];

  w[0] = w_dout;
  w[1] = w_hiout;
  w[2] = w_trout;
  w[3] = w_c;
  w[4] = w_trin;
  w[5] = w_shin;
  w[6] = w_din;
  gc[0] = rgc;
  gc[1] = sgc;
  gc[2] = tgc;
  gc[3] = dgc;

  for (i = 0; i < 7; i++)
  {
    if (ulgc[i] != 0x7f && w[i] > 0)
    {
      RelieveRectangle(
	dpy, t->decor_w, offset, offset, width, height, gc[(int)ulgc[i]],
	gc[(int)brgc[i]], w[i]);
    }
    offset += w[i];
    width -= 2 * w[i];
    height -= 2 * w[i];
  }
}

/****************************************************************************
 *
 * Redraws the windows borders
 *
 ****************************************************************************/
static void RedrawBorder(
  common_decorations_type *cd, FvwmWindow *t, Bool has_focus, int force,
  Window expose_win, XRectangle *rclip)
{
  static GC tgc = None;
  int i;
  GC rgc;
  GC sgc;
  DecorFaceStyle *borderstyle;
  Bool is_reversed = False;
  Bool is_clipped = False;
  int w_dout;
  int w_hiout;
  int w_trout;
  int w_c;
  int w_trin;
  int w_shin;
  int w_din;
  int sum;
  int trim;

  if (tgc == None && !HAS_MWM_BORDER(t))
  {
    XGCValues xgcv;

    xgcv.function = GXnoop;
    xgcv.plane_mask = 0;
    tgc = fvwmlib_XCreateGC(dpy, t->frame, GCFunction | GCPlaneMask, &xgcv);
  }
  /*
   * draw the border; resize handles are InputOnly so draw in the decor_w and
   * it will show through. The entire border is redrawn so flush all exposes
   * up to this point.
   */
#if 0
  flush_expose(t->decor_w);
#endif

  if (t->boundary_width < 2)
  {
    /*
     * for mono - put a black border on
     */
    if (Pdepth <2)
    {
      XSetWindowBackgroundPixmap(dpy, t->decor_w, cd->back_pixmap);
      XClearWindow(dpy, t->decor_w);
    }
    else
    {
      if (cd->texture_pixmap != None)
	XSetWindowBackgroundPixmap(dpy, t->decor_w, cd->texture_pixmap);
      XClearWindow(dpy, t->decor_w);
    }
    return;
  }

  /*
   * for color, make it the color of the decoration background
   */

  /* get the border style bits */
  borderstyle = (has_focus) ?
    &GetDecor(t, BorderStyle.active.style) :
    &GetDecor(t, BorderStyle.inactive.style);

  is_reversed ^= (borderstyle->flags.button_relief == DFS_BUTTON_IS_SUNK);
  if (is_reversed)
  {
    sgc = cd->relief_gc;
    rgc = cd->shadow_gc;
  }
  else
  {
    rgc = cd->relief_gc;
    sgc = cd->shadow_gc;
  }

  /*
   * remove any lines drawn in the border if hidden handles or noinset and if
   * a handle was pressed
   */

  if (PressedW == None &&
      (DFS_HAS_NO_INSET(*borderstyle) ||
       DFS_HAS_HIDDEN_HANDLES(*borderstyle)))
  {
    ClipClear(t->decor_w, rclip, False);
  }

  /*
   * Nothing to do if flat
   */

  if (borderstyle->flags.button_relief == DFS_BUTTON_IS_FLAT)
    return;

  /*
   * draw the inside relief
   */

  if (HAS_MWM_BORDER(t))
  {
    /* MWM borders look like this:
     *
     * HHCCCCS  from outside to inside on the left and top border
     * SSCCCCH  from outside to inside on the bottom and right border
     * |||||||
     * |||||||__ w_shin       (inner shadow area)
     * ||||||___ w_c          (transparent area)
     * |||||____ w_c          (transparent area)
     * ||||_____ w_c          (transparent area)
     * |||______ w_c          (transparent area)
     * ||_______ w_hiout      (outer hilight area)
     * |________ w_hiout      (outer hilight area)
     *
     *
     * C = original colour
     * H = hilight
     * S = shadow
     */
    w_dout = 0;
    w_hiout = 2;
    w_trout = 0;
    w_trin = 0;
    w_shin = 1;
    w_din = 0;
    sum = 3;
    trim = sum - t->boundary_width + 1;
  }
  else
  {
    /* Fvwm borders look like this:
     *
     * SHHCCSS  from outside to inside on the left and top border
     * SSCCHHS  from outside to inside on the bottom and right border
     * |||||||
     * |||||||__ w_din        (inner dark area)
     * ||||||___ w_shin       (inner shadow area)
     * |||||____ w_trin       (inner transparent/shadow area)
     * ||||_____ w_c          (transparent area)
     * |||______ w_trout      (outer transparent/hilight area)
     * ||_______ w_hiout      (outer hilight area)
     * |________ w_dout       (outer dark area)
     *
     * C = original colour
     * H = hilight
     * S = shadow
     *
     * reduced to 5 pixels it looks like this:
     *
     * SHHCS
     * SSCHS
     * |||||
     * |||||__ w_din        (inner dark area)
     * ||||___ w_trin       (inner transparent/shadow area)
     * |||____ w_trout      (outer transparent/hilight area)
     * ||_____ w_hiout      (outer hilight area)
     * |______ w_dout       (outer dark area)
     */
    w_dout = 1;
    w_hiout = 1;
    w_trout = 1;
    w_trin = 1;
    w_shin = 1;
    w_din = 1;
    /* w_trout + w_trin counts only as one pixel of border because they let one
     * pixel of the original colour shine through. */
    sum = 6;
    trim = sum - t->boundary_width;
  }
  if (DFS_HAS_NO_INSET(*borderstyle))
  {
    w_shin = 0;
    sum--;
    trim--;
    if (w_trin)
    {
      w_trout = 0;
      w_trin = 0;
      w_din = 0;
      w_hiout = 1;
      sum -= 2;
      trim -= 2;
    }
  }
  /* If the border is too thin to accomodate the standard look, we remove parts
   * of the border so that at least one pixel of the original colour is
   * visible. We make an exception for windows with a border width of 2,
   * though. */
  if ((!IS_SHADED(t) || HAS_TITLE(t)) && t->boundary_width == 2)
    trim--;
  if (trim < 0)
    trim = 0;
  for ( ; trim > 0; trim--)
  {
    if (w_hiout > 1)
      w_hiout--;
    else if (w_shin > 0)
      w_shin--;
    else if (w_hiout > 0)
      w_hiout--;
    else if (w_trout > 0)
    {
      w_trout = 0;
      w_trin = 0;
      w_din = 0;
      w_hiout = 1;
    }
    sum--;
  }
  w_c = t->boundary_width - sum;
  if (rclip)
  {
    XSetClipRectangles(dpy, rgc, 0, 0, rclip, 1, Unsorted);
    XSetClipRectangles(dpy, sgc, 0, 0, rclip, 1, Unsorted);
    is_clipped = True;
  }
  draw_frame_relief(
    t, rgc, sgc, tgc, sgc,
    w_dout, w_hiout, w_trout, w_c, w_trin, w_shin, w_din);

  /*
   * draw the handle marks
   */

  /* draw the handles as eight marks around the border */
  if (HAS_BORDER(t) && (t->boundary_width > 1) &&
      !DFS_HAS_HIDDEN_HANDLES(*borderstyle))
  {
    /* MWM border windows have thin 3d effects
     * FvwmBorders have 3 pixels top/left, 2 bot/right so this makes
     * calculating the length of the marks difficult, top and bottom
     * marks for FvwmBorders are different if NoInset is specified */
    int inset = (w_shin || w_din);
    XSegment marks[8];
    int k;

    /*
     * draw the relief
     */
    for (k = 0; k < cd->relief_width; k++)
    {
      int loff = k;
      int boff = k + w_dout + 1;
      int length = t->boundary_width - boff - inset;

      if (length < 0)
	break;
      /* hilite marks */
      i = 0;
      /* top left */
      marks[i].x1 = t->visual_corner_width + loff;
      marks[i].x2 = t->visual_corner_width + loff;
      marks[i].y1 = w_dout;
      marks[i].y2 = marks[i].y1 + length;
      i++;
      /* top right */
      marks[i].x1 = t->frame_g.width - t->visual_corner_width + loff;
      marks[i].x2 = t->frame_g.width - t->visual_corner_width + loff;
      marks[i].y1 = w_dout;
      marks[i].y2 = marks[i].y1 + length;
      i++;
      /* bot left */
      marks[i].x1 = t->visual_corner_width + loff;
      marks[i].x2 = t->visual_corner_width + loff;
      marks[i].y1 = t->frame_g.height - boff;
      marks[i].y2 = marks[i].y1 - length;
      i++;
      /* bot right */
      marks[i].x1 = t->frame_g.width - t->visual_corner_width + loff;
      marks[i].x2 = t->frame_g.width - t->visual_corner_width + loff;
      marks[i].y1 = t->frame_g.height - boff;
      marks[i].y2 = marks[i].y1 - length;
      i++;
      if (!IS_SHADED(t))
      {
	/* left top */
	marks[i].x1 = w_dout;
	marks[i].x2 = marks[i].x1 + length;
	marks[i].y1 = t->visual_corner_width + loff;
	marks[i].y2 = t->visual_corner_width + loff;
	i++;
	/* left bot */
	marks[i].x1 = w_dout;
	marks[i].x2 = marks[i].x1 + length;
	marks[i].y1 = t->frame_g.height-t->visual_corner_width + loff;
	marks[i].y2 = t->frame_g.height-t->visual_corner_width + loff;
	i++;
	/* right top */
	marks[i].x1 = t->frame_g.width - boff;
	marks[i].x2 = marks[i].x1 - length;
	marks[i].y1 = t->visual_corner_width + loff;
	marks[i].y2 = t->visual_corner_width + loff;
	i++;
	/* right bot */
	marks[i].x1 = t->frame_g.width - boff;
	marks[i].x2 = marks[i].x1 - length;
	marks[i].y1 = t->frame_g.height-t->visual_corner_width + loff;
	marks[i].y2 = t->frame_g.height-t->visual_corner_width + loff;
	i++;
      }
      XDrawSegments(dpy, t->decor_w, rgc, marks, i);

      /* shadow marks, reuse the array (XDrawSegments doesn't trash it) */
      i = 0;
      loff = 1 + k + k;
      /* top left */
      marks[i].x1 -= loff;
      marks[i].x2 -= loff;
      marks[i].y1 += k;
      marks[i].y2 += k;
      i++;
      /* top right */
      marks[i].x1 -= loff;
      marks[i].x2 -= loff;
      marks[i].y1 += k;
      marks[i].y2 += k;
      i++;
      /* bot left */
      marks[i].x1 -= loff;
      marks[i].x2 -= loff;
      marks[i].y1 += k;
      marks[i].y2 += k;
      i++;
      /* bot right */
      marks[i].x1 -= loff;
      marks[i].x2 -= loff;
      marks[i].y1 += k;
      marks[i].y2 += k;
      i++;
      if (!IS_SHADED(t))
      {
	/* left top */
	marks[i].x1 += k;
	marks[i].x2 += k;
	marks[i].y1 -= loff;
	marks[i].y2 -= loff;
	i++;
	/* left bot */
	marks[i].x1 += k;
	marks[i].x2 += k;
	marks[i].y1 -= loff;
	marks[i].y2 -= loff;
	i++;
	/* right top */
	marks[i].x1 += k;
	marks[i].x2 += k;
	marks[i].y1 -= loff;
	marks[i].y2 -= loff;
	i++;
	/* right bot */
	marks[i].x1 += k;
	marks[i].x2 += k;
	marks[i].y1 -= loff;
	marks[i].y2 -= loff;
	i++;
      }
      XDrawSegments(dpy, t->decor_w, sgc, marks, i);
    }
  }

  /*
   * now draw the pressed in part on top if we have depressable borders
   */

  /* a bit hacky to draw twice but you should see the code it replaces, never
   * mind the esoterics, feel the thin-ness */
  if ((HAS_BORDER(t) || PressedW == t->decor_w || PressedW == t->frame) &&
      HAS_DEPRESSABLE_BORDER(t))
  {
    XRectangle r;
    Bool is_pressed = False;

    if (PressedW == t->sides[0])
    {
      /* top */
      r.x = t->visual_corner_width;
      r.y = 1;
      r.width = t->frame_g.width - 2 * t->visual_corner_width;
      r.height = t->boundary_width - 1;
      is_pressed = True;
    }
    else if (PressedW == t->sides[1])
    {
      /* right */
      if (!IS_SHADED(t))
      {
	r.x = t->frame_g.width - t->boundary_width;
	r.y = t->visual_corner_width;
	r.width = t->boundary_width - 1;
	r.height = t->frame_g.height - 2 * t->visual_corner_width;
	is_pressed = True;
      }
    }
    else if (PressedW == t->sides[2])
    {
      /* bottom */
      r.x = t->visual_corner_width;
      r.y = t->frame_g.height - t->boundary_width;
      r.width = t->frame_g.width - 2 * t->visual_corner_width;
      r.height = t->boundary_width - 1;
      is_pressed = True;
    }
    else if (PressedW == t->sides[3])
    {
      /* left */
      if (!IS_SHADED(t))
      {
	r.x = 1;
	r.y = t->visual_corner_width;
	r.width = t->boundary_width - 1;
	r.height = t->frame_g.height - 2 * t->visual_corner_width;
	is_pressed = True;
      }
    }
    else if (PressedW == t->corners[0])
    {
      /* top left */
      r.x = 1;
      r.y = 1;
      r.width = t->visual_corner_width - 1;
      r.height = t->visual_corner_width - 1;
      is_pressed = True;
    }
    else if (PressedW == t->corners[1])
    {
      /* top right */
      r.x = t->frame_g.width - t->visual_corner_width;
      r.y = 1;
      r.width = t->visual_corner_width - 1;
      r.height = t->visual_corner_width - 1;
      is_pressed = True;
    }
    else if (PressedW == t->corners[2])
    {
      /* bottom left */
      r.x = 1;
      r.y = t->frame_g.height - t->visual_corner_width;
      r.width = t->visual_corner_width - 1;
      r.height = t->visual_corner_width - 1;
      is_pressed = True;
    }
    else if (PressedW == t->corners[3])
    {
      /* bottom right */
      r.x = t->frame_g.width - t->visual_corner_width;
      r.y = t->frame_g.height - t->visual_corner_width;
      r.width = t->visual_corner_width - 1;
      r.height = t->visual_corner_width - 1;
      is_pressed = True;
    }
    else if (PressedW == t->decor_w || PressedW == t->frame)
    {
      /* whole border */
      r.x = 1;
      r.y = 1;
      r.width = t->frame_g.width - 2;
      r.height = t->frame_g.height - 2;
      is_pressed = True;
    }

    if (is_pressed == True)
    {
      Bool is_not_empty = True;

      if (rclip)
      {
	is_not_empty = intersect_xrectangles(&r, rclip);
      }
      if (is_not_empty)
      {
	XSetClipRectangles(dpy, rgc, 0, 0, &r, 1, Unsorted);
	XSetClipRectangles(dpy, sgc, 0, 0, &r, 1, Unsorted);
	draw_frame_relief(
	  t, sgc, rgc, tgc, sgc,
	  w_dout, w_hiout, w_trout, w_c, w_trin, w_shin, w_din);
	is_clipped = True;
      }
    }
  }
  if (is_clipped)
  {
    XSetClipMask(dpy, rgc, None);
    XSetClipMask(dpy, sgc, None);
  }

  return;
}

/****************************************************************************
 *
 * Redraws the window buttons
 *
 ****************************************************************************/
static void RedrawButtons(
  common_decorations_type *cd, FvwmWindow *t, Bool has_focus, int force,
  Window expose_win, XRectangle *rclip)
{
  int i;
  Bool is_lowest = True;
  Pixmap *pass_bg_pixmap;

  /* Note: the rclip rectangle is not used in this function. Buttons are usually
   * so small that it makes not much sense to limit drawing to a clip rectangle.
   */

  /*
   * draw buttons
   */

  for (i = 0; i < NUMBER_OF_BUTTONS; i++)
  {
    if (t->button_w[i] != None && (((i & 1) && i / 2 < Scr.nr_right_buttons) ||
				   (!(i & 1) && i / 2 < Scr.nr_left_buttons)))
    {
      mwm_flags stateflags =
	TB_MWM_DECOR_FLAGS(GetDecor(t, buttons[i]));
      Bool toggled =
	(HAS_MWM_BUTTONS(t) &&
	 ((stateflags & MWM_DECOR_MAXIMIZE && IS_MAXIMIZED(t)) ||
	  (stateflags & MWM_DECOR_SHADE && IS_SHADED(t)) ||
	  (stateflags & MWM_DECOR_STICK && IS_STICKY(t))));
      enum ButtonState bs =
	get_button_state(has_focus, toggled, t->button_w[i]);
      DecorFace *df = &TB_STATE(GetDecor(t, buttons[i]))[bs];
      if(flush_expose(t->button_w[i]) || expose_win == t->button_w[i] ||
	 expose_win == None || cd->flags.has_color_changed)
      {
	int is_inverted = (PressedW == t->button_w[i]);

	if (DFS_USE_BORDER_STYLE(df->style))
	{
	  XChangeWindowAttributes(
	    dpy, t->button_w[i], cd->valuemask, &cd->attributes);
	  if (df->u.p)
	  {
	    t->button_background_pixmap[i] = df->u.p->picture;
	  }
	  if (df->u.p && df->u.p->picture)
	  {
	    pass_bg_pixmap = NULL;
	  }
	  else
	  {
	    pass_bg_pixmap = &t->button_background_pixmap[i];
	  }
	}
	else
	{
	  XChangeWindowAttributes(
	    dpy, t->button_w[i], cd->notex_valuemask, &cd->notex_attributes);
	  t->button_background_pixmap[i] = None;
	  pass_bg_pixmap = &t->button_background_pixmap[i];
	}
	XClearWindow(dpy, t->button_w[i]);
	if (DFS_USE_TITLE_STYLE(df->style))
	{
	  DecorFace *tsdf = &TB_STATE(GetDecor(t, titlebar))[bs];

	  is_lowest = True;
#ifdef MULTISTYLE
	  for (; tsdf; tsdf = tsdf->next)
#endif
	  {
	    DrawButton(t, t->button_w[i], t->title_g.height, t->title_g.height,
		       tsdf, cd->relief_gc, cd->shadow_gc,
		       cd->fore_color, cd->back_color, is_lowest,
		       TB_MWM_DECOR_FLAGS(GetDecor(t, buttons[i])), 1, NULL,
		       pass_bg_pixmap);
	    is_lowest = False;
	  }
	}
	is_lowest = True;
#ifdef MULTISTYLE
	for (; df; df = df->next)
#endif
	{
	  DrawButton(t, t->button_w[i], t->title_g.height, t->title_g.height,
		     df, cd->relief_gc, cd->shadow_gc,
		     cd->fore_color, cd->back_color, is_lowest,
		     TB_MWM_DECOR_FLAGS(GetDecor(t, buttons[i])), 1, NULL,
		     pass_bg_pixmap);
	  is_lowest = False;
	}

	{
	  Bool reverse = is_inverted;

	  switch (DFS_BUTTON_RELIEF(
	    TB_STATE(GetDecor(t, buttons[i]))[bs].style))
	  {
	  case DFS_BUTTON_IS_SUNK:
	    reverse = !is_inverted;
	  case DFS_BUTTON_IS_UP:
	    RelieveRectangle2(
	      dpy, t->button_w[i], 0, 0, t->title_g.height - 1,
	      t->title_g.height - 1, (reverse ? cd->shadow_gc : cd->relief_gc),
	      (reverse ? cd->relief_gc : cd->shadow_gc), cd->relief_width);
	    break;
	  default:
	    /* flat */
	    break;
	  }
	}
      }
    } /* if (HAS_BUTTON(i))*/
  } /* for */
}


/****************************************************************************
 *
 *  Redraws just the title bar
 *
 ****************************************************************************/
static void RedrawTitle(
  common_decorations_type *cd, FvwmWindow *t, Bool has_focus, XRectangle *rclip)
{
  int hor_off;
  int w;
  int i;
  enum ButtonState title_state;
  DecorFaceStyle *tb_style;
  Pixmap *pass_bg_pixmap;
  GC rgc = cd->relief_gc;
  GC sgc = cd->shadow_gc;
  Bool reverse = False;
  Bool is_clipped = False;
  Bool toggled =
    (HAS_MWM_BUTTONS(t) &&
     ((TB_HAS_MWM_DECOR_MAXIMIZE(GetDecor(t, titlebar)) && IS_MAXIMIZED(t))||
      (TB_HAS_MWM_DECOR_SHADE(GetDecor(t, titlebar)) && IS_SHADED(t)) ||
      (TB_HAS_MWM_DECOR_STICK(GetDecor(t, titlebar)) && IS_STICKY(t))));

  if (PressedW == t->title_w)
  {
    GC tgc;

    tgc = sgc;
    sgc = rgc;
    rgc = tgc;
  }
#if 0
  flush_expose(t->title_w);
#endif

  if (t->name != (char *)NULL)
  {
#ifdef I18N_MB /* cannot use macro here, rewriting... */
    w = XmbTextEscapement(t->title_font.fontset,t->name, strlen(t->name));
#else
    w = XTextWidth(t->title_font.font, t->name, strlen(t->name));
#endif
    if (w > t->title_g.width - 12)
      w = t->title_g.width - 4;
    if (w < 0)
      w = 0;
  }
  else
  {
    w = 0;
  }

  title_state = get_button_state(has_focus, toggled, t->title_w);
  tb_style = &TB_STATE(GetDecor(t, titlebar))[title_state].style;
  switch (TB_JUSTIFICATION(GetDecor(t, titlebar)))
  {
  case JUST_LEFT:
    hor_off = WINDOW_TITLE_TEXT_OFFSET;
    break;
  case JUST_RIGHT:
    hor_off = t->title_g.width - w - WINDOW_TITLE_TEXT_OFFSET;
    break;
  case JUST_CENTER:
  default:
    hor_off = (t->title_g.width - w) / 2;
    break;
  }

  NewFontAndColor(
    t->title_font.font->fid, cd->fore_color, cd->back_color);

  /* the next bit tries to minimize redraw ([EMAIL PROTECTED]) */
  /* we need to check for UseBorderStyle for the titlebar */
  {
    DecorFace *df = (has_focus) ?
      &GetDecor(t,BorderStyle.active) : &GetDecor(t,BorderStyle.inactive);

    if (DFS_USE_BORDER_STYLE(*tb_style) &&
	DFS_FACE_TYPE(df->style) == TiledPixmapButton &&
	df->u.p)
    {
      XSetWindowBackgroundPixmap(dpy, t->title_w, df->u.p->picture);
      t->title_background_pixmap = df->u.p->picture;
      pass_bg_pixmap = NULL;
    }
    else
    {
      pass_bg_pixmap = &t->title_background_pixmap;
    }
  }
  ClipClear(t->title_w, rclip, False);
  if (rclip)
  {
    XSetClipRectangles(dpy, rgc, 0, 0, rclip, 1, Unsorted);
    XSetClipRectangles(dpy, sgc, 0, 0, rclip, 1, Unsorted);
    XSetClipRectangles(dpy, Scr.TitleGC, 0, 0, rclip, 1, Unsorted);
    is_clipped = True;
  }

  /*
   * draw title
   */

  if (Pdepth < 2)
  {
    /* for mono, we clear an area in the title bar where the window
     * title goes, so that its more legible. For color, no need */
    RelieveRectangle(
      dpy, t->title_w, 0, 0, hor_off - 3, t->title_g.height - 1, rgc, sgc,
      cd->relief_width);
    RelieveRectangle(
      dpy, t->title_w, hor_off + w + 2, 0, t->title_g.width - w - hor_off - 3,
      t->title_g.height - 1, rgc, sgc, cd->relief_width);
    XFillRectangle(
      dpy, t->title_w, ((PressedW == t->title_w) ? sgc : rgc), hor_off - 2, 0,
      w+4,t->title_g.height);
    XDrawLine(
      dpy, t->title_w, sgc, hor_off + w + 1, 0, hor_off + w + 1,
      t->title_g.height);
    if(t->name != (char *)NULL)
#ifdef I18N_MB
      XmbDrawString(dpy, t->title_w, t->title_font.fontset,
		    Scr.TitleGC, hor_off,
		    t->title_font.y + 1, t->name, strlen(t->name));
#else
      XDrawString(dpy, t->title_w, Scr.TitleGC, hor_off,
                  t->title_font.y + 1, t->name, strlen(t->name));
#endif
  }
  else
  {
    DecorFace *df = &TB_STATE(GetDecor(t, titlebar))[title_state];
    /* draw compound titlebar ([EMAIL PROTECTED]) */
    Bool is_lowest = True;

    if (PressedW == t->title_w)
    {
#ifdef MULTISTYLE
      for (; df; df = df->next)
#endif
      {
	DrawButton(
	  t, t->title_w, t->title_g.width, t->title_g.height, df, sgc,
	  rgc, cd->fore_color, cd->back_color, is_lowest, 0, 1, rclip,
          pass_bg_pixmap);
	is_lowest = False;
      }
    }
    else
    {
#ifdef MULTISTYLE
      for (; df; df = df->next)
#endif
      {
	DrawButton(
	  t, t->title_w, t->title_g.width, t->title_g.height, df, rgc,
	  sgc, cd->fore_color, cd->back_color, is_lowest, 0, 1, rclip,
          pass_bg_pixmap);
	is_lowest = False;
      }
    }
    /*
     * draw title relief
     */
    switch (DFS_BUTTON_RELIEF(*tb_style))
    {
    case DFS_BUTTON_IS_SUNK:
      reverse = 1;
    case DFS_BUTTON_IS_UP:
      RelieveRectangle2(
	dpy, t->title_w, 0, 0, t->title_g.width - 1, t->title_g.height - 1,
	(reverse) ? sgc : rgc, (reverse) ? rgc : sgc, cd->relief_width);
      break;
    default:
      /* flat */
      break;
    }

#ifdef I18N_MB
    if(t->name != (char *)NULL)
      XmbDrawString(dpy, t->title_w, t->title_font.fontset,
		    Scr.TitleGC, hor_off,
		    t->title_font.y + 1, t->name, strlen(t->name));
#else
    if(t->name != (char *)NULL)
      XDrawString(dpy, t->title_w, Scr.TitleGC, hor_off,
		  t->title_font.y + 1, t->name, strlen(t->name));
#endif
  }

  /*
   * draw the 'sticky' lines
   */

  if (IS_STICKY(t) || HAS_STIPPLED_TITLE(t))
  {
    /* an odd number of lines every WINDOW_TITLE_STICK_VERT_DIST pixels */
    int num =
      (int)(t->title_g.height / WINDOW_TITLE_STICK_VERT_DIST / 2) * 2 - 1;
    int min = t->title_g.height / 2 - num * 2 + 1;
    int max =
            t->title_g.height / 2 + num * 2 - WINDOW_TITLE_STICK_VERT_DIST + 1;
    int left_x = WINDOW_TITLE_STICK_OFFSET;
    int left_w = hor_off - left_x - WINDOW_TITLE_TO_STICK_GAP;
    int right_x = hor_off + w + WINDOW_TITLE_TO_STICK_GAP - 1;
    int right_w = t->title_g.width - right_x - WINDOW_TITLE_STICK_OFFSET;

    if (left_w < WINDOW_TITLE_STICK_MIN_WIDTH)
    {
      left_x = 0;
      left_w = WINDOW_TITLE_STICK_MIN_WIDTH;
    }
    if (right_w < WINDOW_TITLE_STICK_MIN_WIDTH)
    {
      right_w = WINDOW_TITLE_STICK_MIN_WIDTH;
      right_x = t->title_g.width - WINDOW_TITLE_STICK_MIN_WIDTH - 1;
    }
    for (i = min; i <= max; i += WINDOW_TITLE_STICK_VERT_DIST)
    {
      if (left_w > 0)
      {
	RelieveRectangle(
          dpy, t->title_w, left_x, i, left_w, 1, sgc, rgc, 1);
      }
      if (right_w > 0)
      {
	RelieveRectangle(
          dpy, t->title_w, right_x, i, right_w, 1, sgc, rgc, 1);
      }
    }
  }

  if (is_clipped)
  {
    XSetClipMask(dpy, rgc, None);
    XSetClipMask(dpy, sgc, None);
    XSetClipMask(dpy, Scr.TitleGC, None);
  }

  return;
}


void SetupTitleBar(FvwmWindow *tmp_win, int w, int h)
{
  XWindowChanges xwc;
  unsigned long xwcm;
  int i;
  int buttons = 0;
  int ww = tmp_win->frame_g.width - 2 * tmp_win->boundary_width;
  int rest = 0;
  int bw;

  if (HAS_BOTTOM_TITLE(tmp_win))
  {
    tmp_win->title_g.y =
      h - tmp_win->boundary_width - tmp_win->title_g.height;
    tmp_win->title_top_height = 0;
  }
  else
  {
    tmp_win->title_g.y = tmp_win->boundary_width;
    tmp_win->title_top_height = tmp_win->title_g.height;
  }
  tmp_win->title_g.x = tmp_win->boundary_width;

  xwcm = CWX | CWY | CWHeight | CWWidth;
  xwc.y = tmp_win->title_g.y;
  xwc.width = tmp_win->title_g.height;
  xwc.height = tmp_win->title_g.height;
  for (i = 0; i < NUMBER_OF_BUTTONS; i++)
  {
    if (tmp_win->button_w[i])
      buttons++;
  }
  ww = tmp_win->frame_g.width - 2 * tmp_win->boundary_width -
    tmp_win->title_g.width;
  if (ww < buttons * xwc.width)
  {
    xwc.width = ww / buttons;
    if (xwc.width < 1)
      xwc.width = 1;
    if (xwc.width > tmp_win->title_g.height)
      xwc.width = tmp_win->title_g.height;
    rest = ww - buttons * xwc.width;
    if (rest > 0)
      xwc.width++;
  }
  /* left */
  xwc.x = tmp_win->boundary_width;
  for (i = 0; i < NUMBER_OF_BUTTONS; i += 2)
  {
    if (tmp_win->button_w[i] != None)
    {
      if (xwc.x + xwc.width < w - tmp_win->boundary_width)
      {
	XConfigureWindow(dpy, tmp_win->button_w[i], xwcm, &xwc);
	xwc.x += xwc.width;
        tmp_win->title_g.x += xwc.width;
      }
      else
      {
	xwc.x = -tmp_win->title_g.height;
	XConfigureWindow(dpy, tmp_win->button_w[i], xwcm, &xwc);
      }
      rest--;
      if (rest == 0)
      {
        xwc.width--;
      }
    }
  }
  bw = xwc.width;

  /* title */
  if (tmp_win->title_g.width <= 0 || ww < 0)
    tmp_win->title_g.x = -10;
  xwc.x = tmp_win->title_g.x;
  xwc.width = tmp_win->title_g.width;
  XConfigureWindow(dpy, tmp_win->title_w, xwcm, &xwc);

  /* right */
  xwc.width = bw;
  xwc.x = w - tmp_win->boundary_width - xwc.width;
  for (i = 1 ; i < NUMBER_OF_BUTTONS; i += 2)
  {
    if (tmp_win->button_w[i] != None)
    {
      if (xwc.x > tmp_win->boundary_width)
      {
	XConfigureWindow(dpy, tmp_win->button_w[i], xwcm, &xwc);
	xwc.x -= xwc.width;
      }
      else
      {
	xwc.x = -tmp_win->title_g.height;
	XConfigureWindow(dpy, tmp_win->button_w[i], xwcm, &xwc);
      }
      rest--;
      if (rest == 0)
      {
        xwc.width--;
        xwc.x++;
      }
    }
  }

  return;
}


static void get_common_decorations(
  common_decorations_type *cd, FvwmWindow *t, draw_window_parts draw_parts,
  Bool has_focus, int force, Window expose_win, Bool is_border,
  Bool do_change_gcs)
{
  color_quad *draw_colors;

  if (has_focus)
  {
    /* are we using textured borders? */
    if (DFS_FACE_TYPE(
      GetDecor(t, BorderStyle.active.style)) == TiledPixmapButton)
    {
      cd->texture_pixmap = GetDecor(t, BorderStyle.active.u.p->picture);
    }
    cd->back_pixmap= Scr.gray_pixmap;
    if (is_border)
      draw_colors = &(t->border_hicolors);
    else
      draw_colors = &(t->hicolors);
  }
  else
  {
    if (DFS_FACE_TYPE(GetDecor(t, BorderStyle.inactive.style)) ==
	TiledPixmapButton)
    {
      cd->texture_pixmap = GetDecor(t, BorderStyle.inactive.u.p->picture);
    }
    if(IS_STICKY(t))
      cd->back_pixmap = Scr.sticky_gray_pixmap;
    else
      cd->back_pixmap = Scr.light_gray_pixmap;
    if (is_border)
      draw_colors = &(t->border_colors);
    else
      draw_colors = &(t->colors);
  }
  cd->fore_color = draw_colors->fore;
  cd->back_color = draw_colors->back;
  if (do_change_gcs)
  {
    Globalgcv.foreground = draw_colors->hilight;
    Globalgcm = GCForeground;
    XChangeGC(dpy, Scr.ScratchGC1, Globalgcm, &Globalgcv);
    cd->relief_gc = Scr.ScratchGC1;
    Globalgcv.foreground = draw_colors->shadow;
    XChangeGC(dpy, Scr.ScratchGC2, Globalgcm, &Globalgcv);
    cd->shadow_gc = Scr.ScratchGC2;
  }

  /* MWMBorder style means thin 3d effects */
  cd->relief_width = (HAS_MWM_BORDER(t) ? 1 : 2);

  if (cd->texture_pixmap)
  {
    cd->attributes.background_pixmap = cd->texture_pixmap;
    cd->valuemask = CWBackPixmap;
    if (Pdepth < 2)
    {
      cd->notex_attributes.background_pixmap = cd->back_pixmap;
      cd->notex_valuemask = CWBackPixmap;
    }
    else
    {
      cd->notex_attributes.background_pixel = cd->back_color;
      cd->notex_valuemask = CWBackPixel;
    }
  }
  else
  {
    if (Pdepth < 2)
    {
      cd->attributes.background_pixmap = cd->back_pixmap;
      cd->valuemask = CWBackPixmap;
      cd->notex_attributes.background_pixmap = cd->back_pixmap;
      cd->notex_valuemask = CWBackPixmap;
    }
    else
    {
      cd->attributes.background_pixel = cd->back_color;
      cd->valuemask = CWBackPixel;
      cd->notex_attributes.background_pixel = cd->back_color;
      cd->notex_valuemask = CWBackPixel;
    }
  }
}

void draw_clipped_decorations(
  FvwmWindow *t, draw_window_parts draw_parts, Bool has_focus, int force,
  Window expose_win, XRectangle *rclip)
{
  common_decorations_type cd;
  Bool is_frame_redraw_allowed = False;
  Bool is_button_redraw_allowed = False;
  Bool is_title_redraw_allowed = False;
  Bool do_redraw_title = False;
  Bool do_redraw_buttons = False;
  Bool do_change_gcs = False;

  if (!t)
    return;
  memset(&cd, 0, sizeof(cd));
  if (force || expose_win == None)
  {
    is_frame_redraw_allowed = True;
    is_button_redraw_allowed = True;
    is_title_redraw_allowed = True;
  }
  else if (expose_win == t->title_w)
    is_title_redraw_allowed = True;
  else if (expose_win == t->frame || expose_win == t->decor_w)
    /* sides and corners are InputOnly and incapable of triggering Expose events
     */
  {
    is_frame_redraw_allowed = True;
  }
  else if (expose_win != None)
    is_button_redraw_allowed = True;

  if (has_focus)
  {
    /* don't re-draw just for kicks */
    if(!force && Scr.Hilite == t)
    {
      is_frame_redraw_allowed = False;
      is_button_redraw_allowed = False;
      is_title_redraw_allowed = False;
    }
    else
    {
      if (Scr.Hilite != t || force > 1)
	cd.flags.has_color_changed = True;
      if (Scr.Hilite != t && Scr.Hilite != NULL)
      {
	/* make sure that the previously highlighted window got
	 * unhighlighted */
	DrawDecorations(Scr.Hilite, DRAW_ALL, False, True, None);
      }
      Scr.Hilite = t;
    }
  }
  else
  {
    /* don't re-draw just for kicks */
    if (!force && Scr.Hilite != t)
    {
      is_frame_redraw_allowed = False;
      is_button_redraw_allowed = False;
      is_title_redraw_allowed = False;
    }
    else if (Scr.Hilite == t || force > 1)
    {
      cd.flags.has_color_changed = True;
      Scr.Hilite = NULL;
    }
  }
  if(IS_ICONIFIED(t))
  {
    DrawIconWindow(t);
    return;
  }

  /* calculate some values and flags */
  if ((draw_parts & DRAW_TITLE) && HAS_TITLE(t) && is_title_redraw_allowed)
  {
    do_redraw_title = True;
    do_change_gcs = True;
  }
  if ((draw_parts & DRAW_BUTTONS) && HAS_TITLE(t) && is_button_redraw_allowed)
  {
    do_redraw_buttons = True;
    do_change_gcs = True;
  }
  get_common_decorations(
    &cd, t, draw_parts, has_focus, force, expose_win, False, do_change_gcs);

  /* redraw */
  if (cd.flags.has_color_changed && HAS_TITLE(t))
  {
    if (!do_redraw_title || !rclip)
    {
      if (t->title_background_pixmap == None || force)
      {
	change_window_background(
	  t->title_w, cd.notex_valuemask, &cd.notex_attributes);
	t->title_background_pixmap = None;
      }
    }
  }
  if (do_redraw_buttons)
  {
    RedrawButtons(&cd, t, has_focus, force, expose_win, rclip);
  }
  if (do_redraw_title)
  {
    RedrawTitle(&cd, t, has_focus, rclip);
  }
  if (cd.flags.has_color_changed ||
      ((draw_parts & DRAW_FRAME) && (is_frame_redraw_allowed)))
  {
    get_common_decorations(
      &cd, t, draw_parts, has_focus, force, expose_win, True, True);
    if (!rclip || !IS_WINDOW_BORDER_DRAWN(t))
    {
      change_window_background(t->decor_w, cd.valuemask, &cd.attributes);
      SET_WINDOW_BORDER_DRAWN(t, 1);
      rclip = NULL;
    }
    RedrawBorder(&cd, t, has_focus, force, expose_win, rclip);
  }

  /* Sync to make the change look fast! */
  XSync(dpy,0);
}

void DrawDecorations(
  FvwmWindow *t, draw_window_parts draw_parts, Bool has_focus, int force,
  Window expose_win)
{
  draw_clipped_decorations(t, draw_parts, has_focus, force, expose_win, NULL);

  return;
}


/***********************************************************************
 *
 *  Procedure:
 *      SetupFrame - set window sizes
 *
 *  Inputs:
 *      tmp_win - the FvwmWindow pointer
 *      x       - the x coordinate of the upper-left outer corner of the frame
 *      y       - the y coordinate of the upper-left outer corner of the frame
 *      w       - the width of the frame window w/o border
 *      h       - the height of the frame window w/o border
 *
 *  Special Considerations:
 *      This routine will check to make sure the window is not completely
 *      off the display, if it is, it'll bring some of it back on.
 *
 *      The tmp_win->frame_XXX variables should NOT be updated with the
 *      values of x,y,w,h prior to calling this routine, since the new
 *      values are compared against the old to see whether a synthetic
 *      ConfigureNotify event should be sent.  (It should be sent if the
 *      window was moved but not resized.)
 *
 ************************************************************************/

void SetupFrame(
  FvwmWindow *tmp_win, int x, int y, int w, int h, Bool sendEvent)
{
  XWindowChanges xwc;
  unsigned long xwcm;
  int i;
  Bool is_resized = False;
  Bool is_moved = False;
  int xwidth;
  int ywidth;
  int left;
  int right;
  int shaded = IS_SHADED(tmp_win);

  int xmove_sign;
  int ymove_sign;
  int xsize_sign;
  int ysize_sign;
  int i0 = 0;
  int i1 = 0;
  int id = 0;
  int dx;
  int dy;
  int px;
  int py;
  int decor_gravity;
  Bool is_order_reversed = False;

#ifdef FVWM_DEBUG_MSGS
  fvwm_msg(DBG,"SetupFrame",
           "Routine Entered (x == %d, y == %d, w == %d, h == %d)",
           x, y, w, h);
#endif

  if (w < 1)
  {
    w = 1;
  }
  if (h < 1)
  {
    h = 1;
  }
  if (w != tmp_win->frame_g.width || h != tmp_win->frame_g.height)
    is_resized = True;
  if (x != tmp_win->frame_g.x || y != tmp_win->frame_g.y)
    is_moved = True;

  /*
   * According to the July 27, 1988 ICCCM draft, we should send a
   * "synthetic" ConfigureNotify event to the client if the window
   * was moved but not resized.
   */
  if (is_moved && !is_resized)
    sendEvent = True;

  /* optimization code for opaque resize */
#define SIGN(x) (((x) < 0) ? -1 : !!(x))
  xmove_sign = SIGN(x - tmp_win->frame_g.x);
  ymove_sign = SIGN(y - tmp_win->frame_g.y);
  xsize_sign = SIGN(w - tmp_win->frame_g.width);
  ysize_sign = SIGN(h - tmp_win->frame_g.height);
#undef SIGN

  if (HAS_BOTTOM_TITLE(tmp_win))
  {
    decor_gravity = SouthEastGravity;
  }
  else
  {
    decor_gravity = NorthWestGravity;
  }

  if (xsize_sign != 0 || ysize_sign != 0)
  {
    if (ysize_sign == 0 || xsize_sign > 0)
      is_order_reversed = (xsize_sign >= 0);
    else
      is_order_reversed = (ysize_sign >= 0);
  }
  else
  {
    is_order_reversed = False;
  }

  /*
   * Set up the title geometry first
   */

  if (is_resized)
  {
    left = tmp_win->nr_left_buttons;
    right = tmp_win->nr_right_buttons;
    tmp_win->title_g.width = w - (left + right) * tmp_win->title_g.height
                           - 2 * tmp_win->boundary_width;
    if(tmp_win->title_g.width < 1)
      tmp_win->title_g.width = 1;
  }

  /*
   * Now set up the client, the parent and the frame window
   */

  set_decor_gravity(
    tmp_win, decor_gravity, NorthWestGravity, NorthWestGravity);
  tmp_win->attr.width = w - 2 * tmp_win->boundary_width;
  tmp_win->attr.height =
    h - tmp_win->title_g.height - 2*tmp_win->boundary_width;
  if (tmp_win->attr.height <= 1 || tmp_win->attr.width <= 1)
    is_order_reversed = 0;
  px = tmp_win->boundary_width;
  py = tmp_win->boundary_width + tmp_win->title_top_height;
  dx = 0;
  dy = 0;
  if (is_order_reversed && decor_gravity == SouthEastGravity)
  {
    dx = tmp_win->frame_g.width - w;
    dy = tmp_win->frame_g.height - h;
  }

  if (is_order_reversed)
  {
    i0 = 3;
    i1 = -1;
    id = -1;
  }
  else
  {
    i0 = 0;
    i1 = 4;
    id = 1;
  }
  for (i = i0; i != i1; i += id)
  {
    switch(i)
    {
    case 0:
      XMoveResizeWindow(dpy, tmp_win->frame, x, y, w, h);
      break;
    case 1:
      if (!shaded)
      {
	XMoveResizeWindow(
	dpy, tmp_win->Parent, px, py, max(tmp_win->attr.width, 1),
	max(tmp_win->attr.height, 1));
      }
      else
      {
        XMoveWindow(dpy, tmp_win->Parent, px, py);
      }
      break;
    case 2:
      if (!shaded)
      {
	XResizeWindow(
	  dpy, tmp_win->w, max(tmp_win->attr.width, 1),
	  max(tmp_win->attr.height, 1));
	/* This reduces flickering */
	XSync(dpy, 0);
      }
      else if (HAS_BOTTOM_TITLE(tmp_win))
      {
	rectangle big_g;

	big_g = (IS_MAXIMIZED(tmp_win)) ? tmp_win->max_g : tmp_win->normal_g;
	get_relative_geometry(&big_g, &big_g);
	XMoveWindow(dpy, tmp_win->w, 0, 1 - big_g.height +
		    2 * tmp_win->boundary_width + tmp_win->title_g.height);
      }
      else
      {
	XMoveWindow(dpy, tmp_win->w, 0, 0);
      }

      break;
    case 3:
      XMoveResizeWindow(dpy, tmp_win->decor_w, dx, dy, w, h);
      break;
    }
  }
  set_decor_gravity(
    tmp_win, NorthWestGravity, NorthWestGravity, NorthWestGravity);
  XMoveWindow(dpy, tmp_win->decor_w, 0, 0);
  tmp_win->frame_g.x = x;
  tmp_win->frame_g.y = y;
  tmp_win->frame_g.width = w;
  tmp_win->frame_g.height = h;

  /*
   * Set up the decoration windows
   */

  if (is_resized)
  {
    if (HAS_TITLE(tmp_win))
    {
      SetupTitleBar(tmp_win, w, h);
    }

    if(HAS_BORDER(tmp_win))
    {
      int add;

      tmp_win->visual_corner_width = tmp_win->corner_width;
      if(w < 2*tmp_win->corner_width)
        tmp_win->visual_corner_width = w / 3;
      if (h < 2*tmp_win->corner_width && !shaded)
        tmp_win->visual_corner_width = h / 3;
      xwidth = w - 2 * tmp_win->visual_corner_width;
      ywidth = h - 2 * tmp_win->visual_corner_width;
      xwcm = CWWidth | CWHeight | CWX | CWY;
      if(xwidth<2)
        xwidth = 2;
      if(ywidth<2)
        ywidth = 2;

      if (IS_SHADED(tmp_win))
	add = tmp_win->visual_corner_width / 3;
      else
	add = 0;
      for(i = 0; i < 4; i++)
      {
        if(i==0)
        {
          xwc.x = tmp_win->visual_corner_width;
          xwc.y = 0;
          xwc.height = tmp_win->boundary_width;
          xwc.width = xwidth;
        }
        else if (i==1)
        {
	  xwc.x = w - tmp_win->boundary_width;
          xwc.y = tmp_win->visual_corner_width;
	  if (IS_SHADED(tmp_win))
	  {
	    xwc.y /= 3;
	    xwc.height = add + 2;
	  }
	  else
	  {
	    xwc.height = ywidth;
	  }
          xwc.width = tmp_win->boundary_width;

        }
        else if(i==2)
        {
          xwc.x = tmp_win->visual_corner_width;
          xwc.y = h - tmp_win->boundary_width;
          xwc.height = tmp_win->boundary_width;
          xwc.width = xwidth;
        }
        else
        {
	  xwc.x = 0;
          xwc.y = tmp_win->visual_corner_width;
	  if (IS_SHADED(tmp_win))
	  {
	    xwc.y /= 3;
	    xwc.height = add + 2;
	  }
	  else
	  {
	    xwc.height = ywidth;
	  }
          xwc.width = tmp_win->boundary_width;
        }
	XConfigureWindow(dpy, tmp_win->sides[i], xwcm, &xwc);
      }

      xwcm = CWX|CWY|CWWidth|CWHeight;
      xwc.width = tmp_win->visual_corner_width;
      xwc.height = tmp_win->visual_corner_width;
      for(i = 0; i < 4; i++)
      {
        if (i & 0x1)
          xwc.x = w - tmp_win->visual_corner_width;
        else
          xwc.x = 0;

        if (i & 0x2)
          xwc.y = h - tmp_win->visual_corner_width + add;
        else
          xwc.y = -add;

	XConfigureWindow(dpy, tmp_win->corners[i], xwcm, &xwc);
      }
    }
  }


  /*
   * Set up window shape
   */

  if (FShapesSupported && tmp_win->wShaped && is_resized)
  {
    SetShape(tmp_win, w);
  }

  /*
   * Send ConfigureNotify
   */

  XSync(dpy,0);
  /* must not send events to shaded windows because this might cause them to
   * look at their current geometry */
  if (sendEvent && !shaded)
  {
    SendConfigureNotify(tmp_win, x, y, w, h, 0, True);
#ifdef FVWM_DEBUG_MSGS
    fvwm_msg(DBG,"SetupFrame","Sent ConfigureNotify (w == %d, h == %d)",
             w,h);
#endif
  }

  XSync(dpy,0);
  BroadcastConfig(M_CONFIGURE_WINDOW,tmp_win);
#if 0
fprintf(stderr,"sf: window '%s'\n", tmp_win->name);
fprintf(stderr,"    frame:  %5d %5d, %4d x %4d\n", tmp_win->frame_g.x, tmp_win->frame_g.y, tmp_win->frame_g.width, tmp_win->frame_g.height);
fprintf(stderr,"    normal: %5d %5d, %4d x %4d\n", tmp_win->normal_g.x, tmp_win->normal_g.y, tmp_win->normal_g.width, tmp_win->normal_g.height);
if (IS_MAXIMIZED(tmp_win))
fprintf(stderr,"    max:    %5d %5d, %4d x %4d, %5d %5d\n", tmp_win->max_g.x, tmp_win->max_g.y, tmp_win->max_g.width, tmp_win->max_g.height, tmp_win->max_offset.x, tmp_win->max_offset.y);
#endif
}

void ForceSetupFrame(
  FvwmWindow *tmp_win, int x, int y, int w, int h, Bool sendEvent)
{
  tmp_win->frame_g.x = x + 1;
  tmp_win->frame_g.y = y + 1;
  tmp_win->frame_g.width = 0;
  tmp_win->frame_g.height = 0;
  SetupFrame(tmp_win, x, y, w, h, sendEvent);
}

void set_decor_gravity(
  FvwmWindow *tmp_win, int gravity, int parent_gravity, int client_gravity)
{
  int valuemask = CWWinGravity;
  XSetWindowAttributes xcwa;
  int i;

  xcwa.win_gravity = client_gravity;
  XChangeWindowAttributes(dpy, tmp_win->w, valuemask, &xcwa);
  xcwa.win_gravity = parent_gravity;
  XChangeWindowAttributes(dpy, tmp_win->Parent, valuemask, &xcwa);
  xcwa.win_gravity = gravity;
  XChangeWindowAttributes(dpy, tmp_win->decor_w, valuemask, &xcwa);
  if (HAS_TITLE(tmp_win))
  {
    int left_gravity;
    int right_gravity;

    if (gravity == StaticGravity || gravity == ForgetGravity)
    {
      left_gravity = gravity;
      right_gravity = gravity;
    }
    else
    {
      /* West...Gravity */
      left_gravity = gravity - ((gravity - 1) % 3);
      /* East...Gravity */
      right_gravity = left_gravity + 2;
    }
    xcwa.win_gravity = left_gravity;
    XChangeWindowAttributes(dpy, tmp_win->title_w, valuemask, &xcwa);
    for (i = 0; i < NUMBER_OF_BUTTONS; i++)
    {
      if (i == NR_LEFT_BUTTONS)
	xcwa.win_gravity = right_gravity;
      if (tmp_win->button_w[i])
	XChangeWindowAttributes(dpy, tmp_win->button_w[i], valuemask, &xcwa);
    }
  }
}


/****************************************************************************
 *
 * Sets up the shaped window borders
 *
 ****************************************************************************/
void SetShape(FvwmWindow *tmp_win, int w)
{
  if (FShapesSupported)
  {
    if (tmp_win->wShaped)
    {
      XRectangle rect;

      FShapeCombineShape(
	dpy, tmp_win->frame, FShapeBounding, tmp_win->boundary_width,
	tmp_win->title_top_height + tmp_win->boundary_width, tmp_win->w,
	FShapeBounding, FShapeSet);
      if (tmp_win->title_w)
      {
	/* windows w/ titles */
	rect.x = tmp_win->boundary_width;
	rect.y = tmp_win->title_g.y;
	rect.width = w - 2*tmp_win->boundary_width;
	rect.height = tmp_win->title_g.height;

	FShapeCombineRectangles(
	  dpy, tmp_win->frame, FShapeBounding, 0, 0, &rect, 1, FShapeUnion,
	  Unsorted);
      }
    }
    else
    {
      /* unset shape */
      FShapeCombineMask(
	dpy, tmp_win->frame, FShapeBounding, 0, 0, None, FShapeSet);
    }
  }
}

/****************************************************************************
 *
 *  Sets the allowed button states
 *
 ****************************************************************************/
void CMD_ButtonState(F_CMD_ARGS)
{
  char *token;

  while ((token = PeekToken(action, &action)))
  {
    static char first = True;
    if (!token && first)
    {
      Scr.gs.use_active_down_buttons = DEFAULT_USE_ACTIVE_DOWN_BUTTONS;
      Scr.gs.use_inactive_buttons = DEFAULT_USE_INACTIVE_BUTTONS;
      return;
    }
    first = False;
    if (StrEquals("activedown", token))
    {
      Scr.gs.use_active_down_buttons =
	ParseToggleArgument(action, &action, DEFAULT_USE_ACTIVE_DOWN_BUTTONS,
			    True);
    }
    else if (StrEquals("inactive", token))
    {
      Scr.gs.use_inactive_buttons =
	ParseToggleArgument(action, &action, DEFAULT_USE_ACTIVE_DOWN_BUTTONS,
			    True);
    }
    else
    {
      Scr.gs.use_active_down_buttons = DEFAULT_USE_ACTIVE_DOWN_BUTTONS;
      Scr.gs.use_inactive_buttons = DEFAULT_USE_INACTIVE_BUTTONS;
      fvwm_msg(ERR, "cmd_button_state", "unknown button state %s\n", token);
      return;
    }
  }
  return;
}


/****************************************************************************
 *
 *  Sets the border style ([EMAIL PROTECTED])
 *
 ****************************************************************************/
void CMD_BorderStyle(F_CMD_ARGS)
{
  char *parm;
  char *prev;
#ifdef USEDECOR
  FvwmDecor *decor = Scr.cur_decor ? Scr.cur_decor : &Scr.DefaultDecor;
#else
  FvwmDecor *decor = &Scr.DefaultDecor;
#endif

  Scr.flags.do_need_window_update = 1;
  decor->flags.has_changed = 1;

  for (prev = action; (parm = PeekToken(action, &action)); prev = action)
  {
    if (StrEquals(parm, "active") || StrEquals(parm, "inactive"))
    {
      int len;
      char *end, *tmp;
      DecorFace tmpdf, *df;
      memset(&tmpdf.style, 0, sizeof(tmpdf.style));
      DFS_FACE_TYPE(tmpdf.style) = SimpleButton;
#ifdef MULTISTYLE
      tmpdf.next = NULL;
#endif
#ifdef MINI_ICONS
      tmpdf.u.p = NULL;
#endif
      if (StrEquals(parm,"active"))
	df = &decor->BorderStyle.active;
      else
	df = &decor->BorderStyle.inactive;
      df->flags.has_changed = 1;
      while (isspace(*action))
	++action;
      if (*action != '(')
      {
	if (!*action)
	{
	  fvwm_msg(ERR,"SetBorderStyle",
		   "error in %s border specification", parm);
	  return;
	}
	while (isspace(*action))
	  ++action;
	if (ReadDecorFace(action, &tmpdf,-1,True))
	{
	  FreeDecorFace(dpy, df);
	  *df = tmpdf;
	}
	break;
      }
      end = strchr(++action, ')');
      if (!end)
      {
	fvwm_msg(ERR,"SetBorderStyle",
		 "error in %s border specification", parm);
	return;
      }
      len = end - action + 1;
      tmp = safemalloc(len);
      strncpy(tmp, action, len - 1);
      tmp[len - 1] = 0;
      ReadDecorFace(tmp, df,-1,True);
      free(tmp);
      action = end + 1;
    }
    else if (strcmp(parm,"--")==0)
    {
      if (ReadDecorFace(prev, &decor->BorderStyle.active,-1,True))
	ReadDecorFace(prev, &decor->BorderStyle.inactive,-1,False);
      decor->BorderStyle.active.flags.has_changed = 1;
      decor->BorderStyle.inactive.flags.has_changed = 1;
      break;
    }
    else
    {
      DecorFace tmpdf;
      memset(&tmpdf.style, 0, sizeof(tmpdf.style));
      DFS_FACE_TYPE(tmpdf.style) = SimpleButton;
#ifdef MULTISTYLE
      tmpdf.next = NULL;
#endif
#ifdef MINI_ICONS
      tmpdf.u.p = NULL;
#endif
      if (ReadDecorFace(prev, &tmpdf,-1,True))
      {
	FreeDecorFace(dpy,&decor->BorderStyle.active);
	decor->BorderStyle.active = tmpdf;
	ReadDecorFace(prev, &decor->BorderStyle.inactive,-1,False);
	decor->BorderStyle.active.flags.has_changed = 1;
	decor->BorderStyle.inactive.flags.has_changed = 1;
      }
      break;
    }
  }
}

Reply via email to