/*
 *  WindowMaker window manager
 * 
 *  Copyright (c) 1997 Alfredo K. Kojima
 * 
 *  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.
 */

#include "wconfig.h"

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

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#ifdef SHAPE
#include <X11/extensions/shape.h>
#endif

#include <wraster.h>

#include "generic/wwmlib.h"
#include "WindowMaker.h"
#include "texture.h"
#include "def_pixmaps.h"
#include "screen.h"
#include "pixmap.h"
#include "button.h"
#include "menu.h"
#include "resource.h"
#include "funcs.h"
#include "actions.h"
#include "properties.h"
#include "dock.h"

#define EVENT_MASK (LeaveWindowMask|EnterWindowMask|PropertyChangeMask\
		|SubstructureNotifyMask|PointerMotionMask \
		|SubstructureRedirectMask|ButtonPressMask|ButtonReleaseMask\
		|KeyPressMask|KeyReleaseMask)


#define STIPPLE_WIDTH 2
#define STIPPLE_HEIGHT 2
static char STIPPLE_DATA[] = {0x02, 0x01};


/**** Global variables ****/

extern Cursor wCursor[WCUR_LAST];
extern WPreferences wPreferences;
extern char *PixmapPath;
extern Atom _XA_WINDOWMAKER_STATE;


/*
 *----------------------------------------------------------------------
 * alreadyRunningError--
 * 	X error handler used to catch errors when trying to do
 * XSelectInput() on the root window. These errors probably mean that
 * there already is some other window manager running.
 * 
 * Returns:
 * 	Nothing, unless something really evil happens...
 * 
 * Side effects:
 * 	The program is aborted.
 *---------------------------------------------------------------------- 
 */
static int 
alreadyRunningError(Display *dpy, XErrorEvent *error)
{
    wFatal(_("it seems that there already is a window manager running"));
    exit(1);
    return -1;
}


/*
 *---------------------------------------------------------------------- 
 * allocButtonPixmaps--
 * 	Allocate pixmaps used on window operation buttons (those in the
 * titlebar). The pixmaps are linked to the program. If XPM is supported
 * XPM pixmaps are used otherwise, equivalent bitmaps are used.
 * 
 * Returns:
 * 	Nothing
 * 
 * Side effects:
 * 	Allocates shared pixmaps for the screen. These pixmaps should 
 * not be freed by anybody.
 *---------------------------------------------------------------------- 
 */
static void
allocButtonPixmaps(WScreen *scr)
{
    WPixmap *pix;

    /* create predefined pixmaps */
#ifndef NEWSTYLE
# ifdef XPM
    pix = wPixmapCreateFromXPMData(scr, PRED_CLOSE_XPM);
    if (pix)
      pix->shared = 1;
    scr->b_pixmaps[WBUT_CLOSE] = pix;

    pix = wPixmapCreateFromXPMData(scr, PRED_BROKENCLOSE_XPM);
    if (pix)
      pix->shared = 1;
    scr->b_pixmaps[WBUT_BROKENCLOSE] = pix;

    pix = wPixmapCreateFromXPMData(scr, PRED_ICONIFY_XPM);
    if (pix)
      pix->shared = 1;
    scr->b_pixmaps[WBUT_ICONIFY] = pix;
                
    /* create predefined bitmaps (xpm not available) */
# else /* !XPM */
    pix = wPixmapCreateFromXBMData(scr, (char*)PRED_CLOSE_XBM_DATA, 
				   (char*)PRED_CLOSE_XBM_DATA, 
				   PRED_CLOSE_XBM_WIDTH, 
				   PRED_CLOSE_XBM_HEIGHT, 
				   scr->black_pixel, scr->white_pixel);
    if (pix)
      pix->shared = 1;
    scr->b_pixmaps[WBUT_CLOSE] = pix;

    pix = wPixmapCreateFromXBMData(scr, (char*)PRED_BROKEN_CLOSE_XBM_DATA, 
				   (char*)PRED_BROKEN_CLOSE_XBM_DATA, 
				   PRED_BROKEN_CLOSE_XBM_WIDTH, 
				   PRED_BROKEN_CLOSE_XBM_HEIGHT, 
				   scr->black_pixel, scr->white_pixel);
    if (pix)
      pix->shared = 1;
    scr->b_pixmaps[WBUT_BROKENCLOSE] = pix;

    pix = wPixmapCreateFromXBMData(scr, (char*)PRED_ICONIFY_XBM_DATA, 
				   (char*)PRED_ICONIFY_XBM_DATA, 
				   PRED_ICONIFY_XBM_WIDTH, 
				   PRED_ICONIFY_XBM_HEIGHT, 
				   scr->black_pixel, scr->white_pixel);
    if (pix)
      pix->shared = 1;
    scr->b_pixmaps[WBUT_ICONIFY] = pix;
# endif /* !XPM */
#else /* NEWSTYLE */
    pix = wPixmapCreateFromXBMData(scr, (char*)PRED_CLOSE_XBM_DATA, 
				   (char*)PRED_CLOSE_XBM_DATA, 
				   PRED_CLOSE_XBM_WIDTH,
				   PRED_CLOSE_XBM_HEIGHT,
				   scr->ftitle_pixel, scr->white_pixel);
    if (pix)
      pix->shared = 1;
    scr->b_pixmaps[WBUT_CLOSE] = pix;

    pix = wPixmapCreateFromXBMData(scr, (char*)PRED_BROKENCLOSE_XBM_DATA, 
				   (char*)PRED_BROKENCLOSE_XBM_DATA, 
				   PRED_BROKENCLOSE_XBM_WIDTH, 
				   PRED_BROKENCLOSE_XBM_HEIGHT, 
				   scr->ftitle_pixel, scr->white_pixel);
    if (pix)
      pix->shared = 1;
    scr->b_pixmaps[WBUT_BROKENCLOSE] = pix;

    pix = wPixmapCreateFromXBMData(scr, (char*)PRED_ICONIFY_XBM_DATA, 
				   (char*)PRED_ICONIFY_XBM_DATA, 
				   PRED_ICONIFY_XBM_WIDTH, 
				   PRED_ICONIFY_XBM_HEIGHT, 
				   scr->ftitle_pixel, scr->white_pixel);
    if (pix)
      pix->shared = 1;
    scr->b_pixmaps[WBUT_ICONIFY] = pix;


    pix = wPixmapCreateFromXBMData(scr, (char*)PRED_CLOSE_XBM_DATA, 
				   (char*)PRED_CLOSE_XBM_DATA, 
				   PRED_CLOSE_XBM_WIDTH,
				   PRED_CLOSE_XBM_HEIGHT,
				   scr->utitle_pixel, scr->white_pixel);
    if (pix)
      pix->shared = 1;
    scr->b_pixmaps[WBUT_LAST+WBUT_CLOSE] = pix;

    pix = wPixmapCreateFromXBMData(scr, (char*)PRED_BROKENCLOSE_XBM_DATA, 
				   (char*)PRED_BROKENCLOSE_XBM_DATA, 
				   PRED_BROKENCLOSE_XBM_WIDTH, 
				   PRED_BROKENCLOSE_XBM_HEIGHT, 
				   scr->utitle_pixel, scr->white_pixel);
    if (pix)
      pix->shared = 1;
    scr->b_pixmaps[WBUT_LAST+WBUT_BROKENCLOSE] = pix;

    pix = wPixmapCreateFromXBMData(scr, (char*)PRED_ICONIFY_XBM_DATA, 
				   (char*)PRED_ICONIFY_XBM_DATA, 
				   PRED_ICONIFY_XBM_WIDTH, 
				   PRED_ICONIFY_XBM_HEIGHT, 
				   scr->utitle_pixel, scr->white_pixel);
    if (pix)
      pix->shared = 1;
    scr->b_pixmaps[WBUT_LAST+WBUT_ICONIFY] = pix;
#endif /* NEWSTYLE */
}

