/*
 *  [ ctwm ]
 *
 *  Copyright 1992 Claude Lecommandeur.
 *
 * Permission to use, copy, modify  and distribute this software  [ctwm] and
 * its documentation for any purpose is hereby granted without fee, provided
 * that the above  copyright notice appear  in all copies and that both that
 * copyright notice and this permission notice appear in supporting documen-
 * tation, and that the name of  Claude Lecommandeur not be used in adverti-
 * sing or  publicity  pertaining to  distribution of  the software  without
 * specific, written prior permission. Claude Lecommandeur make no represen-
 * tations  about the suitability  of this software  for any purpose.  It is
 * provided "as is" without express or implied warranty.
 *
 * Claude Lecommandeur DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL  IMPLIED WARRANTIES OF  MERCHANTABILITY AND FITNESS.  IN NO
 * EVENT SHALL  Claude Lecommandeur  BE LIABLE FOR ANY SPECIAL,  INDIRECT OR
 * CONSEQUENTIAL  DAMAGES OR ANY  DAMAGES WHATSOEVER  RESULTING FROM LOSS OF
 * USE, DATA  OR PROFITS,  WHETHER IN AN ACTION  OF CONTRACT,  NEGLIGENCE OR
 * OTHER  TORTIOUS ACTION,  ARISING OUT OF OR IN  CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 * Author:  Claude Lecommandeur [ lecom@sic.epfl.ch ][ April 1992 ]
 */
#include <stdio.h>
#include <ctype.h>
#include "twm.h"
#include "util.h"
#include "parse.h"
#include "screen.h"
#include "icons.h"
#include "resize.h"
#include "add_window.h"
#include "events.h"
#include "clicktofocus.h"
#include "cursor.h"
#include "list.h"
#include "workmgr.h"
#ifdef VMS
#include <string.h>
#include <decw$include/Xos.h>
#include <decw$include/Xatom.h>
#include <X11Xmu/CharSet.h>
#include <decw$include/Xresource.h>
#else
#include <X11/Xos.h>
#include <X11/Xatom.h>
#include <X11/Xmu/CharSet.h>
#include <X11/Xresource.h>
#endif
#ifdef macII
int strcmp(); /* missing from string.h in AUX 2.0 */
#endif
#ifdef BUGGY_HP700_SERVER
static void fakeRaiseLower ();
#endif

#ifdef GNOME
#  include "gnomewindefs.h" /* include GNOME hints definitions */
   extern Atom _XA_WIN_WORKSPACE;
   extern Atom _XA_WIN_STATE;
#endif /* GNOME */

extern void twmrc_error_prefix (void); /* in gram.c */
extern char *captivename;

/***********************************************************************
 *
 *  Procedure:
 *	CreateWorkSpaceManager - create the workspace manager window
 *		for this screen.
 *
 *  Returned Value:
 *	none
 *
 *  Inputs:
 *	none
 *
 ***********************************************************************
 */
#define WSPCWINDOW    0
#define OCCUPYWINDOW  1
#define OCCUPYBUTTON  2

static void Vanish			(virtualScreen *vs,
					 TwmWindow *tmp_win);
static void DisplayWin			(virtualScreen *vs,
					 TwmWindow *tmp_win);
static void CreateWorkSpaceManagerWindow (virtualScreen *vs);
static void CreateOccupyWindow		(void);
static unsigned int GetMaskFromResource	(TwmWindow *win, char *res);
static int GetPropertyFromMask		(unsigned int mask, char *prop,
					 long *gwkspc);
static void PaintWorkSpaceManagerBorder	(virtualScreen *vs);
static void PaintButton			(int which,
					 virtualScreen *vs, Window w,
					 char *label,
					 ColorPair cp, int state);
static void WMapRemoveFromList		(TwmWindow *win, WorkSpace *ws);
static int WMapWindowMayBeAdded         (TwmWindow *win);
static void WMapAddToList		(TwmWindow *win, WorkSpace *ws);
static void ResizeWorkSpaceManager	(virtualScreen *vs, TwmWindow *win);
static void ResizeOccupyWindow		(TwmWindow *win);
static WorkSpace *GetWorkspace		(char *wname);
static void WMapRedrawWindow		(Window window, int width, int height,
					 ColorPair cp, char *label);
static int CanChangeOccupation          (TwmWindow **twm_winp);
void safecopy                           (char *dest, char *src, int size);

Atom _XA_WM_OCCUPATION;
Atom _XA_WM_CURRENTWORKSPACE;
Atom _XA_WM_WORKSPACESLIST;
Atom _XA_WM_CTWMSLIST;
Atom _XA_WM_CTWM_VSCREENMAP;
Atom _OL_WIN_ATTR;

int       fullOccupation    = 0;
int       useBackgroundInfo = False;
XContext  MapWListContext = (XContext) 0;
static Cursor handCursor  = (Cursor) 0;
static Bool DontRedirect (Window window);

extern Bool donttoggleworkspacemanagerstate;
extern Bool MaybeAnimate;
extern FILE *tracefile;

void InitWorkSpaceManager (void)
{
    Scr->workSpaceMgr.count	    = 0;
    Scr->workSpaceMgr.workSpaceList = NULL;
    Scr->workSpaceMgr.initialstate  = BUTTONSSTATE;
    Scr->workSpaceMgr.geometry      = NULL;
    Scr->workSpaceMgr.buttonStyle   = STYLE_NORMAL;
    Scr->workSpaceMgr.windowcp.back = Scr->White;
    Scr->workSpaceMgr.windowcp.fore = Scr->Black;
    Scr->workSpaceMgr.windowcpgiven = False;

    Scr->workSpaceMgr.occupyWindow = calloc(1, sizeof (OccupyWindow));
    Scr->workSpaceMgr.occupyWindow->name      = "Occupy Window";
    Scr->workSpaceMgr.occupyWindow->icon_name = "Occupy Window Icon";
    Scr->workSpaceMgr.occupyWindow->geometry  = NULL;
    Scr->workSpaceMgr.occupyWindow->columns   = 0;
    Scr->workSpaceMgr.occupyWindow->twm_win   = (TwmWindow*) 0;
    Scr->workSpaceMgr.occupyWindow->vspace    = Scr->WMgrVertButtonIndent;
    Scr->workSpaceMgr.occupyWindow->hspace    = Scr->WMgrHorizButtonIndent;

    Scr->workSpaceMgr.curColors.back  = Scr->Black;
    Scr->workSpaceMgr.curColors.fore  = Scr->White;
    Scr->workSpaceMgr.defColors.back  = Scr->White;
    Scr->workSpaceMgr.defColors.fore  = Scr->Black;	
    Scr->workSpaceMgr.curImage        = None;
    Scr->workSpaceMgr.curPaint        = False;
    Scr->workSpaceMgr.defImage        = None;
    Scr->workSpaceMgr.vspace          = Scr->WMgrVertButtonIndent;
    Scr->workSpaceMgr.hspace          = Scr->WMgrHorizButtonIndent;
    Scr->workSpaceMgr.name	      = "WorkSpaceManager";
    Scr->workSpaceMgr.icon_name       = "WorkSpaceManager Icon";

    Scr->workSpaceMgr.windowFont.basename =
      "-adobe-courier-medium-r-normal--10-100-75-75-m-60-iso8859-1";
    /*"-adobe-courier-bold-r-normal--8-80-75-75-m-50-iso8859-1";*/

    XrmInitialize ();
    if (MapWListContext == (XContext) 0) MapWListContext = XUniqueContext ();
}

void ConfigureWorkSpaceManager (void) {
    virtualScreen *vs;

    for (vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
	/*
	 * Make sure this is all properly initialized to nothing.  Otherwise
	 * bad and undefined behavior can show up in certain cases (e.g.,
	 * with no Workspaces {} defined in .ctwmrc, the only defined
	 * workspace will be random memory bytes, which can causes crashes on
	 * e.g.  f.menu "TwmWindows".)
	 */
	WorkSpaceWindow *wsw = (WorkSpaceWindow*) calloc (1, sizeof (WorkSpaceWindow));
	wsw->state = Scr->workSpaceMgr.initialstate; /* BUTTONSSTATE */
	vs->wsw = wsw;
    }
}

void CreateWorkSpaceManager (void)
{
    char wrkSpcList [512];
    char vsmapbuf    [1024], *vsmap;
    virtualScreen    *vs;
    WorkSpace        *ws, *fws;
    int len, vsmaplen;
    long junk;

    if (! Scr->workSpaceManagerActive) return;

    Scr->workSpaceMgr.windowFont.basename =
      "-adobe-courier-medium-r-normal--10-100-75-75-m-60-iso8859-1";
    Scr->workSpaceMgr.buttonFont = Scr->IconManagerFont;
    Scr->workSpaceMgr.cp	 = Scr->IconManagerC;
    if (!Scr->BeNiceToColormap) GetShadeColors (&Scr->workSpaceMgr.cp);

    _XA_WM_OCCUPATION       = XInternAtom (dpy, "WM_OCCUPATION",        False);
    _XA_WM_CURRENTWORKSPACE = XInternAtom (dpy, "WM_CURRENTWORKSPACE",  False);
    _XA_WM_CTWM_VSCREENMAP  = XInternAtom (dpy, "WM_CTWM_VSCREENMAP", False);
#ifdef GNOME
    _XA_WM_WORKSPACESLIST   = XInternAtom (dpy, "_WIN_WORKSPACE_NAMES", False);
#else /* GNOME */
    _XA_WM_WORKSPACESLIST   = XInternAtom (dpy, "WM_WORKSPACESLIST", False);
#endif /* GNOME */
    _OL_WIN_ATTR            = XInternAtom (dpy, "_OL_WIN_ATTR",         False);

    NewFontCursor (&handCursor, "top_left_arrow");

    vsmaplen = sizeof(vsmapbuf);
    if(CtwmGetVScreenMap(dpy, Scr->Root, vsmapbuf, &vsmaplen) == True)
	vsmap = strtok(vsmapbuf, ",");
    else
	vsmap = NULL;

    /*
     * weird things can happen if the config file is changed or the atom
     * returned above is messed with.  Sometimes windows may disappear in
     * that case depending on what's changed.  (depending on where they were
     * on the actual screen.
     */
    ws = Scr->workSpaceMgr.workSpaceList;
    for (vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
      WorkSpaceWindow *wsw = vs->wsw;
      if(vsmap)
          fws = GetWorkspace(vsmap);
      else
          fws = NULL;
      if(fws) {
          wsw->currentwspc = fws;
	  vsmap = strtok(NULL, ",");
      } else {
          wsw->currentwspc = ws;
          ws = ws->next;
      }
      CreateWorkSpaceManagerWindow (vs);
    }
    CreateOccupyWindow ();

    for (vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
      WorkSpaceWindow *wsw = vs->wsw;
      WorkSpace *ws2 = wsw->currentwspc;
      MapSubwindow *msw = wsw->mswl [ws2->number];
      if (Scr->workSpaceMgr.curImage == None) {
	if (Scr->workSpaceMgr.curPaint) {
	  XSetWindowBackground (dpy, msw->w, Scr->workSpaceMgr.curColors.back);
	}
      } else {
	XSetWindowBackgroundPixmap (dpy, msw->w, Scr->workSpaceMgr.curImage->pixmap);
      }
      XSetWindowBorder (dpy, msw->w, Scr->workSpaceMgr.curBorderColor);
      XClearWindow (dpy, msw->w);

      if (useBackgroundInfo && ! Scr->DontPaintRootWindow) {
	if (ws2->image == None)
	    XSetWindowBackground       (dpy, vs->window, ws2->backcp.back);
	else
	    XSetWindowBackgroundPixmap (dpy, vs->window, ws2->image->pixmap);
	XClearWindow (dpy, vs->window);
      }
    }
    len = GetPropertyFromMask (0xFFFFFFFFu, wrkSpcList, &junk);
    XChangeProperty (dpy, Scr->Root, _XA_WM_WORKSPACESLIST, XA_STRING, 8, 
		     PropModeReplace, (unsigned char *) wrkSpcList, len);
}

void GotoWorkSpaceByName (virtualScreen *vs, char *wname)
{
    WorkSpace *ws;

    if (! Scr->workSpaceManagerActive) return;
    if (!vs) return;
    ws = GetWorkspace(wname);
    if (ws == NULL) return;
    GotoWorkSpace (vs, ws);
}

/* 6/19/1999 nhd for GNOME compliance */
void GotoWorkSpaceByNumber(virtualScreen *vs, int workspacenum)
{
    WorkSpace *ws;
    if(! Scr->workSpaceManagerActive) return;
    if (!vs) return;
    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
        if (ws->number == workspacenum) break;
    }
    if (ws == NULL) return;
    GotoWorkSpace (vs, ws);
}
 /* */
 
void GotoPrevWorkSpace (virtualScreen *vs)
{
    WorkSpace *ws1, *ws2;

    if (! Scr->workSpaceManagerActive) return;
    if (!vs) return;
    ws1 = Scr->workSpaceMgr.workSpaceList;
    if (ws1 == NULL) return;
    ws2 = ws1->next;

    while ((ws2 != vs->wsw->currentwspc) && (ws2 != NULL)) {
	ws1 = ws2;
	ws2 = ws2->next;
    }
    GotoWorkSpace (vs, ws1);
}

void GotoNextWorkSpace (virtualScreen *vs)
{
    WorkSpace *ws;
    if (! Scr->workSpaceManagerActive) return;
    if (!vs) return;

    ws = vs->wsw->currentwspc;
    ws = (ws->next != NULL) ? ws->next : Scr->workSpaceMgr.workSpaceList;
    GotoWorkSpace (vs, ws);
}

void GotoRightWorkSpace (virtualScreen *vs)
{
    WorkSpace *ws;
    int number, columns, count;

    if (!Scr->workSpaceManagerActive)
	return;
    if (!vs)
	return;

    ws      = vs->wsw->currentwspc;
    number  = ws->number;
    columns = Scr->workSpaceMgr.columns;
    count   = Scr->workSpaceMgr.count;
    number++;
    if ((number % columns) == 0)
	number -= columns;
    else if (number >= count)
	number = (number / columns) * columns;

    GotoWorkSpaceByNumber(vs, number);
}

void GotoLeftWorkSpace (virtualScreen *vs)
{
    WorkSpace *ws;
    int number, columns, count;

    if (!Scr->workSpaceManagerActive)
	return;
    if (!vs)
	return;

    ws      = vs->wsw->currentwspc;
    number  = ws->number;
    columns = Scr->workSpaceMgr.columns;
    count   = Scr->workSpaceMgr.count;
    number += (number % columns) ? -1 : (columns - 1);
    if (number >= count)
	number = count - 1;
    GotoWorkSpaceByNumber(vs, number);
}

void GotoUpWorkSpace (virtualScreen *vs)
{
    WorkSpace *ws;
    int number, lines, columns, count;

    if (!Scr->workSpaceManagerActive)
	return;
    if (!vs)
	return;

    ws      = vs->wsw->currentwspc;
    number  = ws->number;
    lines   = Scr->workSpaceMgr.lines;
    columns = Scr->workSpaceMgr.columns;
    count   = Scr->workSpaceMgr.count;
    number -=  columns;
    if (number < 0) {
	number += lines * columns;
	/* If the number of workspaces is not a multiple of nr of columns */
	if (number >= count)
	    number -= columns;
    }
    GotoWorkSpaceByNumber(vs, number);
}

void GotoDownWorkSpace (virtualScreen *vs)
{
    WorkSpace *ws;
    int number, columns, count;

    if (!Scr->workSpaceManagerActive)
	return;
    if (!vs)
	return;

    ws      = vs->wsw->currentwspc;
    number  = ws->number;
    columns = Scr->workSpaceMgr.columns;
    count   = Scr->workSpaceMgr.count;
    number +=  columns;
    if (number >= count) {
	number %= columns;
    }
    GotoWorkSpaceByNumber(vs, number);
}

void ShowBackground (virtualScreen *vs)
{
  static int state = 0;
  TwmWindow *twmWin;

  if (state) {
    for (twmWin = Scr->FirstWindow; twmWin != NULL; twmWin = twmWin->next) {
      if (twmWin->savevs == vs) {
	DisplayWin (vs, twmWin);
      }
      twmWin->savevs = NULL;
    }
    state = 0;
  } else {
    for (twmWin = Scr->FirstWindow; twmWin != NULL; twmWin = twmWin->next) {
      if (twmWin->vs == vs) {
	twmWin->savevs = twmWin->vs;
	Vanish (vs, twmWin);
      }
    }
    state = 1;
  }
}

