/*
 * Copyright (c) 1997 Raphaël Goulais <velephys@hol.fr>
 * Copyright (c) 1997 Guylhem Aznar <guylhem@danmark.linux.eu.org>
 * Copyright (C) 1996 Frank Fejes
 * Copyright (C) 1995 Bo Yang
 * Copyright (C) 1993 Robert Nation
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/****************************************************************************
 * 
 * Configure.c: reads the configuration files, interprets them,
 * and sets up menus, bindings, colors, and fonts as specified
 *
 ***************************************************************************/

#include "../configure.h"

#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <pwd.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>

#include <X11/Xproto.h>
#include <X11/Xatom.h>

#include "afterstep.h"
#include "menus.h"
#include "misc.h"
#include "parse.h"
#include "screen.h"
#include "asbuttons.h"

#ifdef XPM
#include <X11/xpm.h>
char *PixmapPath = AFTER_XPMDIR;
#endif
char *IconPath = AFTER_BPMDIR;
char *ModulePath = AFTER_MODULEDIR;

int shall_override_config_file;
char *config_file;
char *config_file_to_override;
char *white = "white";
char *black = "black";
char *grey = "SlateGrey";
char *Menuback;
char *Menufore;
char *Menustipple;
char *Stdback;
char *Stdfore;
char *Stickyback;
char *Stickyfore;
char *Hiback;
char *Hifore;

int MeltStartMenu();
void GetColors(void);
Pixel GetColor(char *);
MenuRoot *NewMenuRoot(char *name);
char *stripcpy(char *);
char *stripcpy2(char *, int, Bool);
char *stripcpy3(char *, Bool);
void bad_binding(int num);
void nofont(char *name);
void nocolor(char *note, char *name);

/* used by MeltStartMenu & analyse */
int i, k, l, count;
extern int errno;
char *as_tmp, *as_dir;
FILE *start_menu, *as_source;
struct dirent **list;
/* That's all, folks ! */

int contexts;
int mods, func, func_val_1, func_val_2;

#ifndef NO_TEXTURE
#include "stepgfx.h"
char *TitleStyle = NULL;
char *TexTypes = NULL;
char *TexMaxcols = NULL;
char *TColor = NULL, *IColor = NULL, *MColor = NULL, *UColor = NULL,
*SColor = NULL;
char *TGColor = NULL;
char *TPixmap = NULL, *UPixmap = NULL, *SPixmap = NULL, *MTPixmap = NULL;
char *MPixmap = NULL, *MTPixmapMask = NULL, *MArrowPixmap = NULL;
char hincolor[15];
char hircolor[15];
char hiscolor[15];
char loncolor[15];
char lorcolor[15];
char loscolor[15];

extern void InitTextureData(TextureInfo * info, char *title, char *utitle,
		     char *mtitle, char *menu, char *sticky, char *text);
int IconTexType = TEXTURE_BUILTIN;
char *IconBgColor;
char *IconTexColor;
int IconMaxColors = 16;
char *IconPixmapFile;
int IconTexFlags = 0;
#endif

unsigned PopupCount = 0;
MenuRoot *PopupTable[MAXPOPUPS];
int dummy;

extern XContext MenuContext;	/* context for afterstep menus */
extern Bool DoHandlePageing;

/* value for the rubberband XORing */
unsigned long XORvalue;
int have_the_colors = 0;
char *RMGeom = NULL;

/*
 * Order is important here! if one keyword is the same as the first part of
 * another keyword, the shorter one must come first!
 */
struct config main_config[] =
{
    {"OpaqueResize", SetFlag, (char **) OpaqueResize, (int *) 0},
    {"StubbornIcons", SetFlag, (char **) StubbornIcons, (int *) 0},
  {"StubbornPlacement", SetFlag, (char **) StubbornPlacement, (int *) 0},
    {"StubbornIconPlacement", SetFlag, (char **) StubbornIconPlacement,
     (int *) 0},
    {"Font", assign_string, &Scr.StdFont.name, (int *) 0},
    {"WindowFont", assign_string, &Scr.WindowFont.name, (int *) 0},
    {"MenuForeColor", assign_string, &Menufore, (int *) 0},
    {"MenuBackColor", assign_string, &Menuback, (int *) 0},
    {"MenuStippleColor", assign_string, &Menustipple, (int *) 0},
    {"StdForeColor", assign_string, &Stdfore, (int *) 0},
    {"StdBackColor", assign_string, &Stdback, (int *) 0},
#ifdef XPM
    {"PixmapPath", assign_string, &PixmapPath, (int *) 0},
#endif
    {"StickyForeColor", assign_string, &Stickyfore, (int *) 0},
    {"StickyBackColor", assign_string, &Stickyback, (int *) 0},
    {"HiForeColor", assign_string, &Hifore, (int *) 0},
    {"HiBackColor", assign_string, &Hiback, (int *) 0},
    {"IconPath", assign_string, &IconPath, (int *) 0},
    {"IconBox", SetBox, (char **) 0, (int *) 0},
    {"StickyIcons", SetFlag, (char **) StickyIcons, (int *) 0},
    {"IconFont", assign_string, &Scr.IconFont.name, (int *) 0},
    {"IconTitle", SetFlag, (char **) IconTitle, (int *) 0},
    {"KeepIconWindows", SetFlag, (char **) KeepIconWindows, (int *) 0},
    {"NoPPosition", SetFlag, (char **) NoPPosition, (int *) 0},
{"CirculateSkipIcons", SetFlag, (char **) CirculateSkipIcons, (int *) 0},

    {"Style", ParseStyle, (char **) 0, (int *) 0},
    {"EdgeScroll", SetInts, (char **) &Scr.EdgeScrollX,
     &Scr.EdgeScrollY},
    {"RandomPlacement", SetFlag, (char **) RandomPlacement, (int *) 0},
    {"SmartPlacement", SetFlag, (char **) SMART_PLACEMENT, (int *) 0},
    {"DontMoveOff", SetFlag, (char **) DontMoveOff, (int *) 0},
{"DecorateTransients", SetFlag, (char **) DecorateTransients, (int *) 0},
  {"CenterOnCirculate", SetFlag, (char **) CenterOnCirculate, (int *) 0},
    {"AutoRaise", SetInts, (char **) &Scr.AutoRaiseDelay, &dummy},
    {"DeskTopScale", SetInts, (char **) &Scr.VScale, &dummy},
    {"DeskTopSize", SetInts, (char **) &Scr.VxMax, &Scr.VyMax},
    {"ClickTime", SetInts, (char **) &Scr.ClickTime, &dummy},
    {"OpaqueMove", SetInts, (char **) &Scr.OpaqueSize, &dummy},
    {"XorValue", SetInts, (char **) &XORvalue, &dummy},
    {"Mouse", ParseMouseEntry, (char **) 1, (int *) 0},
    {"Popup", ParsePopupEntry, (char **) 1, (int *) 0},
    {"Function", ParsePopupEntry, (char **) 1, (int *) 0},
    {"Key", ParseKeyEntry, (char **) 1, (int *) 0},
{"ClickToFocus", SetFlag, (char **) ClickToFocus, (int *) EatFocusClick},
 {"ClickToRaise", SetButtonList, (char **) &Scr.RaiseButtons, (int *) 0},
    {"MenusHigh", SetFlag, (char **) MenusHigh, (int *) 0},
    {"SloppyFocus", SetFlag, (char **) SloppyFocus, (int *) 0},
    {"Cursor", SetCursor, (char **) 0, (int *) 0},
    {"PagingDefault", SetInts, (char **) &DoHandlePageing, &dummy},
    {"EdgeResistance", SetInts, (char **) &Scr.ScrollResistance,
     &Scr.MoveResistance},
    {"BackingStore", SetFlag, (char **) BackingStore, (int *) 0},
    {"AppsBackingStore", SetFlag, (char **) AppsBackingStore, (int *) 0},
    {"SaveUnders", SetFlag, (char **) SaveUnders, (int *) 0},
    {"ModulePath", assign_string, &ModulePath, (int *) 0},
#ifndef NO_TEXTURE
    {"TitleBarStyle", assign_string, &TitleStyle, (int *) 0},
    {"TextureTypes", assign_string, &TexTypes, (int *) 0},
    {"TextureMaxColors", assign_string, &TexMaxcols, (int *) 0},
    {"TitleTextureColor", assign_string, &TColor, (int *) 0},	/* title */
    {"MenuTextureColor", assign_string, &IColor, (int *) 0},	/* menu items */
    {"UTitleTextureColor", assign_string, &UColor, (int *) 0},	/* unfocused title */
    {"MTitleTextureColor", assign_string, &MColor, (int *) 0},	/* menu title */
    {"STitleTextureColor", assign_string, &SColor, (int *) 0},	/* sticky title */
    {"MenuPixmap", assign_string, &MPixmap, (int *) 0},		/* menu entry pixmap */
    {"MTitlePixmap", assign_string, &MTPixmap, (int *) 0},	/* menu title pixmap */
    {"MArrowPixmap", assign_string, &MArrowPixmap, (int *) 0},
    {"TitlePixmap", assign_string, &TPixmap, (int *) 0},	/* menu arrow pixmap */
    {"UTitlePixmap", assign_string, &UPixmap, (int *) 0},	/* title pixmap */
    {"STitlePixmap", assign_string, &SPixmap, (int *) 0},	/* title pixmap */
    {"TextGradientColor", assign_string, &TGColor, (int *) 0},	/* title text */
 {"TexturedHandle", SetTextureFlag, (char **) TexturedHandle, (int *) 0},
 {"TitlebarNoPush", SetTextureFlag, (char **) TitlebarNoPush, (int *) 0},
    {"GradientText", SetTextureFlag, (char **) GradientText, (int *) 0},
    {"ButtonTextureType", SetInts, (char **) &IconTexType, &dummy},
    {"ButtonBgColor", assign_string, &IconBgColor, (int *) 0},
    {"ButtonTextureColor", assign_string, &IconTexColor, (int *) 0},
    {"ButtonMaxColors", SetInts, (char **) &IconMaxColors, &dummy},
    {"ButtonPixmap", assign_string, &IconPixmapFile, (int *) 0},
    {"ButtonNoBorder", SetIconFlag, (char **) IconNoBorder, (int *) 0},
#endif
    {"TitleTextAlign", SetInts, (char **) &Scr.TitleTextAlign, &dummy},
    {"TitleButton", SetTitleButton, (char **) 1, (int *) 0},
    {"TitleTextMode", SetTitleText, (char **) 1, (int *) 0},
    {"ResizeMoveGeometry", assign_string, &RMGeom, (int *) 0},
    {"", 0, (char **) 0, (int *) 0}
};

struct config func_config[] =
{
    {"Nop", set_func, (char **) F_NOP},
    {"Title", set_func, (char **) F_TITLE},
    {"Beep", set_func, (char **) F_BEEP},
    {"Quit", set_func, (char **) F_QUIT},
    {"Refresh", set_func, (char **) F_REFRESH},
    {"Move", set_func, (char **) F_MOVE},
    {"Iconify", set_func, (char **) F_ICONIFY},
    {"Maximize", set_func, (char **) F_MAXIMIZE},
    {"Shade", set_func, (char **) F_SHADE},
    {"Resize", set_func, (char **) F_RESIZE},
    {"RaiseLower", set_func, (char **) F_RAISELOWER},
    {"Raise", set_func, (char **) F_RAISE},
    {"PutOnTop", set_func, (char **) F_PUTONTOP},
    {"PutOnBack", set_func, (char **) F_PUTONBACK},
    {"Lower", set_func, (char **) F_LOWER},
    {"Delete", set_func, (char **) F_DELETE},
    {"Close", set_func, (char **) F_CLOSE},
    {"Destroy", set_func, (char **) F_DESTROY},
    {"PopUp", set_func, (char **) F_POPUP},
    {"Function", set_func, (char **) F_FUNCTION},
    {"CursorMove", set_func, (char **) F_MOVECURSOR},
    {"Stick", set_func, (char **) F_STICK},
    {"CirculateUp", set_func, (char **) F_CIRCULATE_UP},
    {"CirculateDown", set_func, (char **) F_CIRCULATE_DOWN},
    {"ChangeWindowUp", set_func, (char **) F_CHANGEWINDOW_UP},
    {"ChangeWindowDown", set_func, (char **) F_CHANGEWINDOW_DOWN},
    {"GetHelp", set_func, (char **) F_GETHELP},
    {"Wait", set_func, (char **) F_WAIT},
    {"Warp", set_func, (char **) F_WARP},
    {"Desk", set_func, (char **) F_DESK},
    {"WindowsDesk", set_func, (char **) F_CHANGE_WINDOWS_DESK},
    {"Focus", set_func, (char **) F_FOCUS},
    {"Module", set_func, (char **) F_MODULE},
    {"QuickRestart", set_func, (char **) F_QUICKRESTART},
    {"Send_WindowList", set_func, (char **) F_SEND_WINDOW_LIST},

#ifndef NO_VIRTUAL
    {"Scroll", set_func, (char **) F_SCROLL},
    {"GotoPage", set_func, (char **) F_GOTO_PAGE},
    {"TogglePage", set_func, (char **) F_TOGGLE_PAGE},
#endif
    {"Exec", set_func, (char **) F_EXEC},
    {"Background", set_func, (char **) F_BACKGROUND},
    {"ChangeLook", set_func, (char **) F_CHANGELOOK},
    {"ChangeFeel", set_func, (char **) F_CHANGEFEEL},
    {"Restart", set_func, (char **) F_RESTART},
#ifndef NO_WINDOWLIST
    {"WindowList", set_func, (char **) F_WINDOWLIST},
#endif
    {"", 0, (char **) 0}
};