/*
 *----------------------------------------------------------------------
 * makeIconBackImage--
 * 	Creates a plain 24bit RGB RImage of a gray square to be
 * used as a default icon background.
 * 
 * Returns:
 * 	The image to be used as icon background.
 *
 *---------------------------------------------------------------------- 
 */
static RImage*
makeIconBackImage(WScreen *scr)
{
    RImage *img;

    img = RCreateImage(ICON_WIDTH, ICON_HEIGHT, False);
    if (!img) {
	wWarning("could not create default icon background:%s", RErrorString);
	return NULL;
    }
    memset(img->data[0], 0xbe, ICON_WIDTH*ICON_HEIGHT);
    memset(img->data[1], 0xbe, ICON_WIDTH*ICON_HEIGHT);
    memset(img->data[2], 0xbe, ICON_WIDTH*ICON_HEIGHT);
    RBevelImage(img, WBEV_RAISED2);

    return img;
}


/*
 *---------------------------------------------------------------------- 
 * loadFont--
 * 	Loads a single font into the WFont structure, initializing
 * data in it. If the font could not be loaded, fixed will be used.
 * If fixed can't be loaded either, the program is aborted.
 * 
 * Returns:
 * 	None
 * 
 *---------------------------------------------------------------------- 
 */
static void
loadFont(char *font_name, WFont *font)
{
#ifdef I18N_MB
    char **missing;
    int num_missing = 0;
    char *default_string;
    int skyline;   
 
    font->font = XCreateFontSet(dpy, font_name, &missing, &num_missing, 
				&default_string);
    if (num_missing > 0) {
        wWarning(_("The following character sets are missing:"));
        for (skyline = 0; skyline < num_missing; skyline++)
	  wWarning(missing[skyline]);
        XFreeStringList(missing);
        wWarning(_("The string \"%s\" will be used in place"),
		 default_string);
        wWarning(_("of any characters from those sets."));
    }
    if (!font->font) {
	wWarning(_("could not create font set %s. Using fixed"), font_name);
	font->font = XCreateFontSet(dpy, "fixed", &missing, &num_missing, 
				    &default_string);
	if (num_missing > 0) {
	    XFreeStringList(missing);
	}
	if (!font->font) {
	    wFatal(_("could not load any usable font set"));
	    wAbort();
	    exit(1);
	}
    }
    font->height = XExtentsOfFontSet(font->font)->max_logical_extent.height;
    font->y = font->height+XExtentsOfFontSet(font->font)->max_logical_extent.y;
    font->y = font->height-font->y;
#else
    font->font = XLoadQueryFont(dpy, font_name);
    if (!font->font) {
	wWarning(_("could not load font %s. Using fixed"), font_name);
	font->font = XLoadQueryFont(dpy, "fixed");
	if (!font->font) {
	    wFatal(_("could not load any usable font"));
	    wAbort();
	    exit(1);
	}
    }
    font->height = font->font->ascent+font->font->descent;
    font->y = font->font->ascent;
#endif /* !I18N_MB */
}


