/* FvwmForm is original work of Thomas Zuwei Feng.
 *
 * Copyright Feb 1995, Thomas Zuwei Feng.  No guarantees or warantees are
 * provided or implied in any way whatsoever.  Use this program at your own
 * risk.  Permission to use, modify, and redistribute this program is hereby
 * given, provided that this copyright is kept intact. */

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

#include "config.h"

#include <stdio.h>
#include <ctype.h>
#include <signal.h>
#include <stdlib.h>                     /* for getenv */

#include <sys/time.h>
#include <fcntl.h>

#include <X11/Xlib.h>
#include <X11/X.h>
#include <X11/Xutil.h>
#ifdef I18N_MB
#include <X11/Xlocale.h>
#endif
#include <X11/cursorfont.h>
#define XK_MISCELLANY
#include <X11/keysymdef.h>

#include "libs/Module.h"                /* for headersize, etc. */
#include "libs/fvwmlib.h"
#include "libs/fvwmsignal.h"

#include <libs/Picture.h>               /* for InitPictureCMap */
#include "libs/Colorset.h"               /* for InitPictureCMap */
#include "libs/FScreen.h"
#include "libs/FShape.h"

#include "FvwmForm.h"                   /* common FvwmForm stuff */

/* globals that are exported, keep in sync with externs in FvwmForm.h */
Form cur_form;                   /* current form */

/* Link list roots */
Item *root_item_ptr;             /* pointer to root of item list */
Line root_line = {&root_line,    /* ->next */
                  0,             /* number of items */
                  L_CENTER,0,0,  /* justify, size x/y */
                  0,             /* current array size */
                  0};            /* items ptr */
Line *cur_line = &root_line;            /* curr line in parse rtns */
char preload_yorn='n';           /* init to non-preload */
Item *item;                             /* current during parse */
Item *cur_sel, *cur_button;             /* current during parse */
Display *dpy;
int fd_x;                  /* fd for X connection */
Window root, ref;
int screen;
char *color_names[4];
char bg_state = 'd';                    /* in default state */
  /* d = default state */
  /* s = set by command (must be in "d" state for first "back" cmd to set it) */
  /* u = used (color allocated, too late to accept "back") */
char endDefaultsRead = 'n';
char *font_names[3];
char *screen_background_color;
char *MyName;
int MyNameLen;
int Channel[2];

/* Font/color stuff
   The colors are:
   . defaults are changed by commands during parse
   . copied into items during parse
   . displayed in the customization dialog
   */
static int colorset = -1;
static int itemcolorset = 0;

/* prototypes */
static void AssignDrawTable(Item *);
static void AddItem();
static void PutDataInForm(char *);
static void ReadFormData();
static void FormVarsCheck(char **);

/* copy a string until '"', or '\n', or '\0' */
static char *CopyQuotedString (char *cp)
{
  char *dp, *bp, c;
  bp = dp = (char *)safemalloc(strlen(cp) + 1);
  while (1) {
    switch (c = *(cp++)) {
    case '\\':
      *(dp++) = *(cp++);
      break;
    case '\"':
    case '\n':
    case '\0':
      *dp = '\0';
      return bp;
      break;
    default:
      *(dp++) = c;
      break;
    }
  }
}

/* copy a string until the first space */
static char *CopySolidString (char *cp)
{
  char *dp, *bp, c;
  bp = dp = (char *)safemalloc(strlen(cp) + 1);
  while (1) {
    c = *(cp++);
    if (c == '\\') {
      *(dp++) = '\\';
      *(dp++) = *(cp++);
    } else if (isspace((unsigned char)c) || c == '\0') {
      *dp = '\0';
      return bp;
    } else
      *(dp++) = c;
  }
}

/* get the font height */
static int FontHeight (XFontStruct *xfs)
{
  return (xfs->ascent + xfs->descent);
}

/* get the font width, for fixed-width font only */
int FontWidth (XFontStruct *xfs)
{
  return (xfs->per_char[0].width);
}


/* Command parsing section */

/* FvwmAnimate++ type command table */
typedef struct CommandTable
{
  char *name;
  void (*function)(char *);
} ct;
static void ct_ActivateOnPress(char *);
static void ct_Back(char *);
static void ct_Button(char *);
static void ct_ButtonFont(char *);
static void ct_ButtonInPointer(char *);
static void ct_ButtonInPointerBack(char *);
static void ct_ButtonInPointerFore(char *);
static void ct_ButtonPointer(char *);
static void ct_ButtonPointerBack(char *);
static void ct_ButtonPointerFore(char *);
static void ct_Choice(char *);
static void ct_Command(char *);
static void ct_Font(char *);
static void ct_Fore(char *);
static void ct_GrabServer(char *);
static void ct_Input(char *);
static void ct_InputFont(char *);
static void ct_ItemBack(char *);
static void ct_ItemFore(char *);
static void ct_Line(char *);
static void ct_Message(char *);
static void ct_Geometry(char *);
static void ct_Position(char *);
static void ct_Read(char *);
static void ct_Selection(char *);
static void ct_Text(char *);
static void ct_InputPointer(char *);
static void ct_InputPointerBack(char *);
static void ct_InputPointerFore(char *);
static void ct_Title(char *);
static void ct_UseData(char *);
static void ct_padVText(char *);
static void ct_WarpPointer(char *);
static void ct_Colorset(char *);
static void ct_ItemColorset(char *);

/* Must be in Alphabetic order (caseless) */
static struct CommandTable ct_table[] =
{
  {"ActivateOnPress",ct_ActivateOnPress},
  {"Back",ct_Back},
  {"Button",ct_Button},
  {"ButtonFont",ct_ButtonFont},
  {"ButtonInPointer",ct_ButtonInPointer},
  {"ButtonInPointerBack",ct_ButtonInPointerBack},
  {"ButtonInPointerFore",ct_ButtonInPointerFore},
  {"ButtonPointer",ct_ButtonPointer},
  {"ButtonPointerBack",ct_ButtonPointerBack},
  {"ButtonPointerFore",ct_ButtonPointerFore},
  {"Choice",ct_Choice},
  {"Colorset",ct_Colorset},
  {"Command",ct_Command},
  {"Font",ct_Font},
  {"Fore",ct_Fore},
  {"Geometry",ct_Geometry},
  {"GrabServer",ct_GrabServer},
  {"Input",ct_Input},
  {"InputFont",ct_InputFont},
  {"InputPointer",ct_InputPointer},
  {"InputPointerBack",ct_InputPointerBack},
  {"InputPointerFore",ct_InputPointerFore},
  {"ItemBack",ct_ItemBack},
  {"ItemColorset",ct_ItemColorset},
  {"ItemFore",ct_ItemFore},
  {"Line",ct_Line},
  {"Message",ct_Message},
  {"PadVText",ct_padVText},
  {"Position",ct_Position},
  {"Selection",ct_Selection},
  {"Text",ct_Text},
  {"Title",ct_Title},
  {"UseData",ct_UseData},
  {"WarpPointer",ct_WarpPointer}
};
/* These commands are the default setting commands,
   read before the other form defining commands. */
static struct CommandTable def_table[] =
{
  {"ActivateOnPress",ct_ActivateOnPress},
  {"Back",ct_Back},
  {"ButtonFont",ct_ButtonFont},
  {"ButtonInPointer",ct_ButtonInPointer},
  {"ButtonInPointerBack",ct_ButtonInPointerBack},
  {"ButtonInPointerFore",ct_ButtonInPointerFore},
  {"ButtonPointer",ct_ButtonPointer},
  {"ButtonPointerBack",ct_ButtonPointerBack},
  {"ButtonPointerFore",ct_ButtonPointerFore},
  {"Colorset",ct_Colorset},
  {"Font",ct_Font},
  {"Fore",ct_Fore},
  {"InputFont",ct_InputFont},
  {"InputPointer",ct_InputPointer},
  {"InputPointerBack",ct_InputPointerBack},
  {"InputPointerFore",ct_InputPointerFore},
  {"ItemBack",ct_ItemBack},
  {"ItemColorset",ct_ItemColorset},
  {"ItemFore",ct_ItemFore},
  {"Read",ct_Read}
};

/* If there were vars on the command line, do env var sustitution on
   all input. */
static void FormVarsCheck(char **p)
{
  if (CF.have_env_var) {                /* if cmd line vars */
    if (strlen(*p) + 200 > CF.expand_buffer_size) { /* fast and loose */
      CF.expand_buffer_size = strlen(*p) + 2000; /* new size */
      if (CF.expand_buffer) {           /* already have one */
        CF.expand_buffer = saferealloc(CF.expand_buffer, CF.expand_buffer_size);
      } else {                          /* first time */
        CF.expand_buffer = safemalloc(CF.expand_buffer_size);
      }
    }
    strcpy(CF.expand_buffer,*p);
    *p = CF.expand_buffer;
    envExpand(*p,CF.expand_buffer_size); /* expand the command in place */
  }
}

static void ParseDefaults(char *buf)
{
  char *p;
  struct CommandTable *e;
  if (buf[strlen(buf)-1] == '\n') {     /* if line ends with newline */
    buf[strlen(buf)-1] = '\0';	/* strip off \n */
  }
  /* Accept commands beginning with "*FvwmFormDefault".
     This is to make sure defaults are read first.
     Note the hack w. bg_state. */
  if (strncasecmp(buf, "*FvwmFormDefault", 16) == 0) {
    p=buf+16;
    FormVarsCheck(&p);                   /* do var substitution if called for */
    e = FindToken(p,def_table,struct CommandTable);/* find cmd in table */
    if (e != 0) {                       /* if its valid */
      p=p+strlen(e->name);              /* skip over name */
      while (isspace((unsigned char)*p)) p++;          /* skip whitespace */
      e->function(p);                   /* call cmd processor */
      bg_state = 'd';                   /* stay in default state */
    }
  }
} /* end function */