void GotoWorkSpace (virtualScreen *vs, WorkSpace *ws)
{
    TwmWindow		 *twmWin;
    WorkSpace		 *oldws, *newws;
    WList		 *wl, *wl1;
    WinList		 winl;
    XSetWindowAttributes attr;
    XWindowAttributes    winattrs;
    unsigned long	 eventMask;
    IconMgr		 *iconmgr;
    Window		 oldw;
    Window		 neww;
    TwmWindow		 *focuswindow;
    TwmWindow		 *last_twmWin = NULL;
    virtualScreen	 *tmpvs;

    if (! Scr->workSpaceManagerActive) return;
    for (tmpvs = Scr->vScreenList; tmpvs != NULL; tmpvs = tmpvs->next) {
      if (ws == tmpvs->wsw->currentwspc) {
	XBell (dpy, 0);
	return;
      }
    }
    oldws = vs->wsw->currentwspc;
    newws = ws;
    if (oldws == newws) return;

    attr.backing_store = NotUseful;
    attr.save_under    = False;

    if (useBackgroundInfo && ! Scr->DontPaintRootWindow) {
	if (newws->image == None)
	    XSetWindowBackground       (dpy, vs->window, newws->backcp.back);
	else
	    XSetWindowBackgroundPixmap (dpy, vs->window, newws->image->pixmap);
	XClearWindow (dpy, vs->window);
    }

    /* If SaveWorkspaceFocus is on, save the focus of the last window. */
    if ( Scr->SaveWorkspaceFocus ) {
        oldws->save_focus = Scr->Focus;
    }

    focuswindow = (TwmWindow *)NULL;
    for (twmWin = Scr->FirstWindow; twmWin != NULL; twmWin = twmWin->next) {
	if (twmWin->vs == vs) {
	  if (!OCCUPY (twmWin, newws)) {
	    virtualScreen *tvs;
	    Vanish (vs, twmWin);
	    for (tvs = Scr->vScreenList; tvs != NULL; tvs = tvs->next) {
	      if (tvs == vs) continue;
	      if (OCCUPY (twmWin, tvs->wsw->currentwspc)) {
		DisplayWin (tvs, twmWin);
		break;
	      }
	    }
	  } else if (twmWin->hasfocusvisible) {
	      focuswindow = twmWin;
	      SetFocusVisualAttributes (focuswindow, False);
	  }
	}
    }
    /* Move to the end of the twmWin list */
    for (twmWin = Scr->FirstWindow; twmWin != NULL; twmWin = twmWin->next) {
	last_twmWin = twmWin;
    }
    /* Iconise in reverse order */
    for (twmWin = last_twmWin; twmWin != NULL; twmWin = twmWin->prev) {
	if (OCCUPY (twmWin, newws) && !twmWin->vs) DisplayWin (vs, twmWin);
    }
/*
   Reorganize icon manager window lists
*/
    for (twmWin = Scr->FirstWindow; twmWin != NULL; twmWin = twmWin->next) {
	wl = twmWin->iconmanagerlist;
	if (wl == NULL) continue;
	if (OCCUPY (wl->iconmgr->twm_win, newws)) continue;
	wl1 = wl;
	wl  = wl->nextv;
	while (wl != NULL) {
	    if (OCCUPY (wl->iconmgr->twm_win, newws)) break;
	    wl1 = wl;
	    wl  = wl->nextv;
	}
	if (wl != NULL) {
	    wl1->nextv = wl->nextv;
	    wl->nextv  = twmWin->iconmanagerlist;
	    twmWin->iconmanagerlist = wl;
	}
    }
    wl = (WList*)0;
    for (iconmgr = newws->iconmgr; iconmgr; iconmgr = iconmgr->next) {
	if (iconmgr->first) {
	    wl = iconmgr->first;
	    break;
	}
    }	
    CurrentIconManagerEntry (wl);
    if (focuswindow) {
	SetFocusVisualAttributes (focuswindow, True);
    }
    vs->wsw->currentwspc = newws;
    if (Scr->ReverseCurrentWorkspace && vs->wsw->state == MAPSTATE) {
        MapSubwindow *msw = vs->wsw->mswl [oldws->number];
	for (winl = msw->wl; winl != NULL; winl = winl->next) {
	    WMapRedrawName (vs, winl);
	}
	msw = vs->wsw->mswl [newws->number];
	for (winl = msw->wl; winl != NULL; winl = winl->next) {
	    WMapRedrawName (vs, winl);
	}
    } else
    if (vs->wsw->state == BUTTONSSTATE) {
        ButtonSubwindow *bsw = vs->wsw->bswl [oldws->number];
	PaintButton (WSPCWINDOW, vs, bsw->w, oldws->label, oldws->cp, off);
	bsw = vs->wsw->bswl [newws->number];
	PaintButton (WSPCWINDOW, vs, bsw->w, newws->label, newws->cp,  on);
    }
    oldws->iconmgr = Scr->iconmgr;
    Scr->iconmgr = newws->iconmgr;

    oldw = vs->wsw->mswl [oldws->number]->w;
    neww = vs->wsw->mswl [newws->number]->w;
    if (useBackgroundInfo) {
	if (oldws->image == None || Scr->NoImagesInWorkSpaceManager)
	    XSetWindowBackground       (dpy, oldw, oldws->backcp.back);
	else
	    XSetWindowBackgroundPixmap (dpy, oldw, oldws->image->pixmap);
    }
    else {
	if (Scr->workSpaceMgr.defImage == None || Scr->NoImagesInWorkSpaceManager)
	    XSetWindowBackground       (dpy, oldw, Scr->workSpaceMgr.defColors.back);
	else
	    XSetWindowBackgroundPixmap (dpy, oldw, Scr->workSpaceMgr.defImage->pixmap);
    }
    attr.border_pixel = Scr->workSpaceMgr.defBorderColor;
    XChangeWindowAttributes (dpy, oldw, CWBorderPixel, &attr);

    if (Scr->workSpaceMgr.curImage == None) {
	if (Scr->workSpaceMgr.curPaint) XSetWindowBackground (dpy, neww, Scr->workSpaceMgr.curColors.back);
    }
    else {
	XSetWindowBackgroundPixmap (dpy, neww, Scr->workSpaceMgr.curImage->pixmap);
    }
    attr.border_pixel =  Scr->workSpaceMgr.curBorderColor;
    XChangeWindowAttributes (dpy, neww, CWBorderPixel, &attr);

    XClearWindow (dpy, oldw);
    XClearWindow (dpy, neww);

    XGetWindowAttributes(dpy, Scr->Root, &winattrs);
    eventMask = winattrs.your_event_mask;
    XSelectInput(dpy, Scr->Root, eventMask & ~PropertyChangeMask);

    XChangeProperty (dpy, Scr->Root, _XA_WM_CURRENTWORKSPACE, XA_STRING, 8, 
		     PropModeReplace, (unsigned char *) newws->name, strlen (newws->name));
#ifdef GNOME
/* nhd 6/19/1999 for GNOME compliance
 * Publish which workspace the root window shows/contains.
 * Olaf Rhialto Seibert: However, don't do it when the root window is
 * captive, since it will be moved itself: for non-root windows this
 * property is used to indicate in which workspace it is contained.
 */
    
    if (!Scr->CaptiveRoot)
	XChangeProperty (dpy, Scr->Root, _XA_WIN_WORKSPACE, XA_CARDINAL, 32,
		    PropModeReplace, (unsigned char *) &(newws->number), 1);
#endif /* GNOME */
    XSelectInput(dpy, Scr->Root, eventMask);

    /*    XDestroyWindow (dpy, cachew);*/
    if (Scr->ChangeWorkspaceFunction.func != 0) {
	char *action;
	XEvent event;

	action = Scr->ChangeWorkspaceFunction.item ?
		Scr->ChangeWorkspaceFunction.item->action : NULL;
	ExecuteFunction (Scr->ChangeWorkspaceFunction.func, action,
			   (Window) 0, (TwmWindow*) 0, &event, C_ROOT, FALSE);
    }

    /* If SaveWorkspaceFocus is on, try to restore the focus to the last
       window which was focused when we left this workspace. */
    if (Scr->SaveWorkspaceFocus && newws->save_focus) {
	twmWin = newws->save_focus;
	if (OCCUPY(twmWin, newws)) {	/* check should not even be needed anymore */
	    WarpToWindow(twmWin, 0);
	} else {
	    newws->save_focus = NULL;
	}
    }

    /* keep track of the order of the workspaces across restarts */
    CtwmSetVScreenMap(dpy, Scr->Root, Scr->vScreenList);

    XSync (dpy, 0);
    if (Scr->ClickToFocus || Scr->SloppyFocus) set_last_window (newws);
    MaybeAnimate = True;
}

char *GetCurrentWorkSpaceName (virtualScreen *vs)
{
    if (! Scr->workSpaceManagerActive) return (NULL);
    if (!vs) vs = Scr->vScreenList;
    return vs->wsw->currentwspc->name;
}

void AddWorkSpace (char *name, char *background, char *foreground,
		   char *backback, char *backfore, char *backpix)
{
    WorkSpace *ws;
    int	      wsnum;
    Image     *image;

    wsnum = Scr->workSpaceMgr.count;
    if (wsnum == MAXWORKSPACE) return;

    fullOccupation |= (1 << wsnum);
    ws = (WorkSpace*) malloc (sizeof (WorkSpace));
    ws->FirstWindowRegion = NULL;
#if 0 /* def VMS */
    {
       char *ftemp;
       ftemp = (char *) malloc((strlen(name)+1)*sizeof(char));
       ws->name = strcpy (ftemp,name);
       ftemp = (char *) malloc((strlen(name)+1)*sizeof(char));
       ws->label = strcpy (ftemp,name);
    }
#else
    ws->name  = (char*) strdup (name);
    ws->label = (char*) strdup (name);
#endif
    ws->clientlist = NULL;
    ws->save_focus = NULL;

    if (background == NULL)
	ws->cp.back = Scr->IconManagerC.back;
    else
	GetColor (Scr->Monochrome, &(ws->cp.back), background);

    if (foreground == NULL)
	ws->cp.fore = Scr->IconManagerC.fore;
    else
	GetColor (Scr->Monochrome, &(ws->cp.fore), foreground);

#ifdef COLOR_BLIND_USER
    ws->cp.shadc = Scr->White;
    ws->cp.shadd = Scr->Black;
#else
    if (!Scr->BeNiceToColormap) GetShadeColors (&ws->cp);
#endif

    if (backback == NULL)
	GetColor (Scr->Monochrome, &(ws->backcp.back), "Black");
    else {
	GetColor (Scr->Monochrome, &(ws->backcp.back), backback);
	useBackgroundInfo = True;
    }

    if (backfore == NULL)
	GetColor (Scr->Monochrome, &(ws->backcp.fore), "White");
    else {
	GetColor (Scr->Monochrome, &(ws->backcp.fore), backfore);
	useBackgroundInfo = True;
    }
    if ((image = GetImage (backpix, ws->backcp)) != None) {
	ws->image = image;
	useBackgroundInfo = True;
    }
    else {
	ws->image = None;
    }
    ws->next   = NULL;
    ws->number = wsnum;
    Scr->workSpaceMgr.count++;

    if (Scr->workSpaceMgr.workSpaceList == NULL) {
	Scr->workSpaceMgr.workSpaceList = ws;
    }
    else {
	WorkSpace *wstmp = Scr->workSpaceMgr.workSpaceList;
	while (wstmp->next != NULL) { wstmp = wstmp->next; }
	wstmp->next = ws;
    }
    Scr->workSpaceManagerActive = 1;
}

static XrmOptionDescRec table [] = {
    {"-xrm",		NULL,		XrmoptionResArg, (XPointer) NULL},
};

void SetupOccupation (TwmWindow *twm_win,
		      int occupation_hint) /* <== [ Matthew McNeill Feb 1997 ] == */
{
    TwmWindow		*t;
    unsigned char	*prop;
    unsigned long	nitems, bytesafter;
    Atom		actual_type;
    int			actual_format;
    int			state;
    Window		icon;
    char		**cliargv = NULL;
    int			cliargc;
    Bool		status;
    char		*str_type;
    XrmValue		value;
    char		wrkSpcList [512];
    int			len;
    WorkSpace    	*ws;
    XWindowAttributes winattrs;
    unsigned long     eventMask;
    XrmDatabase       db = NULL;
    virtualScreen     *vs;
    long gwkspc = 0; /* for GNOME - which workspace we occupy */

    if (! Scr->workSpaceManagerActive) {
	twm_win->occupation = 1 << 0;   /* occupy workspace #0 */
	/*
	 * Choose some valid virtual screen.
	 * InitVirtualScreens() always seems to set this to non-NULL.
	 */
	twm_win->vs = Scr->vScreenList; /* only one virtual screen */
	/* more?... */

	return;
    }
    if (twm_win->wspmgr) return;

    /*twm_win->occupation = twm_win->iswinbox ? fullOccupation : 0;*/
    twm_win->occupation = 0;

    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	if (LookInList (ws->clientlist, twm_win->full_name, &twm_win->class)) {
            twm_win->occupation |= 1 << ws->number;
	}
    }

    if (LookInList (Scr->OccupyAll, twm_win->full_name, &twm_win->class)) {
        twm_win->occupation = fullOccupation;
    }

    if (XGetCommand (dpy, twm_win->w, &cliargv, &cliargc)) {
	XrmParseCommand (&db, table, 1, "ctwm", &cliargc, cliargv);
	status = XrmGetResource (db, "ctwm.workspace", "Ctwm.Workspace", &str_type, &value);
	if ((status == True) && (value.size != 0)) {
	    strncpy (wrkSpcList, value.addr, value.size);
	    twm_win->occupation = GetMaskFromResource (twm_win, wrkSpcList);
	}
	XrmDestroyDatabase (db);
	XFreeStringList (cliargv);
    }

    if (RestartPreviousState) {
	if (XGetWindowProperty (dpy, twm_win->w, _XA_WM_OCCUPATION, 0L, 2500, False,
				XA_STRING, &actual_type, &actual_format, &nitems,
				&bytesafter, &prop) == Success) {
	    if (nitems != 0) {
		twm_win->occupation = GetMaskFromProperty (prop, nitems);
		XFree ((char *) prop);
	    }
	}
    }

    if (twm_win->iconmgr) return; /* someone tried to modify occupation of icon managers */

    if (! Scr->TransientHasOccupation) {
	if (twm_win->transient) {
	    t = GetTwmWindow(twm_win->transientfor);
	    if (t != NULL) twm_win->occupation = t->occupation;
	}
	else
	if (twm_win->group != 0) {
	    t = GetTwmWindow(twm_win->group);
	    if (t != NULL) twm_win->occupation = t->occupation;
	}
    }

    /*============[ Matthew McNeill Feb 1997 ]========================*
     * added in functionality of specific occupation state. The value 
     * should be a valid occupation bit-field or 0 for the default action
     */

    if (occupation_hint != 0)
      twm_win->occupation = occupation_hint;

    /*================================================================*/

    if ((twm_win->occupation & fullOccupation) == 0) {
      vs = Scr->currentvs;
      if (vs && vs->wsw->currentwspc) 
	twm_win->occupation = 1 << vs->wsw->currentwspc->number;
      else {
	twm_win->occupation = 1;
      }
    }
    twm_win->vs = NULL;
    for (vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
      if (OCCUPY (twm_win, vs->wsw->currentwspc)) {
	twm_win->vs = vs;
	break;
      }
    }
	
    len = GetPropertyFromMask (twm_win->occupation, wrkSpcList, &gwkspc);

    if (!XGetWindowAttributes(dpy, twm_win->w, &winattrs)) return;
    eventMask = winattrs.your_event_mask;
    XSelectInput(dpy, twm_win->w, eventMask & ~PropertyChangeMask);

    XChangeProperty (dpy, twm_win->w, _XA_WM_OCCUPATION, XA_STRING, 8, 
		     PropModeReplace, (unsigned char *) wrkSpcList, len);
#ifdef GNOME
    XChangeProperty (dpy, twm_win->w, _XA_WIN_WORKSPACE, XA_CARDINAL, 32,
		     PropModeReplace, (unsigned char *)(&gwkspc), 1);
    if (XGetWindowProperty (dpy, twm_win->w, _XA_WIN_STATE, 0L, 32, False,
			    XA_CARDINAL, &actual_type, &actual_format, &nitems,
			    &bytesafter, &prop) 
	!= Success || nitems == 0) {
	gwkspc = 0;
    } else {
	gwkspc = (int)*prop;
	XFree ((char *)prop);
    }
    if (twm_win->occupation == fullOccupation)
      gwkspc |= WIN_STATE_STICKY;
    else
      gwkspc &= ~WIN_STATE_STICKY;
    XChangeProperty (dpy, twm_win->w, _XA_WIN_STATE, XA_CARDINAL, 32, 
		     PropModeReplace, (unsigned char *)&gwkspc, 1); 