/*
 *----------------------------------------------------------------------
 * parseColor--
 * 	Parses a color name. If the color could not be parsed, the color
 * will be set to black if def_black is true or white otherwise.
 * 
 * Returns:
 * 	0 if the color could not be parsed or
 * 	1 on success
 *---------------------------------------------------------------------- 
 */
static int
parseColor(WScreen *scr, char *color_name, XColor *color, int def_black)
{
    if (!XParseColor(dpy, scr->colormap, color_name, color)) {
	wWarning(_("could not parse color %s"), color_name);
	color->red = (def_black ? 0 : 0xffff);
	color->green = (def_black ? 0 : 0xffff);
	color->blue = (def_black ? 0 : 0xffff);
	return 0;
    }
    return 1;
}


/*
 *---------------------------------------------------------------------- 
 * allocFonts--
 * 	Allocates all fonts needed by the window manager. The font names
 * are taken from what was read in preferences. Default names from
 * wconfig.h are used if some setting was not specified. If some font 
 * cannot be loaded, fixed is used. If it can't be loaded too, the 
 * program is aborted.
 *
 * Returns:
 * 	None
 * 
 * Side effects:
 * 	Fonts are loaded and the font names in preferences are freed.
 *
 *---------------------------------------------------------------------- 
 */
static void
allocFonts(WScreen *scr)
{
    if (wPreferences.title_font) {
	loadFont(wPreferences.title_font, &scr->title_font);
	free(wPreferences.title_font);
    } else
      loadFont(DEF_TITLE_FONT, &scr->title_font);
    
    if (wPreferences.menu_title_font) {
	loadFont(wPreferences.menu_title_font, &scr->menu_title_font);
	free(wPreferences.menu_title_font);
    } else
      loadFont(DEF_MENU_TITLE_FONT, &scr->menu_title_font);
    
    if (wPreferences.menu_entry_font) {
	loadFont(wPreferences.menu_entry_font, &scr->menu_entry_font);
	free(wPreferences.menu_entry_font);
    } else
      loadFont(DEF_MENU_ENTRY_FONT, &scr->menu_entry_font);

    if (wPreferences.icon_title_font) {
	loadFont(wPreferences.icon_title_font, &scr->icon_title_font);
	free(wPreferences.icon_title_font);
    } else
      loadFont(DEF_ICON_TITLE_FONT, &scr->icon_title_font);

    if (wPreferences.panel_title_font) {
	loadFont(wPreferences.panel_title_font, &scr->panel_title_font);
	free(wPreferences.panel_title_font);
    } else
      loadFont(DEF_PANEL_TITLE_FONT, &scr->panel_title_font);
    
    if (wPreferences.panel_text_font) {
	loadFont(wPreferences.panel_text_font, &scr->panel_text_font);
	free(wPreferences.panel_text_font);
    } else
      loadFont(DEF_PANEL_TEXT_FONT, &scr->panel_text_font);
    
    if (wPreferences.info_font) {
	loadFont(wPreferences.info_font, &scr->info_text_font);
	free(wPreferences.info_font);
    } else
      loadFont(DEF_INFO_TEXT_FONT, &scr->info_text_font);
}


static WTexture*
makeTexture(scr, border, type, scolor, gcolor, def_scolor, def_black,
	    freedata)
WScreen *scr;			       /* screen descriptor */
int border;			       /* border type */
int type;			       /* type of texture */
char *scolor;			       /* color for solid */
W2Color *gcolor;		       /* colors for gradient */
char *def_scolor;		       /* default solid color */
int def_black;			       /* assume black on failure? */
int freedata;
{
    XColor color, color2;
    WTexture *tex;
    int ok=1;

    switch (type) {
     case WTEX_VGRADIENT:
     case WTEX_HGRADIENT:
     case WTEX_DGRADIENT:
	if (scr->w_visual->class==PseudoColor) {
	    ok=0;	    
	    if (scr->w_depth!=8)
	      wWarning(_("Gradient texture not supported for your display "\
			"(PseudoColor %ibpp)"), scr->w_depth);
	    else ok=1;
	}
	if ((scr->w_visual->class==PseudoColor && ok)
	    || scr->w_visual->class==TrueColor) {
	    if (gcolor->color1 && gcolor->color2
		&& parseColor(scr, gcolor->color1, &color, 0)
		&& parseColor(scr, gcolor->color2, &color2, 0)) {
		tex = (WTexture*)wTextureMakeGradient(scr, type, &color,
						      &color2, border);
		if (tex) break;
	    }
	} else
	  wWarning(_("Gradient texture available only for PseudoColor and "\
			"TrueColor displays"));
	  
	/* if others failed, make solid */
     case WTEX_SOLID:
     default:
	if (!scolor) {
	    parseColor(scr, def_scolor, &color, def_black);
	} else {
	    parseColor(scr, scolor, &color, def_black);
	}
	tex = (WTexture*)wTextureMakeSolid(scr, &color, border);
	break;
    }
    if (freedata) {
	if (gcolor->color1)
	  wfree(gcolor->color1);
	if (gcolor->color2)
	  wfree(gcolor->color2);
	if (scolor)
	  free(scolor);
    }
    if (!tex) {
	wFatal(_("could not allocate texture"));
	wAbort();
	exit(1);
    }
    return tex;
}