struct charstring {
    char key;
    int value;
};


/* The keys must be in lower case! */
struct charstring win_contexts[] =
{
    {'w', C_WINDOW},
    {'t', C_TITLE},
    {'i', C_ICON},
    {'r', C_ROOT},
    {'f', C_FRAME},
    {'s', C_SIDEBAR},
    {'1', C_L1},
    {'2', C_R1},
    {'3', C_L2},
    {'4', C_R2},
    {'5', C_L3},
    {'6', C_R3},
    {'7', C_L4},
    {'8', C_R4},
    {'9', C_L5},
    {'0', C_R5},
    {'a', C_WINDOW | C_TITLE | C_ICON | C_ROOT | C_FRAME | C_SIDEBAR |
     C_L1 | C_L2 | C_L3 | C_L4 | C_L5 | C_R1 | C_R2 | C_R3 | C_R4 | C_R5},
    {0, 0}
};

/* The keys musat be in lower case! */
struct charstring key_modifiers[] =
{
    {'s', ShiftMask},
    {'c', ControlMask},
    {'m', Mod1Mask},
    {'1', Mod1Mask},
    {'2', Mod2Mask},
    {'3', Mod3Mask},
    {'4', Mod4Mask},
    {'5', Mod5Mask},
    {'a', AnyModifier},
    {'n', 0},
    {0, 0}
};

void find_context(char *, int *, struct charstring *);
char *orig_tline;
FILE *config_fd = (FILE *) 0;
/* we'll let modules get the afterstep_file that configuration was based on
 * for consistency */
char *afterstep_file;

/***************************************************************
 * 
 * Read a XPM file
 * 
 **************************************************************/
Pixmap
GetXPMTile(char *file)
{
    XWindowAttributes root_attr;
    XpmAttributes xpm_attributes;
    extern char *PixmapPath;
    char *path = NULL;
    Pixmap pix, mask;
#ifdef XPM

    path = findIconFile(file, PixmapPath, R_OK);
    if (path == NULL)
	return None;

    XGetWindowAttributes(dpy, Scr.Root, &root_attr);
/* ßßß    xpm_attributes.colormap = root_attr.colormap; */
/* ßßß    xpm_attributes.closeness = 40000; */
/* Allow for "similar" colors */
/* ßßß   xpm_attributes.valuemask = XpmReturnPixels | XpmColormap | XpmCloseness; */
    xpm_attributes.valuemask = (XpmReturnPixels | XpmReturnExtensions);

    if (XpmReadFileToPixmap(dpy, Scr.Root, path,
			    &pix, &mask,
			    &xpm_attributes) != XpmSuccess) {
	free(path);
	return None;
    }
    free(path);
    if (mask != None)
	XFreePixmap(dpy, mask);
#else
    pix = None;
#endif
    return pix;
}

Pixmap GetXPMTileMask(char *file, Pixmap * mask)
{
    XWindowAttributes root_attr;
    XpmAttributes xpm_attributes;
    extern char *PixmapPath;
    char *path = NULL;
    Pixmap pix;
#ifdef XPM

    path = findIconFile(file, PixmapPath, R_OK);
    if (path == NULL)
	return None;

    XGetWindowAttributes(dpy, Scr.Root, &root_attr);
/*  xpm_attributes.colormap = root_attr.colormap; */
/*  xpm_attributes.closeness = 40000; */
/* Allow for "similar" colors */

    xpm_attributes.valuemask = (XpmReturnPixels | XpmReturnExtensions);

    if (XpmReadFileToPixmap(dpy, Scr.Root, path,
			    &pix, mask,
			    &xpm_attributes) != XpmSuccess) {
	free(path);
	return None;
    }
    free(path);
#else
    pix = None;
#endif

    return pix;
}


/*****************************************************************************
 * 
 * This routine is responsible for reading and parsing the config file
 *
 ****************************************************************************/
void MakeMenus(const char *display_name, int thisdesktop, int shallmeltstart)
{
    char configfile_n[10][256];
    char *system_file, *realconfigfile;
    char one_configfile[256], one_systemfile[256];
    char line[256], *tline;
    int thisconfigfile = 0, nbrconfigfiles;
#ifndef NO_TEXTURE
    int icol, mcol, ucol, tcol, scol;	/* texture colors */
    int defcol;
#endif

    XORvalue = (((unsigned long) 1) << Scr.d_depth) - 1;

    Menuback = white;
    Menufore = black;
    Menustipple = grey;
    Stdback = white;
    Stdfore = black;
    Stickyback = NULL;
    Stickyfore = NULL;
    Hiback = white;
    Hifore = black;

    /* initialize some lists */
    Scr.MouseButtonRoot = NULL;
    Scr.FuncKeyRoot.next = NULL;
    Scr.TheList = NULL;

    Scr.DefaultIcon = NULL;

    /* find the home directory to look in */
    if (shallmeltstart == 1) {
#ifndef NO_MAKESTARTMENU
	if (shallmeltstart == 1)
	    MeltStartMenu();
#endif
	fprintf(stderr, "Colordepth : %d\n", Scr.d_depth);
    }

if (shall_override_config_file == 0) {

    sprintf(configfile_n[0], "base.%dbpp", Scr.d_depth);
    sprintf(configfile_n[1], "desktop/desks/desk%d/look.%dbpp", thisdesktop, Scr.d_depth);
    sprintf(configfile_n[2], "startmenu");
/* I don't see why menus should change with colordepth !!! */
    sprintf(configfile_n[3], "desktop/desks/desk%d/feel.%dbpp", thisdesktop, Scr.d_depth);
    sprintf(configfile_n[4], "autoexec.%dbpp", Scr.d_depth);

    /* For now, there're 5 different config. files */
    nbrconfigfiles = 5;
}
else /* yes, override config. files */
    nbrconfigfiles = 0;

    do {
if (shall_override_config_file == 0) {
	sprintf(one_configfile, "%s/%s", AFTER_DIR, configfile_n[thisconfigfile]);
	sprintf(one_systemfile, "%s/%s", AFTER_SHAREDIR, configfile_n[thisconfigfile++]);
} else { /* yes, override config. files */
    sprintf (one_configfile, "%s", config_file_to_override);
}
	config_file = one_configfile;
	system_file = one_systemfile;

	realconfigfile = PutHome (config_file);

	afterstep_file = realconfigfile;
	config_fd = fopen(realconfigfile, "r");
	free(realconfigfile);
	if (config_fd == (FILE *) NULL) {
	    afterstep_file = system_file;
	    config_fd = fopen(system_file, "r");
	}
	if (config_fd == (FILE *) NULL) {
	    afterstep_err("can't open %s or %s", system_file, config_file, NULL);
	    exit(1);
	}

	tline = fgets(line, (sizeof line) - 1, config_fd);
	orig_tline = tline;
	while (tline != (char *) 0) {
	    while (isspace(*tline))
		tline++;
	    if ((strlen(&tline[0]) > 1) && (tline[0] != '#') && (tline[0] != '*'))
		match_string(main_config, tline, "error in config:", config_fd);
	    tline = fgets(line, (sizeof line) - 1, config_fd);
	    orig_tline = tline;
	}
	fclose(config_fd);
	config_fd = (FILE *) NULL;
    }
    while (thisconfigfile < nbrconfigfiles);


#ifndef NO_TEXTURE
    if (!TexTypes) {
	Textures.Ttype = 0;
	Textures.Itype = 0;
	Textures.Utype = 0;
	Textures.Mtype = 0;
	Textures.Stype = 0;
    } else {
	scol = tcol = ucol = mcol = icol = -1;
	sscanf(TexTypes, "%i %i %i %i %i", &tcol, &ucol, &scol, &mcol, &icol);
	Textures.Ttype = (tcol >= 0 ? tcol : 0);
	Textures.Itype = (icol >= 0 ? icol : 0);
	Textures.Utype = (ucol >= 0 ? ucol : 0);
	Textures.Mtype = (mcol >= 0 ? mcol : 0);
	Textures.Stype = (scol >= 0 ? scol : 0);
    }
    if (Scr.d_depth > 8) {
	defcol = 32;
    } else {
	defcol = 10;
    }
    if (!TexMaxcols) {
	Textures.Tmaxcols = defcol;
	Textures.Imaxcols = defcol;
	Textures.Mmaxcols = defcol;
	Textures.Umaxcols = defcol;
	Textures.Smaxcols = defcol;
    } else {
	scol = tcol = ucol = mcol = icol = -1;
	sscanf(TexMaxcols, "%i %i %i %i %i", &tcol, &ucol, &scol, &mcol, &icol);
	Textures.Tmaxcols = (tcol >= 0 ? tcol : defcol);
	Textures.Imaxcols = (icol >= 0 ? icol : defcol);
	Textures.Umaxcols = (ucol >= 0 ? ucol : defcol);
	Textures.Mmaxcols = (mcol >= 0 ? mcol : defcol);
	Textures.Smaxcols = (scol >= 0 ? scol : defcol);
    }
    InitTextureData(&Textures, TColor, UColor, MColor, IColor, SColor,
		    TGColor);
    /* load titlebar pixmaps */
    if (Textures.Ttype == TEXTURE_PIXMAP) {
	if (TPixmap == NULL)
	    Textures.Ttype = 0;
	else {
	    if ((Scr.ForeTitle = GetXPMTile(TPixmap)) == None) {
		printf("couldn't load Titlebar tile pixmap\n");
		Textures.Ttype = 0;
	    }
	}
    }
    if (Textures.Utype == TEXTURE_PIXMAP) {
	if (UPixmap == NULL)
	    Textures.Utype = 0;
	else {
	    if ((Scr.BackTitle = GetXPMTile(UPixmap)) == None) {
		printf("couldn't load unfocused Titlebar tile pixmap\n");
		Textures.Utype = 0;
	    }
	}
    }
    if (Textures.Stype == TEXTURE_PIXMAP) {
	if (SPixmap == NULL)
	    Textures.Stype = 0;
	else {
	    if ((Scr.StickyTitle = GetXPMTile(SPixmap)) == None) {
		printf("couldn't load sticky Titlebar tile pixmap\n");
		Textures.Stype = 0;
	    }
	}
    }
    if (Textures.Mtype == TEXTURE_PIXMAP) {
	if (MPixmap == NULL)
	    Textures.Mtype = 0;
	else {
	    if ((Scr.MenuItem = GetXPMTile(MPixmap)) == None) {
		printf("couldn't load Menu Item pixmap\n");
		Textures.Mtype = 0;
	    }
	}
    }
    if (Textures.Itype == TEXTURE_PIXMAP) {
	if (MTPixmap == NULL)
	    Textures.Itype = 0;
	else {
/* ßßß      if ((Scr.MenuTitle = GetXPMTileMask(MTPixmap, &Scr.MenuTitleMask)) == None) {
 */
	    if ((Scr.MenuTitle = GetXPMTile(MTPixmap)) == None) {
		printf("couldn't load Menu Titlebar tile pixmap\n");
		Textures.Itype = 0;
	    }
	}
    }
    if (MArrowPixmap == NULL)
	Scr.MenuArrow = None;
    else if ((Scr.MenuArrow = GetXPMTile(MArrowPixmap)) == None) {
	printf("couldn't load menu arrow pixmap\n");
	Scr.MenuArrow = None;
    }
    /* cache textures for ideal cases (types 2 and 3) */
    if (Textures.Ttype == 2 || Textures.Ttype == 3) {
	Scr.ForeTitle = XCreatePixmap(dpy, Scr.Root, Scr.MyDisplayWidth - 1,
				      NS_TITLE_HEIGHT, Scr.d_depth);
	if (Scr.ForeTitle != None) {
	    if (!DrawHGradient(dpy, Scr.ForeTitle, 0, 0,
			       Scr.MyDisplayWidth - 1, NS_TITLE_HEIGHT,
			       Textures.Tfrom, Textures.Tto, 1,
			       Textures.Tmaxcols, Textures.Ttype - 2)) {
		XFreePixmap(dpy, Scr.ForeTitle);
		Scr.ForeTitle = None;
		Textures.Ttype = 0;
	    }
	} else {
	    Textures.Ttype = 0;
	}
    }
    if (Textures.Ttype != 0) {
	unsigned long light, dark;
	XGCValues gcv;

	if (MakeShadowColors(dpy, Textures.Tfrom, Textures.Tto,
			     &dark, &light)) {
	    gcv.foreground = light;
	    Scr.BevelReliefGC = XCreateGC(dpy, Scr.Root, GCForeground, &gcv);
	    gcv.foreground = dark;
	    Scr.BevelShadowGC = XCreateGC(dpy, Scr.Root, GCForeground, &gcv);
	} else {
	    Scr.BevelReliefGC = Scr.BevelShadowGC = None;
	}
    } else {
	Scr.BevelReliefGC = Scr.BevelShadowGC = None;
    }
    if (Textures.flags & GradientText) {
	Scr.TitleGradient = XCreatePixmap(dpy, Scr.Root, Scr.MyDisplayWidth - 1,
				     Scr.WindowFont.height, Scr.d_depth);
	if (!DrawHGradient(dpy, Scr.TitleGradient, 0, 0,
			   Scr.MyDisplayWidth - 1, Scr.WindowFont.height,
			   Textures.TGfrom, Textures.TGto, 0, 6, 0)) {
	    XFreePixmap(dpy, Scr.TitleGradient);
	    Scr.TitleGradient = None;
	    Textures.flags &= ~GradientText;
	}
    }
    if (Textures.Utype == 2 || Textures.Utype == 3) {
	Scr.BackTitle = XCreatePixmap(dpy, Scr.Root, Scr.MyDisplayWidth - 1,
				      NS_TITLE_HEIGHT, Scr.d_depth);
	if (Scr.BackTitle != None) {
	    if (!DrawHGradient(dpy, Scr.BackTitle, 0, 0,
			       Scr.MyDisplayWidth - 1, NS_TITLE_HEIGHT,
			       Textures.Ufrom, Textures.Uto, 1,
			       Textures.Umaxcols, Textures.Utype - 2)) {
		XFreePixmap(dpy, Scr.BackTitle);
		Scr.BackTitle = None;
		Textures.Utype = 0;
	    }
	} else
	    Textures.Utype = 0;
    }
    if (Textures.Stype == 2 || Textures.Stype == 3) {
	Scr.StickyTitle = XCreatePixmap(dpy, Scr.Root, Scr.MyDisplayWidth - 1,
					NS_TITLE_HEIGHT, Scr.d_depth);
	if (Scr.StickyTitle != None) {
	    if (!DrawHGradient(dpy, Scr.StickyTitle, 0, 0,
			       Scr.MyDisplayWidth - 1, NS_TITLE_HEIGHT,
			       Textures.Sfrom, Textures.Sto, 1,
			       Textures.Smaxcols, Textures.Stype - 2)) {
		XFreePixmap(dpy, Scr.StickyTitle);
		Scr.StickyTitle = None;
		Textures.Stype = 0;
	    }
	} else
	    Textures.Stype = 0;
    }
#endif				/* ! NO_TEXTURE */

    /* create pixmaps for buttons */
    ButtonStyle();

    /* If no edge scroll line is provided in the setup file, use
     * a default */
    if (Scr.EdgeScrollX == -100000)
	Scr.EdgeScrollX = 25;
    if (Scr.EdgeScrollY == -100000)
	Scr.EdgeScrollY = Scr.EdgeScrollX;

    /* if edgescroll >1000 and < 100000m
     * wrap at edges of desktop (a "spherical" desktop) */
    if (Scr.EdgeScrollX >= 1000) {
	Scr.EdgeScrollX /= 1000;
	Scr.flags |= EdgeWrapX;
    }
    if (Scr.EdgeScrollY >= 1000) {
	Scr.EdgeScrollY /= 1000;
	Scr.flags |= EdgeWrapY;
    }
    Scr.EdgeScrollX = Scr.EdgeScrollX * Scr.MyDisplayWidth / 100;
    Scr.EdgeScrollY = Scr.EdgeScrollY * Scr.MyDisplayHeight / 100;

    Scr.VxMax = Scr.VxMax * Scr.MyDisplayWidth - Scr.MyDisplayWidth;
    Scr.VyMax = Scr.VyMax * Scr.MyDisplayHeight - Scr.MyDisplayHeight;
    if (Scr.VxMax < 0)
	Scr.VxMax = 0;
    if (Scr.VyMax < 0)
	Scr.VyMax = 0;

    if (Scr.VxMax == 0)
	Scr.flags &= ~EdgeWrapX;
    if (Scr.VyMax == 0)
	Scr.flags &= ~EdgeWrapY;

    GetColors();

    /* create pixmap for icon button background */
    IconStyle();

    if ((Scr.flags & ClickToRaise) && (Scr.AutoRaiseDelay == 0))
	Scr.AutoRaiseDelay = -1;

    return;
}