#endif /* GNOME */
    XSelectInput (dpy, twm_win->w, eventMask);

/* kludge */
    state = NormalState;
    if (!(RestartPreviousState && GetWMState (twm_win->w, &state, &icon) &&
	 (state == NormalState || state == IconicState || state == InactiveState))) {
	if (twm_win->wmhints && (twm_win->wmhints->flags & StateHint))
	    state = twm_win->wmhints->initial_state;
    }
    if (visible (twm_win)) {
	if (state == InactiveState) SetMapStateProp (twm_win, NormalState);
    } else {
	if (state ==   NormalState) SetMapStateProp (twm_win, InactiveState);
    }
}

void safecopy(char *dest, char *src, int size)
{
    strncpy(dest, src, size - 1);
    dest[size - 1] = '\0';
}

Bool RedirectToCaptive (Window window)
{
    unsigned long	nitems, bytesafter;
    Atom		actual_type;
    int			actual_format;
    char		**cliargv = NULL;
    int			cliargc;
    Bool		status;
    char		*str_type;
    XrmValue		value;
    int			ret;
    Atom		_XA_WM_CTWM_ROOT;
    char		*atomname;
    Window		newroot;
    XWindowAttributes	wa;
    XrmDatabase         db = NULL;

    if (DontRedirect (window)) return (False);
    if (!XGetCommand (dpy, window, &cliargv, &cliargc)) return (False);
    XrmParseCommand (&db, table, 1, "ctwm", &cliargc, cliargv);
    if (db == NULL) {
        if (cliargv) XFreeStringList (cliargv);
	return False;
    }
    ret = False;
    status = XrmGetResource (db, "ctwm.redirect", "Ctwm.Redirect", &str_type, &value);
    if ((status == True) && (value.size != 0)) {
	char	         cctwm [64];
	Window		*prop;

	safecopy (cctwm, value.addr, sizeof(cctwm));
	atomname = (char*) malloc (strlen ("WM_CTWM_ROOT_") + strlen (cctwm) + 1);
	sprintf (atomname, "WM_CTWM_ROOT_%s", cctwm);
	_XA_WM_CTWM_ROOT = XInternAtom (dpy, atomname, False);
	
	if (XGetWindowProperty (dpy, Scr->Root, _XA_WM_CTWM_ROOT,
		0L, 1L, False, AnyPropertyType, &actual_type, &actual_format,
		&nitems, &bytesafter, (unsigned char **)&prop) == Success) {
	    if (actual_type == XA_WINDOW && actual_format == 32 &&
			nitems == 1 /*&& bytesafter == 0*/) {
		newroot = *prop;
		if (XGetWindowAttributes (dpy, newroot, &wa)) {
		    XReparentWindow (dpy, window, newroot, 0, 0);
		    XMapWindow (dpy, window);
		    ret = True;
		}
	    }
	    XFree ((char *)prop);
	}
    }
    status = XrmGetResource (db, "ctwm.rootWindow", "Ctwm.RootWindow", &str_type, &value);
    if ((status == True) && (value.size != 0)) {
	char rootw [32];
	unsigned int scanned;

	safecopy (rootw, value.addr, sizeof(rootw));
	if (sscanf (rootw, "%x", &scanned) == 1) {
	    newroot = scanned;
	    if (XGetWindowAttributes (dpy, newroot, &wa)) {
		XReparentWindow (dpy, window, newroot, 0, 0);
		XMapWindow (dpy, window);
		ret = True;
	    }
	}
    }
    XrmDestroyDatabase (db);
    XFreeStringList (cliargv);
    return (ret);
}

/*
 * The window whose occupation is being manipulated.
 */
static TwmWindow *occupyWin = (TwmWindow*) 0;

static int CanChangeOccupation(TwmWindow **twm_winp)
{
    TwmWindow *twm_win;

    if (!Scr->workSpaceManagerActive)
	return 0;
    if (occupyWin != NULL)
	return 0;
    twm_win = *twm_winp;
    if (twm_win->iconmgr)
	return 0;
    if (!Scr->TransientHasOccupation) {
	if (twm_win->transient)
	    return 0;
	if (twm_win->group != (Window) 0 && twm_win->group != twm_win->w) {
	    /*
	     * When trying to modify a group member window,
	     * operate on the group leader instead
	     * (and thereby on all group member windows as well).
	     * If we can't find the group leader, pretend it isn't set.
	     */
	    twm_win = GetTwmWindow(twm_win->group);
	    if (!twm_win)
		return 1;
	    *twm_winp = twm_win;
	}
    }
    return 1;
}

void Occupy (TwmWindow *twm_win)
{
    int		 x, y, junkX, junkY;
    unsigned int junkB, junkD;
    unsigned int width, height;
    int	       	 xoffset, yoffset;
    Window     	 junkW, w;
    unsigned int junkK;
    struct OccupyWindow    *occupyWindow;

    if (!CanChangeOccupation(&twm_win))
	return;

    occupyWindow = Scr->workSpaceMgr.occupyWindow;
    occupyWindow->tmpOccupation = twm_win->occupation;
    w = occupyWindow->w;
    XGetGeometry  (dpy, w, &junkW, &junkX, &junkY, &width, &height, &junkB, &junkD);
    XQueryPointer (dpy, Scr->Root, &junkW, &junkW, &junkX, &junkY, &x, &y, &junkK);
    x -= (width  / 2);
    y -= (height / 2);
    if (x < 0) x = 0;
    if (y < 0) y = 0;
    xoffset = width  + 2 * Scr->BorderWidth;
    yoffset = height + 2 * Scr->BorderWidth + Scr->TitleHeight;
    if ((x + xoffset) > Scr->rootw) x = Scr->rootw - xoffset;
    if ((y + yoffset) > Scr->rooth) y = Scr->rooth - yoffset;

    occupyWindow->twm_win->occupation = twm_win->occupation;
    if (occupyWindow->twm_win->vs != Scr->currentvs) {
	XReparentWindow(dpy, occupyWindow->twm_win->frame, Scr->Root, x, y);
	occupyWindow->twm_win->vs = Scr->currentvs;
    } else
	XMoveWindow(dpy, occupyWindow->twm_win->frame, x, y);

    SetMapStateProp (occupyWindow->twm_win, NormalState);
    XMapWindow      (dpy, occupyWindow->w);
    XMapRaised      (dpy, occupyWindow->twm_win->frame);
    occupyWindow->twm_win->mapped = TRUE;
    occupyWin = twm_win;
}

void OccupyHandleButtonEvent (XEvent *event)
{
    WorkSpace	 *ws;
    OccupyWindow *occupyW;
    Window	 buttonW;

    if (! Scr->workSpaceManagerActive) return;
    if (occupyWin == (TwmWindow*) 0) return;

    buttonW = event->xbutton.window;
    if (buttonW == 0) return; /* icon */

    XGrabPointer (dpy, Scr->Root, True,
		  ButtonPressMask | ButtonReleaseMask,
		  GrabModeAsync, GrabModeAsync,
		  Scr->Root, None, CurrentTime);

    occupyW = Scr->workSpaceMgr.occupyWindow;
    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	if (occupyW->obuttonw [ws->number] == buttonW) break;
    }
    if (ws != NULL) {
	int mask = 1 << ws->number;
	if ((occupyW->tmpOccupation & mask) == 0) {
	    PaintButton (OCCUPYWINDOW, NULL, occupyW->obuttonw [ws->number],
			 ws->label, ws->cp, on);
	} else {
	    PaintButton (OCCUPYWINDOW, NULL, occupyW->obuttonw [ws->number],
			 ws->label, ws->cp, off);
	}
	occupyW->tmpOccupation ^= mask;
    }
    else
    if (buttonW == occupyW->OK) {
	if (occupyW->tmpOccupation == 0) return;
	ChangeOccupation (occupyWin, occupyW->tmpOccupation);
	XUnmapWindow (dpy, occupyW->twm_win->frame);
	occupyW->twm_win->mapped = FALSE;
	occupyW->twm_win->occupation = 0;
	occupyWin = (TwmWindow*) 0;
	XSync (dpy, 0);
    }
    else
    if (buttonW == occupyW->cancel) {
	XUnmapWindow (dpy, occupyW->twm_win->frame);
	occupyW->twm_win->mapped = FALSE;
	occupyW->twm_win->occupation = 0;
	occupyWin = (TwmWindow*) 0;
	XSync (dpy, 0);
    }
    else
    if (buttonW == occupyW->allworkspc) {
	for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	    PaintButton (OCCUPYWINDOW, NULL, occupyW->obuttonw [ws->number],
			 ws->label, ws->cp, on);
	}
	occupyW->tmpOccupation = fullOccupation;
    }
    if (ButtonPressed == -1) XUngrabPointer (dpy, CurrentTime);
}

void OccupyAll (TwmWindow *twm_win)
{
    IconMgr *save;

    if (!CanChangeOccupation(&twm_win))
	return;
    save = Scr->iconmgr;
    Scr->iconmgr = Scr->workSpaceMgr.workSpaceList->iconmgr;
    ChangeOccupation (twm_win, fullOccupation);
    Scr->iconmgr = save;
}

void AddToWorkSpace (char *wname, TwmWindow *twm_win)
{
    WorkSpace *ws;
    int newoccupation;

    if (!CanChangeOccupation(&twm_win))
	return;
    ws = GetWorkspace (wname);
    if (!ws)
	return;

    if (twm_win->occupation & (1 << ws->number))
	return;
    newoccupation = twm_win->occupation | (1 << ws->number);
    ChangeOccupation (twm_win, newoccupation);
}

void RemoveFromWorkSpace (char *wname, TwmWindow *twm_win)
{
    WorkSpace *ws;
    int newoccupation;

    if (!CanChangeOccupation(&twm_win))
	return;
    ws = GetWorkspace (wname);
    if (!ws)
	return;

    newoccupation = twm_win->occupation & ~(1 << ws->number);
    if (!newoccupation) return;
    ChangeOccupation (twm_win, newoccupation);
}

void ToggleOccupation (char *wname, TwmWindow *twm_win)
{
    WorkSpace *ws;
    int newoccupation;

    if (!CanChangeOccupation(&twm_win))
	return;
    ws = GetWorkspace (wname);
    if (!ws) return;

    newoccupation = twm_win->occupation ^ (1 << ws->number);
    if (!newoccupation) return;
    ChangeOccupation (twm_win, newoccupation);
}

void MoveToNextWorkSpace (virtualScreen *vs, TwmWindow *twm_win)
{
    WorkSpace *wlist1, *wlist2;
    int newoccupation;

    if (!CanChangeOccupation(&twm_win))
	return;

    wlist1 = vs->wsw->currentwspc;
    wlist2 = wlist1->next;
    wlist2 = wlist2 ? wlist2 : Scr->workSpaceMgr.workSpaceList;

    newoccupation = (twm_win->occupation ^ (1 << wlist1->number))
				         | (1 << wlist2->number);
    ChangeOccupation (twm_win, newoccupation);
}


void MoveToNextWorkSpaceAndFollow (virtualScreen *vs, TwmWindow *twm_win)
{
    if (!CanChangeOccupation(&twm_win))
	return;

    MoveToNextWorkSpace(vs, twm_win);
    GotoNextWorkSpace(vs);
#if 0
    RaiseWindow(twm_win);	/* XXX really do this? */
#endif
}


void MoveToPrevWorkSpace (virtualScreen *vs, TwmWindow *twm_win)
{
    WorkSpace *wlist1, *wlist2;
    int newoccupation;

    if (!CanChangeOccupation(&twm_win))
	return;

    wlist1 = Scr->workSpaceMgr.workSpaceList;
    wlist2 = vs->wsw->currentwspc;
    if (wlist1 == NULL) return;

    while (wlist1->next != wlist2 && wlist1->next != NULL) {
	wlist1 = wlist1->next;
    }

    newoccupation = (twm_win->occupation ^ (1 << wlist2->number))
					 | (1 << wlist1->number);
    ChangeOccupation (twm_win, newoccupation);
}

void MoveToPrevWorkSpaceAndFollow (virtualScreen *vs, TwmWindow *twm_win)
{
    if (!CanChangeOccupation(&twm_win))
	return;

    MoveToPrevWorkSpace(vs, twm_win);
    GotoPrevWorkSpace(vs);
#if 0
    RaiseWindow(twm_win);		/* XXX really do this? */
#endif
}

static WorkSpace *GetWorkspace (char *wname)
{
    WorkSpace *ws;

    if (!wname) return (NULL);
    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	if (strcmp (ws->label, wname) == 0) break;
    }
    if (ws == NULL) {
	for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	    if (strcmp (ws->name, wname) == 0) break;
	}
    }
    return (ws);
}

void AllocateOthersIconManagers (void)
{
    IconMgr   *p = NULL, *ip, *oldp, *oldv;
    WorkSpace *ws;

    if (! Scr->workSpaceManagerActive) return;

    oldp = Scr->iconmgr;
    for (ws = Scr->workSpaceMgr.workSpaceList->next; ws != NULL; ws = ws->next) {
	ws->iconmgr  = (IconMgr *) malloc (sizeof (IconMgr));
	*ws->iconmgr = *oldp;
	oldv = ws->iconmgr;
	oldp->nextv = ws->iconmgr;
	oldv->nextv = NULL;

	for (ip = oldp->next; ip != NULL; ip = ip->next) {
	    p  = (IconMgr *) malloc (sizeof (IconMgr));
	    *p = *ip;
	    ip->nextv  = p;
	    p->next    = NULL;
	    p->prev    = oldv;
	    p->nextv   = NULL;
	    oldv->next = p;
	    oldv = p;
        }
	for (ip = ws->iconmgr; ip != NULL; ip = ip->next) {
	    ip->lasti = p;
        }
	oldp = ws->iconmgr;
    }
    Scr->workSpaceMgr.workSpaceList->iconmgr = Scr->iconmgr;
}

static void Vanish (virtualScreen *vs, TwmWindow *tmp_win)
{
    XWindowAttributes winattrs;
    unsigned long     eventMask;

    if (vs && tmp_win->vs && tmp_win->vs != vs)
	return;
    if (tmp_win->UnmapByMovingFarAway) {
	XMoveWindow (dpy, tmp_win->frame, Scr->rootw + 1, Scr->rooth + 1);
    } else if (tmp_win->mapped) {
	XGetWindowAttributes(dpy, tmp_win->w, &winattrs);
	eventMask = winattrs.your_event_mask;
	XSelectInput (dpy, tmp_win->w, eventMask & ~StructureNotifyMask);
	XUnmapWindow (dpy, tmp_win->w);
	XUnmapWindow (dpy, tmp_win->frame);
	XSelectInput (dpy, tmp_win->w, eventMask);

	if (!tmp_win->DontSetInactive)
	SetMapStateProp (tmp_win, InactiveState);
    } else if (tmp_win->icon_on && tmp_win->icon && tmp_win->icon->w) {
	XUnmapWindow (dpy, tmp_win->icon->w);
	IconDown (tmp_win);
    }

    /*
     * XXX - this may need to be tweaked to find the real window at 0x0.
     * Most people will setup virtualscreens left to right, but some
     * may not.  The purpose of this is in the event of a ctwm death/restart,
     * geometries of windows that were on unmapped workspaces will show
     * up where they belong.
     * XXX - XReparentWindow() messes up the stacking order of windows.
     * It should be avoided as much as possible. This already affects
     * switching away from and back to a workspace. Therefore do this only
     * if there are at least 2 virtual screens AND the new one (firstvs)
     * differs from where the window currently is. (Olaf Seibert).
     */

    if (Scr->vScreenList && Scr->vScreenList->next) {
	int x, y;
	unsigned int junk;
	Window junkW, w = tmp_win->frame;
	virtualScreen *firstvs = NULL;

	for (firstvs = Scr->vScreenList; firstvs; firstvs = firstvs->next)
	    if (firstvs->x == 0 && firstvs->y == 0)
		break;
	if (firstvs && firstvs != vs) {
	    XGetGeometry (dpy, w, &junkW, &x, &y, &junk, &junk, &junk, &junk);
	    XReparentWindow(dpy, w, firstvs->window, x, y);
	    tmp_win->vs = firstvs;
	}
    }

    tmp_win->old_parent_vs = tmp_win->vs;
    tmp_win->vs = NULL;
}