/*
 *----------------------------------------------------------------------
 * initTextures--
 * 	Creates textures to be used for decorations, depending on
 * user preferences or default values in wconfig.h
 * 
 * Returns:
 * 	None
 * 
 * Side effects:
 * 	Textures are allocated for the screen and strings allocated
 * during preference file read are freed.
 *----------------------------------------------------------------------
 */
static void
initTextures(WScreen *scr)
{    
    /* focused titlebar */
    scr->focused_texture = makeTexture(scr, WREL_TITLEBAR,
				       wPreferences.ftitle_texture,
				       wPreferences.ftitle_bcolor,
				       &wPreferences.ftitle_gradient,
				       DEF_FOCUSED_TEXTURE_COLOR, 1, False);
    scr->icon_title_texture = 
      wTextureMakeSolid(scr, &scr->focused_texture->any.color, WREL_TITLEBAR);

    /* semi-focused titlebar */
    scr->pfocused_texture = makeTexture(scr, WREL_TITLEBAR,
					wPreferences.ptitle_texture,
					wPreferences.ptitle_bcolor,
					&wPreferences.ptitle_gradient,
					DEF_PREV_FOCUSED_TEXTURE_COLOR, 0,
					True);

    /* unfocused titlebar */
    scr->unfocused_texture = makeTexture(scr, WREL_TITLEBAR,
					 wPreferences.utitle_texture,
					 wPreferences.utitle_bcolor,
					 &wPreferences.utitle_gradient,
					 DEF_UNFOCUSED_TEXTURE_COLOR, 0, True);

    /* menu title */
    scr->menu_title_texture = makeTexture(scr, WREL_MENUTITLE,
					  wPreferences.menu_title_texture,
					  wPreferences.menu_title_bcolor,
					  &wPreferences.menu_title_gradient,
					  DEF_FOCUSED_TEXTURE_COLOR, 1, True);
    /* appmenu titlebar */
    scr->appmenu_title_texture = makeTexture(scr, WREL_MENUTITLE,
					     wPreferences.ftitle_texture,
					     wPreferences.ftitle_bcolor,
					     &wPreferences.ftitle_gradient,
					     DEF_FOCUSED_TEXTURE_COLOR, 1,
					     True);
    /* menu text */
    scr->menu_item_texture = makeTexture(scr, WREL_MENUENTRY,
					 wPreferences.menu_text_texture,
					 wPreferences.menu_text_bcolor,
					 &wPreferences.menu_text_gradient,
					 DEF_UNFOCUSED_TEXTURE_COLOR, 1, True);

    if (scr->menu_item_texture->any.type==WTEX_SOLID) {
	scr->menu_item_auxtexture = (WTexSolid*)scr->menu_item_texture;
    } else {
	scr->menu_item_auxtexture = 
	  wTextureMakeSolid(scr,&scr->menu_item_texture->any.color, WREL_FLAT);
    }
    
    /* pushed buttons */
    {
	XColor color;
#ifdef NEWSTYLE
	color.red = 0x2000;
	color.green = 0x2000;
	color.blue = 0x2000;
#else    
	color.red = 0xffff;
	color.green = 0xffff;
	color.blue = 0xffff;
#endif /* !NEWSTYLE */
	scr->pushed_button_texture = (WTexture*)wTextureMakeSolid(scr, &color,
								  WREL_SUNKEN);
	color.red = 0xbebe;
	color.green = 0xbebe;
	color.blue = 0xbebe;
	scr->normal_button_texture =
	  (WTexture*)wTextureMakeSolid(scr, &color, WREL_RAISED);
    }
#undef GETGRADCOLORS
    /* resizebar */
    scr->resizebar_texture = 
      (WTexture*)wTextureMakeSolid(scr, &scr->unfocused_texture->any.color, 
				   WREL_FLAT);
}

#ifdef DOCK
static WPixmap*
make3Dots(WScreen *scr)
{
    WPixmap *wpix;
    GC gc2, gc;
    XGCValues gcv;
    Pixmap pix, mask;
    
    /*
   RImage *img=NULL;
    if (wPreferences.icon_dots_file) {
	char *path;
	
	path = FindImage(wPreferences.icon_dots_file);
	if (path) {
	    img = RLoadImage(scr->rcontext, path, 0);
	    free(path);
	    if (!img)
	      wWarning(_("couldn't load icon dots image:%s"), RErrorString);
	    else {
		if (!RConvertImageMask(scr->rcontext, img, &pix, &mask)) {
		    wWarning("%s", RErrorString);
		    RDestroyImage(img);
		else {
		    RDestroyImage(img);
		    return wPixmapCreate(scr, pix, mask);
		}
	    }
	} else {
	    wWarning(_("couldn't find image file \"%s\""), 
		     wPixmapCreate.icon_dots_file);
	}
    }
*/
    gc = scr->copy_gc;
    pix = XCreatePixmap(dpy, scr->w_win, ICON_WIDTH, ICON_HEIGHT,
			scr->w_depth);
    XSetForeground(dpy, gc, scr->black_pixel);
    XFillRectangle(dpy, pix, gc, 0, 0, ICON_WIDTH, ICON_HEIGHT);
    XSetForeground(dpy, gc, scr->white_pixel);
    XDrawLine(dpy, pix, gc, 5, 58, 6, 58);
    XDrawLine(dpy, pix, gc, 10, 58, 11, 58);
    XDrawLine(dpy, pix, gc, 15, 58, 16, 58);
    XDrawPoint(dpy, pix, gc, 6, 57);
    XDrawPoint(dpy, pix, gc, 11, 57);
    XDrawPoint(dpy, pix, gc, 16, 57);

    mask = XCreatePixmap(dpy, scr->w_win, ICON_WIDTH, ICON_HEIGHT, 1);
    gcv.foreground = 0;
    gc2 = XCreateGC(dpy, mask, GCForeground, &gcv);
    XFillRectangle(dpy, mask, gc2, 0, 0, ICON_WIDTH, ICON_HEIGHT);
    XSetForeground(dpy, gc2, 1);
    XFillRectangle(dpy, mask, gc2, 4, 57, 3, 2);
    XFillRectangle(dpy, mask, gc2, 9, 57, 3, 2);
    XFillRectangle(dpy, mask, gc2, 14, 57, 3, 2);
    
    XFreeGC(dpy, gc2);
    
    wpix = wPixmapCreate(scr, pix, mask);
    wpix->shared = 1;

    return wpix;
}