/*****************************************************************************
 * 
 * Copies a text string from the config file to a specified location
 *
 ****************************************************************************/
void assign_string(char *text, FILE * fd, char **arg, int *junk)
{
    *arg = stripcpy(text);
}

/****************************************************************************
 *
 *  Read TitleText Controls
 *
 ****************************************************************************/
void SetTitleText(char *tline, FILE * fd, char **junk, int *junk2)
{
    int n;
    int ttype, y;
    n = sscanf(tline, "%d %d %s %s %s %s %s %s", &ttype, &y, hircolor,
	       hiscolor, hincolor, lorcolor, loscolor, loncolor);

    if (n != 8) {
	printf("wrong number of parameters given to TitleText\n");
	printf("t=%i y=%i 1=%s 2=%s 3=%s 4=%s 5=%s 6=%s\n", ttype, y, hircolor,
	       hiscolor, hincolor, lorcolor, loscolor, loncolor);
	return;
    }
    Scr.TitleTextType = ttype;
    Scr.TitleTextY = y;
}

/*****************************************************************************
 * 
 * read the button pixmaps
 *
 ****************************************************************************/
void ButtonStyle()
{
    XWindowAttributes root_attr;
    XpmAttributes xpm_attributes;
    Pixmap mask;


    XGetWindowAttributes(dpy, Scr.Root, &root_attr);
    xpm_attributes.colormap = root_attr.colormap;
    xpm_attributes.closeness = 4000;
    xpm_attributes.valuemask = XpmSize | XpmReturnPixels | XpmColormap | XpmCloseness;

    if (!Scr.button_pixmap[2]) {
	if (XpmCreatePixmapFromData(dpy, Scr.Root, closebutton_xpm,
				    &Scr.button_pixmap[2], &mask,
				    &xpm_attributes) != XpmSuccess) {
	    afterstep_err("Can not read closebutton_xpm to buttonstyles", NULL, NULL, NULL);
	    return;
	}
	Scr.button_width[2] = xpm_attributes.width;
	Scr.button_height[2] = xpm_attributes.height;
	Scr.button_style[2] = XPM_BUTTON_STYLE;

	if (mask != None)
	    XFreePixmap(dpy, mask);
    }
    if (!Scr.button_pixmap[1]) {
	if (XpmCreatePixmapFromData(dpy, Scr.Root, minbutton_xpm,
				    &Scr.button_pixmap[1], &mask,
				    &xpm_attributes) != XpmSuccess) {
	    afterstep_err("Can not read minbutton_xpm to buttonstyles", NULL, NULL, NULL);
	    return;
	}
	Scr.button_width[1] = xpm_attributes.width;
	Scr.button_height[1] = xpm_attributes.height;
	Scr.button_style[1] = XPM_BUTTON_STYLE;

	if (mask != None)
	    XFreePixmap(dpy, mask);
    }
}

/****************************************************************************
 * 
 *  Read Titlebar pixmap button
 *
 ****************************************************************************/
void SetTitleButton(char *tline, FILE * fd, char **junk, int *junk2)
{
#ifdef XPM
    XWindowAttributes root_attr;
    XpmAttributes xpm_attributes;
    extern char *PixmapPath;
    char *path = NULL;
    int num;
    char file[256];
    char file2[256];
    int n;

    n = sscanf(tline, "%d %s %s", &num, file, file2);

    if (n != 3) {
	printf("wrong number of parameters given with TitleButton (15x15)\n");
    }
    if (num < 0 || num > 9) {
	printf("invalid Titlebar button number: %d\n", num);
    }
    path = findIconFile(file, PixmapPath, R_OK);
    if (path == NULL) {
	printf("couldn't find Titlebar button %s\n", file);
	return;
    }
    XGetWindowAttributes(dpy, Scr.Root, &root_attr);
    xpm_attributes.colormap = root_attr.colormap;
    xpm_attributes.closeness = 40000;	/* Allow for "similar" colors */
    xpm_attributes.valuemask = XpmSize | XpmReturnPixels | XpmColormap | XpmCloseness;

    if (XpmReadFileToPixmap(dpy, Scr.Root, path,
		   &Scr.button_pixmap[num], &Scr.button_pixmap_mask[num],
			    &xpm_attributes) != XpmSuccess) {
	printf("couldn't read Titlebar button pixmap %s\n", path);
	free(path);
	return;
    }
    Scr.button_width[num] = xpm_attributes.width;
    Scr.button_height[num] = xpm_attributes.height;
    Scr.button_style[num] = XPM_BUTTON_STYLE;

    free(path);

    path = findIconFile(file2, PixmapPath, R_OK);
    if (path == NULL) {
	printf("couldn't find Titlebar button %s\n", file2);
	return;
    }
    Scr.ButtonType = 1;

    XGetWindowAttributes(dpy, Scr.Root, &root_attr);
    xpm_attributes.colormap = root_attr.colormap;
    xpm_attributes.closeness = 40000;	/* Allow for "similar" colors */
    xpm_attributes.valuemask = XpmSize | XpmReturnPixels | XpmColormap | XpmCloseness;

    if (XpmReadFileToPixmap(dpy, Scr.Root, path,
		 &Scr.dbutton_pixmap[num], &Scr.dbutton_pixmap_mask[num],
			    &xpm_attributes) != XpmSuccess) {
	printf("couldn't read Titlebar button pixmap %s\n", path);
	free(path);
	return;
    }
    Scr.button_width[num] = xpm_attributes.width;
    Scr.button_height[num] = xpm_attributes.height;
    Scr.button_style[num] = XPM_BUTTON_STYLE;

    free(path);

#endif				/* XPM */
}

/****************************************************************************
 *
 * Use builtin pixmap for iconized window background (aka Buttons)
 *
 ****************************************************************************/
static void SetBuiltInIconBg()
{
#ifdef XPM
    XWindowAttributes root_attr;
    XpmAttributes xpm_attributes;

    XGetWindowAttributes(dpy, Scr.Root, &root_attr);
    xpm_attributes.colormap = root_attr.colormap;
    xpm_attributes.closeness = 40000;	/* Allow for "similar" colors */
    xpm_attributes.valuemask = XpmSize | XpmReturnPixels | XpmColormap | XpmCloseness;

    if (XpmCreatePixmapFromData(dpy, Scr.Root, button_xpm,
				&Scr.IconBgPixmap, &Scr.IconBgMask,
				&xpm_attributes) == XpmSuccess) {
	Scr.IconBgWidth = xpm_attributes.width;
	Scr.IconBgHeight = xpm_attributes.height;
	Scr.IconBgDepth = Scr.d_depth;
    } else {
	afterstep_err("couldn't create background icon pixmap.", NULL, NULL, NULL);
	exit(1);
    }

#endif				/* XPM */

}