static void ParseConfigLine(char *buf)
{
  char *p;
  struct CommandTable *e;
  if (buf[strlen(buf)-1] == '\n') {     /* if line ends with newline */
    buf[strlen(buf)-1] = '\0';	/* strip off \n */
  }

  if (strncasecmp(buf, XINERAMA_CONFIG_STRING,
		  sizeof(XINERAMA_CONFIG_STRING)-1) == 0)
  {
    FScreenConfigureModule(buf + sizeof(XINERAMA_CONFIG_STRING)-1);
    return;
  }
  if (strncasecmp(buf, "Colorset", 8) == 0) {
    LoadColorset(&buf[8]);
    return;
  }
  if (strncasecmp(buf, MyName, MyNameLen) != 0) {/* If its not for me */
    return;
  } /* Now I know its for me. */
  p = buf+MyNameLen;                  /* jump to end of my name */
  /* at this point we have recognized "*FvwmForm" */
  FormVarsCheck(&p);
  e = FindToken(p,ct_table,struct CommandTable);/* find cmd in table */
  if (e == 0) {                       /* if no match */
    fprintf(stderr,"%s: unknown command: %s\n",MyName+1,buf);
    return;
  }

  p=p+strlen(e->name);                  /* skip over name */
  while (isspace((unsigned char)*p)) p++;              /* skip whitespace */

  FormVarsCheck(&p);                     /* do var substitution if called for */

  e->function(p);                       /* call cmd processor */
  return;
} /* end function */

/* Expands item array */
static void ExpandArray(Line *this_line)
{
  if (this_line->n + 1 >= this_line->item_array_size) { /* no empty space */
    this_line->item_array_size += ITEMS_PER_EXPANSION; /* get bigger */
    this_line->items =
      (Item **)saferealloc((void *)this_line->items,
			  (sizeof(Item *) * this_line->item_array_size));
  } /* end array full */
}

/* Function to add an item to the current line */
static void AddToLine(Item *newItem)
{
  ExpandArray(cur_line);                /* expand item array if needed */
  cur_line->items[cur_line->n++] = newItem; /* add to lines item array */
  cur_line->size_x += newItem->header.size_x; /* incr lines width */
  if (cur_line->size_y < newItem->header.size_y) { /* new item bigger */
    cur_line->size_y = newItem->header.size_y; /* then line is bigger */
  }
}


/* All the functions starting with "ct_" (command table) are called thru
   their function pointers. Arg 1 is always the rest of the command. */
static void ct_ActivateOnPress(char *cp)
{
  /* arg can be "on", "off", "0", 1", "true", "false", "" */
  char option[6];
  int i,j;
  if (strlen(cp) > 5) {
    fprintf(stderr,"%s: arg for ActivateOnPress (%s) too long\n",
            MyName+1,cp);
    return;
  }
  for (i=0,j=0;i<strlen(cp);i++) {
    if (cp[i] != ' ') {                   /* remove any spaces */
      option[j++]=tolower(cp[i]);
    }
  }
  option[j]=0;
  if (strcmp(option,"on") == 0
      || strcmp(option,"true") == 0
      || strcmp(option,"1") == 0
      || (cp == 0 || strcmp(option,"")) == 0) {
    CF.activate_on_press = 1;
  } else if (strcmp(option,"off") == 0
             || strcmp(option,"false") == 0
             || strcmp(option,"0") == 0) {
    CF.activate_on_press = 0;
  } else {
    fprintf(stderr,"%s: arg for ActivateOnPress (%s/%s) invalid\n",
            MyName+1,option,cp);
  }
}
static void ct_GrabServer(char *cp)
{
  CF.grab_server = 1;
}
static void ct_WarpPointer(char *cp)
{
  CF.warp_pointer = 1;
}
static void ct_Position(char *cp)
{
  CF.have_geom = 1;
  CF.gx = atoi(cp);
  CF.xneg = 0;
  if (CF.gx < 0)
  {
    CF.gx = -1 - CF.gx;
    CF.xneg = 1;
  }
  while (!isspace((unsigned char)*cp)) cp++;
  while (isspace((unsigned char)*cp)) cp++;
  CF.gy = atoi(cp);
  CF.yneg = 0;
  if (CF.gy < 0)
  {
    CF.gy = -1 - CF.gy;
    CF.yneg = 1;
  }
  myfprintf((stderr, "Position @ (%c%d%c%d)\n",
	     (CF.xneg)?'-':'+',CF.gx, (CF.yneg)?'-':'+',CF.gy));
}
static void ct_Geometry(char *cp)
{
  int x = 0;
  int y = 0;
  int flags;
  unsigned int dummy;

  while (isspace(*cp))
    cp++;
  flags = FScreenParseGeometry(cp, &x, &y, &dummy, &dummy);
  if (flags&XValue)
  {
    CF.have_geom = 1;
    CF.gx = x;
    CF.xneg = (flags&XNegative);
  }
  if (flags&YValue)
  {
    CF.have_geom = 1;
    CF.gy = y;
    CF.yneg = (flags&YNegative);
  }
  myfprintf((stderr, "Geometry @ (%c%d%c%d)\n",
	     (CF.xneg)?'-':'+',CF.gx, (CF.yneg)?'-':'+',CF.gy));
}
static void ct_Fore(char *cp)
{
  if (color_names[c_fg])
    free(color_names[c_fg]);
  color_names[c_fg] = safestrdup(cp);
  colorset = -1;
  myfprintf((stderr, "ColorFore: %s\n", color_names[c_fg]));
}
static void ct_Back(char *cp)
{
  if (color_names[c_bg])
    free(color_names[c_bg]);
  color_names[c_bg] = safestrdup(cp);
  if (bg_state == 'd')
  {
    if (screen_background_color)
      free(screen_background_color);
    screen_background_color = safestrdup(color_names[c_bg]);
    bg_state = 's';                     /* indicate set by command */
  }
  colorset = -1;
  myfprintf((stderr, "ColorBack: %s, screen background %s, bg_state %c\n",
	     color_names[c_bg],screen_background_color,(int)bg_state));
}
static void ct_Colorset(char *cp)
{
  sscanf(cp, "%d", &colorset);
  AllocColorset(colorset);
}
static void ct_ItemFore(char *cp)
{
  if (color_names[c_item_fg])
    free(color_names[c_item_fg]);
  color_names[c_item_fg] = safestrdup(cp);
  itemcolorset = -1;
  myfprintf((stderr, "ColorItemFore: %s\n", color_names[c_item_fg]));
}
static void ct_ItemBack(char *cp)
{
  if (color_names[c_item_bg])
    free(color_names[c_item_bg]);
  color_names[c_item_bg] = safestrdup(cp);
  itemcolorset = -1;
  myfprintf((stderr, "ColorItemBack: %s\n", color_names[c_item_bg]));
}
static void ct_ItemColorset(char *cp)
{
  sscanf(cp, "%d", &itemcolorset);
  AllocColorset(itemcolorset);
}
static void ct_Font(char *cp)
{
  if (font_names[f_text])
    free(font_names[f_text]);
  CopyStringWithQuotes(&font_names[f_text], cp);
  myfprintf((stderr, "Font: %s\n", font_names[f_text]));
}
static void ct_ButtonFont(char *cp)
{
  if (font_names[f_button])
    free(font_names[f_button]);
  CopyStringWithQuotes(&font_names[f_button], cp);
  myfprintf((stderr, "ButtonFont: %s\n", font_names[f_button]));
}
static void ct_ButtonInPointer(char *cp)
{
  int cursor;
  cursor=fvwmCursorNameToIndex(cp);
  if (cursor == -1) {
    fprintf(stderr,"ButtonInPointer: invalid cursor name %s\n",cp);
  } else {
    CF.pointer[button_in_pointer] = XCreateFontCursor(dpy,cursor);
  }
}
static void ct_ButtonPointer(char *cp)
{
  int cursor;
  cursor=fvwmCursorNameToIndex(cp);
  if (cursor == -1) {
    fprintf(stderr,"ButtonPointer: invalid cursor name %s\n",cp);
  } else {
    CF.pointer[button_pointer] = XCreateFontCursor(dpy,cursor);
  }
}
static void ct_InputFont(char *cp)
{
  if (font_names[f_input])
    free(font_names[f_input]);
  CopyStringWithQuotes(&font_names[f_input], cp);
  myfprintf((stderr, "InputFont: %s\n", font_names[f_input]));
}
static void ct_InputPointer(char *cp)
{
  int cursor;
  cursor=fvwmCursorNameToIndex(cp);
  if (cursor == -1) {
    fprintf(stderr,"InputPointer: invalid cursor name %s\n",cp);
  } else {
    CF.pointer[input_pointer] = XCreateFontCursor(dpy,cursor);
  }
}
/* process buttons using an index to the ___ array */
static void color_arg(Ptr_color *color_cell, char *color_name);
static void color_arg(Ptr_color *color_cell, char *color_name) {
  if (color_name && *color_name) {
    color_cell->pointer_color.pixel = GetColor(color_name);
    color_cell->used=1;
  }
}

/* arg is a color name, alloc the color */
static void ct_ButtonInPointerBack(char *cp) {
  color_arg(&CF.p_c[button_in_back],cp);
}
static void ct_ButtonInPointerFore(char *cp) {
  color_arg(&CF.p_c[button_in_fore],cp);
}
static void ct_ButtonPointerBack(char *cp) {
  color_arg(&CF.p_c[button_back],cp);
}
static void ct_ButtonPointerFore(char *cp) {
  color_arg(&CF.p_c[button_fore],cp);
}
static void ct_InputPointerBack(char *cp) {
  color_arg(&CF.p_c[input_back],cp);
}
static void ct_InputPointerFore(char *cp) {
  color_arg(&CF.p_c[input_fore],cp);
}

static void ct_Line(char *cp)
{
  /* malloc new line */
  cur_line->next = (struct _line *)safemalloc(sizeof(struct _line));
  memset(cur_line->next, 0, sizeof(struct _line));
  cur_line = cur_line->next;            /* new current line */
  cur_line->next = &root_line;          /* new next ptr, (actually root) */

  if (strncasecmp(cp, "left", 4) == 0)
    cur_line->justify = L_LEFT;
  else if (strncasecmp(cp, "right", 5) == 0)
    cur_line->justify = L_RIGHT;
  else if (strncasecmp(cp, "center", 6) == 0)
    cur_line->justify = L_CENTER;
  else
    cur_line->justify = L_LEFTRIGHT;
}
/* Define an area on the form that contains the current
   fvwm error message.
   syntax: *FFMessage Length nn, Font fn, Fore color, Back color
   len default is 70
   font,fg.bg default to text.
  */