static void DisplayWin (virtualScreen *vs, TwmWindow *tmp_win)
{
    XWindowAttributes	winattrs;
    unsigned long	eventMask;

    /*
     * A window cannot be shown in multiple virtual screens, even if
     * it occupies both corresponding workspaces.
     */
    if (vs && tmp_win->vs)
	return;
    tmp_win->vs = vs;

    if (!tmp_win->mapped) {
	if (tmp_win->isicon) {
	    if (tmp_win->icon_on) {
		if (tmp_win->icon && tmp_win->icon->w) {
		    if (vs && vs != tmp_win->old_parent_vs) {
			int x, y;
			unsigned int junk;
			Window junkW, w = tmp_win->icon->w;
			XGetGeometry (dpy, w, &junkW, &x, &y, &junk, &junk, &junk, &junk);
			XReparentWindow (dpy, w, vs->window, x, y);
		    }

		    IconUp (tmp_win);
		    XMapWindow (dpy, tmp_win->icon->w);
		    return;
		}
	    }
	}
	return;
    }
    if (tmp_win->UnmapByMovingFarAway) {
        if (vs)		/* XXX I don't believe the handling of UnmapByMovingFarAway is quite correct */
	    XReparentWindow (dpy, tmp_win->frame, vs->window,
		tmp_win->frame_x, tmp_win->frame_y);
	else
	    XMoveWindow (dpy, tmp_win->frame, tmp_win->frame_x, tmp_win->frame_y);
    } else {
	if (!tmp_win->squeezed) {
	    XGetWindowAttributes(dpy, tmp_win->w, &winattrs);
	    eventMask = winattrs.your_event_mask;
	    XSelectInput (dpy, tmp_win->w, eventMask & ~StructureNotifyMask);
	    XMapWindow   (dpy, tmp_win->w);
	    XSelectInput (dpy, tmp_win->w, eventMask);
	}
	if (vs && vs != tmp_win->old_parent_vs) {
	    XReparentWindow (dpy, tmp_win->frame, vs->window, tmp_win->frame_x, tmp_win->frame_y);
	}
	XMapWindow (dpy, tmp_win->frame);
	SetMapStateProp (tmp_win, NormalState);
    }
}

void ChangeOccupation (TwmWindow *tmp_win, int newoccupation)
{
    TwmWindow *t;
    virtualScreen *vs;
    WorkSpace *ws;
    int	      oldoccupation;
    char      namelist [512];
    int	      len;
    int	      final_x, final_y;
    XWindowAttributes winattrs;
    unsigned long     eventMask;
    long      gwkspc = 0; /* for gnome - the workspace of this window */
    int changedoccupation;
#ifdef GNOME
    unsigned char *prop;
    unsigned long bytesafter, numitems;
    Atom actual_type;
    int actual_format; 	
#endif /* GNOME */

    if ((newoccupation == 0) || /* in case the property has been broken by another client */
	(newoccupation == tmp_win->occupation)) {
	len = GetPropertyFromMask (tmp_win->occupation, namelist, &gwkspc);
	XGetWindowAttributes(dpy, tmp_win->w, &winattrs);
	eventMask = winattrs.your_event_mask;
	XSelectInput(dpy, tmp_win->w, eventMask & ~PropertyChangeMask);

	XChangeProperty (dpy, tmp_win->w, _XA_WM_OCCUPATION, XA_STRING, 8, 
			 PropModeReplace, (unsigned char *) namelist, len);
#ifdef GNOME
 	XChangeProperty (dpy, tmp_win->w, _XA_WIN_WORKSPACE, XA_CARDINAL, 32,
			 PropModeReplace, (unsigned char *)(&gwkspc), 1);
 	if (XGetWindowProperty(dpy, tmp_win->w, _XA_WIN_STATE, 0L, 32, False,
			      XA_CARDINAL, &actual_type, &actual_format, &numitems,
			      &bytesafter, &prop) 
	   != Success || numitems == 0) {
	    gwkspc = 0;
	} else {
	    gwkspc = (int)*prop;
	    XFree((char *)prop);
	}
 	if (tmp_win->occupation == fullOccupation)
	  gwkspc |= WIN_STATE_STICKY;
 	else
	  gwkspc &= ~WIN_STATE_STICKY;
 	XChangeProperty (dpy, tmp_win->w, _XA_WIN_STATE, XA_CARDINAL, 32, 
			 PropModeReplace, (unsigned char *)&gwkspc, 1); 
#endif /* GNOME */
	XSelectInput (dpy, tmp_win->w, eventMask);
	return;
    }
    oldoccupation = tmp_win->occupation;
    tmp_win->occupation = newoccupation & ~oldoccupation;
    AddIconManager (tmp_win);
    tmp_win->occupation = newoccupation;
    RemoveIconManager (tmp_win);

    if (tmp_win->vs && !OCCUPY (tmp_win, tmp_win->vs->wsw->currentwspc)) {
	Vanish (tmp_win->vs, tmp_win);
    }
    /*
     * If a window occupies multiple workspaces, try to find another workspace
     * which is currently in another virtual screen, so that the window
     * can be shown there now.
     */
    if (!tmp_win->vs) {
      for (vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
	if (OCCUPY (tmp_win, vs->wsw->currentwspc)) {
	  DisplayWin (vs, tmp_win);
	  break;
	}
      }
    }
    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	int mask = 1 << ws->number;
	if (oldoccupation & mask) {
	    if (!(newoccupation & mask)) {
		RemoveWindowFromRegion (tmp_win);
		if (PlaceWindowInRegion (tmp_win, &final_x, &final_y))
		    XMoveWindow (dpy, tmp_win->frame, final_x, final_y);
	    }
	    break;
	}
    }
    len = GetPropertyFromMask (newoccupation, namelist, &gwkspc);
    XGetWindowAttributes(dpy, tmp_win->w, &winattrs);
    eventMask = winattrs.your_event_mask;
    XSelectInput(dpy, tmp_win->w, eventMask & ~PropertyChangeMask);

    XChangeProperty (dpy, tmp_win->w, _XA_WM_OCCUPATION, XA_STRING, 8, 
		     PropModeReplace, (unsigned char *) namelist, len);

#ifdef GNOME
    /* Tell GNOME where this window lives */
    XChangeProperty (dpy, tmp_win->w, _XA_WIN_WORKSPACE, XA_CARDINAL, 32,
		     PropModeReplace, (unsigned char *)(&gwkspc), 1); 
    if (XGetWindowProperty (dpy, tmp_win->w, _XA_WIN_STATE, 0L, 32, False,
			    XA_CARDINAL, &actual_type, &actual_format, &numitems,
			    &bytesafter, &prop) 
	!= Success || numitems == 0) {
	gwkspc = 0;
    } else {
	gwkspc = (int)*prop;
	XFree ((char *)prop);
    }
    if (tmp_win->occupation == fullOccupation)
      gwkspc |= WIN_STATE_STICKY;
    else
      gwkspc &= ~WIN_STATE_STICKY;
    XChangeProperty (dpy, tmp_win->w, _XA_WIN_STATE, XA_CARDINAL, 32, 
		     PropModeReplace, (unsigned char *)&gwkspc, 1); 
#endif /* GNOME */
    XSelectInput(dpy, tmp_win->w, eventMask);

    if (!WMapWindowMayBeAdded(tmp_win)) {
	newoccupation = 0;
    }
    if (Scr->workSpaceMgr.noshowoccupyall) {
	/* We can safely change new/oldoccupation here, it's only used
	 * for WMapAddToList()/WMapRemoveFromList() from here on.
	 */
	/* if (newoccupation == fullOccupation)
	    newoccupation = 0; */
	if (oldoccupation == fullOccupation)
	    oldoccupation = 0;
    }
    changedoccupation = oldoccupation ^ newoccupation;
    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	int mask = 1 << ws->number;
	if (changedoccupation & mask) {
	    if (newoccupation & mask) {
		WMapAddToList (tmp_win, ws);
	    } else {
		WMapRemoveFromList (tmp_win, ws);
                if (Scr->SaveWorkspaceFocus && ws->save_focus == tmp_win) {
                    ws->save_focus = NULL;
                }
	    }
	}
    }

    if (! Scr->TransientHasOccupation) {
	for (t = Scr->FirstWindow; t != NULL; t = t->next) {
	    if (t != tmp_win &&
		((t->transient && t->transientfor == tmp_win->w) ||
		 t->group == tmp_win->w)) {
		ChangeOccupation (t, tmp_win->occupation);
	    }
	}
    }
}

void WmgrRedoOccupation (TwmWindow *win)
{
    WorkSpace *ws;
    int       newoccupation;

    if (LookInList (Scr->OccupyAll, win->full_name, &win->class)) {
	newoccupation = fullOccupation;
    }
    else {
	newoccupation = 0;
	for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	    if (LookInList (ws->clientlist, win->full_name, &win->class)) {
		newoccupation |= 1 << ws->number;
	    }
	}
    }
    if (newoccupation != 0) ChangeOccupation (win, newoccupation);
}

void WMgrRemoveFromCurrentWorkSpace (virtualScreen *vs, TwmWindow *win)
{
    WorkSpace *ws;
    int	      newoccupation;

    ws = vs->wsw->currentwspc;
    if (! OCCUPY (win, ws)) return;

    newoccupation = win->occupation & ~(1 << ws->number);
    if (newoccupation == 0) return;

    ChangeOccupation (win, newoccupation);
}

void WMgrAddToCurrentWorkSpaceAndWarp (virtualScreen *vs, char *winname)
{
    TwmWindow *tw;
    int       newoccupation;

    for (tw = Scr->FirstWindow; tw != NULL; tw = tw->next) {
	if (match (winname, tw->full_name)) break;
    }
    if (!tw) {
	for (tw = Scr->FirstWindow; tw != NULL; tw = tw->next) {
	    if (match (winname, tw->class.res_name)) break;
	}
	if (!tw) {
	    for (tw = Scr->FirstWindow; tw != NULL; tw = tw->next) {
		if (match (winname, tw->class.res_class)) break;
	    }
	}
    }
    if (!tw) {
	XBell (dpy, 0);
	return;
    }
    if ((! Scr->WarpUnmapped) && (! tw->mapped)) {
	XBell (dpy, 0);
	return;
    }
    if (! OCCUPY (tw, vs->wsw->currentwspc)) {
	newoccupation = tw->occupation | (1 << vs->wsw->currentwspc->number);
	ChangeOccupation (tw, newoccupation);
    }

    if (! tw->mapped) DeIconify (tw);
    WarpToWindow (tw, Scr->RaiseOnWarp);
}

static void CreateWorkSpaceManagerWindow (virtualScreen *vs)
{
    int		  mask;
    int		  lines, vspace, hspace, count, columns;
    unsigned int  width, height, bwidth, bheight;
    char	  *name, *icon_name, *geometry;
    int		  i, j;
    ColorPair	  cp;
    MyFont	  font;
    WorkSpace     *ws;
    int		  x, y, strWid, wid;
    unsigned long border;
    TwmWindow	  *tmp_win;
    XSetWindowAttributes	attr;
    XWindowAttributes		wattr;
    unsigned long		attrmask;
    XSizeHints	  sizehints;
    XWMHints	  wmhints;
    int		  gravity;
	XRectangle inc_rect;
	XRectangle logical_rect;

    name      = Scr->workSpaceMgr.name;
    icon_name = Scr->workSpaceMgr.icon_name;
    geometry  = Scr->workSpaceMgr.geometry;
    columns   = Scr->workSpaceMgr.columns;
    vspace    = Scr->workSpaceMgr.vspace;
    hspace    = Scr->workSpaceMgr.hspace;
    font      = Scr->workSpaceMgr.buttonFont;
    cp        = Scr->workSpaceMgr.cp;
    border    = Scr->workSpaceMgr.defBorderColor;

    count = 0;
    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) count++;
    Scr->workSpaceMgr.count = count;

    if (columns == 0) {
	lines   = 2;
	columns = ((count - 1) / lines) + 1;
    }
    else {
	lines   = ((count - 1) / columns) + 1;
    }
    Scr->workSpaceMgr.lines   = lines;
    Scr->workSpaceMgr.columns = columns;

    strWid = 0;
    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	XmbTextExtents(font.font_set, ws->label, strlen (ws->label),
		       &inc_rect, &logical_rect);
	wid = logical_rect.width;
	if (wid > strWid) strWid = wid;
    }
    if (geometry != NULL) {
	mask = XParseGeometry (geometry, &x, &y, &width, &height);
	bwidth  = (mask & WidthValue)  ? ((width - (columns * hspace)) / columns) : strWid + 10;
	bheight = (mask & HeightValue) ? ((height - (lines  * vspace)) / lines) : 22;
	width   = columns * (bwidth  + hspace);
	height  = lines   * (bheight + vspace);

	if (! (mask & YValue)) {
            y = 0;
	    mask |= YNegative;
	}
	if (mask & XValue) {
	    if (mask & XNegative) {
		x += vs->w - width;
		gravity = (mask & YNegative) ? SouthEastGravity : NorthEastGravity;
	    }
	    else {
		gravity = (mask & YNegative) ? SouthWestGravity : NorthWestGravity;
	    }
	}
	else {
	    x = (vs->w - width) / 2;
	    gravity = (mask & YValue) ? ((mask & YNegative) ?
			SouthGravity : NorthGravity) : SouthGravity;
	}
	if (mask & YNegative) y += vs->h - height;
    }
    else {
	bwidth  = strWid + 2 * Scr->WMgrButtonShadowDepth + 6;
	bheight = 22;
	width   = columns * (bwidth  + hspace);
	height  = lines   * (bheight + vspace);
	x       = (vs->w - width) / 2;
	y       = vs->h - height;
	gravity = NorthWestGravity;
    }

#define Dummy	1

    vs->wsw->width   = Dummy;
    vs->wsw->height  = Dummy;
    vs->wsw->bswl    = (ButtonSubwindow**)
      malloc (Scr->workSpaceMgr.count * sizeof (ButtonSubwindow*));
    vs->wsw->mswl    = (MapSubwindow**)
      malloc (Scr->workSpaceMgr.count * sizeof (MapSubwindow*));

    vs->wsw->w = XCreateSimpleWindow (dpy, Scr->Root, x, y, width, height, 0,
				      Scr->Black, cp.back);
    i = 0; j = 0;
    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
        Window mapsw, butsw;
        MapSubwindow *msw;
        ButtonSubwindow *bsw;

	vs->wsw->bswl [ws->number] = bsw = 
	    (ButtonSubwindow*) malloc (sizeof (ButtonSubwindow));
        vs->wsw->mswl [ws->number] = msw =
	    (MapSubwindow*)    malloc (sizeof (MapSubwindow));

        butsw = bsw->w =
	  XCreateSimpleWindow (dpy, vs->wsw->w,
			       Dummy /* x */, Dummy /* y */,
			       Dummy /* width */, Dummy /* height */,
			       0, Scr->Black, ws->cp.back);

	mapsw = msw->w =
	    XCreateSimpleWindow (dpy, vs->wsw->w,
				 Dummy /* x */, Dummy /* y */,
				 Dummy /* width */, Dummy /* height */,
				 1, border, ws->cp.back);

	if (vs->wsw->state == BUTTONSSTATE)
	    XMapWindow (dpy, butsw);
	else
	    XMapWindow (dpy, mapsw);

	vs->wsw->mswl [ws->number]->wl = NULL;
	if (useBackgroundInfo) {
	    if (ws->image == None || Scr->NoImagesInWorkSpaceManager)
		XSetWindowBackground       (dpy, mapsw, ws->backcp.back);
	    else
		XSetWindowBackgroundPixmap (dpy, mapsw, ws->image->pixmap);
	}
	else {
	    if (Scr->workSpaceMgr.defImage == None || Scr->NoImagesInWorkSpaceManager)
		XSetWindowBackground       (dpy, mapsw, Scr->workSpaceMgr.defColors.back);
	    else
		XSetWindowBackgroundPixmap (dpy, mapsw, Scr->workSpaceMgr.defImage->pixmap);
	}
	XClearWindow (dpy, butsw);
	i++;
	if (i == columns) {i = 0; j++;};
    }

    sizehints.flags       = USPosition | PBaseSize | PMinSize | PResizeInc | PWinGravity;
    sizehints.x           = x;
    sizehints.y           = y;
    sizehints.base_width  = columns * hspace;
    sizehints.base_height = lines   * vspace;
    sizehints.width_inc   = columns;
    sizehints.height_inc  = lines;
    sizehints.min_width   = columns  * (hspace + 2);
    sizehints.min_height  = lines    * (vspace + 2);
    sizehints.win_gravity = gravity;

    XSetStandardProperties (dpy, vs->wsw->w,
			name, icon_name, None, NULL, 0, NULL);
    XSetWMSizeHints (dpy, vs->wsw->w, &sizehints, XA_WM_NORMAL_HINTS);

    wmhints.flags	  = InputHint | StateHint;
    wmhints.input         = True;
    wmhints.initial_state = NormalState;
    XSetWMHints (dpy, vs->wsw->w, &wmhints);
    XSaveContext (dpy, vs->wsw->w, VirtScreenContext, (XPointer) vs);
    tmp_win = AddWindow (vs->wsw->w, 3, Scr->iconmgr);
    if (! tmp_win) {
	fprintf (stderr, "cannot create workspace manager window, exiting...\n");
	exit (1);
    }
    tmp_win->occupation = fullOccupation;
    tmp_win->vs = vs;
    tmp_win->attr.width = width;
    tmp_win->attr.height = height;
    ResizeWorkSpaceManager(vs, tmp_win);

    attrmask = 0;
    attr.cursor = Scr->ButtonCursor;
    attrmask |= CWCursor;
    attr.win_gravity = gravity;
    attrmask |= CWWinGravity;
    XChangeWindowAttributes (dpy, vs->wsw->w, attrmask, &attr);

    XGetWindowAttributes (dpy, vs->wsw->w, &wattr);
    attrmask = wattr.your_event_mask | KeyPressMask | KeyReleaseMask | ExposureMask;
    XSelectInput (dpy, vs->wsw->w, attrmask);

    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
        Window buttonw = vs->wsw->bswl [ws->number]->w;
        Window mapsubw = vs->wsw->mswl [ws->number]->w;
	XSelectInput (dpy, buttonw, ButtonPressMask | ButtonReleaseMask | ExposureMask);
	XSaveContext (dpy, buttonw, TwmContext,    (XPointer) tmp_win);
	XSaveContext (dpy, buttonw, ScreenContext, (XPointer) Scr);

	XSelectInput (dpy, mapsubw, ButtonPressMask | ButtonReleaseMask);
	XSaveContext (dpy, mapsubw, TwmContext,    (XPointer) tmp_win);
	XSaveContext (dpy, mapsubw, ScreenContext, (XPointer) Scr);
    }
    SetMapStateProp (tmp_win, WithdrawnState);
    vs->wsw->twm_win = tmp_win;

    ws = Scr->workSpaceMgr.workSpaceList;
    if (useBackgroundInfo && ! Scr->DontPaintRootWindow) {
	if (ws->image == None)
	    XSetWindowBackground (dpy, Scr->Root, ws->backcp.back);
	else
	    XSetWindowBackgroundPixmap (dpy, Scr->Root, ws->image->pixmap);
	XClearWindow (dpy, Scr->Root);
    }
    PaintWorkSpaceManager (vs);
}