/****************************************************************************
 *
 * Giv'em that raised look ... the eazy way :-)
 *
 ****************************************************************************/
static void DrawOutline(Drawable d, int w, int h)
{
    if (IconTexFlags & IconNoBorder)
	return;
    /* top */
    XDrawLine(dpy, d, Scr.NormalGC, 0, 0, w - 1, 0);
    XDrawLine(dpy, d, Scr.StdReliefGC, 0, 1, w - 1, 1);
    /* bottom */
    XFillRectangle(dpy, d, Scr.StdShadowGC, 0, h - 2, w - 1, h - 1);

    /* left */
    XDrawLine(dpy, d, Scr.NormalGC, 0, 1, 0, h - 1);
    XDrawLine(dpy, d, Scr.StdReliefGC, 1, 2, 1, h - 2);
    /* right */
    XDrawLine(dpy, d, Scr.StdShadowGC, w - 1, 1, w - 1, h - 1);
    XDrawLine(dpy, d, Scr.StdShadowGC, w - 2, 2, w - 2, h - 2);
}

/***************************************************************
 * 
 * Read a XPM Icon Background from file
 * 
 **************************************************************/
static int GetXPMIconFile(char *file)
{
#ifdef XPM
    XWindowAttributes root_attr;
    XpmAttributes xpm_attributes;
    extern char *PixmapPath;
    char *path = NULL;

    path = findIconFile(file, PixmapPath, R_OK);
    if (path == NULL)
	return False;

    XGetWindowAttributes(dpy, Scr.Root, &root_attr);
    xpm_attributes.colormap = root_attr.colormap;
    xpm_attributes.closeness = 40000;	/* Allow for "similar" colors */
    xpm_attributes.valuemask = XpmSize | XpmReturnPixels | XpmColormap | XpmCloseness;

    if (XpmReadFileToPixmap(dpy, Scr.Root, path,
			    &Scr.IconBgPixmap, &Scr.IconBgMask,
			    &xpm_attributes) != XpmSuccess) {
	free(path);
	return False;
    }
    free(path);


    Scr.IconBgWidth = xpm_attributes.width;
    Scr.IconBgHeight = xpm_attributes.height;
    Scr.IconBgDepth = Scr.d_depth;

    DrawOutline(Scr.IconBgPixmap, Scr.IconBgWidth, Scr.IconBgHeight);

    return True;
#else
    return False;
#endif
}

/*******************************************************************
 * 
 * Make a gradient pixmap for iconized windows background (aka Buttons)
 * 
 *******************************************************************/

static int GetXPMGradient(int from[3], int to[3], int maxcols, int type)
{
    Scr.IconBgPixmap = XCreatePixmap(dpy, Scr.Root, 64, 64, Scr.d_depth);
    Scr.IconBgMask = None;
    Scr.IconBgWidth = 64;
    Scr.IconBgHeight = 64;
    Scr.IconBgDepth = Scr.d_depth;

    switch (type) {
    case TEXTURE_GRADIENT:
	if (!DrawDegradeRelief(dpy, Scr.IconBgPixmap, 0, 0, 64, 64,
			       from, to, 0, maxcols)) {
	    XFreePixmap(dpy, Scr.IconBgPixmap);
	    return 0;
	}
	break;
    case TEXTURE_HGRADIENT:
    case TEXTURE_HCGRADIENT:
	if (!DrawHGradient(dpy, Scr.IconBgPixmap, 0, 0, 64, 64,
		       from, to, 0, maxcols, type - TEXTURE_HGRADIENT)) {
	    XFreePixmap(dpy, Scr.IconBgPixmap);
	    return 0;
	}
	break;
    case TEXTURE_VGRADIENT:
    case TEXTURE_VCGRADIENT:
	if (!DrawVGradient(dpy, Scr.IconBgPixmap, 0, 0, 64, 64,
		       from, to, 0, maxcols, type - TEXTURE_VGRADIENT)) {
	    XFreePixmap(dpy, Scr.IconBgPixmap);
	    return 0;
	}
	break;
    default:
	return 0;
    }

    DrawOutline(Scr.IconBgPixmap, 64, 64);

    return 1;
}

/*******************************************************************
 * 
 * Make a solid color pixmap for iconized windows background (aka Buttons)
 * 
 *******************************************************************/

static int GetSolidXPM(Pixel pixel)
{
    GC gc;
    XGCValues gcv;

    gcv.foreground = pixel;
    gc = XCreateGC(dpy, Scr.Root, GCForeground, &gcv);

    Scr.IconBgPixmap = XCreatePixmap(dpy, Scr.Root, 64, 64, Scr.d_depth);
    Scr.IconBgMask = None;
    Scr.IconBgWidth = 64;
    Scr.IconBgHeight = 64;
    Scr.IconBgDepth = Scr.d_depth;

    XFillRectangle(dpy, Scr.IconBgPixmap, gc, 0, 0, 64, 64);

    XFreeGC(dpy, gc);
    DrawOutline(Scr.IconBgPixmap, 64, 64);

    return 1;
}

/*****************************************************************************
 * 
 * Create pixmap for icon background
 *
 ****************************************************************************/
void IconStyle()
{
#ifndef NO_ICON_BACKGROUND
    int FromColor[3] =
    {0x4000, 0x4000, 0x4000};
    int ToColor[3] =
    {0x8000, 0x8000, 0x8000};
    Pixel BgColor;
#endif				/* ! NO_ICON_BACKGROUND */

#ifdef XPM
    /* Free resources if this is a restart */
    if (Scr.IconBgPixmap != None)
	XFreePixmap(dpy, Scr.IconBgPixmap);
    if (Scr.IconBgMask != None)
	XFreePixmap(dpy, Scr.IconBgMask);
#endif				/* XPM */

#ifndef NO_ICON_BACKGROUND
    switch (IconTexType) {
    case TEXTURE_PIXMAP:
	if (IconPixmapFile == NULL) {
	    fprintf(stderr, "No Icon background pixmap defined. Using default.\n");
	    SetBuiltInIconBg();
	} else if (!GetXPMIconFile(IconPixmapFile)) {
	    fprintf(stderr, "Unable to load %s. Using default.\n", IconPixmapFile);
	    SetBuiltInIconBg();
	}
	break;

    case TEXTURE_GRADIENT:
    case TEXTURE_HGRADIENT:
    case TEXTURE_HCGRADIENT:
    case TEXTURE_VGRADIENT:
    case TEXTURE_VCGRADIENT:
	if (IconTexColor) {
	    if (!ParseColor(IconTexColor, FromColor, ToColor)) {
		afterstep_err("Invalid ButtonTextureColor %s\n", IconTexColor, NULL, NULL);
	    }
	}
	if (!GetXPMGradient(FromColor, ToColor, IconMaxColors, IconTexType)) {
	    afterstep_err("couldn't create Textured Button. Using default.\n", NULL, NULL, NULL);
	    SetBuiltInIconBg();
	}
	break;

    case TEXTURE_SOLID:
	if (IconBgColor) {
	    BgColor = GetColor(IconBgColor);
	} else {
	    BgColor = GetColor("grey");
	}

	if (!GetSolidXPM(BgColor)) {
	    afterstep_err("couldn't create Solid Button. Using default.\n", NULL, NULL, NULL);
	    SetBuiltInIconBg();
	}
	break;

    case TEXTURE_BUILTIN:
    default:
	SetBuiltInIconBg();
    }
    /* Icon window window will be made "on fly" */
    SetBuiltInIconBg();
    /* if the the user want icon titles, draw the titlebar into the
     * pixmap, so that there's less flickering, don't do that if
     * NO_ICON_BACKGROUND because there's a separate window for that !
     */
    if (Scr.flags & IconTitle) {
	XSetForeground(dpy, Scr.IconGC, Scr.HiColors.back);
	XFillRectangle(dpy, Scr.IconBgPixmap, Scr.IconGC, 1, 1,
		       Scr.IconBgWidth - 2, Scr.IconFont.height + 2);
    }
#endif				/* ! NO_ICON_BACKGROUND */
}

/*****************************************************************************
 * 
 * Changes a cursor def.
 *
 ****************************************************************************/
void SetCursor(char *text, FILE * fd, char **arg, int *junk)
{
    int num, cursor_num, cursor_style;

    num = sscanf(text, "%d %d", &cursor_num, &cursor_style);
    if ((num != 2) || (cursor_num >= MAX_CURSORS) || (cursor_num < 0)) {
	afterstep_err("Bad Cursor in line %s", orig_tline, NULL, NULL);
	return;
    }
    Scr.ASCursors[cursor_num] = XCreateFontCursor(dpy, cursor_style);
}

/*****************************************************************************
 * 
 * Sets a boolean flag to true
 *
 ****************************************************************************/
void SetFlag(char *text, FILE * fd, char **arg, int *another)
{
    Scr.flags |= (unsigned long) arg;
    if (another) {
	long i = strtol(text, NULL, 0);
	if (i)
	    Scr.flags |= (unsigned long) another;
    }
}
void SetTextureFlag(char *text, FILE * fd, char **arg, int *junk)
{
    Textures.flags |= (unsigned long) arg;
}
void SetIconFlag(char *text, FILE * fd, char **arg, int *junk)
{
    IconTexFlags |= (unsigned long) arg;
}

/*****************************************************************************
 * 
 * Reads in one or two integer values
 *
 ****************************************************************************/
void SetInts(char *text, FILE * fd, char **arg1, int *arg2)
{
    sscanf(text, "%d%*c%d", (int *) arg1, (int *) arg2);
}


/*****************************************************************************
 * 
 * Reads in a list of mouse button numbers
 *
 ****************************************************************************/
void SetButtonList(char *text, FILE * fd, char **arg1, int *arg2)
{
    int i, b;
    char *next;
    for (i = 0; i < MAX_BUTTONS; i++) {
	b = (int) strtol(text, &next, 0);
	if (next == text)
	    break;
	text = next;
	if (*text == ',')
	    text++;
	if ((b > 0) && (b <= MAX_BUTTONS))
	    Scr.RaiseButtons |= 1 << b;
    }
    Scr.flags |= ClickToRaise;
}


/*****************************************************************************
 * 
 * Reads Dimensions for an icon box from the config file
 *
 ****************************************************************************/
void SetBox(char *text, FILE * fd, char **arg, int *junk)
{
    int num;

    if (Scr.NumBoxes < MAX_BOXES) {
	/* Standard X11 geometry string */
	num = sscanf(text, "%d%d%d%d", &Scr.IconBoxes[Scr.NumBoxes][0],
		     &Scr.IconBoxes[Scr.NumBoxes][1],
		     &Scr.IconBoxes[Scr.NumBoxes][2],
		     &Scr.IconBoxes[Scr.NumBoxes][3]);

	/* check for negative locations */
	if (Scr.IconBoxes[Scr.NumBoxes][0] < 0)
	    Scr.IconBoxes[Scr.NumBoxes][0] += Scr.MyDisplayWidth;
	if (Scr.IconBoxes[Scr.NumBoxes][1] < 0)
	    Scr.IconBoxes[Scr.NumBoxes][1] += Scr.MyDisplayHeight;

	if (Scr.IconBoxes[Scr.NumBoxes][2] < 0)
	    Scr.IconBoxes[Scr.NumBoxes][2] += Scr.MyDisplayWidth;
	if (Scr.IconBoxes[Scr.NumBoxes][3] < 0)
	    Scr.IconBoxes[Scr.NumBoxes][3] += Scr.MyDisplayHeight;

	if (num == 4)
	    Scr.NumBoxes++;
    }
}


/****************************************************************************
 *
 * This routine computes the shadow color from the background color
 *
 ****************************************************************************/
Pixel
GetShadow(Pixel background)
{
    XColor bg_color;
    XWindowAttributes attributes;
    unsigned int r, g, b;

    XGetWindowAttributes(dpy, Scr.Root, &attributes);

    bg_color.pixel = background;
    XQueryColor(dpy, attributes.colormap, &bg_color);

    r = bg_color.red % 0xffff;
    g = bg_color.green % 0xffff;
    b = bg_color.blue % 0xffff;

    r = r >> 1;
    g = g >> 1;
    b = b >> 1;

    /* pure black: use gray */
    if (r == 0 && g == 0 && b == 0)
	r = g = b = 0x7fff;
    bg_color.red = r;
    bg_color.green = g;
    bg_color.blue = b;

    if (!XAllocColor(dpy, attributes.colormap, &bg_color)) {
	nocolor("alloc shadow", "");
	bg_color.pixel = background;
    }
    return bg_color.pixel;
}