static void ct_Message(char *cp)
{
  AddItem();
  bg_state = 'u';                       /* indicate b/g color now used. */
  item->type = I_TEXT;
  /* Item now added to list of items, now it needs a pointer
     to the correct DrawTable. */
  AssignDrawTable(item);
  item->header.name = "FvwmMessage";    /* No purpose to this? dje */
  item->text.value = safemalloc(80);    /* point at last error recvd */
  item->text.n = 80;
  strcpy(item->text.value,"A mix of chars. MM20"); /* 20 mixed width chars */
  item->header.size_x = (XTextWidth(item->header.dt_ptr->dt_font_struct,
                                   item->text.value,
                                   item->text.n/4) * 4) + 2 * TEXT_SPC;
  item->header.size_y = FontHeight(item->header.dt_ptr->dt_font_struct)
    + CF.padVText;

  memset(item->text.value,' ',80);      /* Clear it out */
  myfprintf((stderr, "Message area [%d, %d]\n",
             item->header.size_x, item->header.size_y));
  AddToLine(item);
  CF.last_error = item;                 /* save location of message item */
}
/* allocate colors and fonts needed */
static void CheckAlloc(Item *this_item,DrawTable *dt)
{
  static XGCValues xgcv;
  static int xgcv_mask = GCBackground | GCForeground | GCFont;

  if (dt->dt_used == 2) {               /* fonts colors shadows */
    return;
  }
  if (dt->dt_used == 0) {               /* if nothing allocated */
    dt->dt_colors[c_fg] = (colorset < 0)
      ? GetColor(dt->dt_color_names[c_fg])
      : Colorset[colorset].fg;
    dt->dt_colors[c_bg] = (colorset < 0)
      ? GetColor(dt->dt_color_names[c_bg])
      : Colorset[colorset].bg;

    xgcv.foreground = dt->dt_colors[c_fg];
    xgcv.background = dt->dt_colors[c_bg];
    xgcv.font = dt->dt_font;
    dt->dt_GC = fvwmlib_XCreateGC(dpy, CF.frame, xgcv_mask, &xgcv);

    dt->dt_used = 1;                    /* fore/back font allocated */
  }
  if (this_item->type == I_TEXT) {      /* If no shadows needed */
    return;
  }
  dt->dt_colors[c_item_fg] = (itemcolorset < 0)
    ? GetColor(dt->dt_color_names[c_item_fg])
    : Colorset[itemcolorset].fg;
  dt->dt_colors[c_item_bg] = (itemcolorset < 0)
    ? GetColor(dt->dt_color_names[c_item_bg])
    : Colorset[itemcolorset].bg;
  xgcv.foreground = dt->dt_colors[c_item_fg];
  xgcv.background = dt->dt_colors[c_item_bg];
  xgcv.font = dt->dt_font;
  dt->dt_item_GC = fvwmlib_XCreateGC(dpy, CF.frame, GCForeground | GCFont, &xgcv);
  if (Pdepth < 2) {
    dt->dt_colors[c_itemlo] = BlackPixel(dpy, screen);
    dt->dt_colors[c_itemhi] = WhitePixel(dpy, screen);
  } else {
    dt->dt_colors[c_itemlo] = (itemcolorset < 0)
      ? GetShadow(dt->dt_colors[c_item_bg])
      : Colorset[itemcolorset].shadow;
    dt->dt_colors[c_itemhi] = (itemcolorset < 0)
      ? GetHilite(dt->dt_colors[c_item_bg])
      : Colorset[itemcolorset].hilite;
  }
  dt->dt_used = 2;                     /* fully allocated */
}


/* Input is the current item.  Assign a drawTable entry to it. */
static void AssignDrawTable(Item *adt_item)
{
  DrawTable *find_dt, *last_dt;
  char *match_text_fore;
  char *match_text_back;
  char *match_item_fore;
  char *match_item_back;
  char *match_font;
  DrawTable *new_dt;                    /* pointer to a new one */

  match_text_fore = color_names[c_fg];
  match_text_back = color_names[c_bg];
  match_item_fore = color_names[c_item_fg];
  match_item_back = color_names[c_item_bg];
  if (adt_item->type == I_TEXT) {
    match_font = font_names[f_text];
  } else if (adt_item->type == I_INPUT) {
    match_font = font_names[f_input];
  } else {
    match_font = font_names[f_button]; /* choices same as buttons */
  }
  last_dt = 0;
  for (find_dt = CF.roots_dt;              /* start at front */
       find_dt != 0;                    /* until end */
       find_dt = find_dt->dt_next) {    /* follow next pointer */
    last_dt = find_dt;
    if ((strcasecmp(match_text_fore,find_dt->dt_color_names[c_fg]) == 0) &&
        (strcasecmp(match_text_back,find_dt->dt_color_names[c_bg]) == 0) &&
        (strcasecmp(match_item_fore,find_dt->dt_color_names[c_item_fg]) == 0) &&
        (strcasecmp(match_item_back,find_dt->dt_color_names[c_item_bg]) == 0) &&
        (strcasecmp(match_font,find_dt->dt_font_name) == 0)) { /* Match */
      adt_item->header.dt_ptr = find_dt;       /* point item to drawtable */
      return;                           /* done */
    } /* end match */
  } /* end all drawtables checked, no match */

  /* Time to add a DrawTable */
  /* get one */
  new_dt = (struct _drawtable *)safemalloc(sizeof(struct _drawtable));
  memset(new_dt, 0, sizeof(struct _drawtable));
  new_dt->dt_next = 0;                  /* new end of list */
  if (CF.roots_dt == 0) {                  /* If first entry in list */
    CF.roots_dt = new_dt;                  /* set root pointer */
  } else {                              /* not first entry */
    last_dt->dt_next = new_dt;          /* link old to new */
  }

  new_dt->dt_font_name = safestrdup(match_font);
  new_dt->dt_color_names[c_fg]      = safestrdup(match_text_fore);
  new_dt->dt_color_names[c_bg]      = safestrdup(match_text_back);
  new_dt->dt_color_names[c_item_fg] = safestrdup(match_item_fore);
  new_dt->dt_color_names[c_item_bg] = safestrdup(match_item_back);
  new_dt->dt_used = 0;                  /* show nothing allocated */
  new_dt->dt_font_struct = GetFontOrFixed(dpy, new_dt->dt_font_name);
  new_dt->dt_font = new_dt->dt_font_struct->fid;
  myfprintf((stderr,"Created drawtable with %s %s %s %s %s\n",
             new_dt->dt_color_names[c_fg], new_dt->dt_color_names[c_bg],
             new_dt->dt_color_names[c_item_fg],
             new_dt->dt_color_names[c_item_bg],
             new_dt->dt_font_name));
  adt_item->header.dt_ptr = new_dt;            /* point item to new drawtable */
}

/* input/output is global "item" - currently allocated last item */
static void AddItem()
{
  Item *save_item;
  save_item = (Item *)item;             /* save current item */
  item = (Item *)safecalloc(sizeof(Item),1); /* get a new item */
  if (save_item == 0) {                 /* if first item */
    root_item_ptr = item;               /* save root item */
  } else {                              /* else not first item */
    save_item->header.next = item;      /* link prior to new */
  }
}

static void ct_Text(char *cp)
{
  /* syntax: *FFText "<text>" */
  AddItem();
  bg_state = 'u';                       /* indicate b/g color now used. */
  item->type = I_TEXT;
  /* Item now added to list of items, now it needs a pointer
     to the correct DrawTable. */
  AssignDrawTable(item);
  item->header.name = "";
  if (*cp == '\"')
    item->text.value = CopyQuotedString(++cp);
  else
    item->text.value = "";
  item->text.n = strlen(item->text.value);

  item->header.size_x = XTextWidth(item->header.dt_ptr->dt_font_struct,
                                   item->text.value,
                                   item->text.n) + 2 * TEXT_SPC;
  item->header.size_y = FontHeight(item->header.dt_ptr->dt_font_struct)
    + CF.padVText;

  myfprintf((stderr, "Text \"%s\" [%d, %d]\n", item->text.value,
             item->header.size_x, item->header.size_y));
  AddToLine(item);
}
/* Set the form's title.
   The default is the aliasname.
   If there is no quoted string, create a blank title. */