void WMgrHandleExposeEvent (virtualScreen *vs, XEvent *event)
{
    WorkSpace *ws;
    Window buttonw;

    if (vs->wsw->state == BUTTONSSTATE) {
	for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	    buttonw = vs->wsw->bswl [ws->number]->w;
	    if (event->xexpose.window == buttonw) break;
	}
	if (ws == NULL) {
	  PaintWorkSpaceManagerBorder (vs);
	}
	else
	if (ws == vs->wsw->currentwspc)
	    PaintButton (WSPCWINDOW, vs, buttonw, ws->label, ws->cp, on);
	else
	    PaintButton (WSPCWINDOW, vs, buttonw, ws->label, ws->cp, off);
    }
    else {
	WinList	  wl;

        if (XFindContext (dpy, event->xexpose.window, MapWListContext,
		(XPointer *) &wl) == XCNOENT) return;
	if (wl && wl->twm_win && wl->twm_win->mapped) {
	    WMapRedrawName (vs, wl);
	}
    }
}

void PaintWorkSpaceManager (virtualScreen *vs)
{
    WorkSpace *ws;

    PaintWorkSpaceManagerBorder (vs);
    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
        Window buttonw = vs->wsw->bswl [ws->number]->w;
	if (ws == vs->wsw->currentwspc)
	    PaintButton (WSPCWINDOW, vs, buttonw, ws->label, ws->cp, on);
	else
	    PaintButton (WSPCWINDOW, vs, buttonw, ws->label, ws->cp, off);
    }
}

static void PaintWorkSpaceManagerBorder (virtualScreen *vs)
{
    int width, height;

    width  = vs->wsw->width;
    height = vs->wsw->height;
    Draw3DBorder (vs->wsw->w, 0, 0, width, height, 2, Scr->workSpaceMgr.cp, off, True, False);
}

ColorPair occupyButtoncp;

char *ok_string		= "OK",
     *cancel_string	= "Cancel",
     *everywhere_string	= "All";

/*
 * Create the Occupy window. Do not do the layout of the parts, only
 * calculate the inial total size. For the layout, call ResizeOccupyWindow()
 * at the end.
 * There is only one Occupy window (per Screen), it is reparented to each
 * virtual screen as needed.
 */
static void CreateOccupyWindow (void) {
    int		  width, height, lines, columns;
    int		  bwidth, bheight, owidth, oheight, hspace, vspace;
    int		  min_bwidth, min_width;
    int		  i, j;
    Window	  w, OK, cancel, allworkspc;
    char	  *name, *icon_name;
    ColorPair	  cp;
    TwmWindow	  *tmp_win;
    WorkSpace     *ws;
    XSizeHints	  sizehints;
    XWMHints      wmhints;
    MyFont	  font;
    XSetWindowAttributes attr;
    XWindowAttributes	 wattr;
    unsigned long attrmask;
    OccupyWindow  *occwin;
    virtualScreen *vs;
    XRectangle inc_rect;
    XRectangle logical_rect;

    occwin = Scr->workSpaceMgr.occupyWindow;
    occwin->font     = Scr->IconManagerFont;
    occwin->cp       = Scr->IconManagerC;
#ifdef COLOR_BLIND_USER
    occwin->cp.shadc = Scr->White;
    occwin->cp.shadd = Scr->Black;
#else
    if (!Scr->BeNiceToColormap) GetShadeColors (&occwin->cp);
#endif
    vs        = Scr->vScreenList;
    name      = occwin->name;
    icon_name = occwin->icon_name;
    lines     = Scr->workSpaceMgr.lines;
    columns   = Scr->workSpaceMgr.columns;
    bwidth    = vs->wsw->bwidth;
    bheight   = vs->wsw->bheight;
    oheight   = bheight;
    vspace    = occwin->vspace;
    hspace    = occwin->hspace;
    cp        = occwin->cp;
    height    = ((bheight + vspace) * lines) + oheight + (2 * vspace);
    font      = occwin->font;
    XmbTextExtents(font.font_set, ok_string, strlen(ok_string),
		       &inc_rect, &logical_rect);
    min_bwidth = logical_rect.width;
    XmbTextExtents(font.font_set, cancel_string, strlen (cancel_string),
		   &inc_rect, &logical_rect);
    i = logical_rect.width;
    if (i > min_bwidth) min_bwidth = i;
    XmbTextExtents(font.font_set,everywhere_string, strlen (everywhere_string),
		   &inc_rect, &logical_rect);
    i = logical_rect.width;
    if (i > min_bwidth) min_bwidth = i;
    min_bwidth = (min_bwidth + hspace); /* normal width calculation */
    width = columns * (bwidth  + hspace); 
    min_width = 3 * (min_bwidth + hspace); /* width by text width */

    if (columns < 3) {
	owidth = min_bwidth + 2 * Scr->WMgrButtonShadowDepth + 2;
	if (width < min_width) width = min_width;
	bwidth = (width - columns * hspace) / columns;
    }
    else {
	owidth = min_bwidth + 2 * Scr->WMgrButtonShadowDepth + 2;
	width  = columns * (bwidth  + hspace);
    }
    occwin->lines   = lines;
    occwin->columns = columns;
    occwin->owidth  = owidth;

    w = occwin->w = XCreateSimpleWindow (dpy, Scr->Root, 0, 0, width, height,
					 1, Scr->Black, cp.back);
    occwin->obuttonw = (Window*) malloc (Scr->workSpaceMgr.count * sizeof (Window));
    i = 0; j = 0;
    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	Window bw =
	occwin->obuttonw [j * columns + i] =
	  XCreateSimpleWindow(dpy, w,
			      Dummy /* x */,
			      Dummy /* y */,
			      Dummy /* width */,
			      Dummy /* height */,
			      0, Scr->Black, ws->cp.back);
	XMapWindow (dpy, bw);
	i++;
	if (i == columns) {i = 0; j++;}
    }
    GetColor (Scr->Monochrome, &(occupyButtoncp.back), "gray50");
    occupyButtoncp.fore = Scr->White;
    if (!Scr->BeNiceToColormap) GetShadeColors (&occupyButtoncp);

    OK = XCreateSimpleWindow (dpy, w, Dummy, Dummy, Dummy, Dummy, 0,
			Scr->Black, occupyButtoncp.back);
    XMapWindow (dpy, OK);
    cancel = XCreateSimpleWindow (dpy, w, Dummy, Dummy, Dummy, Dummy, 0,
			Scr->Black, occupyButtoncp.back);
    XMapWindow (dpy, cancel);
    allworkspc = XCreateSimpleWindow (dpy, w, Dummy, Dummy, Dummy, Dummy, 0,
			Scr->Black, occupyButtoncp.back);
    XMapWindow (dpy, allworkspc);

    occwin->OK         = OK;
    occwin->cancel     = cancel;
    occwin->allworkspc = allworkspc;

    sizehints.flags       = PBaseSize | PMinSize | PResizeInc;
    sizehints.base_width  = columns;
    sizehints.base_height = lines;
    sizehints.width_inc   = columns;
    sizehints.height_inc  = lines;
    sizehints.min_width   = 2 * columns;
    sizehints.min_height  = 2 * lines;
    XSetStandardProperties (dpy, w, name, icon_name, None, NULL, 0, &sizehints);

    wmhints.flags	  = InputHint | StateHint;
    wmhints.input         = True;
    wmhints.initial_state = NormalState;
    XSetWMHints (dpy, w, &wmhints);
    tmp_win = AddWindow (w, FALSE, Scr->iconmgr);
    if (! tmp_win) {
	fprintf (stderr, "cannot create occupy window, exiting...\n");
	exit (1);
    }
    tmp_win->vs = None;
    tmp_win->occupation = 0;

    attrmask = 0;
    attr.cursor = Scr->ButtonCursor;
    attrmask |= CWCursor;
    XChangeWindowAttributes (dpy, w, attrmask, &attr);

    XGetWindowAttributes (dpy, w, &wattr);
    attrmask = wattr.your_event_mask | KeyPressMask | KeyReleaseMask | ExposureMask;
    XSelectInput (dpy, w, attrmask);

    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
        Window bw = occwin->obuttonw [ws->number];
	XSelectInput (dpy, bw, ButtonPressMask | ButtonReleaseMask | ExposureMask);
	XSaveContext (dpy, bw, TwmContext,    (XPointer) tmp_win);
	XSaveContext (dpy, bw, ScreenContext, (XPointer) Scr);
    }
    XSelectInput (dpy, occwin->OK, ButtonPressMask | ButtonReleaseMask | ExposureMask);
    XSaveContext (dpy, occwin->OK, TwmContext,    (XPointer) tmp_win);
    XSaveContext (dpy, occwin->OK, ScreenContext, (XPointer) Scr);
    XSelectInput (dpy, occwin->cancel, ButtonPressMask | ButtonReleaseMask | ExposureMask);
    XSaveContext (dpy, occwin->cancel, TwmContext,    (XPointer) tmp_win);
    XSaveContext (dpy, occwin->cancel, ScreenContext, (XPointer) Scr);
    XSelectInput (dpy, occwin->allworkspc, ButtonPressMask | ButtonReleaseMask | ExposureMask);
    XSaveContext (dpy, occwin->allworkspc, TwmContext,    (XPointer) tmp_win);
    XSaveContext (dpy, occwin->allworkspc, ScreenContext, (XPointer) Scr);

    SetMapStateProp (tmp_win, WithdrawnState);
    occwin->twm_win = tmp_win;
    Scr->workSpaceMgr.occupyWindow = occwin;

    tmp_win->attr.width = width;
    tmp_win->attr.height = height;
    ResizeOccupyWindow(tmp_win);	/* place all parts in the right place */
}

void PaintOccupyWindow (void)
{
    WorkSpace    *ws;
    OccupyWindow *occwin;
    int 	 width, height;

    occwin = Scr->workSpaceMgr.occupyWindow;
    width  = occwin->width;
    height = occwin->height;

    Draw3DBorder (occwin->w, 0, 0, width, height, 2, occwin->cp, off, True, False);

    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
        Window bw = occwin->obuttonw [ws->number];
	if (occwin->tmpOccupation & (1 << ws->number))
	    PaintButton (OCCUPYWINDOW, NULL, bw, ws->label, ws->cp, on);
	else
	    PaintButton (OCCUPYWINDOW, NULL, bw, ws->label, ws->cp, off);
    }
    PaintButton (OCCUPYBUTTON, NULL, occwin->OK,         ok_string,         occupyButtoncp, off);
    PaintButton (OCCUPYBUTTON, NULL, occwin->cancel,     cancel_string,     occupyButtoncp, off);
    PaintButton (OCCUPYBUTTON, NULL, occwin->allworkspc, everywhere_string, occupyButtoncp, off);
}

static void PaintButton (int which,
			 virtualScreen *vs, Window w,
			 char *label,
			 ColorPair cp, int state)
{
    OccupyWindow *occwin;
    int        bwidth, bheight;
    MyFont     font;
    int        strWid, strHei, hspace, vspace;
    XRectangle inc_rect;
    XRectangle logical_rect;

    occwin = Scr->workSpaceMgr.occupyWindow;
    if (which == WSPCWINDOW) {
	bwidth  = vs->wsw->bwidth;
	bheight = vs->wsw->bheight;
	font    = Scr->workSpaceMgr.buttonFont;
    }
    else
    if (which == OCCUPYWINDOW) {
	bwidth  = occwin->bwidth;
	bheight = occwin->bheight;
	font    = occwin->font;
    }
    else
    if (which == OCCUPYBUTTON) {
	bwidth  = occwin->owidth;
	bheight = occwin->bheight;
	font    = occwin->font;
    }
    else return;

    XmbTextExtents(font.font_set, label, strlen (label), &inc_rect, &logical_rect);
    strHei = logical_rect.height;
    vspace = ((bheight + strHei - font.descent) / 2);
    strWid = logical_rect.width;
    hspace = (bwidth - strWid) / 2;
    if (hspace < (Scr->WMgrButtonShadowDepth + 1)) hspace = Scr->WMgrButtonShadowDepth + 1;
    XClearWindow (dpy, w);

    if (Scr->Monochrome == COLOR) {
	Draw3DBorder (w, 0, 0, bwidth, bheight, Scr->WMgrButtonShadowDepth,
			cp, state, True, False);

	switch (Scr->workSpaceMgr.buttonStyle) {
	    case STYLE_NORMAL :
		break;

	    case STYLE_STYLE1 :
		Draw3DBorder (w,
			Scr->WMgrButtonShadowDepth - 1,
			Scr->WMgrButtonShadowDepth - 1,
			bwidth  - 2 * Scr->WMgrButtonShadowDepth + 2,
			bheight - 2 * Scr->WMgrButtonShadowDepth + 2,
			1, cp, (state == on) ? off : on, True, False);
		break;

	    case STYLE_STYLE2 :
		Draw3DBorder (w,
			Scr->WMgrButtonShadowDepth / 2,
			Scr->WMgrButtonShadowDepth / 2,
			bwidth  - Scr->WMgrButtonShadowDepth,
			bheight - Scr->WMgrButtonShadowDepth,
			1, cp, (state == on) ? off : on, True, False);
		break;

	    case STYLE_STYLE3 :
		Draw3DBorder (w,
			1,
			1,
			bwidth  - 2,
			bheight - 2,
			1, cp, (state == on) ? off : on, True, False);
		break;
	}
	FB (cp.fore, cp.back);
	XmbDrawString (dpy, w, font.font_set, Scr->NormalGC, hspace, vspace,
		       label, strlen (label));
    }
    else {
	Draw3DBorder (w, 0, 0, bwidth, bheight, Scr->WMgrButtonShadowDepth,
		cp, state, True, False);
	if (state == on) {
	    FB (cp.fore, cp.back);
	    XmbDrawImageString (dpy, w, font.font_set, Scr->NormalGC, hspace, vspace,
				label, strlen (label));
	}
	else {
	    FB (cp.back, cp.fore);
	    XmbDrawImageString (dpy, w, font.font_set, Scr->NormalGC, hspace, vspace,
				label, strlen (label));
	}
    }
}