/****************************************************************************
 *
 * This routine computes the hilight color from the background color
 *
 ****************************************************************************/
Pixel
GetHilite(Pixel background)
{
    XColor bg_color, white_p;
    XWindowAttributes attributes;

    XGetWindowAttributes(dpy, Scr.Root, &attributes);

    bg_color.pixel = background;
    XQueryColor(dpy, attributes.colormap, &bg_color);

    white_p.pixel = GetColor(white);
    XQueryColor(dpy, attributes.colormap, &white_p);

#ifndef min
#define min(a,b) (((a)<(b)) ? (a) : (b))
#define max(a,b) (((a)>(b)) ? (a) : (b))
#endif

    /* pure black: use gray */
    if (bg_color.red == 0 && bg_color.green == 0 && bg_color.blue == 0)
	bg_color.red = bg_color.green = bg_color.blue = 0xbfff;
    else {
	bg_color.red = max((white_p.red / 5), bg_color.red);
	bg_color.green = max((white_p.green / 5), bg_color.green);
	bg_color.blue = max((white_p.blue / 5), bg_color.blue);

	bg_color.red = min(white_p.red, (bg_color.red * 140) / 100);
	bg_color.green = min(white_p.green, (bg_color.green * 140) / 100);
	bg_color.blue = min(white_p.blue, (bg_color.blue * 140) / 100);
    }
#undef min
#ifdef max
#undef max
#endif

    if (!XAllocColor(dpy, attributes.colormap, &bg_color)) {
	nocolor("alloc hilight", "");
	bg_color.pixel = background;
    }
    return bg_color.pixel;
}

/****************************************************************************
 *
 * These routines put together files from start directory
 *
 ***************************************************************************/

/* Here we scan directories, searching for menu entries and directories to
 * scan ...
 */

void analyse(char *directory, char *parent)
{
    struct dirent **an_list;
    char *an_char;
    char an_dir[254];
    FILE *an_file;
    FILE *an_tmp;
    FILE *an_source;
    int an_count;
    int an_i;
    int token;

    /*
     *            Opening files ...
     */

    if ((an_char = (char *) malloc(sizeof(char) * (strlen(parent) + strlen(directory) + 300))) == NULL)
	 perror("Bad Malloc");
    sprintf(an_char, "%s/tmp/__tmp%3d", as_dir, count);
    if ((an_file = fopen(an_char, "w")) == NULL)
	perror(an_char);
    sprintf(an_char, "%s/tmp/__dir%3d", as_dir, count);
    if ((an_tmp = fopen(an_char, "w")) == NULL)
	perror(an_char);

    /*
     *            Writing menu himself.
     */

    fprintf(an_file, "Popup \"%s\"\n", directory);
    fprintf(an_file, " Title \"%s\"\n", directory);
    sprintf(an_char, "%s%s", parent, directory);

    /*
     *            Scanning directory, if file,
     *              then add entry to menu ...
     *              ... else, store directory name
     *              in a temporary file, for later
     *              treatment.
     */

    if ((an_i = scandir(an_char, &an_list, 0, 0)) == -1)
	perror(an_char);
    token = 0;
    for (i = 0; i < an_i; i++)
	if (an_list[i]->d_name != strchr(an_list[i]->d_name, '.')) {
	    sprintf(an_char, "%s%s/%s", parent, directory, an_list[i]->d_name);
	    if (chdir(an_char) != 0) {
		if ((an_source = fopen(an_char, "r")) == NULL)
		    perror(an_char);
		fgets(an_char, 254, an_source);
		fclose(an_source);
		fprintf(an_file, " Exec \"%s\" exec %s", an_list[i]->d_name, an_char);
	    } else {
		fprintf(an_file, " Popup \"%s\" %s\n", an_list[i]->d_name, an_list[i]->d_name);
		fprintf(an_tmp, "%s\n", an_list[i]->d_name);
		token++;
	    }
	}
    fprintf(an_file, "EndPopup\n\n.\n");
    fclose(an_file);
    fprintf(an_tmp, ".\n");
    fclose(an_tmp);

    /*
     *            Now, we reopen temp file,
     *              and analyse each directory
     *              stored ...
     */

    an_i = 0;
    an_count = count;
    while (token != 0) {
	sprintf(an_char, "%s/tmp/__dir%3d", as_dir, an_count);
	if ((an_tmp = fopen(an_char, "r")) == NULL)
	    perror(an_char);
	fseek(an_tmp, an_i, SEEK_SET);
	fgets(an_dir, 254, an_tmp);
	an_i = strlen(an_dir) + an_i;
	strcpy(strpbrk(an_dir, "\n"), "\0");
	fclose(an_tmp);
	sprintf(an_char, "%s%s/", parent, directory);
	count--;
	analyse(an_dir, an_char);
	token--;
    }

    /*
     *            Remove temp file ...
     */

    sprintf(an_char, "%s/tmp/__dir%3d", as_dir, an_count);
    if ((remove(an_char)) != 0)
	perror(an_char);

    /*
     *            Bye ...
     */

    return;
}

		/*
		 * We should clean-up tmp dir before
		 * 
		 * 
		 */


int MeltStartMenu()
{
/*
	if ((as_dir = (char *) malloc(sizeof(char) * (strlen(getenv("HOME")) + 36))) == NULL)
         perror("Bad Malloc : ");
*/

    if ((as_tmp = (char *) malloc(sizeof(char) * 4000)) == NULL)
	 perror("Bad Malloc : ");

    /* 
     *    Here we test the existence of various
     *      directories used for the generation.
     */

/* Set ASDIR */
as_dir = PutHome(AFTER_DIR);

    if (chdir(as_dir) != 0) {
	free(as_dir);
	perror(as_dir);
	exit(errno);
    }

/* 3 special directory out of start, but browsed too */

    sprintf(as_tmp, "%s/desktop/backgrounds", as_dir);
    if (chdir(as_tmp) != 0) {
	perror(as_tmp);
	exit(errno);
    }
    sprintf(as_tmp, "%s/desktop/looks", as_dir);
    if (chdir(as_tmp) != 0) {
	perror(as_tmp);
	exit(errno);
    }
    sprintf(as_tmp, "%s/desktop/feels", as_dir);
    if (chdir(as_tmp) != 0) {
	perror(as_tmp);
	exit(errno);
    }
    sprintf(as_tmp, "%s/start", as_dir);
    if (chdir(as_tmp) != 0) {
	perror(as_tmp);
	exit(errno);
    }
    sprintf(as_tmp, "%s/tmp", as_dir);
    if (chdir(as_tmp) != 0) {
	perror(as_tmp);
	exit(errno);
    }
    /*
     *    Opening startmenu,
     *      overwriting the old version.
     */

    sprintf(as_tmp, "%s/startmenu", as_dir);
    if ((start_menu = fopen(as_tmp, "w")) == NULL) {
	perror(as_tmp);
	exit(errno);
    }
    /*
     *    Popup menu Quit
     */

    fprintf(start_menu, "Popup \"quit\"\n Title \"Quit ...\"\n");
    fprintf(start_menu, " Module \"Quit ?\" Form QuitForm\n Quit \"Close this session\"\n");
    fprintf(start_menu, " Restart \"Restart this session\" afterstep\n");
    fprintf(start_menu, "EndPopup\n\n");

    /*
     *    Definition of pictures menu.
     *      We scan backgrounds
     *      for files and assume they're
     *      pictures.
     */

    fprintf(start_menu, "Popup \"Pictures\"\n Title \"Pictures\"\n");
    sprintf(as_tmp, "%s/desktop/backgrounds", as_dir);
    k = scandir(as_tmp, &list, 0, 0);
    for (i = 0; i < k; i++) {
	if (list[i]->d_name != strchr(list[i]->d_name, '.')) {
	    fprintf(start_menu, " Background \"%s\" %s/%s\n", list[i]->d_name, as_tmp, list[i]->d_name);
	}
    }
    fprintf(start_menu, "EndPopup\n\n");

    /*
     *    Here we start the generation of Look
     *      menu. We scan looks for available
     *      looks and place them in the menu. It also
     *      updates steprc ...
     */

    fprintf(start_menu, "Popup \"Look\"\n Title \"Look\"\n");
    sprintf(as_tmp, "%s/desktop/looks", as_dir);
    k = scandir(as_tmp, &list, 0, 0);
    for (i = 0; i < k; i++) {
	if (list[i]->d_name != strchr(list[i]->d_name, '.')) {

	    fprintf(start_menu, " ChangeLook \"%s\" %s/%s\n", list[i]->d_name, as_tmp, list[i]->d_name);
	}
    }
    fprintf(start_menu, "EndPopup\n\n");

    /* Idem for feels */

    fprintf(start_menu, "Popup \"Feel\"\n Title \"Feel\"\n");
    sprintf(as_tmp, "%s/desktop/feels", as_dir);
    k = scandir(as_tmp, &list, 0, 0);
    for (i = 0; i < k; i++) {
	if (list[i]->d_name != strchr(list[i]->d_name, '.')) {

	    fprintf(start_menu, " ChangeFeel \"%s\" %s/%s\n", list[i]->d_name, as_tmp, list[i]->d_name);
	}
    }
    fprintf(start_menu, "EndPopup\n\n");


    /*
     *    Desktop & Backgrounds menu, nothing to say,
     *      quite simple ...
     */

    fprintf(start_menu, "Popup \"Desktop\"\n Title \"Desktop\"\n");
    fprintf(start_menu, " Popup \"Backgrounds pictures\" Pictures\n");
    fprintf(start_menu, " Popup \"Look\" Look\n");
    fprintf(start_menu, " Popup \"Feel\" Feel\n");
    fprintf(start_menu, "EndPopup\n\n");

    /*
     *            scanning start directory,
     *              I call analyse for each directory
     *              found.
     */

    count = 250;
    sprintf(as_tmp, "%s/start", as_dir);
    k = scandir(as_tmp, &list, 0, 0);
    for (l = 0; l < k; l++)
	if (list[l]->d_name != strchr(list[l]->d_name, '.')) {
	    sprintf(as_tmp, "%s/start/%s", as_dir, list[l]->d_name);
	    if (chdir(as_tmp) == 0) {
		if (strcmp(list[l]->d_name, "Desktop") == 0)
		    printf("No directory \"Desktop\" in start directory allowed ...\n");
		else if (strcmp(list[l]->d_name, "quit") == 0)
		    printf("No directory \"quit\" in start directory allowed ...\n");
		else if (strcmp(list[l]->d_name, "Modules") == 0)
		    fprintf(stderr, "Melting processor ...");
		else {
		    count = count - 1;
		    sprintf(as_tmp, "%s/start/", as_dir);
		    analyse(list[l]->d_name, as_tmp);
		}
	    }
	}
    /*
     *            Merging temp files resulting
     *              form the scanning of start
     *              directory.
     */

    while (count < 250) {
	sprintf(as_tmp, "%s/tmp/__tmp%3d", as_dir, count);
	if ((as_source = fopen(as_tmp, "r")) == NULL)
	    perror(as_tmp);
	while (strcmp(fgets(as_tmp, 3998, as_source), ".\n") != 0)
	    fprintf(start_menu, "%s", as_tmp);
	fclose(as_source);
	sprintf(as_tmp, "%s/tmp/__tmp%3d", as_dir, count);
	if ((remove(as_tmp)) != 0)
	    perror(as_tmp);
	count++;
    }

    /*
     *            Here we scan start/Modules directory ...
     *              Later we should directly scan the file
     *              afterstep.modules ...
     */

    sprintf(as_tmp, "%s/start/Modules", as_dir);
    if ((k = scandir(as_tmp, &list, 0, 0)) != -1) {
	fprintf(start_menu, "Popup \"modules\"\n");
	fprintf(start_menu, " Title \"Modules\"\n");
	for (i = 0; i < k; i++)
	    if (list[i]->d_name != strchr(list[i]->d_name, '.')) {
		sprintf(as_tmp, "%s/start/Modules/%s", as_dir, list[i]->d_name);
		if (chdir(as_tmp) != 0) {
		    if ((as_source = fopen(as_tmp, "r")) == NULL)
			perror(as_tmp);
		    fgets(as_tmp, 254, as_source);
		    fclose(as_source);
		    fprintf(start_menu, " Module \"%s\"  %s\n", list[i]->d_name, as_tmp);
		}
	    }
	fprintf(start_menu, "EndPopup\n\n");
    } else
	perror(as_tmp);

    /*
     *            Here's the start menu himself,
     *              we're at the end ... ;-)
     */

    fprintf(start_menu, "Popup \"start\"\n");
    fprintf(start_menu, " Title \"Start ...\"\n");
    fprintf(start_menu, " Popup \"Desktop\" Desktop\n");
    sprintf(as_tmp, "%s/start", as_dir);
    k = scandir(as_tmp, &list, 0, 0);
    for (i = 0; i < k; i++)
	if (list[i]->d_name != strchr(list[i]->d_name, '.')) {
	    sprintf(as_tmp, "%s/start/%s", as_dir, list[i]->d_name);
	    if (chdir(as_tmp) == 0) {
		if (strcmp(list[i]->d_name, "Desktop") == 0)
		    printf("\nProblem : found Desktop\n");
		else if (strcmp(list[i]->d_name, "quit") == 0)
		    printf("\nProblem : found quit\n");
		else if (strcmp(list[i]->d_name, "Modules") == 0)
		    printf(" done !\n");
		else
		    fprintf(start_menu, " Popup \"%s\" %s\n", list[i]->d_name, list[i]->d_name);
	    }
	}
    fprintf(start_menu, " Popup \"Modules\" modules\n");
    fprintf(start_menu, " Popup \"Quit ...\" quit\n");
    sprintf(as_tmp, "%s/start", as_dir);
    k = scandir(as_tmp, &list, 0, 0);
    for (i = 0; i < k; i++)
	if (list[i]->d_name != strchr(list[i]->d_name, '.')) {
	    sprintf(as_tmp, "%s/start/%s", as_dir, list[i]->d_name);
	    if (chdir(as_tmp) != 0) {
		if ((as_source = fopen(as_tmp, "r")) == NULL)
		    perror(as_tmp);
		fgets(as_tmp, 254, as_source);
		fclose(as_source);
		fprintf(start_menu, " Exec \"%s\" exec %s", list[i]->d_name, as_tmp);
	    }
	}
    fprintf(start_menu, "EndPopup\n\n");
    fclose(start_menu);

/* default directory is HOME */

    sprintf(as_dir, "%s", getenv("HOME"));
    if (chdir(as_dir) != 0) {
	free(as_dir);
	perror(as_dir);
	exit(errno);
    }
    free(as_dir);

    return 0;
}