static void ct_Title(char *cp)
{
  /* syntax: *FFTitle "<text>" */
  if (*cp == '\"')
    CF.title = CopyQuotedString(++cp);
  else
    CF.title = "";
  myfprintf((stderr, "Title \"%s\"\n", CF.title));
}
static void ct_padVText(char *cp)
{
  /* syntax: *FFText "<padVText pixels>" */
  CF.padVText = atoi(cp);
  myfprintf((stderr, "Text Vertical Padding %d\n", CF.padVText));
}
static void ct_Input(char *cp)
{
  int j;
  /* syntax: *FFInput <name> <size> "<init_value>" */
  AddItem();
  item->type = I_INPUT;
  AssignDrawTable(item);
  item->header.name = CopySolidString(cp);
  cp += strlen(item->header.name);
  while (isspace((unsigned char)*cp)) cp++;
  item->input.size = atoi(cp);
  while (!isspace((unsigned char)*cp)) cp++;
  while (isspace((unsigned char)*cp)) cp++;
  item->input.init_value = safestrdup("");          /* init */
  if (*cp == '\"') {
    free(item->input.init_value);
    item->input.init_value = CopyQuotedString(++cp);
  }
  item->input.blanks = (char *)safemalloc(item->input.size);
  for (j = 0; j < item->input.size; j++)
    item->input.blanks[j] = ' ';
  item->input.buf = strlen(item->input.init_value) + 1;
  item->input.value = (char *)safemalloc(item->input.buf);
  item->input.value[0] = 0;             /* avoid reading unitialized data */

  item->header.size_x = FontWidth(item->header.dt_ptr->dt_font_struct)
    * item->input.size + 2 * TEXT_SPC + 2 * BOX_SPC;
  item->header.size_y = FontHeight(item->header.dt_ptr->dt_font_struct)
    + 3 * TEXT_SPC + 2 * BOX_SPC;
  myfprintf((stderr,"Input size_y is %d\n",item->header.size_y));

  if (CF.cur_input == 0) {                 /* first input field */
    item->input.next_input = item;      /* ring, next field is first field */
    item->input.prev_input = item;      /* ring, prev field is first field */
    CF.first_input = item;                 /* save loc of first item */
  } else {                              /* not first field */
    CF.cur_input->input.next_input = item; /* old next ptr point to this item */
    item->input.prev_input = CF.cur_input; /* current items prev is old item */
    item->input.next_input = CF.first_input; /* next is first item */
    CF.first_input->input.prev_input = item; /* prev in first is this item */
  }
  CF.cur_input = item;                     /* new current input item */
  myfprintf((stderr, "Input, %s, [%d], \"%s\"\n", item->header.name,
          item->input.size, item->input.init_value));
  AddToLine(item);
}
static void ct_Read(char *cp)
{
  /* syntax: *FFRead 0 | 1 */
  myfprintf((stderr,"Got read command, char is %c\n",(int)*cp));
  endDefaultsRead = *cp;                /* copy whatever it is */
}
/* read and save vars from a file for later use in form
   painting.
*/
static void ct_UseData(char *cp)
{
  /* syntax: *FFUseData filename cmd_prefix */
  CF.file_to_read = CopySolidString(cp);
  if (*CF.file_to_read == 0) {
    fprintf(stderr,"UseData command missing first arg, File to read\n");
    return;
  }
  cp += strlen(CF.file_to_read);
  while (isspace((unsigned char)*cp)) cp++;
  CF.leading = CopySolidString(cp);
  if (*CF.leading == 0) {
    fprintf(stderr,"UseData command missing second arg, Leading\n");
    CF.file_to_read = 0;                   /* stop read */
    return;
  }
  /* Cant do the actual reading of the data file here,
     we are already in a readconfig loop. */
}
static void ReadFormData()
{
  int leading_len;
  char *line_buf;                       /* ptr to curr config line */
  char cmd_buffer[200];
  sprintf(cmd_buffer,"read %s Quiet",CF.file_to_read);
  SendText(Channel,cmd_buffer,0);       /* read data */
  leading_len = strlen(CF.leading);
  InitGetConfigLine(Channel, CF.leading); /* ask for certain lines */
  while (GetConfigLine(Channel,&line_buf),line_buf) { /* while there is some */
    if (strncasecmp(line_buf, CF.leading, leading_len) == 0) { /* leading = */
      if (line_buf[strlen(line_buf)-1] == '\n') { /* if line ends with nl */
        line_buf[strlen(line_buf)-1] = '\0'; /* strip off \n */
      }
      PutDataInForm(line_buf+leading_len); /* pass arg, space, value */
    } /* end match on arg 2, "leading" */
  } /* end while there is config data */
  free(CF.file_to_read);                /* dont need it anymore */
  free(CF.leading);
  CF.file_to_read = 0;
  CF.leading = 0;
}
/*
  Input is a line with varname space value.
  Search form for matching input fields and set values.
  If you don't get a match on an input field, try a choice.
*/
static void PutDataInForm(char *cp)
{
  char *var_name;
  char *var_value;
  int var_len, i;
  Item *item;
  Line *line;

  var_name = CopySolidString(cp);       /* var */
  if (*var_name == 0) {
    return;
  }
  cp += strlen(var_name);
  while (isspace((unsigned char)*cp)) cp++;
  var_value = cp;
  if (CF.cur_input != 0) {
    item = CF.cur_input;
    do {
      if (strcasecmp(var_name,item->header.name) == 0) {
        var_len = strlen(cp);
	if (item->input.init_value)
	  free(item->input.init_value);
        item->input.init_value = safemalloc(var_len+1);
        strcpy(item->input.init_value,cp); /* new initial value in field */
	if (item->input.value)
	  free(item->input.value);
        item->input.buf = var_len+1;
        item->input.value = safemalloc(item->input.buf);
        strcpy(item->input.value,cp);     /* new value in field */
        /* New value, but don't change length */
        free(var_name);                 /* goto's have their uses */
        return;
      }
      item=item->input.next_input;        /* next input field */
    } while (item != CF.cur_input);       /* while not end of ring */
  }
  /* You have a matching line, but it doesn't match an input
     field.  What to do?  I know, try a choice. */
  line = &root_line;                     /* start at first line */
  do {                                  /* for all lines */
    for (i = 0; i < line->n; i++) {     /* all items on line */
      item = line->items[i];
      if (item->type == I_CHOICE) {     /* choice is good */
        if (strcasecmp(var_name,item->header.name) == 0) { /* match */
          item->choice.init_on = 0;
          if (strncasecmp(cp, "on", 2) == 0) {
            item->choice.init_on = 1;   /* set default state */
            free(var_name);             /* goto's have their uses */
            return;
          }
        }
      }
    } /* end all items in line */
    line = line->next;                  /* go to next line */
  } while (line != &root_line);         /* do all lines */
  /* You have a matching line, it didn't match an input field,
     and it didn't match a choice.  I've got it, it may match a
     selection, in which case we should use the value to
     set the choices! */
  for (item = root_item_ptr; item != 0;
       item = item->header.next) {/* all items */
    if (item->type == I_SELECT) {     /* selection is good */
      if (strcasecmp(var_name,item->header.name) == 0) { /* match */
        /* Now find the choices for this selection... */
        Item *choice_ptr;
        for (i = 0;
             i<item->selection.n;
             i++) {
          choice_ptr=item->selection.choices[i];
          /* if the choice value matches the selection */
          if (strcmp(choice_ptr->choice.value,var_value) == 0) {
            choice_ptr->choice.init_on = 1;
          } else {
            choice_ptr->choice.init_on = 0;
          }
        } /* end all choices */
      } /* end match */
    } /* end selection */
  } /* end all items */
  free(var_name);                       /* not needed now */
}
static void ct_Selection(char *cp)
{
  /* syntax: *FFSelection <name> single | multiple */
  AddItem();
  cur_sel = item;                       /* save ptr as cur_sel */
  cur_sel->type = I_SELECT;
  cur_sel->header.name = CopySolidString(cp);
  cp += strlen(cur_sel->header.name);
  while (isspace((unsigned char)*cp)) cp++;
  if (strncasecmp(cp, "multiple", 8) == 0)
    cur_sel->selection.key = IS_MULTIPLE;
  else
    cur_sel->selection.key = IS_SINGLE;
}
static void ct_Choice(char *cp)
{
  /* syntax: *FFChoice <name> <value> [on | _off_] ["<text>"] */
  /* This next edit is a liitle weak, the selection should be right
     before the choice. At least a core dump is avoided. */
  if (cur_sel == 0) {                   /* need selection for a choice */
    fprintf(stderr,"%s: Need selection for choice %s\n",
            MyName+1, cp);
    return;
  }
  bg_state = 'u';                       /* indicate b/g color now used. */
  AddItem();
  item->type = I_CHOICE;
  AssignDrawTable(item);
  item->header.name = CopySolidString(cp);
  cp += strlen(item->header.name);
  while (isspace((unsigned char)*cp)) cp++;
  item->choice.value = CopySolidString(cp);
  cp += strlen(item->choice.value);
  while (isspace((unsigned char)*cp)) cp++;
  if (strncasecmp(cp, "on", 2) == 0)
    item->choice.init_on = 1;
  else
    item->choice.init_on = 0;
  while (!isspace((unsigned char)*cp)) cp++;
  while (isspace((unsigned char)*cp)) cp++;
  if (*cp == '\"')
    item->choice.text = CopyQuotedString(++cp);
  else
    item->choice.text = "";
  item->choice.n = strlen(item->choice.text);
  item->choice.sel = cur_sel;

  if (cur_sel->selection.choices_array_count
      <= cur_sel->selection.n) {           /* no room */
    cur_sel->selection.choices_array_count += CHOICES_PER_SEL_EXPANSION;
    cur_sel->selection.choices =
      (Item **)saferealloc((void *)cur_sel->selection.choices,
			   sizeof(Item *) *
			   cur_sel->selection.choices_array_count); /* expand */
  }

  cur_sel->selection.choices[cur_sel->selection.n++] = item;
  item->header.size_y = FontHeight(item->header.dt_ptr->dt_font_struct)
    + 2 * TEXT_SPC;
  item->header.size_x = FontHeight(item->header.dt_ptr->dt_font_struct)
    + 4 * TEXT_SPC +
    XTextWidth(item->header.dt_ptr->dt_font_struct,
               item->choice.text, item->choice.n);

  myfprintf((stderr, "Choice %s, \"%s\", [%d, %d]\n", item->header.name,
          item->choice.text, item->header.size_x, item->header.size_y));
  AddToLine(item);
}
static void ct_Button(char *cp)
{
  /* syntax: *FFButton continue | restart | quit "<text>" */
  AddItem();
  item->type = I_BUTTON;
  AssignDrawTable(item);
  item->header.name = "";
  if (strncasecmp(cp, "restart", 7) == 0)
    item->button.key = IB_RESTART;
  else if (strncasecmp(cp, "quit", 4) == 0)
    item->button.key = IB_QUIT;
  else
    item->button.key = IB_CONTINUE;
  while (!isspace((unsigned char)*cp)) cp++;
  while (isspace((unsigned char)*cp)) cp++;
  if (*cp == '\"') {
    item->button.text = CopyQuotedString(++cp);
    cp += strlen(item->button.text) + 1;
    while (isspace((unsigned char)*cp)) cp++;
  } else
    item->button.text = "";
  if (*cp == '^')
    item->button.keypress = *(++cp) - '@';
  else if (*cp == 'F')
    item->button.keypress = 256 + atoi(++cp);
  else
    item->button.keypress = -1;
  item->button.len = strlen(item->button.text);
  item->header.size_y = FontHeight(item->header.dt_ptr->dt_font_struct)
    + 2 * TEXT_SPC + 2 * BOX_SPC;
  item->header.size_x = 2 * TEXT_SPC + 2 * BOX_SPC
    + XTextWidth(item->header.dt_ptr->dt_font_struct, item->button.text,
                 item->button.len);
  AddToLine(item);
  cur_button = item;
  myfprintf((stderr,"Created button, fore %s, bg %s, text %s\n",
             item->header.dt_ptr->dt_color_names[c_item_fg],
             item->header.dt_ptr->dt_color_names[c_item_bg],
             item->button.text));
}
static void ct_Command(char *cp)
{
  /* syntax: *FFCommand <command> */
  if (cur_button->button.button_array_size <= cur_button->button.n)
  {
    cur_button->button.button_array_size += BUTTON_COMMAND_EXPANSION;
    cur_button->button.commands =
      (char **)saferealloc((void *)cur_button->button.commands,
			   sizeof(char *) *
			   cur_button->button.button_array_size);
  }
  cur_button->button.commands[cur_button->button.n++] = safestrdup(cp);
}

/* End of ct_ routines */

/* Init constants with values that can be freed later. */
static void InitConstants () {
  color_names[0]=safestrdup("Light Gray");
  color_names[1]=safestrdup("Black");
  color_names[2]=safestrdup("Gray50");
  color_names[3]=safestrdup("Wheat");
  font_names[0]=safestrdup("8x13bold");
  font_names[1]=safestrdup("8x13bold");
  font_names[2]=safestrdup("8x13bold");
  screen_background_color=safestrdup("Light Gray");
  CF.p_c[input_fore].pointer_color.pixel = WhitePixel(dpy, screen);
  CF.p_c[input_back].pointer_color.pixel = BlackPixel(dpy, screen);
  CF.p_c[button_fore].pointer_color.pixel = BlackPixel(dpy, screen);
  CF.p_c[button_back].pointer_color.pixel = WhitePixel(dpy, screen);
  /* The in pointer is the reverse of the hover pointer. */
  CF.p_c[button_in_fore].pointer_color.pixel = WhitePixel(dpy, screen);
  CF.p_c[button_in_back].pointer_color.pixel = BlackPixel(dpy, screen);
}