static unsigned int GetMaskFromResource (TwmWindow *win, char *res)
{
    char      *name;
    char      wrkSpcName [64];
    WorkSpace *ws;
    int       mask, num, mode;

    mode = 0;
    if (*res == '+') {
	mode = 1;
	res++;
    }
    else
    if (*res == '-') {
	mode = 2;
	res++;
    }
    mask = 0;
    while (*res != '\0') {
	while (*res == ' ') res++;
	if (*res == '\0') break;
	name = wrkSpcName;
	while ((*res != '\0') && (*res != ' ')) {
	    if (*res == '\\') res++;
	    *name = *res;
	    name++; res++;
	}
	*name = '\0';
	if (strcmp (wrkSpcName, "all") == 0) {
	    mask = fullOccupation;
	    break;
	}
	if (strcmp (wrkSpcName, "current") == 0) {
	    virtualScreen *vs = Scr->currentvs;
	    if (vs) mask |= (1 << vs->wsw->currentwspc->number);
	    continue;
	}
	num  = 0;
	for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	    if (strcmp (wrkSpcName, ws->label) == 0) break;
	    num++;
	}
	if (ws != NULL) mask |= (1 << num);
	else {
	    twmrc_error_prefix ();
	    fprintf (stderr, "unknown workspace : %s\n", wrkSpcName);
	}
    }
    switch (mode) {
	case 0 :
	    return (mask);
	case 1 :
	    return (mask | win->occupation);
	case 2 :
	    return (win->occupation & ~mask);
    }
    return (0);			/* Never supposed to reach here, but just
				   in case... */
}

unsigned int GetMaskFromProperty (unsigned char *prop, unsigned long len)
{
    char         wrkSpcName [256];
    WorkSpace    *ws;
    unsigned int mask;
    int          num, l;

    mask = 0;
    l = 0;
    while (l < len) {
	strcpy (wrkSpcName, (char *)prop);
	l    += strlen ((char *)prop) + 1;
	prop += strlen ((char *)prop) + 1;
	if (strcmp (wrkSpcName, "all") == 0) {
	    mask = fullOccupation;
	    break;
	}
	num = 0;
	for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	    if (strcmp (wrkSpcName, ws->label) == 0) break;
	    num++;
	}
	if (ws == NULL) {
	    fprintf (stderr, "unknown workspace : %s\n", wrkSpcName);
	}
	else {
	    mask |= (1 << num);
	}
    }
    return (mask);
}

static int GetPropertyFromMask (unsigned int mask, char *prop, long *gwkspc)
{
    WorkSpace *ws;
    int       len;
    char      *p;

    if (mask == fullOccupation) {
	strcpy (prop, "all");
	return (3);
    }
    len = 0;
    p = prop;
    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	if (mask & (1 << ws->number)) {
	    strcpy (p, ws->label);
	    p   += strlen (ws->label) + 1;
	    len += strlen (ws->label) + 1;
	    *gwkspc = ws->number;
	}
    }
    return (len);
}

void AddToClientsList (char *workspace, char *client)
{
    WorkSpace *ws;

    if (strcmp (workspace, "all") == 0) {
	for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	    AddToList (&ws->clientlist, client, "");
	}
	return;
    }

    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	if (strcmp (ws->label, workspace) == 0) break;
    }
    if (ws == NULL) return;
    AddToList (&ws->clientlist, client, "");
}

void WMapToggleState (virtualScreen *vs)
{
    if (vs->wsw->state == BUTTONSSTATE) {
	WMapSetMapState (vs);
    } else {
	WMapSetButtonsState (vs);
    }
}

void WMapSetMapState (virtualScreen *vs)
{
    WorkSpace     *ws;

    if (vs->wsw->state == MAPSTATE) return;
    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	XUnmapWindow (dpy, vs->wsw->bswl [ws->number]->w);
	XMapWindow   (dpy, vs->wsw->mswl [ws->number]->w);
    }
    vs->wsw->state = MAPSTATE;
    MaybeAnimate = True;
}

void WMapSetButtonsState (virtualScreen *vs)
{
    WorkSpace     *ws;

    if (vs->wsw->state == BUTTONSSTATE) return;
    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	XUnmapWindow (dpy, vs->wsw->mswl [ws->number]->w);
	XMapWindow   (dpy, vs->wsw->bswl [ws->number]->w);
    }
    vs->wsw->state = BUTTONSSTATE;
}

/*
 * Verify if a window may be added to the workspace map.
 * This is not allowed for
 * - icon managers
 * - the occupy window
 * - workspace manager windows
 * - or, optionally, windows with full occupation.
 */
int WMapWindowMayBeAdded(TwmWindow *win)
{
    if (win->iconmgr)
	return 0;
    if (win == Scr->workSpaceMgr.occupyWindow->twm_win)
	return 0;
    if (win->wspmgr)
	return 0;
    if (Scr->workSpaceMgr.noshowoccupyall &&
	win->occupation == fullOccupation)
	return 0;
    return 1;
}

void WMapAddWindow (TwmWindow *win)
{
    WorkSpace     *ws;

    if (!WMapWindowMayBeAdded(win))
	return;

    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	if (OCCUPY (win, ws)) WMapAddToList (win, ws);
    }
}

void WMapDestroyWindow (TwmWindow *win)
{
    WorkSpace *ws;

    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	if (OCCUPY (win, ws)) WMapRemoveFromList (win, ws);
    }
    if (win == occupyWin) {
	OccupyWindow *occwin = Scr->workSpaceMgr.occupyWindow;
	XUnmapWindow (dpy, occwin->twm_win->frame);
	occwin->twm_win->mapped = FALSE;
	occwin->twm_win->occupation = 0;
	occupyWin = (TwmWindow*) 0;
    }
}

void WMapMapWindow (TwmWindow *win)
{
    virtualScreen *vs;
    WorkSpace *ws;
    WinList   wl;

    for (vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
      for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	for (wl = vs->wsw->mswl [ws->number]->wl; wl != NULL; wl = wl->next) {
	    if (wl->twm_win == win) {
		XMapWindow (dpy, wl->w);
		WMapRedrawName (vs, wl);
		break;
	    }
	}
      }
    }
}

void WMapSetupWindow (TwmWindow *win, int x, int y, int w, int h)
{
    virtualScreen *vs;
    WorkSpace     *ws;
    WinList	  wl;
    float	  wf, hf;

    if (win->iconmgr) return;
    if (!win->vs) return;

    if (win->wspmgr) {
	if (w == -1) return;
	ResizeWorkSpaceManager (win->vs, win);
	return;
    }
    if (win == Scr->workSpaceMgr.occupyWindow->twm_win) {
	if (w == -1) return;
	ResizeOccupyWindow (win);
	return;
    }
    for (vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
      WorkSpaceWindow *wsw = vs->wsw;
      wf = (float) (wsw->wwidth  - 2) / (float) vs->w;
      hf = (float) (wsw->wheight - 2) / (float) vs->h;
      for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	for (wl = wsw->mswl [ws->number]->wl; wl != NULL; wl = wl->next) {
	    if (win == wl->twm_win) {
		wl->x = (int) (x * wf);
		wl->y = (int) (y * hf);
		if (w == -1) {
		    XMoveWindow (dpy, wl->w, wl->x, wl->y);
		}
		else {
		    wl->width  = (unsigned int) ((w * wf) + 0.5);
		    wl->height = (unsigned int) ((h * hf) + 0.5);
		    if (!Scr->use3Dwmap) {
			wl->width  -= 2;
			wl->height -= 2;
		    }
		    if (wl->width  < 1) wl->width  = 1;
		    if (wl->height < 1) wl->height = 1;
		    XMoveResizeWindow (dpy, wl->w, wl->x, wl->y, wl->width, wl->height);
		}
		break;
	    }
	}
      }
    }
}

void WMapIconify (TwmWindow *win)
{
    WorkSpace *ws;
    WinList    wl;

    if (!win->vs) return;
    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	for (wl = win->vs->wsw->mswl [ws->number]->wl; wl != NULL; wl = wl->next) {
	    if (win == wl->twm_win) {
		XUnmapWindow (dpy, wl->w);
		break;
	    }
	}
    }
}

void WMapDeIconify (TwmWindow *win)
{
    WorkSpace *ws;
    WinList    wl;

    if (!win->vs) return;
    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	for (wl = win->vs->wsw->mswl [ws->number]->wl; wl != NULL; wl = wl->next) {
	    if (win == wl->twm_win) {
		if (Scr->NoRaiseDeicon)
		    XMapWindow (dpy, wl->w);
		else
		    XMapRaised (dpy, wl->w);
		WMapRedrawName (win->vs, wl);
		break;
	    }
	}
    }
}

void WMapRaiseLower (TwmWindow *win)
{
    WorkSpace *ws;

    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	if (OCCUPY (win, ws)) WMapRestack (ws);
    }
}

void WMapLower (TwmWindow *win)
{
    WorkSpace *ws;

    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	if (OCCUPY (win, ws)) WMapRestack (ws);
    }
}

void WMapRaise (TwmWindow *win)
{
    WorkSpace *ws;

    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	if (OCCUPY (win, ws)) WMapRestack (ws);
    }
}

void WMapRestack (WorkSpace *ws)
{
    virtualScreen *vs;
    TwmWindow	*win;
    WinList	wl;
    Window	root;
    Window	parent;
    Window	*children, *smallws;
    unsigned int number;
    int		i, j;

    number = 0;
    XQueryTree (dpy, Scr->Root, &root, &parent, &children, &number);
    smallws = (Window*) malloc (number * sizeof (Window));

    for (vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
      j = 0;
      for (i = number - 1; i >= 0; i--) {
	if (!(win = GetTwmWindow(children [i]))) {
	    continue;
	}
	if (win->frame != children [i]) continue; /* skip icons */
	if (! OCCUPY (win, ws)) continue;
	if (tracefile) {
	    fprintf (tracefile, "WMapRestack : w = %lx, win = %p\n", children [i], (void *)win);
	    fflush (tracefile);
	}
	for (wl = vs->wsw->mswl [ws->number]->wl; wl != NULL; wl = wl->next) {
	if (tracefile) {
	    fprintf (tracefile, "WMapRestack : wl = %p, twm_win = %p\n", (void *)wl, (void *)wl->twm_win);
	    fflush (tracefile);
	}
	    if (win == wl->twm_win) {
		smallws [j++] = wl->w;
		break;
	    }
	}
      }
      XRestackWindows (dpy, smallws, j);
    }
    XFree ((char *) children);
    free  (smallws);
    return;
}

void WMapUpdateIconName (TwmWindow *win)
{
    virtualScreen *vs;
    WorkSpace *ws;
    WinList   wl;

    for (vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
      for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	for (wl = vs->wsw->mswl [ws->number]->wl; wl != NULL; wl = wl->next) {
	    if (win == wl->twm_win) {
		WMapRedrawName (vs, wl);
		break;
	    }
	}
      }
    }
}

void WMgrHandleKeyReleaseEvent (virtualScreen *vs, XEvent *event)
{
    char	*keyname;
    KeySym	keysym;

    keysym  = XLookupKeysym ((XKeyEvent*) event, 0);
    if (! keysym) return;
    keyname = XKeysymToString (keysym);
    if (! keyname) return;
    if ((strcmp (keyname, "Control_R") == 0) || 
	(strcmp (keyname, "Control_L") == 0)) 
      {
	/* DontToggleWorkSpaceManagerState added 20040607 by dl*/
	if (!donttoggleworkspacemanagerstate)
	  {
	    WMapToggleState (vs);
	  }
	return;
      }
}

void WMgrHandleKeyPressEvent (virtualScreen *vs, XEvent *event)
{
    WorkSpace *ws;
    int	      len, i, lname;
    char      key [16];
    unsigned char k;
    char      name [128];
    char      *keyname;
    KeySym    keysym;

    keysym  = XLookupKeysym   ((XKeyEvent*) event, 0);
    if (! keysym) return;
    keyname = XKeysymToString (keysym);
    if (! keyname) return;
    if ((strcmp (keyname, "Control_R") == 0) || 
	(strcmp (keyname, "Control_L") == 0)) 
      {
	/* DontToggleWorkSpaceManagerState added 20040607 by dl*/
	if (!donttoggleworkspacemanagerstate)
	  {
	    WMapToggleState (vs);
	  }
	return;
    }
    if (vs->wsw->state == MAPSTATE) return;

    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	if (vs->wsw->bswl [ws->number]->w == event->xkey.subwindow) break;
    }
    if (ws == NULL) return;

    strcpy (name, ws->label);
    lname = strlen (name);
    len   = XLookupString (&(event->xkey), key, 16, NULL, NULL);
    for (i = 0; i < len; i++) {
        k = key [i];
	if (isprint (k)) {
	    name [lname++] = k;
	}
	else
	if ((k == 127) || (k == 8)) {
	    if (lname != 0) lname--;
	}
	else
	    break;
    }
    name [lname] = '\0';
    ws->label = realloc (ws->label, (strlen (name) + 1));
    strcpy (ws->label, name);
    if (ws == vs->wsw->currentwspc)
	PaintButton (WSPCWINDOW, vs, vs->wsw->bswl [ws->number]->w, ws->label, ws->cp,  on);
    else
	PaintButton (WSPCWINDOW, vs, vs->wsw->bswl [ws->number]->w, ws->label, ws->cp, off);
}