/****************************************************************************
 *
 * This routine loads all needed colors, and fonts,
 * and creates the GC's
 *
 ***************************************************************************/
void GetColors(void)
{
    extern MyFont *IconFont;

    if (have_the_colors)
	return;

    if (Stickyback == NULL)
	Stickyback = Stdback;
    if (Stickyfore == NULL)
	Stickyfore = Stdfore;
    have_the_colors = 1;

    /* setup default colors */
    if (Scr.d_depth < 2) {
	/* black and white - override user choices */

	Scr.MenuColors.back = GetColor(white);
	Scr.MenuColors.fore = GetColor(black);
	Scr.MenuStippleColors.back = GetColor(white);
	Scr.MenuStippleColors.fore = GetColor(black);
	Scr.MenuRelief.back = GetColor(black);
	Scr.MenuRelief.fore = GetColor(white);
	Scr.StdColors.back = GetColor(white);
	Scr.StdColors.fore = GetColor(black);
	Scr.StickyColors.back = GetColor(white);
	Scr.StickyColors.fore = GetColor(black);
	Scr.HiColors.back = GetColor(white);
	Scr.HiColors.fore = GetColor(black);
	Scr.StdRelief.back = GetColor(black);
	Scr.StdRelief.fore = GetColor(white);
	Scr.StickyRelief.back = GetColor(black);
	Scr.StickyRelief.fore = GetColor(white);
	Scr.HiRelief.back = GetColor(black);
	Scr.HiRelief.fore = GetColor(white);
    } else {
	/* color - accept user choices */

	Scr.MenuColors.back = GetColor(Menuback);
	Scr.MenuColors.fore = GetColor(Menufore);
	Scr.MenuStippleColors.back = GetColor(Menuback);
	Scr.MenuStippleColors.fore = GetColor(Menustipple);
	Scr.MenuRelief.back = GetShadow(Scr.MenuColors.back);
	Scr.MenuRelief.fore = GetHilite(Scr.MenuColors.back);
	Scr.StdColors.back = GetColor(Stdback);
	Scr.StdColors.fore = GetColor(Stdfore);
	Scr.StickyColors.back = GetColor(Stickyback);
	Scr.StickyColors.fore = GetColor(Stickyfore);
	Scr.HiColors.back = GetColor(Hiback);
	Scr.HiColors.fore = GetColor(Hifore);
	Scr.StdRelief.back = GetShadow(Scr.StdColors.back);
	Scr.StdRelief.fore = GetHilite(Scr.StdColors.back);
	Scr.StickyRelief.back = GetShadow(Scr.StickyColors.back);
	Scr.StickyRelief.fore = GetHilite(Scr.StickyColors.back);
	Scr.HiRelief.back = GetShadow(Scr.HiColors.back);
	Scr.HiRelief.fore = GetHilite(Scr.HiColors.back);
    }

    /* load the font */
    if ((Scr.StdFont.font = XLoadQueryFont(dpy, Scr.StdFont.name)) == NULL) {
	nofont(Scr.StdFont.name);
	if ((Scr.StdFont.font = XLoadQueryFont(dpy, "fixed")) == NULL)
	    exit(1);
    }
    Scr.StdFont.height = Scr.StdFont.font->ascent + Scr.StdFont.font->descent;
    Scr.StdFont.y = Scr.StdFont.font->ascent;
    Scr.EntryHeight = Scr.StdFont.height + HEIGHT_EXTRA + 2;
    /* load the window-title font */
    if ((Scr.WindowFont.font = XLoadQueryFont(dpy, Scr.WindowFont.name)) == NULL) {
	nofont(Scr.WindowFont.name);
	if ((Scr.WindowFont.font = XLoadQueryFont(dpy, "fixed")) == NULL)
	    exit(1);
    }
    Scr.WindowFont.height =
	Scr.WindowFont.font->ascent + Scr.WindowFont.font->descent;
    Scr.WindowFont.y = Scr.WindowFont.font->ascent;

    IconFont = &Scr.StdFont;

    if (Scr.IconFont.name != NULL) {
	if ((Scr.IconFont.font = XLoadQueryFont(dpy, Scr.IconFont.name)) != NULL) {
	    Scr.IconFont.height =
		Scr.IconFont.font->ascent + Scr.IconFont.font->descent;
	    Scr.IconFont.y = Scr.IconFont.font->ascent;
	    IconFont = &Scr.IconFont;
	} else
	    nofont(Scr.IconFont.name);
    } else {
	if ((Scr.IconFont.font = XLoadQueryFont(dpy, "fixed")) != NULL) {
	    Scr.IconFont.height =
		Scr.IconFont.font->ascent + Scr.IconFont.font->descent;
	    Scr.IconFont.y = Scr.IconFont.font->ascent;
	    IconFont = &Scr.IconFont;
	} else {
	    nofont("fixed: that's bad...");
	    exit(1);
	}
    }


    /* create graphics contexts */
    CreateGCs();
    XSync(dpy, 0);
    return;
}

/****************************************************************************
 * 
 *  Prints an error message for font loading
 *
 ****************************************************************************/
void nofont(char *name)
{
    afterstep_err("can't get font %s", name, NULL, NULL);
}

/****************************************************************************
 * 
 *  Processes a menu body definition
 *
 ****************************************************************************/
MenuRoot *
 ParseMenuBody(char *name, FILE * fd)
{
    MenuRoot *mr;
    char newline[256];
    register char *pline;
    char unit_1, unit_2;
    int n;

    pline = fgets(newline, (sizeof newline) - 1, fd);
    orig_tline = pline;
    if (pline == NULL)
	return 0;

    mr = NewMenuRoot(name);
    GetColors();

    while (isspace(*pline))
	pline++;
    while ((pline != (char *) 0)
	   && (mystrncasecmp("End", pline, 3) != 0)) {
	if ((*pline != '#') && (*pline != 0) && (*pline != '*')) {
	    char *ptr2 = 0;
	    match_string(func_config, pline, "bad menu body function:", fd);
	    if ((func == F_EXEC) || (func == F_POPUP) ||
		(func == F_RESTART) || (func == F_FUNCTION) ||
		(func == F_MODULE) || (func == F_BACKGROUND))
		ptr2 = stripcpy3(pline, True);
	    else
		ptr2 = stripcpy3(pline, False);

	    func_val_1 = 0;
	    func_val_2 = 0;
	    unit_1 = 's';
	    unit_2 = 's';
	    if (ptr2 != NULL) {
		n = sscanf(ptr2, "%d %d", &func_val_1, &func_val_2);
		if (n < 2)
		    n = sscanf(ptr2, "%d%c %d%c", &func_val_1, &unit_1, &func_val_2, &unit_2);
	    }
	    if (*stripcpy2(pline, 1, True))
		AddToMenu(mr, stripcpy2(pline, 1, True), stripcpy2(pline, 2, True),
		     ptr2, func, func_val_1, func_val_2, unit_1, unit_2);
	}
	pline = fgets(newline, (sizeof newline) - 1, fd);
	if (pline == (char *) 0)
	    return NULL;

	orig_tline = pline;

	while (isspace(*pline))
	    pline++;
    }
    MakeMenu(mr);

    return mr;
}

/****************************************************************************
 * 
 *  Parses a popup definition 
 *
 ****************************************************************************/
void ParsePopupEntry(char *tline, FILE * fd, char **junk, int *junk2)
{
    MenuRoot *mr = 0;

    mr = ParseMenuBody(stripcpy2(tline, 0, True), fd);

    if (PopupCount < MAXPOPUPS) {
	PopupTable[PopupCount] = mr;
	PopupCount++;
	if (strcmp(mr->name, "InitFunction") == 0) {
	    Scr.InitFunction = mr;
	} else if (strcmp(mr->name, "RestartFunction") == 0) {
	    Scr.RestartFunction = mr;
	}
    } else {
	fprintf(stderr, "Popup/Function %s ignored, you have more than %u\n",
		mr->name, MAXPOPUPS);
	free(mr);
    }
}

/****************************************************************************
 * 
 *  Parses a mouse binding
 *
 ****************************************************************************/