/* read the configuration file */
static void ReadDefaults ()
{
  char *line_buf;                       /* ptr to curr config line */

  /* default button is for initial functions */
  cur_button = &CF.def_button;
  CF.def_button.button.key = IB_CONTINUE;

   /*
    * Reading .FvwmFormDefault for every form seems slow.
    *
    * This next bit puts a command at the end of the module command
    * queue in fvwm that indicates whether the file has to be read.
    *
    * Read defaults looking for "*FvwmFormDefaultRead"
    * if not there, send read,
    * then "*FvwmFormDefaultRead y",
    * then look thru defaults again.
    * The customization dialog sends "*FvwmFormDefaultRead n"
    * to start over.
    */
  InitGetConfigLine(Channel,"*FvwmFormDefault");
  while (GetConfigLine(Channel,&line_buf),line_buf) { /* get config from fvwm */
    ParseDefaults(line_buf);             /* process default config lines 1st */
  }
  if (endDefaultsRead == 'y') {         /* defaults read already */
    myfprintf((stderr,"Defaults read, no need to read file.\n"));
    return;
  } /* end defaults read already */
  SendText(Channel,"read .FvwmForm Quiet",0); /* read default config */
  SendText(Channel,"*FvwmFormDefaultRead y",0); /* remember you read it */

  InitGetConfigLine(Channel,"*FvwmFormDefault");
  while (GetConfigLine(Channel,&line_buf),line_buf) { /* get config from fvwm */
    ParseDefaults(line_buf);             /* process default config lines 1st */
  }
} /* done */

static void ReadConfig ()
{
  char *line_buf;                       /* ptr to curr config line */

  InitGetConfigLine(Channel,MyName);
  while (GetConfigLine(Channel,&line_buf),line_buf) { /* get config from fvwm */
    ParseConfigLine(line_buf);          /* process config lines */
  }
} /* done */

/* After this config is read, figure it out */
static void MassageConfig()
{
  int i, extra;
  Line *line;                           /* for scanning form lines */

  if (CF.file_to_read) {                /* if theres a data file to read */
    ReadFormData();                     /* go read it */
  }
  /* get the geometry right */
  CF.max_width = 0;
  CF.total_height = ITEM_VSPC;
  line = &root_line;                     /* start at first line */
  do {                                  /* for all lines */
    for (i = 0; i < line->n; i++) {
      line->items[i]->header.pos_y = CF.total_height;
      if (line->items[i]->header.size_y < line->size_y)
        line->items[i]->header.pos_y +=
          (line->size_y - line->items[i]->header.size_y) / 2 + 1 ;
    } /* end all items in line */
    CF.total_height += ITEM_VSPC + line->size_y;
    line->size_x += (line->n + 1) * ITEM_HSPC;
    if (line->size_x > CF.max_width)
      CF.max_width = line->size_x;
    line = line->next;                  /* go to next line */
  } while (line != &root_line);         /* do all lines */
  do {                                  /* note, already back at root_line */
    int width;
    switch (line->justify) {
    case L_LEFT:
      width = ITEM_HSPC;
      for (i = 0; i < line->n; i++) {
	line->items[i]->header.pos_x = width;
	width += ITEM_HSPC + line->items[i]->header.size_x;
      }
      break;
    case L_RIGHT:
      width = CF.max_width - line->size_x + ITEM_HSPC;
      for (i = 0; i < line->n; i++) {
	line->items[i]->header.pos_x = width;
	width += ITEM_HSPC + line->items[i]->header.size_x;
      }
      break;
    case L_CENTER:
      width = (CF.max_width - line->size_x) / 2 + ITEM_HSPC;
      for (i = 0; i < line->n; i++) {
	line->items[i]->header.pos_x = width;
	myfprintf((stderr, "Line Item[%d] @ (%d, %d)\n", i,
	       line->items[i]->header.pos_x, line->items[i]->header.pos_y));
	width += ITEM_HSPC + line->items[i]->header.size_x;
      }
      break;
    case L_LEFTRIGHT:
    /* count the number of inputs on the line - the extra space will be
     * shared amongst these if there are any, otherwise it will be added
     * as space in between the elements
     */
      extra = 0 ;
      for (i = 0 ; i < line->n ; i++) {
        if (line->items[i]->type == I_INPUT)
          extra++ ;
      }
      if (extra == 0) {
        if (line->n < 2) {  /* same as L_CENTER */
	  width = (CF.max_width - line->size_x) / 2 + ITEM_HSPC;
	  for (i = 0; i < line->n; i++) {
	    line->items[i]->header.pos_x = width;
	    width += ITEM_HSPC + line->items[i]->header.size_x;
	  }
        } else {
	  extra = (CF.max_width - line->size_x) / (line->n - 1);
	  width = ITEM_HSPC;
	  for (i = 0; i < line->n; i++) {
	    line->items[i]->header.pos_x = width;
	    width += ITEM_HSPC + line->items[i]->header.size_x + extra;
	  }
        }
      } else {
        extra = (CF.max_width - line->size_x) / extra ;
        width = ITEM_HSPC ;
        for (i = 0 ; i < line->n ; i++) {
          line->items[i]->header.pos_x = width ;
          if (line->items[i]->type == I_INPUT)
            line->items[i]->header.size_x += extra ;
          width += ITEM_HSPC + line->items[i]->header.size_x ;
        }
      }
      break;
    }
    line = line->next;                  /* go to next line */
  } while (line != &root_line);          /* do all lines */
}

/* reset all the values (also done on first display) */
static void Restart ()
{
  Item *item;

  CF.cur_input = NULL;
  CF.abs_cursor = CF.rel_cursor = 0;
  for (item = root_item_ptr; item != 0;
       item = item->header.next) {/* all items */
    switch (item->type) {
    case I_INPUT:
      if (!CF.cur_input)
	CF.cur_input = item;

      /* save old input values in a recall ring. */
      if (item->input.value && item->input.value[0] != 0) { /* ? to save */
        if (item->input.value_history_ptr == 0) {  /* no history yet */
          item->input.value_history_ptr =
	    (char **)safecalloc(sizeof(char *), 50);
          item->input.value_history_ptr[0] = safestrdup(item->input.value);
          item->input.value_history_count = 1; /* next insertion point */
          myfprintf((stderr,"Initial save of %s in slot 0\n",
                     item->input.value_history_ptr[0]));
        } else {                        /* we have a history */
          int prior;
          prior = item->input.value_history_count - 1;
          if (prior < 0) {
            for (prior = VH_SIZE - 1;
                 CF.cur_input->input.value_history_ptr[prior] == 0;
                 --prior);              /* find last used slot */
          }
          myfprintf((stderr,"Prior is %d, compare %s to %s\n",
                     prior, item->input.value,
                     item->input.value_history_ptr[prior]));

          if ( strcmp(item->input.value, item->input.value_history_ptr[prior])
               != 0) {                  /* different value */
            if (item->input.value_history_ptr[item->input.value_history_count]) {
              free(item->input.value_history_ptr[item->input.value_history_count]);
              myfprintf((stderr,"Freeing old item in slot %d\n",
                         item->input.value_history_count));
            }
            item->input.value_history_ptr[item->input.value_history_count] =
              safestrdup(item->input.value); /* save value ptr in array */
            myfprintf((stderr,"Save of %s in slot %d\n",
                       item->input.value,
                       item->input.value_history_count));

            /* leave count pointing at the next insertion point. */
            if (item->input.value_history_count < VH_SIZE - 1) { /* not full */
              ++item->input.value_history_count;    /* next slot */
            } else {
              item->input.value_history_count = 0;  /* wrap around */
            }
          } /* end something different */
        } /* end have a history */
        myfprintf((stderr,"New history yankat %d\n",
                   item->input.value_history_yankat));
      } /* end something to save */
      item->input.value_history_yankat = item->input.value_history_count;
      item->input.n = strlen(item->input.init_value);
      strcpy(item->input.value, item->input.init_value);
      item->input.left = 0;
      item->input.o_cursor = 0;
      break;
    case I_CHOICE:
      item->choice.on = item->choice.init_on;
      break;
    }
  }
}

/* redraw the frame */
void RedrawFrame ()
{
  int x, y;
  Item *item;

  for (item = root_item_ptr; item != 0;
       item = item->header.next) {      /* all items */
    switch (item->type) {
    case I_TEXT:
      RedrawText(item);
      break;
    case I_CHOICE:
      x = item->header.pos_x + TEXT_SPC + item->header.size_y;
      y = item->header.pos_y + TEXT_SPC +
        item->header.dt_ptr->dt_font_struct->ascent;
      XDrawString(dpy, CF.frame, item->header.dt_ptr->dt_GC,
                       x, y, item->choice.text,
                       item->choice.n);
      break;
    }
  }
}

void RedrawText(Item *item)
{
  int x, y;
  int len;
  char *p;

  CheckAlloc(item,item->header.dt_ptr); /* alloc colors and fonts needed */
  x = item->header.pos_x + TEXT_SPC;
  y = item->header.pos_y + ( CF.padVText / 2 ) +
    item->header.dt_ptr->dt_font_struct->ascent;
  len = item->text.n;
  if ((p = memchr(item->text.value, '\0', len)) != NULL)
    len = p - item->text.value;
  XDrawString(dpy, CF.frame, item->header.dt_ptr->dt_GC,
	      x, y, item->text.value, len);

  return;
}