void WMgrHandleButtonEvent (virtualScreen *vs, XEvent *event)
{
    WorkSpaceWindow	*mw;
    WorkSpace		*ws, *oldws, *newws, *cws;
    WinList		wl;
    TwmWindow		*win;
    int			occupation;
    unsigned int	W0, H0, bw;
    int			cont;
    XEvent		ev;
    Window		w, sw, parent;
    int			X0, Y0, X1, Y1, XW, YW, XSW, YSW;
    Position		newX = 0, newY = 0, winX = 0, winY = 0;
    Window		junkW;
    unsigned int	junk;
    unsigned int	button;
    unsigned int	modifier;
    XSetWindowAttributes attrs;
    float		wf, hf;
    Boolean		alreadyvivible, realmovemode, startincurrent;
    Time		etime;

    parent   = event->xbutton.window;
    sw       = event->xbutton.subwindow;
    mw       = vs->wsw;
    button   = event->xbutton.button;
    modifier = event->xbutton.state;
    etime    = event->xbutton.time;

    if (vs->wsw->state == BUTTONSSTATE) {
	for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	    if (vs->wsw->bswl [ws->number]->w == parent) break;
	}
	if (ws == NULL) return;
	GotoWorkSpace (vs, ws);
	return;
    }

    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	if (vs->wsw->mswl [ws->number]->w == parent) break;
    }
    if (ws == NULL) return;
    if (sw == (Window) 0) {
	GotoWorkSpace (vs, ws);
	return;
    }
    oldws = ws;

    if (XFindContext (dpy, sw, MapWListContext, (XPointer *) &wl) == XCNOENT) return;
    win = wl->twm_win;
    if ((! Scr->TransientHasOccupation) && win->transient) return;

    XTranslateCoordinates (dpy, Scr->Root, sw, event->xbutton.x_root, event->xbutton.y_root,
				&XW, &YW, &junkW);
    realmovemode = ( Scr->ReallyMoveInWorkspaceManager && !(modifier & ShiftMask)) ||
		   (!Scr->ReallyMoveInWorkspaceManager &&  (modifier & ShiftMask));
    startincurrent = (oldws == vs->wsw->currentwspc);
    if (win->OpaqueMove) {
	int sw2, ss;

	sw2 = win->frame_width * win->frame_height;
	ss = vs->w * vs->h;
	if (sw2 > ((ss * Scr->OpaqueMoveThreshold) / 100))
	    Scr->OpaqueMove = FALSE;
	else
	    Scr->OpaqueMove = TRUE;
    } else {
	Scr->OpaqueMove = FALSE;
    }
    switch (button) {
	case 1 :
	    XUnmapWindow (dpy, sw);
	    break;
	case 2 :
	    XGetGeometry (dpy, sw, &junkW, &X0, &Y0, &W0, &H0, &bw, &junk);
	    XTranslateCoordinates (dpy, vs->wsw->mswl [oldws->number]->w,
				mw->w, X0, Y0, &X1, &Y1, &junkW);

	    attrs.event_mask       = ExposureMask;
	    attrs.background_pixel = wl->cp.back;
	    attrs.border_pixel     = wl->cp.back;
	    w = XCreateWindow (dpy, mw->w, X1, Y1, W0, H0, bw,
				CopyFromParent,
				(unsigned int) CopyFromParent,
				(Visual *) CopyFromParent,
				CWEventMask | CWBackPixel | CWBorderPixel, &attrs);

	    XMapRaised (dpy, w);
	    WMapRedrawWindow (w, W0, H0, wl->cp, wl->twm_win->icon_name);
	    if (realmovemode && Scr->ShowWinWhenMovingInWmgr) {
		if (Scr->OpaqueMove) {
		    DisplayWin (vs, win);
		} else {
		    MoveOutline (Scr->Root,
			win->frame_x - win->frame_bw,
			win->frame_y - win->frame_bw,
			win->frame_width  + 2 * win->frame_bw,
			win->frame_height + 2 * win->frame_bw,
			win->frame_bw,
			win->title_height + win->frame_bw3D);
		}
	    }
	    break;

	case 3 :
	    occupation = win->occupation & ~(1 << oldws->number);
	    ChangeOccupation (win, occupation);
	    return;
	default :
	    return;
    }

    wf = (float) (mw->wwidth  - 1) / (float) vs->w;
    hf = (float) (mw->wheight - 1) / (float) vs->h;
    XGrabPointer (dpy, mw->w, False, ButtonPressMask | ButtonMotionMask | ButtonReleaseMask,
		  GrabModeAsync, GrabModeAsync, mw->w, Scr->MoveCursor, CurrentTime);

    alreadyvivible = False;
    cont = TRUE;
    while (cont) {
        MapSubwindow *msw;
	XMaskEvent (dpy, ButtonPressMask | ButtonMotionMask |
			 ButtonReleaseMask | ExposureMask, &ev);
	switch (ev.xany.type) {
	    case ButtonPress :
	    case ButtonRelease :
		if (ev.xbutton.button != button) break;
		cont = FALSE;
		newX = ev.xbutton.x;
		newY = ev.xbutton.y;

	    case MotionNotify :
		if (cont) {
		    newX = ev.xmotion.x;
		    newY = ev.xmotion.y;
		}
		if (realmovemode) {
		    for (cws = Scr->workSpaceMgr.workSpaceList;
			 cws != NULL;
			 cws = cws->next) {
		        msw = vs->wsw->mswl [cws->number];
			if ((newX >= msw->x) && (newX <  msw->x + mw->wwidth) &&
			    (newY >= msw->y) && (newY <  msw->y + mw->wheight)) {
			    break;
			}
		    }
		    if (!cws) break;
		    winX = newX - XW;
		    winY = newY - YW;
		    msw = vs->wsw->mswl [cws->number];
		    XTranslateCoordinates (dpy, mw->w, msw->w,
					winX, winY, &XSW, &YSW, &junkW);
		    winX = (int) (XSW / wf);
		    winY = (int) (YSW / hf);
		    if (Scr->DontMoveOff) {
			int width = win->frame_width;
			int height = win->frame_height;

			if ((winX < Scr->BorderLeft) && ((Scr->MoveOffResistance < 0) ||
                             (winX > Scr->BorderLeft - Scr->MoveOffResistance))) {
			    winX = Scr->BorderLeft;
			    newX = msw->x + XW + Scr->BorderLeft * mw->wwidth / vs->w;
			}
			if (((winX + width) > vs->w - Scr->BorderRight) &&
			    ((Scr->MoveOffResistance < 0) ||
			     ((winX + width) < vs->w - Scr->BorderRight + Scr->MoveOffResistance))) {
			    winX = vs->w - Scr->BorderRight - width;
			    newX = msw->x + mw->wwidth *
                                (1 - Scr->BorderRight / (double) vs->w) - wl->width + XW - 2;
			}
			if ((winY < Scr->BorderTop) && ((Scr->MoveOffResistance < 0) ||
                             (winY > Scr->BorderTop - Scr->MoveOffResistance))) {
			    winY = Scr->BorderTop;
			    newY = msw->y + YW + Scr->BorderTop * mw->height / vs->h;
			}
			if (((winY + height) > vs->h - Scr->BorderBottom) &&
			    ((Scr->MoveOffResistance < 0) ||
                             ((winY + height) < vs->h - Scr->BorderBottom + Scr->MoveOffResistance))) {
			    winY = vs->h - Scr->BorderBottom - height;
			    newY = msw->y + mw->wheight *
                                (1 - Scr->BorderBottom / (double) vs->h) - wl->height + YW - 2;
			}
		    }
		    WMapSetupWindow (win, winX, winY, -1, -1);
		    if (Scr->ShowWinWhenMovingInWmgr) goto movewin;
		    if (cws == vs->wsw->currentwspc) {
			if (alreadyvivible) goto movewin;
			if (Scr->OpaqueMove) {
			    XMoveWindow (dpy, win->frame, winX, winY);
			    DisplayWin (vs, win);
			} else {
			    MoveOutline (Scr->Root,
				winX - win->frame_bw, winY - win->frame_bw,
				win->frame_width  + 2 * win->frame_bw,
				win->frame_height + 2 * win->frame_bw,
				win->frame_bw,
				win->title_height + win->frame_bw3D);
			}
			alreadyvivible = True;
			goto move;
		    }
		    if (!alreadyvivible) goto move;
		    if (!OCCUPY (win, vs->wsw->currentwspc) ||
			(startincurrent && (button == 1))) {
			if (Scr->OpaqueMove) {
			    Vanish (vs, win);
			    XMoveWindow (dpy, win->frame, winX, winY);
			} else {
			    MoveOutline (Scr->Root, 0, 0, 0, 0, 0, 0);
			}
			alreadyvivible = False;
			goto move;
		    }
movewin:	    if (Scr->OpaqueMove) {
			XMoveWindow (dpy, win->frame, winX, winY);
		    } else {
			MoveOutline (Scr->Root,
				winX - win->frame_bw, winY - win->frame_bw,
				win->frame_width  + 2 * win->frame_bw,
				win->frame_height + 2 * win->frame_bw,
				win->frame_bw,
				win->title_height + win->frame_bw3D);
		    }
		}
move:		XMoveWindow (dpy, w, newX - XW, newY - YW);
		break;
	    case Expose :
		if (ev.xexpose.window == w) {
		    WMapRedrawWindow (w, W0, H0, wl->cp, wl->twm_win->icon_name);
		    break;
		}
		Event = ev;
		DispatchEvent ();
		break;
	}
    }
    if (realmovemode) {
	if (Scr->ShowWinWhenMovingInWmgr || alreadyvivible) {
	    if (Scr->OpaqueMove && !OCCUPY (win, vs->wsw->currentwspc)) {
		Vanish (vs, win);
	    }
	    if (!Scr->OpaqueMove) {
		MoveOutline (Scr->Root, 0, 0, 0, 0, 0, 0);
		WMapRedrawName (vs, wl);
	    }
	}
	SetupWindow (win, winX, winY, win->frame_width, win->frame_height, -1);
    }
    ev.xbutton.subwindow = (Window) 0;
    ev.xbutton.window = parent;
    XPutBackEvent   (dpy, &ev);
    XUngrabPointer  (dpy, CurrentTime);

    if ((ev.xbutton.time - etime) < 250) {
	KeyCode control_L_code, control_R_code;
	KeySym  control_L_sym,  control_R_sym;
	char keys [32];

	XMapWindow (dpy, sw);
	XDestroyWindow (dpy, w);
	GotoWorkSpace (vs, ws);
	if (!Scr->DontWarpCursorInWMap) WarpToWindow (win, Scr->RaiseOnWarp);
	control_L_sym  = XStringToKeysym  ("Control_L");
	control_R_sym  = XStringToKeysym  ("Control_R");
	control_L_code = XKeysymToKeycode (dpy, control_L_sym);
	control_R_code = XKeysymToKeycode (dpy, control_R_sym);
	XQueryKeymap (dpy, keys);
	if ((keys [control_L_code / 8] & ((char) 0x80 >> (control_L_code % 8))) ||
	     keys [control_R_code / 8] & ((char) 0x80 >> (control_R_code % 8))) {
	    WMapToggleState (vs);
	}
	return;
    }

    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
        MapSubwindow *msw = vs->wsw->mswl [ws->number];
	if ((newX >= msw->x) && (newX < msw->x + mw->wwidth) &&
	    (newY >= msw->y) && (newY < msw->y + mw->wheight)) {
	    break;
	}
    }
    newws = ws;
    switch (button) {
	case 1 :
	    if ((newws == NULL) || (newws == oldws) || OCCUPY (wl->twm_win, newws)) {
		XMapWindow (dpy, sw);
		break;
	    }
	    occupation = (win->occupation | (1 << newws->number)) & ~(1 << oldws->number);
	    ChangeOccupation (win, occupation);
	    if (newws == vs->wsw->currentwspc) {
		RaiseWindow (win);
		WMapRaise (win);
	    }
	    else WMapRestack (newws);
	    break;

	case 2 :
	    if ((newws == NULL) || (newws == oldws) ||
		OCCUPY (wl->twm_win, newws)) break;

	    occupation = win->occupation | (1 << newws->number);
	    ChangeOccupation (win, occupation);
	    if (newws == vs->wsw->currentwspc) {
		RaiseWindow (win);
		WMapRaise (win);
	    }
	    else WMapRestack (newws);
	    break;

	default :
	    return;
    }
    XDestroyWindow (dpy, w);
}

void InvertColorPair (ColorPair *cp)
{
    Pixel save;

    save = cp->fore;
    cp->fore = cp->back;
    cp->back = save;
    save = cp->shadc;
    cp->shadc = cp->shadd;
    cp->shadd = save;
}

void WMapRedrawName (virtualScreen *vs, WinList wl)
{
    int       w = wl->width;
    int       h = wl->height;
    ColorPair cp;
    char      *label;

    label  = wl->twm_win->icon_name;
     cp     = wl->cp;

    if (Scr->ReverseCurrentWorkspace && wl->wlist == vs->wsw->currentwspc) {
	InvertColorPair (&cp);
    }
    WMapRedrawWindow (wl->w, w, h, cp, label);
}

static void WMapRedrawWindow (Window window, int width, int height,
			      ColorPair cp, char *label)
{
    int		x, y, strhei, strwid;
    MyFont	font;
    XFontSetExtents *font_extents;
    XRectangle inc_rect;
    XRectangle logical_rect;
    XFontStruct **xfonts;
    char **font_names;
    register int i;
    int descent;
    int fnum;

    XClearWindow (dpy, window);
    font = Scr->workSpaceMgr.windowFont;

    font_extents = XExtentsOfFontSet(font.font_set);
    strhei = font_extents->max_logical_extent.height;

    if (strhei > height) return;

    XmbTextExtents(font.font_set, label, strlen (label),
		   &inc_rect, &logical_rect);
    strwid = logical_rect.width;
    x = (width  - strwid) / 2;
    if (x < 1) x = 1;

    fnum = XFontsOfFontSet(font.font_set, &xfonts, &font_names);
    for( i = 0, descent = 0; i < fnum; i++){
	/* xf = xfonts[i]; */
	descent = ((descent < (xfonts[i]->max_bounds.descent)) ? (xfonts[i]->max_bounds.descent): descent);
    }
    
    y = ((height + strhei) / 2) - descent;

    if (Scr->use3Dwmap) {
	Draw3DBorder (window, 0, 0, width, height, 1, cp, off, True, False);
	FB(cp.fore, cp.back);
    } else {
	FB (cp.back, cp.fore);
	XFillRectangle (dpy, window, Scr->NormalGC, 0, 0, width, height);
	FB (cp.fore, cp.back);
    }
    if (Scr->Monochrome != COLOR) {
	XmbDrawImageString (dpy, window, font.font_set, Scr->NormalGC, x, y, label, strlen (label));
    } else {
	XmbDrawString (dpy, window, font.font_set,Scr->NormalGC, x, y, label, strlen (label));
    }
}

static void WMapAddToList (TwmWindow *win, WorkSpace *ws)
{
    virtualScreen *vs;
    WinList wl;
    float   wf, hf;
    ColorPair cp;
    XSetWindowAttributes attr;
    unsigned long attrmask;
    unsigned int bw;

    cp.back = win->title.back;
    cp.fore = win->title.fore;
    if (Scr->workSpaceMgr.windowcpgiven) {
	cp.back = Scr->workSpaceMgr.windowcp.back;
	GetColorFromList (Scr->workSpaceMgr.windowBackgroundL,
			win->full_name, &win->class, &cp.back);
	cp.fore = Scr->workSpaceMgr.windowcp.fore;
	GetColorFromList (Scr->workSpaceMgr.windowForegroundL,
		      win->full_name, &win->class, &cp.fore);
    }
    if (Scr->use3Dwmap && !Scr->BeNiceToColormap) {
	GetShadeColors (&cp);
    }
    for (vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
      wf = (float) (vs->wsw->wwidth  - 2) / (float) vs->w;
      hf = (float) (vs->wsw->wheight - 2) / (float) vs->h;
      wl = (WinList) malloc (sizeof (struct winList));
      wl->wlist  = ws;
      wl->x      = (int) (win->frame_x * wf);
      wl->y      = (int) (win->frame_y * hf);
      wl->width  = (unsigned int) ((win->frame_width  * wf) + 0.5);
      wl->height = (unsigned int) ((win->frame_height * hf) + 0.5);
      bw = 0;
      if (!Scr->use3Dwmap) {
	bw = 1;
	wl->width  -= 2;
	wl->height -= 2;
      }
      if (wl->width  < 1) wl->width  = 1;
      if (wl->height < 1) wl->height = 1;
      wl->w = XCreateSimpleWindow (dpy, vs->wsw->mswl [ws->number]->w, wl->x, wl->y,
				   wl->width, wl->height, bw, Scr->Black, cp.back);
      attrmask = 0;
      if (Scr->BackingStore) {
	attr.backing_store = WhenMapped;
	attrmask |= CWBackingStore;
      }
      attr.cursor = handCursor;
      attrmask |= CWCursor;
      XChangeWindowAttributes (dpy, wl->w, attrmask, &attr);
      XSelectInput (dpy, wl->w, ExposureMask);
      XSaveContext (dpy, wl->w, TwmContext, (XPointer) vs->wsw->twm_win);
      XSaveContext (dpy, wl->w, ScreenContext, (XPointer) Scr);
      XSaveContext (dpy, wl->w, MapWListContext, (XPointer) wl);
      wl->twm_win = win;
      wl->cp      = cp;
      wl->next    = vs->wsw->mswl [ws->number]->wl;
      vs->wsw->mswl [ws->number]->wl = wl;
      if (win->mapped) XMapWindow (dpy, wl->w);
    }
}

static void WMapRemoveFromList (TwmWindow *win, WorkSpace *ws)
{
    virtualScreen *vs;
    WinList wl, *prev;

    for (vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
	prev = &vs->wsw->mswl [ws->number]->wl;
	wl = *prev;
	while (wl != NULL) {
	    if (win == wl->twm_win) {
		*prev = wl->next;
		XDeleteContext (dpy, wl->w, TwmContext);
		XDeleteContext (dpy, wl->w, ScreenContext);
		XDeleteContext (dpy, wl->w, MapWListContext);
		XDestroyWindow (dpy, wl->w);
		free (wl);
		break;
	    }
	    prev = &wl->next;
	    wl   = *prev;
	}
    }
}

static void ResizeWorkSpaceManager (virtualScreen *vs, TwmWindow *win)
{
    int           bwidth, bheight;
    int		  wwidth, wheight;
    int           hspace, vspace;
    int           lines, columns;
    int		  neww, newh;
    WorkSpace     *ws;
    TwmWindow	  *tmp_win;
    WinList	  wl;
    int           i, j;
    float	  wf, hf;

    neww = win->attr.width;
    newh = win->attr.height;
    if (neww == vs->wsw->width && newh == vs->wsw->height)
	return;

    hspace  = Scr->workSpaceMgr.hspace;
    vspace  = Scr->workSpaceMgr.vspace;
    lines   = Scr->workSpaceMgr.lines;
    columns = Scr->workSpaceMgr.columns;
    bwidth  = (neww - (columns * hspace)) / columns;
    bheight = (newh - (lines   * vspace)) / lines;
    wwidth  = neww / columns;
    wheight = newh / lines;
    wf = (float) (wwidth  - 2) / (float) vs->w;
    hf = (float) (wheight - 2) / (float) vs->h;

    i = 0;
    j = 0;
    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
        MapSubwindow *msw = vs->wsw->mswl [ws->number];
	XMoveResizeWindow (dpy, vs->wsw->bswl [ws->number]->w,
				i * (bwidth  + hspace) + (hspace / 2),
				j * (bheight + vspace) + (vspace / 2),
				bwidth, bheight);
	msw->x = i * wwidth;
	msw->y = j * wheight;
	XMoveResizeWindow (dpy, msw->w, msw->x, msw->y, wwidth - 2, wheight - 2);
	for (wl = msw->wl; wl != NULL; wl = wl->next) {
	    tmp_win    = wl->twm_win;
	    wl->x      = (int) (tmp_win->frame_x * wf);
	    wl->y      = (int) (tmp_win->frame_y * hf);
	    wl->width  = (unsigned int) ((tmp_win->frame_width  * wf) + 0.5);
	    wl->height = (unsigned int) ((tmp_win->frame_height * hf) + 0.5);
	    XMoveResizeWindow (dpy, wl->w, wl->x, wl->y, wl->width, wl->height);
	}
	i++;
	if (i == columns) {i = 0; j++;};
    }
    vs->wsw->bwidth    = bwidth;
    vs->wsw->bheight   = bheight;
    vs->wsw->width     = neww;
    vs->wsw->height    = newh;
    vs->wsw->wwidth	= wwidth;
    vs->wsw->wheight	= wheight;
    PaintWorkSpaceManager (vs);
}