static WPixmap*
makeTBolt(WScreen *scr)
{
    WPixmap *pix;
    GC gc2, gc;
    XGCValues gcv;
    Pixmap p, m;
#if 0
    pix = wmalloc(sizeof(WPixmap));
    memset(pix, 0, sizeof(WPixmap));
    pix->shared = 1;
    pix->width = ICON_WIDTH;
    pix->height = ICON_HEIGHT;
    pix->depth = scr->w_depth;
    gc = scr->copy_gc;
    pix->image = XCreatePixmap(dpy, scr->w_win, ICON_WIDTH, ICON_HEIGHT,
			       scr->w_depth);
    XSetForeground(dpy, gc, scr->black_pixel);

    pix->mask = XCreatePixmap(dpy, scr->w_win, ICON_WIDTH, ICON_HEIGHT, 1);
    gcv.foreground = 0;
    gc2 = XCreateGC(dpy, pix->mask, GCForeground, &gcv);
    XFillRectangle(dpy, pix->mask, gc2, 0, 0, ICON_WIDTH, ICON_HEIGHT);
    XSetForeground(dpy, gc2, 1);
    XDrawLine(dpy, pix->mask, gc2, ICON_WIDTH - 4, 4, ICON_WIDTH - 7, 7);
    XDrawLine(dpy, pix->mask, gc2, ICON_WIDTH - 5, 7, ICON_WIDTH - 8, 11);
    XDrawPoint(dpy, pix->mask, gc2, ICON_WIDTH - 6, 7);
    XFreeGC(dpy, gc2);
#endif 
    gc = scr->copy_gc;
    p = XCreatePixmap(dpy, scr->w_win, ICON_WIDTH, ICON_HEIGHT, scr->w_depth);
    XSetForeground(dpy, gc, scr->black_pixel);
    XFillRectangle(dpy, p, gc, 0, 0, ICON_WIDTH, ICON_HEIGHT);
    XSetForeground(dpy, gc, scr->white_pixel);
    XDrawLine(dpy, p, gc, ICON_WIDTH - 4, 5, ICON_WIDTH - 3, 5);
    XDrawPoint(dpy, p, gc, ICON_WIDTH - 3, 4);

    m = XCreatePixmap(dpy, scr->w_win, ICON_WIDTH, ICON_HEIGHT, 1);
    gcv.foreground = 0;
    gc2 = XCreateGC(dpy, m, GCForeground, &gcv);
    XFillRectangle(dpy, m, gc2, 0, 0, ICON_WIDTH, ICON_HEIGHT);
    XSetForeground(dpy, gc2, 1);
    XFillRectangle(dpy, m, gc2, ICON_WIDTH - 5, 4, 3, 2);
    
    XFreeGC(dpy, gc2);

    pix = wPixmapCreate(scr, p, m);
    pix->shared = 1;

    return pix;
}
#endif


/*
 *----------------------------------------------------------------------
 * allocResources--
 * 	Allocates resources (fonts, GCs, textures and pixmaps) for the
 * screen.
 * 
 * Returns:
 * 	Nothing.
 * 
 * Side effects:
 * 	GCs, colors, textures etc. are allocated for the screen.
 *---------------------------------------------------------------------- 
 */
static void
allocResources(WScreen *scr)
{
    XGCValues gcv;
    XColor color;
    WPixmap *pix;
    unsigned long mtextcolor;
    int gcm;

    scr->stipple_bitmap = 
      XCreateBitmapFromData(dpy, scr->w_win, STIPPLE_DATA, STIPPLE_WIDTH,
			    STIPPLE_HEIGHT);
    /* fonts */    
    allocFonts(scr);

    gcv.stipple = scr->stipple_bitmap;
    gcv.foreground = scr->white_pixel;
    gcv.fill_style = FillStippled;
    gcm = GCForeground|GCStipple|GCFillStyle;
    scr->stipple_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
    
    gcm = GCForeground;
    /* menu GCs */
    if (!wPreferences.menu_title_fcolor) {
	parseColor(scr, DEF_FOCUSED_TEXT_COLOR, &color, 1);
    } else {
	if (!parseColor(scr, wPreferences.menu_title_fcolor, &color, 1)) {
	    free(wPreferences.menu_title_fcolor);
	    parseColor(scr, DEF_FOCUSED_TEXT_COLOR, &color, 1);
	}
    }
    XAllocColor(dpy, scr->w_colormap, &color);
    scr->mtitle_pixel = color.pixel;
    gcv.foreground = color.pixel;
#ifndef I18N_MB
    gcv.font = scr->menu_title_font.font->fid;
    gcm |= GCFont;
#endif
    scr->menu_title_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);

    if (!wPreferences.menu_text_fcolor) {
	parseColor(scr, DEF_UNFOCUSED_TEXT_COLOR, &color, 0);
    } else {
	if (!parseColor(scr, wPreferences.menu_text_fcolor, &color, 0)) {
	    free(wPreferences.menu_text_fcolor);
	    parseColor(scr, DEF_UNFOCUSED_TEXT_COLOR, &color, 0);
	}
    }
    XAllocColor(dpy, scr->w_colormap, &color);
    scr->mtext_pixel = color.pixel;
    mtextcolor = gcv.foreground = color.pixel;