/* redraw an item */
void RedrawItem (Item *item, int click)
{
  int dx, dy, len, x;
  static XSegment xsegs[4];

  switch (item->type) {
  case I_INPUT:
    /* Create frame (pressed in): */
    dx = item->header.size_x - 1;
    dy = item->header.size_y - 1;
    XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
                   item->header.dt_ptr->dt_colors[c_itemlo]);

    /* around 12/26/99, added XClearArea to this function.
       this was done to deal with display corruption during
       multifield paste.  dje */
    XClearArea(dpy, item->header.win,
               BOX_SPC + TEXT_SPC - 1, BOX_SPC,
               item->header.size_x
               - (2 * BOX_SPC) - 2 - TEXT_SPC,
               (item->header.size_y - 1)
               - 2 * BOX_SPC + 1, False);

    xsegs[0].x1 = 0, xsegs[0].y1 = 0;
    xsegs[0].x2 = 0, xsegs[0].y2 = dy;
    xsegs[1].x1 = 0, xsegs[1].y1 = 0;
    xsegs[1].x2 = dx, xsegs[1].y2 = 0;
    xsegs[2].x1 = 1, xsegs[2].y1 = 1;
    xsegs[2].x2 = 1, xsegs[2].y2 = dy - 1;
    xsegs[3].x1 = 1, xsegs[3].y1 = 1;
    xsegs[3].x2 = dx - 1, xsegs[3].y2 = 1;
    XDrawSegments(dpy, item->header.win, item->header.dt_ptr->dt_item_GC,
                  xsegs, 4);
    XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
                   item->header.dt_ptr->dt_colors[c_itemhi]);
    xsegs[0].x1 = 1, xsegs[0].y1 = dy;
    xsegs[0].x2 = dx, xsegs[0].y2 = dy;
    xsegs[1].x1 = 2, xsegs[1].y1 = dy - 1;
    xsegs[1].x2 = dx, xsegs[1].y2 = dy - 1;
    xsegs[2].x1 = dx, xsegs[2].y1 = 1;
    xsegs[2].x2 = dx, xsegs[2].y2 = dy;
    xsegs[3].x1 = dx - 1, xsegs[3].y1 = 2;
    xsegs[3].x2 = dx - 1, xsegs[3].y2 = dy;
    XDrawSegments(dpy, item->header.win, item->header.dt_ptr->dt_item_GC,
                  xsegs, 4);

    if (click) {
      x = BOX_SPC + TEXT_SPC + FontWidth(item->header.dt_ptr->dt_font_struct)
        * CF.abs_cursor - 1;
      XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
                     item->header.dt_ptr->dt_colors[c_item_bg]);
      XDrawLine(dpy, item->header.win, item->header.dt_ptr->dt_item_GC,
		x, BOX_SPC, x, dy - BOX_SPC);
      myfprintf((stderr,"Line %d/%d - %d/%d (first)\n",
                     x, BOX_SPC, x, dy - BOX_SPC));
    }
    len = item->input.n - item->input.left;
    XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
                   item->header.dt_ptr->dt_colors[c_item_fg]);
    if (len > item->input.size)
      len = item->input.size;
    else
      XDrawString(dpy, item->header.win, item->header.dt_ptr->dt_item_GC,
		       BOX_SPC + TEXT_SPC
                       + FontWidth(item->header.dt_ptr->dt_font_struct) * len,
		       BOX_SPC + TEXT_SPC
                       + item->header.dt_ptr->dt_font_struct->ascent,
		       item->input.blanks, item->input.size - len);
    XDrawString(dpy, item->header.win, item->header.dt_ptr->dt_item_GC,
		     BOX_SPC + TEXT_SPC,
		     BOX_SPC + TEXT_SPC +
                     item->header.dt_ptr->dt_font_struct->ascent,
		     item->input.value + item->input.left, len);
    if (item == CF.cur_input && !click) {
      x = BOX_SPC + TEXT_SPC
        + FontWidth(item->header.dt_ptr->dt_font_struct) * CF.abs_cursor - 1;
      XDrawLine(dpy, item->header.win, item->header.dt_ptr->dt_item_GC,
		x, BOX_SPC, x, dy - BOX_SPC);
      myfprintf((stderr,"Line %d/%d - %d/%d\n",
                 x, BOX_SPC, x, dy - BOX_SPC));
    }
    myfprintf((stderr,"Just drew input field. click %d\n",(int)click));
    break;
  case I_CHOICE:
    dx = dy = item->header.size_y - 1;
    if (item->choice.on) {
	XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
                       item->header.dt_ptr->dt_colors[c_item_fg]);
      if (item->choice.sel->selection.key == IS_MULTIPLE) {
	xsegs[0].x1 = 5, xsegs[0].y1 = 5;
	xsegs[0].x2 = dx - 5, xsegs[0].y2 = dy - 5;
	xsegs[1].x1 = 5, xsegs[1].y1 = dy - 5;
	xsegs[1].x2 = dx - 5, xsegs[1].y2 = 5;
	XDrawSegments(dpy, item->header.win, item->header.dt_ptr->dt_item_GC,
                      xsegs, 2);
      } else {
	XDrawArc(dpy, item->header.win, item->header.dt_ptr->dt_item_GC,
		 5, 5, dx - 10, dy - 10, 0, 360 * 64);
      }
    } else
      XClearWindow(dpy, item->header.win);
    if (item->choice.on)
      XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
                     item->header.dt_ptr->dt_colors[c_itemlo]);
    else
      XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
                     item->header.dt_ptr->dt_colors[c_itemhi]);
    xsegs[0].x1 = 0, xsegs[0].y1 = 0;
    xsegs[0].x2 = 0, xsegs[0].y2 = dy;
    xsegs[1].x1 = 0, xsegs[1].y1 = 0;
    xsegs[1].x2 = dx, xsegs[1].y2 = 0;
    xsegs[2].x1 = 1, xsegs[2].y1 = 1;
    xsegs[2].x2 = 1, xsegs[2].y2 = dy - 1;
    xsegs[3].x1 = 1, xsegs[3].y1 = 1;
    xsegs[3].x2 = dx - 1, xsegs[3].y2 = 1;
    XDrawSegments(dpy, item->header.win, item->header.dt_ptr->dt_item_GC,
                  xsegs, 4);
    if (item->choice.on)
      XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
                     item->header.dt_ptr->dt_colors[c_itemhi]);
    else
      XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
                     item->header.dt_ptr->dt_colors[c_itemlo]);
    xsegs[0].x1 = 1, xsegs[0].y1 = dy;
    xsegs[0].x2 = dx, xsegs[0].y2 = dy;
    xsegs[1].x1 = 2, xsegs[1].y1 = dy - 1;
    xsegs[1].x2 = dx, xsegs[1].y2 = dy - 1;
    xsegs[2].x1 = dx, xsegs[2].y1 = 1;
    xsegs[2].x2 = dx, xsegs[2].y2 = dy;
    xsegs[3].x1 = dx - 1, xsegs[3].y1 = 2;
    xsegs[3].x2 = dx - 1, xsegs[3].y2 = dy;
    XDrawSegments(dpy, item->header.win, item->header.dt_ptr->dt_item_GC,
                  xsegs, 4);
    break;
  case I_BUTTON:
    dx = item->header.size_x - 1;
    dy = item->header.size_y - 1;
    if (click)
      XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
                     item->header.dt_ptr->dt_colors[c_itemlo]);
    else
      XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
                     item->header.dt_ptr->dt_colors[c_itemhi]);
    xsegs[0].x1 = 0, xsegs[0].y1 = 0;
    xsegs[0].x2 = 0, xsegs[0].y2 = dy;
    xsegs[1].x1 = 0, xsegs[1].y1 = 0;
    xsegs[1].x2 = dx, xsegs[1].y2 = 0;
    xsegs[2].x1 = 1, xsegs[2].y1 = 1;
    xsegs[2].x2 = 1, xsegs[2].y2 = dy - 1;
    xsegs[3].x1 = 1, xsegs[3].y1 = 1;
    xsegs[3].x2 = dx - 1, xsegs[3].y2 = 1;
    XDrawSegments(dpy, item->header.win, item->header.dt_ptr->dt_item_GC,
                  xsegs, 4);
    if (click)
      XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
                     item->header.dt_ptr->dt_colors[c_itemhi]);
    else
      XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
                     item->header.dt_ptr->dt_colors[c_itemlo]);
    xsegs[0].x1 = 1, xsegs[0].y1 = dy;
    xsegs[0].x2 = dx, xsegs[0].y2 = dy;
    xsegs[1].x1 = 2, xsegs[1].y1 = dy - 1;
    xsegs[1].x2 = dx, xsegs[1].y2 = dy - 1;
    xsegs[2].x1 = dx, xsegs[2].y1 = 1;
    xsegs[2].x2 = dx, xsegs[2].y2 = dy;
    xsegs[3].x1 = dx - 1, xsegs[3].y1 = 2;
    xsegs[3].x2 = dx - 1, xsegs[3].y2 = dy;
    XDrawSegments(dpy, item->header.win, item->header.dt_ptr->dt_item_GC,
                  xsegs, 4);
    XSetForeground(dpy, item->header.dt_ptr->dt_item_GC,
                   item->header.dt_ptr->dt_colors[c_item_fg]);
    XDrawString(
      dpy, item->header.win, item->header.dt_ptr->dt_item_GC,
      BOX_SPC + TEXT_SPC,
      BOX_SPC + TEXT_SPC + item->header.dt_ptr->dt_font_struct->ascent,
      item->button.text, item->button.len);
    myfprintf((stderr,"Just put %s into a button\n",
               item->button.text));
    break;
  }
  XFlush(dpy);
}

/* execute a command */
void DoCommand (Item *cmd)
{
  int k, dn;
  char *sp;
  Item *item;

  /* pre-command */
  if (cmd->button.key == IB_QUIT)
  {
    if (!XWithdrawWindow(dpy, CF.frame, screen))
    {
      /* hm, what can we do now? just ignore this situation. */
    }
  }

  for (k = 0; k < cmd->button.n; k++) {
    char *parsed_command;
    /* construct command */
    parsed_command = ParseCommand(0, cmd->button.commands[k], '\0', &dn, &sp);
    myfprintf((stderr, "Final command[%d]: [%s]\n", k, parsed_command));

    /* send command */
    if ( parsed_command[0] == '!') {    /* If command starts with ! */
      system(parsed_command+1);         /* Need synchronous execution */
    } else {
      SendText(Channel,parsed_command, ref);
    }
  }

  /* post-command */
  if (CF.last_error) {                  /* if form has last_error field */
    memset(CF.last_error->text.value, ' ', CF.last_error->text.n); /* clear */
    /* To do this more elegantly, the window resize logic should recalculate
       size_x for the Message as the window resizes.  Right now, just clear
       a nice wide area. dje */
    XClearArea(dpy,CF.frame,
               CF.last_error->header.pos_x,
               CF.last_error->header.pos_y,
               /* CF.last_error->header.size_x, */
               2000,
               CF.last_error->header.size_y, False);
  } /* end form has last_error field */
  if (cmd->button.key == IB_QUIT) {
    if (CF.grab_server)
      XUngrabServer(dpy);
    /* This is a temporary bug workaround for the pipe drainage problem */
    SendQuitNotification(Channel);    /* let commands complete */
    /* Note how the window is withdrawn, but execution continues until
       the quit notifcation catches up with this module...
       Should not be a problem, there shouldn't be any more commands
       coming into FvwmForm.  dje */
  }
  else if (cmd->button.key == IB_RESTART) {
    Restart();
    for (item = root_item_ptr; item != 0;
         item = item->header.next) {    /* all items */
      if (item->type == I_INPUT) {
	XClearWindow(dpy, item->header.win);
	RedrawItem(item, 0);
      }
      if (item->type == I_CHOICE)
	RedrawItem(item, 0);
    }
  }
}