static void ResizeOccupyWindow (TwmWindow *win)
{
    int        bwidth, bheight, owidth, oheight;
    int        hspace, vspace;
    int        lines, columns;
    int        neww, newh;
    WorkSpace  *ws;
    int        i, j, x, y;
    OccupyWindow *occwin = Scr->workSpaceMgr.occupyWindow;

    neww = win->attr.width;
    newh = win->attr.height;
    if (occwin->width == neww && occwin->height == newh)
	return;

    hspace  = occwin->hspace;
    vspace  = occwin->vspace;
    lines   = Scr->workSpaceMgr.lines;
    columns = Scr->workSpaceMgr.columns;
    bwidth  = (neww -  columns    * hspace) / columns;
    bheight = (newh - (lines + 2) * vspace) / (lines + 1);
    owidth  = occwin->owidth;
    oheight = bheight;

    i = 0;
    j = 0;
    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	XMoveResizeWindow (dpy, occwin->obuttonw [j * columns + i],
			   i * (bwidth  + hspace) + (hspace / 2),
			   j * (bheight + vspace) + (vspace / 2),
			   bwidth, bheight);
	i++;
	if (i == columns) {i = 0; j++;}
    }
    hspace = (neww - 3 * owidth) / 4;
    x = hspace;
    y = ((bheight + vspace) * lines) + ((3 * vspace) / 2);
    XMoveResizeWindow (dpy, occwin->OK, x, y, owidth, oheight);
    x += owidth + hspace;
    XMoveResizeWindow (dpy, occwin->cancel, x, y, owidth, oheight);
    x += owidth + hspace;
    XMoveResizeWindow (dpy, occwin->allworkspc, x, y, owidth, oheight);

    occwin->width   = neww;
    occwin->height  = newh;
    occwin->bwidth  = bwidth;
    occwin->bheight = bheight;
    occwin->owidth  = owidth;
    PaintOccupyWindow ();
}

void WMapCreateCurrentBackGround (char *border,
				  char *background, char *foreground,
				  char *pixmap)
{
    Image *image;
    WorkSpaceMgr *ws = &Scr->workSpaceMgr;

    ws->curBorderColor = Scr->Black;
    ws->curColors.back = Scr->White;
    ws->curColors.fore = Scr->Black;
    ws->curImage       = None;

    if (border == NULL)
	return;
    GetColor (Scr->Monochrome, &ws->curBorderColor, border);
    if (background == NULL)
	return;
    ws->curPaint = True;
    GetColor (Scr->Monochrome, &ws->curColors.back, background);
    if (foreground == NULL)
	return;
    GetColor (Scr->Monochrome, &ws->curColors.fore, foreground);

    if (pixmap == NULL)
	return;
      if ((image = GetImage (pixmap, Scr->workSpaceMgr.curColors)) == None) {
	fprintf (stderr, "Can't find pixmap %s\n", pixmap);
	return;
    }
    ws->curImage = image;
}

void WMapCreateDefaultBackGround (char *border,
				  char *background, char *foreground,
				  char *pixmap)
{
    Image *image;
    WorkSpaceMgr *ws = &Scr->workSpaceMgr;

    ws->defBorderColor = Scr->Black;
    ws->defColors.back = Scr->White;
    ws->defColors.fore = Scr->Black;
    ws->defImage       = None;

    if (border == NULL)
	return;
    GetColor (Scr->Monochrome, &ws->defBorderColor, border);
    if (background == NULL)
	return;
    GetColor (Scr->Monochrome, &ws->defColors.back, background);
    if (foreground == NULL)
	return;
    GetColor (Scr->Monochrome, &ws->defColors.fore, foreground);
    if (pixmap == NULL)
	return;
    if ((image = GetImage (pixmap, ws->defColors)) == None)
	return;
    ws->defImage = image;
}

Bool AnimateRoot (void)
{
    virtualScreen *vs;
    ScreenInfo *scr;
    int	       scrnum;
    Image      *image;
    WorkSpace  *ws;
    Bool       maybeanimate;

    maybeanimate = False;
    for (scrnum = 0; scrnum < NumScreens; scrnum++) {
	if ((scr = ScreenList [scrnum]) == NULL) continue;
	if (! scr->workSpaceManagerActive) continue;

	for (vs = scr->vScreenList; vs != NULL; vs = vs->next) {
	  if (! vs->wsw->currentwspc) continue;
	  image = vs->wsw->currentwspc->image;
	  if ((image == None) || (image->next == None)) continue;
	  if (scr->DontPaintRootWindow) continue;

	  XSetWindowBackgroundPixmap (dpy, vs->window, image->pixmap);
	  XClearWindow (dpy, scr->Root);
	  vs->wsw->currentwspc->image = image->next;
	  maybeanimate = True;
	}
    }
    for (scrnum = 0; scrnum < NumScreens; scrnum++) {
	if ((scr = ScreenList [scrnum]) == NULL) continue;

	for (vs = scr->vScreenList; vs != NULL; vs = vs->next) {
	  if (vs->wsw->state == BUTTONSSTATE) continue;
	  for (ws = scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
	    image = ws->image;

	    if ((image == None) || (image->next == None)) continue;
	    if (ws == vs->wsw->currentwspc) continue;
	    XSetWindowBackgroundPixmap (dpy, vs->wsw->mswl [ws->number]->w, image->pixmap);
	    XClearWindow (dpy, vs->wsw->mswl [ws->number]->w);
	    ws->image = image->next;
	    maybeanimate = True;
	  }
	}
    }
    return (maybeanimate);
}

static char **GetCaptivesList (int scrnum)
{
    unsigned char	*prop, *p;
    unsigned long	bytesafter;
    unsigned long	len;
    Atom		actual_type;
    int			actual_format;
    char		**ret;
    int			count;
    int			i, l;
    Window		root;

    _XA_WM_CTWMSLIST = XInternAtom (dpy, "WM_CTWMSLIST", True);
    if (_XA_WM_CTWMSLIST == None) return ((char**)0);

    root = RootWindow (dpy, scrnum);
    if (XGetWindowProperty (dpy, root, _XA_WM_CTWMSLIST, 0L, 512,
			False, XA_STRING, &actual_type, &actual_format, &len,
			&bytesafter, &prop) != Success) return ((char**) 0);
    if (len == 0) return ((char**) 0);

    count = 0;
    p = prop;
    l = 0;
    while (l < len) {
	l += strlen ((char*)p) + 1;
	p += strlen ((char*)p) + 1;
	count++;
    }
    ret = (char**) malloc ((count + 1) * sizeof (char*));

    p = prop;
    l = 0;
    i = 0;
    while (l < len) {
	ret [i++] = (char*) strdup ((char*) p);
	l += strlen ((char*)p) + 1;
	p += strlen ((char*)p) + 1;
    }
    ret [i] = (char*) 0;
    XFree ((char *)prop);

    return (ret);
}

static void SetCaptivesList (int scrnum, char **clist)
{
    unsigned long	len;
    char		**cl;
    char		*s, *slist;
    Window		root = RootWindow (dpy, scrnum);

    _XA_WM_CTWMSLIST = XInternAtom (dpy, "WM_CTWMSLIST", False);
    cl  = clist; len = 0;
    while (*cl) { len += strlen (*cl++) + 1; }
    if (len == 0) {
	XDeleteProperty (dpy, root, _XA_WM_CTWMSLIST);
	return;
    }
    slist = (char*) malloc (len * sizeof (char));
    cl = clist; s  = slist;
    while (*cl) {
	strcpy (s, *cl);
	s += strlen (*cl);
	*s++ = '\0';
	cl++;
    }
    XChangeProperty (dpy, root, _XA_WM_CTWMSLIST, XA_STRING, 8, 
		     PropModeReplace, (unsigned char *) slist, len);
}

static void freeCaptiveList (char **clist)
{
    while (clist && *clist) { free (*clist++); }
}

void AddToCaptiveList (void)
{
    int		i, count;
    char	**clist, **cl, **newclist;
    int		busy [32];
    Atom	_XA_WM_CTWM_ROOT;
    char	*atomname;
    int		scrnum = Scr->screen;
    Window	croot  = Scr->Root;
    Window	root   = RootWindow (dpy, scrnum);

    for (i = 0; i < 32; i++) { busy [i] = 0; }
    clist = GetCaptivesList (scrnum);
    cl = clist;
    count = 0;
    while (cl && *cl) {
	count++;
	if (!captivename) {
	    if (!strncmp (*cl, "ctwm-", 5)) {
		int r, n;
		r = sscanf (*cl, "ctwm-%d", &n);
		cl++;
		if (r != 1) continue;
		if ((n < 0) || (n > 31)) continue;
		busy [n] = 1;
	    } else cl++;
	    continue;
	}
	if (!strcmp (*cl, captivename)) {
	    fprintf (stderr, "A captive ctwm with name %s is already running\n", captivename);
	    exit (1);
	}
	cl++;
    }
    if (!captivename) {
	for (i = 0; i < 32; i++) {
	    if (!busy [i]) break;
	}
	if (i == 32) { /* no one can tell we didn't try hard */
	    fprintf (stderr, "Cannot find a suitable name for captive ctwm\n");
	    exit (1);
	}
	captivename = (char*) malloc (8);
	sprintf (captivename, "ctwm-%d", i);
    }
    newclist = (char**) malloc ((count + 2) * sizeof (char*));
    for (i = 0; i < count; i++) {
	newclist [i] = (char*) strdup (clist [i]);
    }
    newclist [count] = (char*) strdup (captivename);
    newclist [count + 1] = (char*) 0;
    SetCaptivesList (scrnum, newclist);
    freeCaptiveList (clist);
    freeCaptiveList (newclist);
    free (clist); free (newclist);

    root = RootWindow (dpy, scrnum);
    atomname = (char*) malloc (strlen ("WM_CTWM_ROOT_") + strlen (captivename) +1);
    sprintf (atomname, "WM_CTWM_ROOT_%s", captivename);
    _XA_WM_CTWM_ROOT = XInternAtom (dpy, atomname, False);
    XChangeProperty (dpy, root, _XA_WM_CTWM_ROOT, XA_WINDOW, 32, 
		     PropModeReplace, (unsigned char *) &croot, 4);
}

void RemoveFromCaptiveList (void)
{
    int	 count;
    char **clist, **cl, **newclist;
    Atom _XA_WM_CTWM_ROOT;
    char *atomname;
    int scrnum = Scr->screen;
    Window root = RootWindow (dpy, scrnum);

    if (!captivename) return;
    clist = GetCaptivesList (scrnum);
    cl = clist; count = 0;
    while (*cl) {
      count++;
      cl++;
    }
    newclist = (char**) malloc (count * sizeof (char*));
    cl = clist; count = 0;
    while (*cl) {
	if (!strcmp (*cl, captivename)) { cl++; continue; }
	newclist [count++] = *cl;
	cl++;
    }
    newclist [count] = (char*) 0;
    SetCaptivesList (scrnum, newclist);
    freeCaptiveList (clist);
    free (clist); free (newclist);

    atomname = (char*) malloc (strlen ("WM_CTWM_ROOT_") + strlen (captivename) +1);
    sprintf (atomname, "WM_CTWM_ROOT_%s", captivename);
    _XA_WM_CTWM_ROOT = XInternAtom (dpy, atomname, True);
    if (_XA_WM_CTWM_ROOT == None) return;
    XDeleteProperty (dpy, root, _XA_WM_CTWM_ROOT);
}

void SetPropsIfCaptiveCtwm (TwmWindow *win)
{
    Window	window = win->w;
    Window	frame  = win->frame;
    Atom	_XA_WM_CTWM_ROOT;

    if (!CaptiveCtwmRootWindow (window)) return;
    _XA_WM_CTWM_ROOT = XInternAtom (dpy, "WM_CTWM_ROOT", True);
    if (_XA_WM_CTWM_ROOT == None) return;

    XChangeProperty (dpy, frame, _XA_WM_CTWM_ROOT, XA_WINDOW, 32, 
		     PropModeReplace, (unsigned char *) &window, 4);
}

Window CaptiveCtwmRootWindow (Window window)
{
    Window	       *prop;
    Window		w;
    unsigned long	bytesafter;
    unsigned long	len;
    Atom		actual_type;
    int			actual_format;
    Atom		_XA_WM_CTWM_ROOT;

    _XA_WM_CTWM_ROOT = XInternAtom (dpy, "WM_CTWM_ROOT", True);
    if (_XA_WM_CTWM_ROOT == None) return ((Window)0);

    if (XGetWindowProperty (dpy, window, _XA_WM_CTWM_ROOT, 0L, 1L,
			False, XA_WINDOW, &actual_type, &actual_format, &len,
			&bytesafter, (unsigned char **)&prop) != Success)
	return ((Window)0);
    if (len == 0) return ((Window)0);
    w = *prop;
    XFree ((char *)prop);
    return w;
}

CaptiveCTWM GetCaptiveCTWMUnderPointer (void)
{
    int		scrnum = Scr->screen;
    Window	root;
    Window	child, croot;
    CaptiveCTWM	cctwm;

    root = RootWindow (dpy, scrnum);
    while (1) {
	XQueryPointer (dpy, root, &JunkRoot, &child,
			&JunkX, &JunkY, &JunkX, &JunkY, &JunkMask);
	if (child && (croot = CaptiveCtwmRootWindow (child))) {
	    root = croot;
	    continue;
	}
	cctwm.root = root;
	XFetchName (dpy, root, &cctwm.name);
	if (!cctwm.name) cctwm.name = (char*) strdup ("Root");
	return (cctwm);
    }
}

void SetNoRedirect (Window window)
{
    Atom	_XA_WM_NOREDIRECT;

    _XA_WM_NOREDIRECT = XInternAtom (dpy, "WM_NOREDIRECT", False);
    if (_XA_WM_NOREDIRECT == None) return;

    XChangeProperty (dpy, window, _XA_WM_NOREDIRECT, XA_STRING, 8, 
		     PropModeReplace, (unsigned char *) "Yes", 4);
}

static Bool DontRedirect (Window window)
{
    unsigned char	*prop;
    unsigned long	bytesafter;
    unsigned long	len;
    Atom		actual_type;
    int			actual_format;
    Atom		_XA_WM_NOREDIRECT;

    _XA_WM_NOREDIRECT = XInternAtom (dpy, "WM_NOREDIRECT", True);
    if (_XA_WM_NOREDIRECT == None) return (False);

    if (XGetWindowProperty (dpy, window, _XA_WM_NOREDIRECT, 0L, 1L,
			False, XA_STRING, &actual_type, &actual_format, &len,
			&bytesafter, &prop) != Success) return (False);
    if (len == 0) return (False);
    XFree ((char *)prop);
    return (True);
}

Bool visible (TwmWindow *tmp_win)
{
  return (tmp_win->vs != NULL);
}

#ifdef BUGGY_HP700_SERVER
static void fakeRaiseLower (display, window)
Display *display;
Window   window;
{
    Window          root;
    Window          parent;
    Window          grandparent;
    Window         *children;
    unsigned int    number;
    XWindowChanges  changes;

    number = 0;
    XQueryTree (display, window, &root, &parent, &children, &number);
    XFree ((char *) children);
    XQueryTree (display, parent, &root, &grandparent, &children, &number);

    changes.stack_mode = (children [number-1] == window) ? Below : Above;
    XFree ((char *) children);
    XConfigureWindow (display, window, CWStackMode, &changes);
}
#endif