#ifndef I18N_MB
    gcv.font = scr->menu_entry_font.font->fid;
#endif
    scr->menu_entry_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);

    /* selected menu entry GC */
    gcm = GCForeground|GCBackground;
#ifndef I18N_MB
    gcv.font = scr->menu_entry_font.font->fid;
    gcm |= GCFont;
#endif
    gcv.foreground = scr->white_pixel;
    gcv.background = scr->white_pixel;
    scr->select_menu_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);

    if (!wPreferences.menu_text_dcolor) {
	color.red = 0x7777;
	color.green = 0x7777;
	color.blue = 0x7777;
    } else {
	if (!parseColor(scr, wPreferences.menu_text_dcolor, &color, 0)) {
	    color.red = 0x7777;
	    color.green = 0x7777;
	    color.blue = 0x7777;
	    free(wPreferences.menu_text_dcolor);
	}
    }
    XAllocColor(dpy, scr->w_colormap, &color);
    scr->dtext_pixel = color.pixel;
    gcm = GCForeground|GCBackground;
#ifndef I18N_MB
    gcv.font = scr->menu_entry_font.font->fid;
    gcm |= GCFont;
#endif    
    if (mtextcolor == color.pixel) {
	gcv.foreground = scr->white_pixel;
	gcv.background = scr->black_pixel;
	gcv.stipple = scr->stipple_bitmap;
	gcv.fill_style = FillStippled;
	gcm |= GCStipple|GCFillStyle;
	scr->disabled_menu_entry_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
    } else {
	gcv.foreground = color.pixel;
	scr->disabled_menu_entry_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
    }

    /* frame GC */
    parseColor(scr, DEF_FRAME_COLOR, &color, 0);

    gcv.foreground = ~color.pixel;
    gcv.function = GXxor;
    gcv.line_width = DEF_FRAME_THICKNESS;
    gcv.subwindow_mode = IncludeInferiors;
    scr->frame_gc = XCreateGC(dpy, scr->root_win, GCForeground
			      |GCFunction|GCSubwindowMode|GCLineWidth, &gcv);
    
    /* line GC */
    gcv.foreground = ~color.pixel;
    gcv.function = GXxor;
    gcv.subwindow_mode = IncludeInferiors;
#ifndef I18N_MB
    gcv.font = scr->info_text_font.font->fid;
    gcm = GCForeground|GCFont|GCFunction|GCSubwindowMode;
#else
    gcm = GCForeground|GCFunction|GCSubwindowMode;
#endif
    scr->line_gc = XCreateGC(dpy, scr->root_win, gcm, &gcv);

    /* copy GC */
    gcv.foreground = scr->white_pixel;
    gcv.background = scr->black_pixel;
    scr->copy_gc = XCreateGC(dpy, scr->w_win, GCForeground|GCBackground, &gcv);
    

    /* window title text GCs */
#ifndef I18N_MB
    gcv.font = scr->title_font.font->fid;
    gcm = GCForeground|GCFont;
#else
    gcm = GCForeground;
#endif

    /* focused window title */
    if (!wPreferences.ftitle_fcolor) {
	parseColor(scr, DEF_FOCUSED_TEXT_COLOR, &color, 1);
    } else {
	if (!parseColor(scr, wPreferences.ftitle_fcolor, &color, 1)) {
	    free(wPreferences.ftitle_fcolor);
	    parseColor(scr, DEF_FOCUSED_TEXT_COLOR, &color, 1);
	}
    }
    XAllocColor(dpy, scr->w_colormap, &color);
    scr->ftitle_pixel = color.pixel;
    gcv.foreground = color.pixel;
    scr->focused_text_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);

    /* icon title GC */
    gcv.foreground = color.pixel;
#ifndef I18N_MB
    gcv.font = scr->icon_title_font.font->fid;
#endif
    scr->icon_title_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);

    /* semi-focused window title */
    if (!wPreferences.ptitle_fcolor) {
	parseColor(scr, DEF_PREV_FOCUSED_TEXTURE_COLOR, &color, 1);
    } else {
	if (!parseColor(scr, wPreferences.ptitle_fcolor, &color, 1)) {
	    free(wPreferences.ptitle_fcolor);
	    parseColor(scr, DEF_PREV_FOCUSED_TEXTURE_COLOR, &color, 1);
	}
    }
    XAllocColor(dpy, scr->w_colormap, &color);
    scr->ptitle_pixel = color.pixel;
    gcv.foreground = color.pixel;
#ifndef I18N_MB
    gcv.font = scr->title_font.font->fid;