/* open the windows */
static void OpenWindows ()
{
  int x, y;
  int gravity = NorthWestGravity;
  Item *item;
  static XSetWindowAttributes xswa;
  static XWMHints wmh = { InputHint, True };
  static XSizeHints sh =
    { PPosition | PSize | USPosition | USSize | PWinGravity};
  XClassHint myclasshints;

  if (!CF.pointer[input_pointer]) {
    CF.pointer[input_pointer] = XCreateFontCursor(dpy, XC_xterm);
  }
  if (!CF.pointer[button_in_pointer]) {
    CF.pointer[button_in_pointer] = XCreateFontCursor(dpy, XC_hand2);
  }
  if (!CF.pointer[button_pointer]) {
    CF.pointer[button_pointer] = XCreateFontCursor(dpy,XC_hand2);
  }
  CF.screen_background = (colorset < 0)
    ? GetColor(screen_background_color)
    : Colorset[colorset].bg;

  if (!CF.p_c[input_back].used) {  /* if not set, use screen b/g */
    CF.p_c[input_back].pointer_color.pixel = CF.screen_background;
  }
  myfprintf((stderr,
             "screen bg %X, getcolor bg %X, colorset bg %X colorset %d\n",
             (int)CF.screen_background,
             (int)GetColor(screen_background_color),
             (int)Colorset[colorset].bg,
             (int)colorset));
  XQueryColor(dpy, Pcmap, &CF.p_c[input_fore].pointer_color);
  XQueryColor(dpy, Pcmap, &CF.p_c[input_back].pointer_color);
  XRecolorCursor(dpy, CF.pointer[input_pointer],
                 &CF.p_c[input_fore].pointer_color,
                 &CF.p_c[input_back].pointer_color);
  myfprintf((stderr,"input fore %X, back %X\n",
          (int)CF.p_c[input_fore].pointer_color.pixel,
          (int)CF.p_c[input_back].pointer_color.pixel));
  /* The input cursor is handled differently than the 2 button cursors. */
  XQueryColor(dpy, Pcmap, &CF.p_c[button_fore].pointer_color);
  XQueryColor(dpy, Pcmap, &CF.p_c[button_back].pointer_color);
  XRecolorCursor(dpy, CF.pointer[button_pointer],
                 &CF.p_c[button_fore].pointer_color,
                 &CF.p_c[button_back].pointer_color);
  myfprintf((stderr,"button fore %X, back %X\n",
          (int)CF.p_c[button_fore].pointer_color.pixel,
          (int)CF.p_c[button_back].pointer_color.pixel));
  XQueryColor(dpy, Pcmap, &CF.p_c[button_in_fore].pointer_color);
  XQueryColor(dpy, Pcmap, &CF.p_c[button_in_back].pointer_color);
  XRecolorCursor(dpy, CF.pointer[button_in_pointer],
                 &CF.p_c[button_in_fore].pointer_color,
                 &CF.p_c[button_in_back].pointer_color);
  myfprintf((stderr,"button in fore %X, back %X\n",
          (int)CF.p_c[button_in_fore].pointer_color.pixel,
          (int)CF.p_c[button_in_back].pointer_color.pixel));
  /* the frame window first */
  if (CF.have_geom)
  {
    if (CF.xneg)
    {
      x = DisplayWidth(dpy, screen) - CF.max_width + CF.gx;
      gravity = NorthEastGravity;
    }
    else
    {
      x = CF.gx;
    }
    if (CF.yneg)
    {
      y = DisplayHeight(dpy, screen) - CF.total_height + CF.gy;
      gravity = SouthWestGravity;
    }
    else
    {
      y = CF.gy;
    }
    if (CF.xneg && CF.yneg)
    {
      gravity = SouthEastGravity;
    }
  } else {
    FScreenCenterOnScreen(
      NULL, FSCREEN_CURRENT, &x, &y, CF.max_width, CF.total_height);
  }
  xswa.background_pixel = CF.screen_background;
  xswa.border_pixel = 0;
  xswa.colormap = Pcmap;
  myfprintf((stderr,
          "going to create window w. bg %s, b/g pixel %X, black pixel %X\n",
          screen_background_color,
          (int)xswa.background_pixel,
          BlackPixel(dpy, screen)));
  CF.frame = XCreateWindow(dpy, root, x, y, CF.max_width, CF.total_height, 0,
			   Pdepth, InputOutput, Pvisual,
			   CWColormap | CWBackPixel | CWBorderPixel, &xswa);
  XSelectInput(dpy, CF.frame,
               KeyPressMask | ExposureMask | StructureNotifyMask |
	       VisibilityChangeMask);
  if (!CF.title) {
    CF.title = MyName+1;
  }
  XStoreName(dpy, CF.frame, CF.title);
  XSetWMHints(dpy, CF.frame, &wmh);
  myclasshints.res_name = MyName+1;
  myclasshints.res_class = "FvwmForm";
  XSetClassHint(dpy,CF.frame,&myclasshints);
  sh.x = x;
  sh.y = y;
  sh.width = CF.max_width;
  sh.height = CF.total_height;
  sh.win_gravity = gravity;
  XSetWMNormalHints(dpy, CF.frame, &sh);

  for (item = root_item_ptr; item != 0;
       item = item->header.next) {      /* all items */
    switch (item->type) {
    case I_INPUT:
      CheckAlloc(item,item->header.dt_ptr); /* alloc colors and fonts needed */
      item->header.win =
	XCreateSimpleWindow(dpy, CF.frame,
			    item->header.pos_x, item->header.pos_y,
			    item->header.size_x, item->header.size_y,
			    0, CF.screen_background,
                            item->header.dt_ptr->dt_colors[c_item_bg]);
      XSelectInput(dpy, item->header.win, ButtonPressMask | ExposureMask);
      xswa.cursor = CF.pointer[input_pointer];
      XChangeWindowAttributes(dpy, item->header.win, CWCursor, &xswa);
      if (itemcolorset >= 0)
      {
        SetWindowBackground(dpy, item->header.win,
                            item->header.size_x, item->header.size_y,
                            &Colorset[(itemcolorset)], Pdepth,
                            item->header.dt_ptr->dt_GC, True);
      }
      break;
    case I_CHOICE:
      CheckAlloc(item,item->header.dt_ptr); /* alloc colors and fonts needed */
      item->header.win =
	XCreateSimpleWindow(dpy, CF.frame,
			    item->header.pos_x, item->header.pos_y,
			    item->header.size_y, item->header.size_y,
			    0, CF.screen_background,
                            item->header.dt_ptr->dt_colors[c_item_bg]);
      XSelectInput(dpy, item->header.win, ButtonPressMask | ExposureMask);
      xswa.cursor = CF.pointer[button_pointer];
      XChangeWindowAttributes(dpy, item->header.win, CWCursor, &xswa);
      if (itemcolorset >= 0)
      {
        SetWindowBackground(dpy, item->header.win,
                            item->header.size_x, item->header.size_y,
                            &Colorset[(itemcolorset)], Pdepth,
                            item->header.dt_ptr->dt_GC, True);
      }
      break;
    case I_BUTTON:
      myfprintf((stderr,"Checking alloc during Openwindow on button\n"));
      CheckAlloc(item,item->header.dt_ptr); /* alloc colors and fonts needed */
      item->header.win =
	XCreateSimpleWindow(dpy, CF.frame,
			    item->header.pos_x, item->header.pos_y,
			    item->header.size_x, item->header.size_y,
			    0, CF.screen_background,
                            item->header.dt_ptr->dt_colors[c_item_bg]);
      XSelectInput(dpy, item->header.win,
		   ButtonPressMask | ExposureMask);
      xswa.cursor = CF.pointer[button_pointer];
      XChangeWindowAttributes(dpy, item->header.win, CWCursor, &xswa);
      if (itemcolorset >= 0)
      {
        SetWindowBackground(dpy, item->header.win,
                            item->header.size_x, item->header.size_y,
                            &Colorset[(itemcolorset)], Pdepth,
                            item->header.dt_ptr->dt_GC, True);
      }
      break;
    }
  }
  Restart();
  if (colorset >= 0)
  {
    SetWindowBackground(dpy, CF.frame, CF.max_width, CF.total_height,
                        &Colorset[(colorset)], Pdepth,
                        root_item_ptr->header.dt_ptr->dt_GC, True);
  }
  if (preload_yorn != 'y') {            /* if not a preload */
    XMapRaised(dpy, CF.frame);
    XMapSubwindows(dpy, CF.frame);
    if (CF.warp_pointer) {
      XWarpPointer(dpy, None, CF.frame, 0, 0, 0, 0,
                   CF.max_width / 2, CF.total_height - 1);
    }
  }
  DoCommand(&CF.def_button);
}

static void process_message(unsigned long, unsigned long *); /* proto */
static void ParseActiveMessage(char *); /* proto */

/* read something from Fvwm */
static void ReadFvwm ()
{

    FvwmPacket* packet = ReadFvwmPacket(Channel[1]);
    if ( packet == NULL )
	exit(0);
    else
	process_message( packet->type, packet->body );
}
static void process_message(unsigned long type, unsigned long *body)
{
  switch (type) {
  case M_CONFIG_INFO:                   /* any module config command */
    myfprintf((stderr,"process_message: Got command: %s\n", (char *)&body[3]));
    ParseActiveMessage((char *)&body[3]);
    break;
  case M_ERROR:
  case M_STRING:
    if (CF.last_error) {                /* if form has message area */
      /* ignore form size, its OK to write outside the window boundary */
      int msg_len;
      char *msg_ptr;
      /* Clear old message first */
      memset(CF.last_error->text.value, ' ', CF.last_error->text.n); /* clear */
      XClearArea(dpy,CF.frame,
                 CF.last_error->header.pos_x,
                 CF.last_error->header.pos_y,
                 2000,
                 CF.last_error->header.size_y, False);
      msg_ptr = (char *)&body[3];
      msg_len = strlen(msg_ptr);
      if (msg_ptr[msg_len-1] == '\n') { /* line ends w newline */
        msg_ptr[msg_len-1] = '\0'; /* strip off \n */
      }
      if (CF.last_error->text.n <= msg_len) { /* if message wont fit */
        CF.last_error->text.value = saferealloc(CF.last_error->text.value,
						msg_len * 2);
        CF.last_error->text.n = msg_len * 2;
      }
      strncpy(CF.last_error->text.value,msg_ptr,
              CF.last_error->text.n);
      RedrawText(CF.last_error);
      break;
    } /* module has last_error field */
  } /* end switch header */
}