void ParseMouseEntry(char *tline, FILE * fd, char **junk, int *junk2)
{
    char context[256], modifiers[256], function[256], *ptr;
    MenuRoot *mr = 0;
    MenuItem *mi = 0;
    MouseButton *temp;
    int button, i, j;
    int n;
    char unit_1, unit_2;

    unit_1 = 's';
    unit_2 = 's';
    func_val_1 = 0;
    func_val_2 = 0;

    n = sscanf(tline, "%d %s %s %s %d %d", &button, context, modifiers, function,
	       &func_val_1, &func_val_2);
    if (n < 6)
	n = sscanf(tline, "%d %s %s %s %d%c %d%c", &button, context, modifiers, function,
		   &func_val_1, &unit_1, &func_val_2, &unit_2);

    find_context(context, &contexts, win_contexts);
    if ((contexts != C_ALL) && (contexts & C_LALL)) {
	/* check for nr_left_buttons */
	i = 0;
	j = (contexts & C_LALL) / C_L1;
	while (j > 0) {
	    i++;
	    j = j >> 1;
	}
	if (Scr.nr_left_buttons < i)
	    Scr.nr_left_buttons = i;
    }
    if ((contexts != C_ALL) && (contexts & C_RALL)) {
	/* check for nr_right_buttons */
	i = 0;
	j = (contexts & C_RALL) / C_R1;
	while (j > 0) {
	    i++;
	    j = j >> 1;
	}
	if (Scr.nr_right_buttons < i)
	    Scr.nr_right_buttons = i;
    }
    find_context(modifiers, &mods, key_modifiers);
    if ((contexts & C_WINDOW) && (((mods == 0) || mods == AnyModifier))) {
	Scr.buttons2grab &= ~(1 << (button - 1));
    }
    func = F_NOP;
    match_string(func_config, function, "bad mouse function:", fd);

    if ((func == F_POPUP) || (func == F_FUNCTION)) {
	unsigned i;
	ptr = stripcpy2(tline, 0, True);
	if (ptr != NULL)
	    for (i = 0; i < PopupCount; i++)
		if (mystrcasecmp(PopupTable[i]->name, ptr) == 0) {
		    mr = PopupTable[i];
		    break;
		}
	if (!mr) {
	    no_popup(ptr);
	    func = F_NOP;
	}
	if (ptr != NULL)
	    free(ptr);
    } else if ((func == F_EXEC) || (func == F_RESTART) ||
	       (func == F_CIRCULATE_UP) || (func == F_CIRCULATE_DOWN) ||
      (func == F_WARP) || (func == F_MODULE) || (func == F_BACKGROUND)) {
	mi = (MenuItem *) safemalloc(sizeof(MenuItem));

	mi->next = (MenuItem *) NULL;
	mi->prev = (MenuItem *) NULL;
	mi->item_num = 0;
	if ((func == F_EXEC) || (func == F_RESTART) || (func == F_MODULE) || (func == F_BACKGROUND)) {
	    mi->item = stripcpy2(tline, 0, True);
	    mi->action = stripcpy3(tline, True);
	} else {
	    mi->item = stripcpy2(tline, 0, False);
	    mi->action = stripcpy3(tline, False);
	}
	mi->state = 0;
	mi->func = func;
	mi->strlen = strlen(mi->item);
	mi->val1 = 0;
	mi->val2 = 0;
	mi->val1_unit = 1;
	mi->val2_unit = 1;
    }
    temp = Scr.MouseButtonRoot;
    Scr.MouseButtonRoot = (MouseButton *) safemalloc(sizeof(MouseButton));
    Scr.MouseButtonRoot->func = func;
    Scr.MouseButtonRoot->menu = mr;
    Scr.MouseButtonRoot->item = mi;
    Scr.MouseButtonRoot->Button = button;
    Scr.MouseButtonRoot->Context = contexts;
    Scr.MouseButtonRoot->Modifier = mods;
    Scr.MouseButtonRoot->NextButton = temp;
    Scr.MouseButtonRoot->val1 = func_val_1;
    Scr.MouseButtonRoot->val2 = func_val_2;
    if ((unit_1 == 'p') || (unit_1 == 'P'))
	Scr.MouseButtonRoot->val1_unit = 100;
    else
	Scr.MouseButtonRoot->val1_unit = Scr.MyDisplayWidth;
    if ((unit_2 == 'p') || (unit_2 == 'P'))
	Scr.MouseButtonRoot->val2_unit = 100;
    else
	Scr.MouseButtonRoot->val2_unit = Scr.MyDisplayHeight;

    return;
}

void no_popup(char *ptr)
{
    if ((ptr) && (orig_tline))
	fprintf(stderr, "Popup '%s' not defined in line %s", ptr, orig_tline);
}


/****************************************************************************
 * 
 *  Processes a line with a key binding
 *
 ****************************************************************************/
void ParseKeyEntry(char *tline, FILE * fd, char **junk, int *junk2)
{
    char context[256], modifiers[256], function[256], *ptr;
    char name[256];
    MenuRoot *mr = 0;
    char unit_1, unit_2;
    int n;


    ptr = NULL;
    func_val_1 = 0;
    func_val_2 = 0;
    unit_1 = 's';
    unit_2 = 's';
    n = sscanf(tline, "%s %s %s %s %d %d", name, context, modifiers, function,
	       &func_val_1, &func_val_2);
    if (n < 6)
	n = sscanf(tline, "%s %s %s %s %d%c %d%c", name, context, modifiers, function,
		   &func_val_1, &unit_1, &func_val_2, &unit_2);
    find_context(context, &contexts, win_contexts);
    find_context(modifiers, &mods, key_modifiers);
    match_string(func_config, function, "bad key function:", fd);

    /* Make CirculateUp and CirculateDown take args. by Y.NOMURA */

    if ((func == F_CIRCULATE_UP) || (func == F_CIRCULATE_DOWN) ||
	(func == F_WARP))
	ptr = stripcpy3(tline, False);

    /* End of addition */

    if ((func == F_EXEC) || (func == F_RESTART) || (func == F_MODULE) || (func == F_BACKGROUND)) {
	ptr = stripcpy3(tline, True);
    } else if ((func == F_POPUP) || (func == F_FUNCTION)) {
	unsigned i;
	ptr = stripcpy2(tline, 0, True);
	if (ptr != NULL) {
	    for (i = 0; i < PopupCount; i++)
		if (mystrcasecmp(PopupTable[i]->name, ptr) == 0) {
		    mr = PopupTable[i];
		    break;
		}
	}
	if (!mr) {
	    no_popup(ptr);
	    func = F_NOP;
	}
    }
    AddFuncKey(name, contexts, mods, func, ptr, func_val_1, func_val_2, mr, unit_1, unit_2);
}

/****************************************************************************
 * 
 * Sets menu/keybinding/mousebinding function to specified value
 *
 ****************************************************************************/
void set_func(char *text, FILE * fd, char **value, int *junk)
{
    func = (unsigned long) value;
}

/****************************************************************************
 * 
 * Turns a  string context of context or modifier values into an array of 
 * true/false values (bits)
 *
 ****************************************************************************/
void find_context(char *string, int *output, struct charstring *table)
{
    int i = 0, j = 0;
    Bool matched;
    char tmp1;

    *output = 0;
    i = 0;
    while (i < strlen(string)) {
	j = 0;
	matched = FALSE;
	while ((!matched) && (table[j].key != 0)) {
	    /* in some BSD implementations, tolower(c) is not defined
	     * unless isupper(c) is true */
	    tmp1 = string[i];
	    if (isupper(tmp1))
		tmp1 = tolower(tmp1);
	    /* end of ugly BSD patch */

	    if (tmp1 == table[j].key) {
		*output |= table[j].value;
		matched = TRUE;
	    }
	    j++;
	}
	if (!matched) {
	    fprintf(stderr, "afterstep: bad entry %c in line %s",
		    string[i], orig_tline);
	}
	i++;
    }
    return;
}

/****************************************************************************
 * 
 * Matches text from config to a table of strings, calls routine
 * indicated in table.
 *
 ****************************************************************************/
void match_string(struct config *table, char *text, char *error_msg, FILE * fd)
{
    int j;
    Bool matched;

    j = 0;
    matched = FALSE;
    while ((!matched) && (strlen(table[j].keyword) > 0)) {
	if (mystrncasecmp(text, table[j].keyword, strlen(table[j].keyword)) == 0) {
	    matched = TRUE;
	    /* found key word */
	    table[j].action(&text[strlen(table[j].keyword)],
			    fd, table[j].arg, table[j].arg2);
	} else
	    j++;
    }
    if (!matched) {
	afterstep_err("%s %s in line %s", error_msg, text, orig_tline);
    }
}




/****************************************************************************
 * 
 * Generates the window for a menu
 *
 ****************************************************************************/
void MakeMenu(MenuRoot * mr)
{
    MenuItem *cur;
    unsigned long valuemask;
    XSetWindowAttributes attributes;
    int y;

    /* lets first size the window accordingly */
    mr->width += 15;
    if (mr->width2 > 0)
	mr->width += 5;
    /* allow two pixels for top border */
    for (y = 0, cur = mr->first; cur != NULL; cur = cur->next) {
	cur->y_offset = y;
	cur->x = 5;
	if (cur->func == F_TITLE)
	    /* Title */
	    cur->y_height = NS_TITLE_HEIGHT + 1;
	else if (cur->func == F_NOP && *cur->item == 0)
	    /* Separator */
	    cur->y_height = HEIGHT_SEPARATOR;
	else
	    /* Normal text entry */
	    cur->y_height = Scr.EntryHeight;
	y += cur->y_height;
	if (mr->width2 == 0) {
	    cur->x2 = cur->x;
	} else {
	    cur->x2 = mr->width - 5;
	}
    }
    mr->in_use = 0;
    mr->height = y;

#ifndef NO_SAVEUNDERS
    valuemask = (CWBackPixel | CWEventMask | CWCursor | CWSaveUnder);
#else
    valuemask = (CWBackPixel | CWEventMask | CWCursor);
#endif
    attributes.background_pixel = Scr.MenuColors.back;
    attributes.event_mask = (ExposureMask | EnterWindowMask);
    attributes.cursor = Scr.ASCursors[MENU];
#ifndef NO_SAVEUNDERS
    attributes.save_under = TRUE;
#endif
    mr->width = mr->width + mr->width2;

    mr->w = XCreateWindow(dpy, Scr.Root, 0, 0, (unsigned int) (mr->width),
			  (unsigned int) mr->height, (unsigned int) 0,
			  CopyFromParent, (unsigned int) InputOutput,
			  (Visual *) CopyFromParent,
			  valuemask, &attributes);
    XSaveContext(dpy, mr->w, MenuContext, (caddr_t) mr);

    return;
}

/***********************************************************************
 * Procedure:
 *	scanForHotkeys - Look for hotkey markers in a MenuItem
 * 							(pete@tecc.co.uk)
 * 
 * Inputs:
 *	it	- MenuItem to scan
 * 	which 	- +1 to look in it->item1 and -1 to look in it->item2.
 *
 ***********************************************************************/

void scanForHotkeys(MenuItem * it, int which)
{
    char *start, *txt;

    start = (which > 0) ? it->item : it->item2;		/* Get start of string  */
    for (txt = start; *txt != '\0'; txt++) {	/* Scan whole string    */
	if (*txt == '&') {	/* A hotkey marker?                     */
	    if (txt[1] == '&') {	/* Just an escaped &                    */
		char *tmp;	/* Copy the string down over it         */
		for (tmp = txt; *tmp != '\0'; tmp++)
		    tmp[0] = tmp[1];
		continue;	/* ...And skip to the key char          */
	    }
	    /* It's a hot key marker - work out the offset value              */
	    it->hotkey = txt[1];
	    for (; txt[1] != '\0'; txt++)
		txt[0] = txt[2];	/* Copy down..  */
	    return;		/* Only one hotkey per item...          */
	}
    }
    it->hotkey = 0;		/* No hotkey found.  Set offset to zero */
}



/***********************************************************************
 *
 *  Procedure:
 *	AddToMenu - add an item to a root menu
 *
 *  Returned Value:
 *	(MenuItem *)
 *
 *  Inputs:
 *	menu	- pointer to the root menu to add the item
 *	item	- the text to appear in the menu
 *	action	- the string to possibly execute
 *	func	- the numeric function
 *
 ***********************************************************************/
void AddToMenu(MenuRoot * menu, char *item, char *item2, char *action, int func,
	       long func_val_1, long func_val_2, char unit_1, char unit_2)
{
    MenuItem *tmp;
    int width;


    if (item == NULL)
	return;
    tmp = (MenuItem *) safemalloc(sizeof(MenuItem));
    if (menu->first == NULL) {
	menu->first = tmp;
	tmp->prev = NULL;
    } else {
	menu->last->next = tmp;
	tmp->prev = menu->last;
    }
    menu->last = tmp;

    tmp->item = item;
    if (item != (char *) 0) {
	scanForHotkeys(tmp, 1);	/* pete@tecc.co.uk */
	tmp->strlen = strlen(item);
    } else
	tmp->strlen = 0;

    tmp->item2 = item2;
    if (item2 != (char *) 0) {
	if (tmp->hotkey == 0)
	    scanForHotkeys(tmp, -1);	/* pete@tecc.co.uk */
	tmp->strlen2 = strlen(item2);
    } else
	tmp->strlen2 = 0;
    tmp->menu = 0;

    if ((func == F_POPUP) || (func == F_FUNCTION)) {
	unsigned i;
	if (action != (char *) 0) {
	    for (i = 0; i < PopupCount; i++)
		if (mystrcasecmp(PopupTable[i]->name, action) == 0) {
		    tmp->menu = PopupTable[i];
		    break;
		}
	}
	if (tmp->menu == (MenuRoot *) 0) {
	    no_popup(action);
	    func = F_NOP;
	}
    }
    tmp->action = action;
    tmp->next = NULL;
    tmp->state = 0;
    tmp->func = func;
    tmp->val1 = func_val_1;
    tmp->val2 = func_val_2;
    if ((unit_1 == 'p') || (unit_1 == 'P'))
	tmp->val1_unit = 100;
    else
	tmp->val1_unit = Scr.MyDisplayWidth;
    if ((unit_2 == 'p') || (unit_2 == 'P'))
	tmp->val2_unit = 100;
    else
	tmp->val2_unit = Scr.MyDisplayHeight;

    if (func == F_TITLE)
	width = XTextWidth(Scr.WindowFont.font, item, tmp->strlen);
    else
	width = XTextWidth(Scr.StdFont.font, item, tmp->strlen);
    if (tmp->func == F_POPUP || tmp->hotkey)
	width += 15;
    if (width <= 0)
	width = 1;
    if (width > menu->width)
	menu->width = width;
    width = XTextWidth(Scr.StdFont.font, item2, tmp->strlen2);
    if (width < 0)
	width = 0;
    if (width > menu->width2)
	menu->width2 = width;
    if ((width == 0) && (tmp->strlen2 > 0))
	menu->width2 = 1;

    tmp->item_num = menu->items++;
}