#endif
    scr->pfocused_text_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);


    /* unfocused window title */
    if (!wPreferences.utitle_fcolor) {
	parseColor(scr, DEF_UNFOCUSED_TEXT_COLOR, &color, 1);
    } else {
	if (!parseColor(scr, wPreferences.utitle_fcolor, &color, 1)) {
	    free(wPreferences.utitle_fcolor);
	    parseColor(scr, DEF_UNFOCUSED_TEXT_COLOR, &color, 1);
	}
    }
    XAllocColor(dpy, scr->w_colormap, &color);
    scr->utitle_pixel = color.pixel;
    gcv.foreground = color.pixel;
#ifndef I18N_MB
    gcv.font = scr->title_font.font->fid;
#endif
    scr->unfocused_text_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);

    /* move/size display GC */
    gcv.foreground = color.pixel;
#ifndef I18N_MB
    gcv.font = scr->info_text_font.font->fid;
    gcm = GCForeground|GCFont;
#else
    gcm = GCForeground;
#endif
    scr->info_text_gc = XCreateGC(dpy, scr->root_win, gcm, &gcv);

    /* textures */
    initTextures(scr);
    
#ifdef XPM
    /* load pixmaps */
    pix = wPixmapCreateFromXPMData(scr, BUTTON_ARROW_XPM);
    if (pix!=NULL)
      pix->shared = 1;
    scr->button_arrow = pix;
#else /* !XPM */
    pix = wPixmapCreateFromXBMData(scr, (char*)BUTTON_ARROW_XBM_DATA, 
				   (char*)BUTTON_ARROW_MASK_XBM_DATA,
				   BUTTON_ARROW_XBM_WIDTH, 
				   BUTTON_ARROW_XBM_HEIGHT,
				   scr->black_pixel, scr->white_pixel);
    if (pix!=NULL)
      pix->shared = 1;
    scr->button_arrow = pix;
#endif /* XPM */

    if (wPreferences.icon_back_file) {
	scr->icon_tile = 
		RLoadImage(scr->rcontext,wPreferences.icon_back_file, 0);
    }
    if (!scr->icon_tile) {
	scr->icon_tile = makeIconBackImage(scr);
    }
    RConvertImage(scr->rcontext, scr->icon_tile, &scr->icon_tile_pixmap);

#ifdef DOCK
    if (!wPreferences.flags.nodock) {
	scr->dock_dots = make3Dots(scr);
	scr->dock_tbolt = makeTBolt(scr);
    }
#endif
    /* titlebar button pixmaps */
    allocButtonPixmaps(scr);
}


/*
 *----------------------------------------------------------------------
 * createInternalWindows--
 * 	Creates some windows used internally by the program. One to
 * receive input focus when no other window can get it and another
 * to display window geometry information during window resize/move.
 * 
 * Returns:
 * 	Nothing
 * 
 * Side effects:
 * 	Windows are created and some colors are allocated for the
 * window background.
 *---------------------------------------------------------------------- 
 */
static void
createInternalWindows(WScreen *scr)
{
    int vmask;
    int bfw;
    XSetWindowAttributes attribs;

#ifdef I18N_MB
    bfw = XmbTextEscapement(scr->info_text_font.font, "-8888 x -8888", 13);
#else
    bfw = XTextWidth(scr->info_text_font.font, "-8888 x -8888", 13);
#endif

    /* window for displaying geometry information during resizes and moves */
    vmask = CWBackPixmap|CWBackPixel|CWCursor|CWSaveUnder|CWOverrideRedirect;
    attribs.save_under = True;
    attribs.override_redirect = True;    
    attribs.cursor = wCursor[WCUR_DEFAULT];
    attribs.background_pixmap = None;
    attribs.background_pixel = scr->unfocused_texture->any.color.pixel;
    scr->geometry_display =
      XCreateWindow(dpy, scr->root_win, (scr->scr_width-bfw)/2, 
		    (scr->scr_height-20)/2, bfw, 20, 1, scr->depth, 
		    CopyFromParent, scr->w_visual, vmask, &attribs);
    
    /* InputOnly window to get the focus when no other window can get it */
    vmask = CWEventMask|CWOverrideRedirect;
    attribs.event_mask = KeyPressMask|FocusChangeMask;
    attribs.override_redirect = True;
    scr->no_focus_win=XCreateWindow(dpy,scr->root_win,-10, -10, 4, 4, 0, 0,
				    InputOnly,CopyFromParent, vmask, &attribs);
    XSelectInput(dpy, scr->no_focus_win, KeyPressMask|KeyReleaseMask);
    XMapWindow(dpy, scr->no_focus_win);

    XSetInputFocus (dpy, scr->no_focus_win, RevertToParent, CurrentTime);
 
#ifdef DOCK
    /* shadow window for dock buttons */
    vmask = CWBackPixmap|CWBackPixel|CWCursor|CWSaveUnder|CWOverrideRedirect;
    attribs.save_under = True;
    attribs.override_redirect = True;
    attribs.background_pixmap = None;
    attribs.background_pixel = scr->white_pixel;
    scr->dock_shadow =
      XCreateWindow(dpy, scr->root_win, 0, 0, ICON_WIDTH, ICON_HEIGHT, 0,
		    scr->depth, CopyFromParent, scr->w_visual, vmask, 
		    &attribs);
#endif
}