/* These are the message from fvwm FvwmForm understands after form is
   active. */
static void am_Map(char *);
static void am_UnMap(char *);
static void am_Stop(char *);
static struct CommandTable am_table[] =
{
  {"Map",am_Map},
  {"Stop",am_Stop},
  {"UnMap",am_UnMap}
};

/* This is similar to the other 2 "Parse" functions. */
static void ParseActiveMessage(char *buf)
{
  char *p;
  struct CommandTable *e;
  if (buf[strlen(buf)-1] == '\n') {     /* if line ends with newline */
    buf[strlen(buf)-1] = '\0';	/* strip off \n */
  }

  if (strncasecmp(buf, "Colorset", 8) == 0) {
    Item *item;
    int n = LoadColorset(&buf[8]);
    if(n == colorset || n == itemcolorset) {
      for (item = root_item_ptr; item != 0;
           item = item->header.next) {
        if (item->header.dt_ptr) {      /* if item has a drawtable */
          item->header.dt_ptr->dt_used = 0;
          if(item->header.dt_ptr->dt_GC) {
            XFreeGC(dpy,item->header.dt_ptr->dt_GC);
            item->header.dt_ptr->dt_GC = NULL;
          }
          if(item->header.dt_ptr->dt_item_GC) {
            XFreeGC(dpy,item->header.dt_ptr->dt_item_GC);
            item->header.dt_ptr->dt_item_GC = NULL;
          }
          CheckAlloc(item,item->header.dt_ptr); /* alloc colors/fonts needed */
          RedrawItem(item, 0);
          if (itemcolorset >= 0 && item->header.win != 0) {
            SetWindowBackground(dpy, item->header.win,
                                item->header.size_x, item->header.size_y,
                                &Colorset[(itemcolorset)], Pdepth,
                                item->header.dt_ptr->dt_GC, True);
          }
        } /* end item has a drawtable */
        if (colorset >= 0)
          {
            SetWindowBackground(dpy, CF.frame, CF.max_width, CF.total_height,
                                &Colorset[(colorset)], Pdepth,
                                root_item_ptr->header.dt_ptr->dt_GC, True);
          }
      } /* end all items */
    }
    return;
  } /* end colorset command */
  if (strncasecmp(buf, XINERAMA_CONFIG_STRING, sizeof(XINERAMA_CONFIG_STRING)-1)
      == 0) {
    FScreenConfigureModule(buf + sizeof(XINERAMA_CONFIG_STRING)-1);
    return;
  }
  if (strncasecmp(buf, MyName, MyNameLen) != 0) {/* If its not for me */
    return;
  } /* Now I know its for me. */
  p = buf+MyNameLen;                  /* jump to end of my name */
  /* at this point we have recognized "*FvwmForm" */
  e = FindToken(p,am_table,struct CommandTable);/* find cmd in table */
  if (e == 0) {                       /* if no match */
    /* this may be a configuration command of another same form */
    if (FindToken(p, ct_table, struct CommandTable) == 0)
      fprintf(stderr,"%s: Active command unknown: %s\n",MyName+1,buf);
    return;                             /* ignore it */
  }

  p=p+strlen(e->name);                  /* skip over name */
  while (isspace((unsigned char)*p)) p++;              /* skip whitespace */

  FormVarsCheck(&p);
  e->function(p);                       /* call cmd processor */
  return;
} /* end function */

static void am_Map(char *cp)
{
  XMapRaised(dpy, CF.frame);
  XMapSubwindows(dpy, CF.frame);
  if (CF.warp_pointer) {
    XWarpPointer(dpy, None, CF.frame, 0, 0, 0, 0,
                 CF.max_width / 2, CF.total_height - 1);
  }
  myfprintf((stderr, "Map: got it\n"));
}
static void am_UnMap(char *cp)
{
  XUnmapWindow(dpy, CF.frame);
  myfprintf((stderr, "UnMap: got it\n"));
}
static void am_Stop(char *cp)
{
  /* syntax: *FFStop */
  myfprintf((stderr,"Got stop command.\n"));
  exit (0);                             /* why bother, just exit. */
}

/* main event loop */
static void MainLoop (void)
{
  fd_set fds;

  while ( !isTerminated ) {
    FD_ZERO(&fds);
    FD_SET(Channel[1], &fds);
    FD_SET(fd_x, &fds);

    XFlush(dpy);
    if (fvwmSelect(32, &fds, NULL, NULL, NULL) > 0) {
      if (FD_ISSET(Channel[1], &fds))
	ReadFvwm();
      if (FD_ISSET(fd_x, &fds))
	ReadXServer();
    }
  }
}


/* signal-handler to make the application quit */
static RETSIGTYPE
TerminateHandler(int sig)
{
  fvwmSetTerminate(sig);
}


/* main procedure */
int main (int argc, char **argv)
{
  int i;
  FILE *fdopen();
  char *s;                              /* FvwmAnimate */
  char mask_mesg[20];
  char cmd[200];

#ifdef DEBUGTOFILE
  freopen(".FvwmFormDebug","w",stderr);
#endif

#ifdef I18N_MB
  setlocale(LC_CTYPE, "");
#endif

  /* From FvwmAnimate start */
  /* Save our program  name - for error events */
  if ((s=strrchr(argv[0], '/')))	/* strip path */
    s++;
  else				/* no slash */
    s = argv[0];
  if(argc>=7)                         /* if override name */
    s = argv[6];                      /* use arg as name */
  MyNameLen=strlen(s)+1;		/* account for '*' */
  MyName = safemalloc(MyNameLen+1);	/* account for \0 */
  *MyName='*';
  strcpy(MyName+1, s);		/* append name */

  /* convert aliases FormFvwmMyFlavor. used in 2.3 to FvwmForm-MyFlavor */
  if (matchWildcards("FormFvwm*.", s))
  {
    strcpy(MyName + 1, "FvwmForm-");
    strncpy(MyName + 10, s + 8, MyNameLen - 10);
    fprintf(stderr, "FvwmForm: Please use alias name %s instead of %s\n",
      MyName + 1, s);
  }

  myfprintf((stderr,"%s: Starting, argv[0] is %s, len %d\n",MyName+1,
             argv[0],MyNameLen));

  if (argc < 6) {                       /* Now MyName is defined */
    fprintf(stderr,"%s Version "VERSION" should only be executed by fvwm!\n",
            MyName+1);
    exit(1);
  }

#ifdef HAVE_SIGACTION
  {
    struct sigaction  sigact;

#ifdef SA_INTERRUPT
    sigact.sa_flags = SA_INTERRUPT;
#else
    sigact.sa_flags = 0;
#endif
    sigemptyset(&sigact.sa_mask);
    sigaddset(&sigact.sa_mask, SIGTERM);
    sigaddset(&sigact.sa_mask, SIGPIPE);
    sigaddset(&sigact.sa_mask, SIGINT);
    sigact.sa_handler = TerminateHandler;

    sigaction(SIGPIPE, &sigact, NULL);
    sigaction(SIGTERM, &sigact, NULL);
    sigaction(SIGINT, &sigact, NULL);
  }
#else
#ifdef USE_BSD_SIGNALS
  fvwmSetSignalMask( sigmask(SIGPIPE) | sigmask(SIGTERM) | sigmask(SIGINT) );
#endif
  signal(SIGPIPE, TerminateHandler);  /* Dead pipe == Fvwm died */
  signal(SIGTERM, TerminateHandler);
  signal(SIGINT, TerminateHandler);
#ifdef HAVE_SIGINTERRUPT
  siginterrupt(SIGPIPE, 1);
  siginterrupt(SIGTERM, 1);
  siginterrupt(SIGINT, 1);
#endif
#endif

  Channel[0] = atoi(argv[1]);
  Channel[1] = atoi(argv[2]);

  dpy = XOpenDisplay("");
  if (dpy==NULL) {
    fprintf(stderr,"%s: could not open display\n",MyName+1);
    exit(1);
  }
  /* From FvwmAnimate end */

  i = 7;
  if (argc >= 8) {                      /* if have arg 7 */
    if (strcasecmp(argv[7],"preload") == 0) { /* if its preload */
      preload_yorn = 'y';               /* remember that. */
      i = 8;
    }
  }
  for (;i<argc;i++) {                   /* look at remaining args */
    if (strchr(argv[i],'=')) {          /* if its a candidate */
      putenv(argv[i]);                  /* save it away */
      CF.have_env_var = 'y';            /* remember we have at least one */
    }
  }
  ref = strtol(argv[4], NULL, 16);      /* capture reference window */
  if (ref == 0) ref = None;
  myfprintf((stderr, "ref == %d\n", (int)ref));

  InitPictureCMap(dpy);
  FScreenInit(dpy);
  /* prevent core dumps if fvwm doesn't provide any colorsets */
  AllocColorset(0);
  FShapeInit(dpy);

  fd_x = XConnectionNumber(dpy);

  screen = DefaultScreen(dpy);
  root = RootWindow(dpy, screen);

  InitConstants();
  ReadDefaults();                       /* get config from fvwm */

  if (strcasecmp(MyName+1,"FvwmForm") != 0) { /* if not already read */
    sprintf(cmd,"read %s Quiet",MyName+1); /* read quiet modules config */
    SendText(Channel,cmd,0);
  }

  ReadConfig();                         /* get config from fvwm */

  MassageConfig();                      /* add data, calc window x/y */

  /* Now tell fvwm we want *Alias commands in real time */
  sprintf(mask_mesg,"SET_MASK %lu\n",(unsigned long)
          (M_SENDCONFIG|M_CONFIG_INFO|M_ERROR|M_STRING));
  SendInfo(Channel, mask_mesg, 0);      /* tell fvwm about our mask */
  OpenWindows();                        /* create initial window */
  SendFinishedStartupNotification(Channel);/* tell fvwm we're running */
  MainLoop();                           /* start */

  return 0;                             /* */
}


void DeadPipe(int nonsense)
{
  exit(0);
}



syntax highlighted by Code2HTML, v. 0.9.1