/***********************************************************************
 *
 *  Procedure:
 *	NewMenuRoot - create a new menu root
 *
 *  Returned Value:
 *	(MenuRoot *)
 *
 *  Inputs:
 *	name	- the name of the menu root
 *
 ***********************************************************************/
MenuRoot *
 NewMenuRoot(char *name)
{
    MenuRoot *tmp;

    tmp = (MenuRoot *) safemalloc(sizeof(MenuRoot));
    tmp->name = name;
    tmp->first = NULL;
    tmp->last = NULL;
    tmp->items = 0;
    tmp->width = 0;
    tmp->width2 = 0;
#ifndef NO_TEXTURE
    tmp->titlebg = None;
    tmp->itembg = None;
#endif
    tmp->w = None;
    return (tmp);
}



/***********************************************************************
 *
 *  Procedure:
 *	AddFuncKey - add a function key to the list
 *
 *  Inputs:
 *	name	- the name of the key
 *	cont	- the context to look for the key press in
 *	mods	- modifier keys that need to be pressed
 *	func	- the function to perform
 *	action	- the action string associated with the function (if any)
 *
 ***********************************************************************/
void AddFuncKey(char *name, int cont, int mods, int func, char *action,
	     int val1, int val2, MenuRoot * mr, char unit_1, char unit_2)
{
    FuncKey *tmp;
    KeySym keysym;
    KeyCode keycode;
    int i, min, max;

    /*
     * Don't let a 0 keycode go through, since that means AnyKey to the
     * XGrabKey call in GrabKeys().
     */
    if ((keysym = XStringToKeysym(name)) == NoSymbol ||
	(keycode = XKeysymToKeycode(dpy, keysym)) == 0)
	return;


    XDisplayKeycodes(dpy, &min, &max);
    for (i = min; i <= max; i++)
	if (XKeycodeToKeysym(dpy, i, 0) == keysym) {
	    tmp = (FuncKey *) safemalloc(sizeof(FuncKey));
	    tmp->next = Scr.FuncKeyRoot.next;
	    Scr.FuncKeyRoot.next = tmp;

	    tmp->name = name;
	    tmp->keycode = i;
	    tmp->cont = cont;
	    tmp->mods = mods;
	    tmp->func = func;
	    tmp->action = action;
	    tmp->val1 = val1;
	    tmp->val2 = val2;
	    if ((unit_1 == 'p') || (unit_1 == 'P'))
		tmp->val1_unit = 100;
	    else
		tmp->val1_unit = Scr.MyDisplayWidth;
	    if ((unit_2 == 'p') || (unit_2 == 'P'))
		tmp->val2_unit = 100;
	    else
		tmp->val2_unit = Scr.MyDisplayHeight;

	    tmp->menu = mr;
	}
    return;
}

/****************************************************************************
 * 
 * Loads a single color
 *
 ****************************************************************************/
Pixel
GetColor(char *name)
{
    XColor color;
    XWindowAttributes attributes;

    XGetWindowAttributes(dpy, Scr.Root, &attributes);
    color.pixel = 0;
    if (!XParseColor(dpy, attributes.colormap, name, &color)) {
	nocolor("parse", name);
    } else if (!XAllocColor(dpy, attributes.colormap, &color)) {
	nocolor("alloc", name);
    }
    return color.pixel;
}

void nocolor(char *note, char *name)
{
    afterstep_err("can't %s color %s", note, name, NULL);
}

/****************************************************************************
 * 
 * Copies a string into a new, malloc'ed string
 * Strips leading spaces and trailing spaces and new lines
 *
 ****************************************************************************/
char *
 stripcpy(char *source)
{
    char *tmp, *ptr;
    int len;

    while (isspace(*source))
	source++;
    len = strlen(source);
    tmp = source + len - 1;
    while (((isspace(*tmp)) || (*tmp == '\n')) && (tmp >= source)) {
	tmp--;
	len--;
    }
    ptr = safemalloc(len + 1);
    strncpy(ptr, source, len);
    ptr[len] = 0;
    return ptr;
}


/****************************************************************************
 * 
 * Copies a string into a new, malloc'ed string
 * Strips all data before the first quote and after the second
 *
 ****************************************************************************/
char *
 stripcpy2(char *source, int tab_sensitive, Bool error)
{
    char *ptr;
    int count;
    while ((*source != '"') && (*source != 0))
	source++;
    if (*source == 0) {
	if (error)
	    bad_binding(2);
	return 0;
    }
    source++;
    ptr = source;
    count = 0;
    if (!tab_sensitive)
	while ((*ptr != '"') && (*ptr != 0)) {
	    ptr++;
	    count++;
    } else if (tab_sensitive == 1)
	while ((*ptr != '"') && (*ptr != 0) && (*ptr != '\t')) {
	    ptr++;
	    count++;
    } else if (tab_sensitive == 2) {
	while ((*ptr != '"') && (*ptr != 0) && (*ptr != '\t')) {
	    source++;
	    ptr++;
	}
	if ((*ptr != '"') && (*ptr != 0)) {
	    ptr++;
	    source++;
	}
	while ((*ptr != '"') && (*ptr != 0)) {
	    ptr++;
	    count++;
	}
    }
    ptr = safemalloc(count + 1);
    strncpy(ptr, source, count);
    ptr[count] = 0;
    return ptr;
}


/****************************************************************************
 * 
 * Copies a string into a new, malloc'ed string
 * Strips all data before the second quote. and strips trailing spaces and
 * new lines
 *
 ****************************************************************************/
char *
 stripcpy3(char *source, Bool Warn)
{
    while ((*source != '"') && (*source != 0))
	source++;
    if (*source != 0)
	source++;
    while ((*source != '"') && (*source != 0))
	source++;
    if (*source == 0) {
	if (Warn)
	    bad_binding(3);
	return 0;
    }
    source++;
    return stripcpy(source);
}

void bad_binding(int num)
{
    afterstep_err("bad binding in line %s", orig_tline, NULL, NULL);
    return;
}


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

    /* create GC's */

    gcm = GCForeground | GCBackground | GCGraphicsExposures;
    gcv.foreground = Scr.StdColors.fore;
    gcv.background = Scr.StdColors.back;
    gcv.graphics_exposures = False;
    Scr.MaskGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

    gcm = GCLineWidth | GCForeground | GCBackground | GCFunction;
    gcv.function = GXcopy;
    gcv.line_width = 1;
    gcv.foreground = Scr.StdColors.fore;
    gcv.background = Scr.StdColors.back;
    Scr.LineGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

    gcm = GCFunction | GCLineWidth | GCForeground | GCSubwindowMode;
    gcv.function = GXxor;
    gcv.line_width = 0;
    gcv.foreground = XORvalue;
    gcv.subwindow_mode = IncludeInferiors;
    Scr.DrawGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

    gcm = GCFunction | GCPlaneMask | GCGraphicsExposures | GCLineWidth | GCForeground | GCBackground | GCFont;
    gcv.line_width = 0;
    gcv.function = GXcopy;
    gcv.plane_mask = AllPlanes;
    if (Scr.TitleTextType != 0) {
	gcv.foreground = GetColor(hircolor);
	gcv.font = Scr.WindowFont.font->fid;
	Scr.HiFontReliefGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

	gcv.foreground = GetColor(hiscolor);
	gcv.font = Scr.WindowFont.font->fid;
	Scr.HiFontShadowGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

	gcv.foreground = GetColor(hincolor);
	gcv.font = Scr.WindowFont.font->fid;
	Scr.HiFontGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

	gcv.foreground = GetColor(lorcolor);
	gcv.font = Scr.WindowFont.font->fid;
	Scr.LoFontReliefGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

	gcv.foreground = GetColor(loscolor);
	gcv.font = Scr.WindowFont.font->fid;
	Scr.LoFontShadowGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

	gcv.foreground = GetColor(loncolor);
	gcv.font = Scr.WindowFont.font->fid;
	Scr.LoFontGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);
    }
    gcv.foreground = Scr.StdColors.fore;
    gcv.background = Scr.StdColors.back;
    gcv.font = Scr.StdFont.font->fid;

    /*
     * Prevent GraphicsExpose and NoExpose events.  We'd only get NoExpose
     * events anyway;  they cause BadWindow errors from XGetWindowAttributes
     * call in FindScreenInfo (events.c) (since drawable is a pixmap).
     */
    gcv.graphics_exposures = False;

    Scr.NormalGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);


    /* GC for pager labels */
    Scr.FontGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

    gcv.fill_style = FillStippled;
    gcv.stipple = Scr.gray_bitmap;
    gcm = GCFunction | GCPlaneMask | GCGraphicsExposures | GCLineWidth | GCForeground |
	GCBackground | GCFont | GCStipple | GCFillStyle;

    Scr.StippleGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

    gcm = GCFunction | GCPlaneMask | GCGraphicsExposures | GCLineWidth | GCForeground |
	GCBackground | GCFont;
    Globalgcm = gcm;
    Globalgcv = gcv;
    gcv.foreground = Scr.HiRelief.fore;
    gcv.background = Scr.HiRelief.back;
    gcv.fill_style = FillSolid;
    Scr.HiReliefGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

    gcv.foreground = Scr.HiRelief.back;
    gcv.background = Scr.HiRelief.fore;
    Scr.HiShadowGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

    if (Scr.BevelReliefGC == None)
	Scr.BevelReliefGC = Scr.HiReliefGC;
    if (Scr.BevelShadowGC == None)
	Scr.BevelShadowGC = Scr.HiShadowGC;
    gcv.foreground = Scr.MenuColors.fore;
    gcv.background = Scr.MenuColors.back;
    Scr.MenuGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

    gcv.foreground = Scr.StdRelief.fore;
    gcv.background = Scr.StdRelief.back;
    Scr.ScratchGC1 = XCreateGC(dpy, Scr.Root, gcm, &gcv);

    if (Scr.d_depth < 2) {
	gcv.fill_style = FillStippled;
	gcv.stipple = Scr.gray_bitmap;
	gcm = GCFunction | GCPlaneMask | GCGraphicsExposures | GCLineWidth | GCForeground |
	    GCBackground | GCFont | GCStipple | GCFillStyle;
	Scr.MenuStippleGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

	gcm = GCFunction | GCPlaneMask | GCGraphicsExposures | GCLineWidth | GCForeground |
	    GCBackground | GCFont;
	gcv.fill_style = FillSolid;
    } else {
	gcv.foreground = Scr.MenuStippleColors.fore;
	gcv.background = Scr.MenuStippleColors.back;
	Scr.MenuStippleGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);
    }
    gcv.foreground = Scr.MenuRelief.fore;
    gcv.background = Scr.MenuRelief.back;
    Scr.MenuReliefGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

    gcv.foreground = Scr.MenuRelief.back;
    gcv.background = Scr.MenuRelief.fore;
    Scr.MenuShadowGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

    gcv.foreground = Scr.StdRelief.fore;
    gcv.background = Scr.StdRelief.back;
    Scr.StdReliefGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

    gcv.foreground = Scr.StdRelief.back;
    gcv.background = Scr.StdRelief.fore;
    Scr.StdShadowGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);
    Scr.ScratchGC2 = XCreateGC(dpy, Scr.Root, gcm, &gcv);

    gcv.foreground = Scr.StickyRelief.fore;
    gcv.background = Scr.StickyRelief.back;
    Scr.StickyReliefGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

    gcv.foreground = Scr.StickyRelief.back;
    gcv.background = Scr.StickyRelief.fore;
    Scr.StickyShadowGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

    gcv.foreground = Scr.HiColors.fore;

    gcv.font = Scr.IconFont.font->fid;

    Scr.IconGC = XCreateGC(dpy, Scr.Root, GCForeground | GCFont, &gcv);
}