/*
 *----------------------------------------------------------------------
 * wScreenInit--
 * 	Initializes the window manager for the given screen and 
 * allocates a WScreen descriptor for it. Many resources are allocated
 * for the screen and the root window is setup appropriately.
 *
 * Returns:
 * 	The WScreen descriptor for the screen.
 * 
 * Side effects:
 * 	Many resources are allocated and the IconSize property is
 * set on the root window.
 *	The program can be aborted if some fatal error occurs.
 * 
 * TODO: User specifiable visual.
 *---------------------------------------------------------------------- 
 */
WScreen *
wScreenInit(int screen_number)
{
    WScreen *scr;
    XIconSize icon_size[1];
    RContextAttributes rattr;
    extern int wVisualID;
    
    /*
    char buffer[16];
     */
    
    scr = wmalloc(sizeof(WScreen));
    memset(scr, 0, sizeof(WScreen));

    /* initialize globals */
    scr->screen = screen_number;
    scr->root_win = RootWindow(dpy, screen_number);
    scr->depth = DefaultDepth(dpy, screen_number);
    scr->colormap = DefaultColormap(dpy, screen_number);

    scr->scr_width = WidthOfScreen(ScreenOfDisplay(dpy, screen_number));
    scr->scr_height = HeightOfScreen(ScreenOfDisplay(dpy, screen_number));

    XSetErrorHandler((XErrorHandler)alreadyRunningError);
    XSelectInput(dpy, scr->root_win, EVENT_MASK);
    XSync(dpy, 0);
    XSetErrorHandler((XErrorHandler)NULL);

    XDefineCursor(dpy, scr->root_win, wCursor[WCUR_DEFAULT]);

    /* screen descriptor for raster graphic library */
    rattr.flags = RC_RenderMode | RC_ColorsPerChannel;
    rattr.render_mode = wPreferences.dithering?RM_DITHER:RM_MATCH;
    rattr.colors_per_channel = wPreferences.cmap_size;

    if (wVisualID>=0) {
	rattr.flags |= RC_VisualID;
	rattr.visualid = wVisualID;
    }
    scr->rcontext = RCreateContext(dpy, screen_number, &rattr);
    if (!scr->rcontext) {
	wWarning(_("could not initialize graphics library context: %s"),
		 RErrorString);
	wAbort();
	exit(1);
    }

    scr->w_win = scr->rcontext->drawable;
    scr->w_visual = scr->rcontext->visual;
    scr->w_depth = scr->rcontext->depth;
    scr->w_colormap = scr->rcontext->cmap;

    scr->black_pixel = scr->rcontext->black;
    scr->white_pixel = scr->rcontext->white;
    
    
    /* allocate shared resources */
    allocResources(scr);

    createInternalWindows(scr);

    /* set icon sizes we can accept from clients */
    icon_size[0].min_width = 1;
    icon_size[0].min_height = 1;
    icon_size[0].max_width = ICON_WIDTH;
    icon_size[0].max_height = ICON_HEIGHT;
    icon_size[0].width_inc = 1;
    icon_size[0].height_inc = 1;
    XSetIconSizes(dpy, scr->root_win, icon_size, 1);

    /* setup window manager selection stuff */
    /*
    sprintf(buffer, "SM_S%i", screen_number);
    scr->selection = XInternAtom(dpy, buffer, False);
    if (XGetSelectionOwner(dpy, scr->selection)!=None) {
	wWarning("taking over selection ownership for %s", buffer);
    }
    XSetSelectionOwner(dpy, scr->selection, scr->selection_owner, CurrentTime);
    */
    
    /* setup WindowMaker protocols property in the root window*/
    PropSetWMakerProtocols(scr->root_win);

   scr->app_menu_x = DEF_APPMENU_X;
   scr->app_menu_y = DEF_APPMENU_Y;
    
    return scr;
}


void
wScreenSaveState(WScreen *scr)
{
    WWorkspaceState wstate;
    WWindow *wwin;

    wstate.flags = 0;
    if (scr->root_menu && scr->root_menu->flags.buttoned) {
	wstate.menu_x = scr->root_menu->frame_x;
	wstate.menu_y = scr->root_menu->frame_y;
	wstate.flags |= WSS_ROOTMENU;
    }

    if (scr->switch_menu && scr->switch_menu->flags.buttoned) {
	wstate.smenu_x = scr->switch_menu->frame_x;
	wstate.smenu_y = scr->switch_menu->frame_y;
	wstate.flags |= WSS_SWITCHMENU;
    }
    
     if (scr->workspace_menu && scr->workspace_menu->flags.buttoned) {
	wstate.wmenu_x = scr->workspace_menu->frame_x;
	wstate.wmenu_y = scr->workspace_menu->frame_y;
	wstate.flags |= WSS_WSMENU;
    }

/* 
 * Save current workspace, so can go back to it upon restart.
 */
    wstate.workspace = scr->workspace;

/*
 * Save y position of dock.
 */
    if (scr->dock) {
       wstate.dock_position = scr->dock->y_pos;
    }

    XChangeProperty(dpy, scr->root_win, _XA_WINDOWMAKER_STATE,
                    _XA_WINDOWMAKER_STATE, 32, PropModeReplace,
                    (unsigned char *) &wstate, 
		    sizeof(WWorkspaceState)/sizeof(int));
    
    /* save state of windows */
    wwin = scr->focused_window;
    while (wwin) {
	wWindowSaveState(wwin);
	wwin = wwin->prev;
    }

    /* save dock state to file */
    if (scr->dock)
      wDockSaveState(scr->dock, find_file(DEF_USERPREF_PATH, DEF_DOCKCONFIG));
}

