/****************************************************************************
 * This module is all original code 
 * by Rob Nation 
 * Copyright 1993, Robert Nation
 *     You may use this code for any purpose, as long as the original
 *     copyright remains in the source code and all documentation
 ****************************************************************************/

#include <FVWMconfig.h>

#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <errno.h>

#include "fvwm.h"
#include "menus.h"
#include "misc.h"
#include "parse.h"
#include "screen.h"
#include "module.h"

static char *exec_shell_name = "/bin/sh";

#include <X11/xpm.h>

/***********************************************************************
 *
 *  Procedure:
 *	DeferExecution - defer the execution of a function to the
 *	    next button press if the context is C_ROOT
 *
 *  Inputs:
 *      eventp  - pointer to XEvent to patch up
 *      w       - pointer to Window to patch up
 *      tmp_win - pointer to FvwmWindow Structure to patch up
 *	context	- the context in which the mouse button was pressed
 *	func	- the function to defer
 *	cursor	- the cursor to display while waiting
 *      finishEvent - ButtonRelease or ButtonPress; tells what kind of event to
 *                    terminate on.
 *
 ***********************************************************************/
int DeferExecution(XEvent * eventp, Window * w, FvwmWindow ** tmp_win,
		   unsigned long *context, int cursor, int FinishEvent)
 {
  int done;
  int finished = 0;
  Window dummy;
  Window original_w;

  original_w = *w;

  if ((*context != C_ROOT) && (*context != C_NO_CONTEXT)) {
    if ((FinishEvent == ButtonPress) || ((FinishEvent == ButtonRelease) &&
					 (eventp->type != ButtonPress))) {
      return FALSE;
    }
  }
  if (!GrabEm(cursor)) {
    XBell(dpy, Scr.screen);
    return True;
  }

  while (!finished) {
    done = 0;
    /* block until there is an event */
    XMaskEvent(dpy, ButtonPressMask | ButtonReleaseMask |
	       ExposureMask | KeyPressMask | VisibilityChangeMask |
	       ButtonMotionMask | PointerMotionMask	/* | EnterWindowMask | 
							   LeaveWindowMask */ , eventp);
    StashEventTime(eventp);

    if (eventp->type == KeyPress)
      Keyboard_shortcuts(eventp, FinishEvent);
    if (eventp->type == FinishEvent)
      finished = 1;
    if (eventp->type == ButtonPress) {
      XAllowEvents(dpy, ReplayPointer, CurrentTime);
      done = 1;
    }
    if (eventp->type == ButtonRelease)
      done = 1;
    if (eventp->type == KeyPress)
      done = 1;

    if (!done) {
      DispatchEvent();
    }

  }


  *w = eventp->xany.window;
  if (((*w == Scr.Root) || (*w == Scr.NoFocusWin))
      && (eventp->xbutton.subwindow != (Window) 0)) {
    *w = eventp->xbutton.subwindow;
    eventp->xany.window = *w;
  }
  if (*w == Scr.Root) {
    *context = C_ROOT;
    XBell(dpy, Scr.screen);
    UngrabEm();
    return TRUE;
  }
  if (XFindContext(dpy, *w, FvwmContext, (caddr_t *) tmp_win) == XCNOENT) {
    *tmp_win = NULL;
    XBell(dpy, Scr.screen);
    UngrabEm();
    return (TRUE);
  }

  if (*w == (*tmp_win)->Parent)
    *w = (*tmp_win)->w;

  if (original_w == (*tmp_win)->Parent)
    original_w = (*tmp_win)->w;

  /* this ugly mess attempts to ensure that the release and press
   * are in the same window. */
  if ((*w != original_w) && (original_w != Scr.Root) &&
      (original_w != None) && (original_w != Scr.NoFocusWin))
    if (!((*w == (*tmp_win)->frame) && (original_w == (*tmp_win)->w))) {
      *context = C_ROOT;
      XBell(dpy, Scr.screen);
      UngrabEm();
      return TRUE;
    }

  *context = GetContext(*tmp_win, eventp, &dummy);

  UngrabEm();
  return FALSE;
}




/**************************************************************************
 *
 * Moves focus to specified window 
 *
 *************************************************************************/
void FocusOn(FvwmWindow * t, int DeIconifyOnly, int RaiseWarp)
{
#ifndef NON_VIRTUAL
  int dx, dy;
  int cx, cy;
#endif
  int x, y;

  if (t == (FvwmWindow *) 0)
    return;

  if (t->Desk != Scr.CurrentDesk && RaiseWarp) {
    changeDesks(0, t->Desk);
  }
#ifndef NON_VIRTUAL
  if (t->flags & ICONIFIED) {
    cx = t->icon_xl_loc + t->icon_w_width / 2;
    cy = t->icon_y_loc + t->icon_p_height + ICON_HEIGHT / 2;
  } else {
    cx = t->frame_x + t->frame_width / 2;
    cy = t->frame_y + t->frame_height / 2;
  }

  dx = (cx + Scr.Vx) / Scr.MyDisplayWidth * Scr.MyDisplayWidth;
  dy = (cy + Scr.Vy) / Scr.MyDisplayHeight * Scr.MyDisplayHeight;

  if (RaiseWarp)
    MoveViewport(dx, dy, True);
#endif

  if (t->flags & ICONIFIED) {
    x = t->icon_xl_loc + t->icon_w_width / 2;
    y = t->icon_y_loc + t->icon_p_height + ICON_HEIGHT / 2;
  } else {
    x = t->frame_x;
    y = t->frame_y;
  }

  if (RaiseWarp) {
#if 0				/* don't want to warp the pointer by default anymore */
    if (!(t->flags & ClickToFocus))
      XWarpPointer(dpy, None, Scr.Root, 0, 0, 0, 0, x + 2, y + 2);
#endif				/* 0 */
    RaiseWindow(t);
    KeepOnTop();

    /* If the window is still not visible, make it visible! */
    if (((t->frame_x + t->frame_height) < 0)
	|| (t->frame_y + t->frame_width < 0)
	|| (t->frame_x > Scr.MyDisplayWidth)
	|| (t->frame_y > Scr.MyDisplayHeight)) {
      SetupFrame(t, 0, 0, t->frame_width, t->frame_height, False);
      if (!(t->flags & ClickToFocus))
	XWarpPointer(dpy, None, Scr.Root, 0, 0, 0, 0, 2, 2);
    }
    UngrabEm();
  }
  SetFocus(t->w, t, 1);		/*0); */
}


/**************************************************************************
 *
 * Moves pointer to specified window 
 *
 *************************************************************************/
void WarpOn(FvwmWindow * t, int warp_x, int x_unit, int warp_y, int y_unit)
{
#ifndef NON_VIRTUAL
  int dx, dy;
  int cx, cy;
#endif
  int x, y;

  if (t == (FvwmWindow *) 0 || (t->flags & ICONIFIED && t->icon_w == None))
    return;

  if (t->Desk != Scr.CurrentDesk) {
    changeDesks(0, t->Desk);
  }
#ifndef NON_VIRTUAL
  if (t->flags & ICONIFIED) {
    cx = t->icon_xl_loc + t->icon_w_width / 2;
    cy = t->icon_y_loc + t->icon_p_height + ICON_HEIGHT / 2;
  } else {
    cx = t->frame_x + t->frame_width / 2;
    cy = t->frame_y + t->frame_height / 2;
  }

  dx = (cx + Scr.Vx) / Scr.MyDisplayWidth * Scr.MyDisplayWidth;
  dy = (cy + Scr.Vy) / Scr.MyDisplayHeight * Scr.MyDisplayHeight;

  MoveViewport(dx, dy, True);
#endif

  if (t->flags & ICONIFIED) {
    x = t->icon_xl_loc + t->icon_w_width / 2 + 2;
    y = t->icon_y_loc + t->icon_p_height + ICON_HEIGHT / 2 + 2;
  } else {
    if (x_unit != Scr.MyDisplayWidth)
      x = t->frame_x + 2 + warp_x;
    else
      x = t->frame_x + 2 + (t->frame_width - 4) * warp_x / 100;
    if (y_unit != Scr.MyDisplayHeight)
      y = t->frame_y + 2 + warp_y;
    else
      y = t->frame_y + 2 + (t->frame_height - 4) * warp_y / 100;
  }
  if (warp_x >= 0 && warp_y >= 0) {
    XWarpPointer(dpy, None, Scr.Root, 0, 0, 0, 0, x, y);
  }
  RaiseWindow(t);
  KeepOnTop();

  /* If the window is still not visible, make it visible! */
  if (((t->frame_x + t->frame_height) < 0)
      || (t->frame_y + t->frame_width < 0)
      || (t->frame_x > Scr.MyDisplayWidth)
      || (t->frame_y > Scr.MyDisplayHeight)) {
    SetupFrame(t, 0, 0, t->frame_width, t->frame_height, False);
    XWarpPointer(dpy, None, Scr.Root, 0, 0, 0, 0, 2, 2);
  }
  UngrabEm();
}



/***********************************************************************
 *
 *  Procedure:
 *	(Un)Maximize a window.
 *
 ***********************************************************************/
void Maximize(XEvent * eventp, Window w, FvwmWindow * tmp_win,
	      unsigned long context, char *action, int *Module)
{
  int new_width, new_height, new_x, new_y;
  int val1, val2, val1_unit, val2_unit, n;

  if (DeferExecution
      (eventp, &w, &tmp_win, &context, SELECT, ButtonRelease)) return;

  if (tmp_win == NULL)
    return;

  if (check_allowed_function2(F_MAXIMIZE, tmp_win) == 0) {
    XBell(dpy, Scr.screen);
    return;
  }
  n = GetTwoArguments(action, &val1, &val2, &val1_unit, &val2_unit);
  if (n != 2) {
    val1 = 100;
    val2 = 100;
    val1_unit = Scr.MyDisplayWidth;
    val2_unit = Scr.MyDisplayHeight;
  }

  if (tmp_win->flags & MAXIMIZED) {
    tmp_win->flags &= ~MAXIMIZED;
    SetupFrame(tmp_win, tmp_win->orig_x, tmp_win->orig_y, tmp_win->orig_wd,
	       tmp_win->orig_ht, TRUE);
    SetBorder(tmp_win, True, True, True, None);
  } else {
    new_width = tmp_win->frame_width;
    new_height = tmp_win->frame_height;
    new_x = tmp_win->frame_x;
    new_y = tmp_win->frame_y;
    if (val1 > 0) {
      new_width = val1 * val1_unit / 100;
      new_x = 0;
    }
    if (val2 > 0) {
      new_height = val2 * val2_unit / 100;
      new_y = 0;
    }
    if ((val1 == 0) && (val2 == 0)) {
      new_x = 0;
      new_y = 0;
      new_height = Scr.MyDisplayHeight;
      new_width = Scr.MyDisplayWidth;
    }
    tmp_win->flags |= MAXIMIZED;
    ConstrainSize(tmp_win, &new_width, &new_height);
    SetupFrame(tmp_win, new_x, new_y, new_width, new_height, TRUE);
    SetBorder(tmp_win, Scr.Hilite == tmp_win, True, True,
	      tmp_win->right_w[0]);
  }
}

/* For Ultrix 4.2 */
#include <sys/types.h>
#include <sys/time.h>


MenuRoot *FindPopup(char *action)
{
  char *tmp;
  MenuRoot *mr;
  int x, y, dummy;

  action = GetNextToken(action, &tmp);

  if (tmp == NULL)
    return NULL;

  mr = Scr.AllMenus;
  while (mr != NULL) {
    if (mr->name != NULL)
      if (strcasecmp(tmp, mr->name) == 0) {
	free(tmp);

	/* we found the menu, now check for additional parameters
	   (coordinates)
	 */
	mr->has_coords = False;
	if (action[0] == 0)
	  return mr;

	if (GetTwoArguments(action, &(mr->x), &(mr->y), &dummy, &dummy) ==
	    2) mr->has_coords = True;

	return mr;
      }
    mr = mr->next;
  }
  free(tmp);
  return NULL;
}



void Bell(XEvent * eventp, Window w, FvwmWindow * tmp_win,
	  unsigned long context, char *action, int *Module)
{
  XBell(dpy, Scr.screen);
}


char *last_menu = NULL;
void add_item_to_menu(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		      unsigned long context, char *action, int *Module)
{
  MenuRoot *mr;
  char *token, *rest, *item;

  rest = GetNextToken(action, &token);
  mr = FindPopup(token);

  if (mr == NULL)
    mr = NewMenuRoot(token, F_POPUP);
  free(token);

  if (last_menu != NULL)
    free(last_menu);

  CopyString(&last_menu, mr->name);
  rest = GetNextToken(rest, &item);

  AddToMenu(mr, item, rest);
  free(item);

  MakeMenu(mr);
  return;
}


void add_another_item(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		      unsigned long context, char *action, int *Module)
{
  MenuRoot *mr;
  char *rest, *item;

  if (last_menu == NULL)
    return;

  mr = FindPopup(last_menu);
  if (mr == NULL)
    return;

  rest = GetNextToken(action, &item);

  AddToMenu(mr, item, rest);
  free(item);

  MakeMenu(mr);
  return;
}

void destroy_menu(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		  unsigned long context, char *action, int *Module)
{
  MenuRoot *mr;

  char *token, *rest;

  rest = GetNextToken(action, &token);
  mr = FindPopup(token);
  if (mr == NULL)
    return;
  DestroyMenu(mr);
  return;
}

void add_item_to_func(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		      unsigned long context, char *action, int *Module)
{
  MenuRoot *mr;
  char *token, *rest, *item;

  rest = GetNextToken(action, &token);
  mr = FindPopup(token);
  if (mr == NULL)
    mr = NewMenuRoot(token, F_FUNCTION);
  if (last_menu != NULL)
    free(last_menu);
  last_menu = token;
  rest = GetNextToken(rest, &item);

  AddToMenu(mr, item, rest);
  free(item);

  return;
}


void Nop_func(XEvent * eventp, Window w, FvwmWindow * tmp_win,
	      unsigned long context, char *action, int *Module)
{

}



void movecursor(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		unsigned long context, char *action, int *Module)
{
#ifndef NON_VIRTUAL
  int x, y, delta_x, delta_y, warp_x, warp_y;
  int val1, val2, val1_unit, val2_unit, n;

  n = GetTwoArguments(action, &val1, &val2, &val1_unit, &val2_unit);

  XQueryPointer(dpy, Scr.Root, &JunkRoot, &JunkChild,
		&x, &y, &JunkX, &JunkY, &JunkMask);
  delta_x = 0;
  delta_y = 0;
  warp_x = 0;
  warp_y = 0;
  if (x >= Scr.MyDisplayWidth - 2) {
    delta_x = Scr.EdgeScrollX;
    warp_x = Scr.EdgeScrollX - 4;
  }
  if (y >= Scr.MyDisplayHeight - 2) {
    delta_y = Scr.EdgeScrollY;
    warp_y = Scr.EdgeScrollY - 4;
  }
  if (x < 2) {
    delta_x = -Scr.EdgeScrollX;
    warp_x = -Scr.EdgeScrollX + 4;
  }
  if (y < 2) {
    delta_y = -Scr.EdgeScrollY;
    warp_y = -Scr.EdgeScrollY + 4;
  }
  if (Scr.Vx + delta_x < 0)
    delta_x = -Scr.Vx;
  if (Scr.Vy + delta_y < 0)
    delta_y = -Scr.Vy;
  if (Scr.Vx + delta_x > Scr.VxMax)
    delta_x = Scr.VxMax - Scr.Vx;
  if (Scr.Vy + delta_y > Scr.VyMax)
    delta_y = Scr.VyMax - Scr.Vy;
  if ((delta_x != 0) || (delta_y != 0)) {
    MoveViewport(Scr.Vx + delta_x, Scr.Vy + delta_y, True);
    XWarpPointer(dpy, Scr.Root, Scr.Root, 0, 0, Scr.MyDisplayWidth,
		 Scr.MyDisplayHeight, x - warp_x, y - warp_y);
  }
#endif
  XWarpPointer(dpy, Scr.Root, Scr.Root, 0, 0, Scr.MyDisplayWidth,
	       Scr.MyDisplayHeight, x + val1 * val1_unit / 100 - warp_x,
	       y + val2 * val2_unit / 100 - warp_y);
}


void iconify_function(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		      unsigned long context, char *action, int *Module)
 {
  long val1;
  int val1_unit, n;

  if (DeferExecution(eventp, &w, &tmp_win, &context, SELECT,
		     ButtonRelease)) return;

  n = GetOneArgument(action, &val1, &val1_unit);

  if (tmp_win->flags & ICONIFIED) {
    if (val1 <= 0)
      DeIconify(tmp_win);
  } else {
    if (check_allowed_function2(F_ICONIFY, tmp_win) == 0) {
      XBell(dpy, Scr.screen);
      return;
    }
    if (val1 >= 0)
      Iconify(tmp_win, eventp->xbutton.x_root - 5,
	      eventp->xbutton.y_root - 5);
  }
}

void raise_function(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		    unsigned long context, char *action, int *Module)
{
  char *junk, *junkC;
  unsigned long junkN;
  int junkD, method, BoxJunk[4];

  if (DeferExecution
      (eventp, &w, &tmp_win, &context, SELECT, ButtonRelease)) return;

  if (tmp_win)
    RaiseWindow(tmp_win);

  if (LookInList
      (Scr.TheList, tmp_win->realname, &tmp_win->class, &junk, &junk,
       &junkD, &junkD, &junkD, &junkC, &junkC, &junkN, BoxJunk,
       &method) & STAYSONTOP_FLAG) tmp_win->flags |= ONTOP;
  KeepOnTop();
}

void lower_function(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		    unsigned long context, char *action, int *Module)
{
  if (DeferExecution
      (eventp, &w, &tmp_win, &context, SELECT, ButtonRelease)) return;

  LowerWindow(tmp_win);

  tmp_win->flags &= ~ONTOP;
}

void destroy_function(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		      unsigned long context, char *action, int *Module)
{
  if (DeferExecution
      (eventp, &w, &tmp_win, &context, DESTROY, ButtonRelease)) return;

  if (check_allowed_function2(F_DESTROY, tmp_win) == 0) {
    XBell(dpy, Scr.screen);
    return;
  }

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

void delete_function(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		     unsigned long context, char *action, int *Module)
{
  if (DeferExecution
      (eventp, &w, &tmp_win, &context, DESTROY, ButtonRelease)) return;

  if (check_allowed_function2(F_DELETE, tmp_win) == 0) {
    XBell(dpy, Scr.screen);
    return;
  }

  if (tmp_win->flags & DoesWmDeleteWindow) {
    send_clientmessage(tmp_win->w, _XA_WM_DELETE_WINDOW, CurrentTime);
    return;
  } else
    XBell(dpy, Scr.screen);
  XSync(dpy, 0);
}

void close_function(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		    unsigned long context, char *action, int *Module)
{
  if (DeferExecution
      (eventp, &w, &tmp_win, &context, DESTROY, ButtonRelease)) return;

  if (check_allowed_function2(F_CLOSE, tmp_win) == 0) {
    XBell(dpy, Scr.screen);
    return;
  }

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

void restart_function(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		      unsigned long context, char *action, int *Module)
{
  Done(1, action);
}

void exec_setup(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		unsigned long context, char *action, int *Module)
{
  char *arg = NULL;

  action = GetNextToken(action, &arg);

  if (arg && (strcmp(arg, "") != 0)) {	/* specific shell was specified */
    exec_shell_name = strdup(arg);
  } else {			/* no arg, so use $SHELL -- not working??? */

    if (getenv("SHELL"))
      exec_shell_name = strdup(getenv("SHELL"));
    else
      exec_shell_name = strdup("/bin/sh");	/* if $SHELL not set, use default */
  }
}

void exec_function(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		   unsigned long context, char *action, int *Module)
{
  char *cmd = NULL;

  /* if it doesn't already have an 'exec' as the first word, add that
   * to keep down number of procs started */

  /* need to parse string better to do this right though, so not doing this
     for now... */
  if (0 && strncasecmp(action, "exec", 4) != 0) {
    cmd = (char *) safemalloc(strlen(action) + 6);
    strcpy(cmd, "exec ");
    strcat(cmd, action);
  } else {
    cmd = strdup(action);
  }

  /* Use to grab the pointer here, but the fork guarantees that
   * we wont be held up waiting for the function to finish,
   * so the pointer-gram just caused needless delay and flashing
   * on the screen */
  /* Thought I'd try vfork and _exit() instead of regular fork().
   * The man page says that its better. */
  /* Not everyone has vfork! */
  if (!(fork())) {		/* child process */
    if (execl(exec_shell_name, exec_shell_name, "-c", cmd, NULL) == -1) {
      fvwm_msg(ERR, "exec_function", "execl failed (%s)", strerror(errno));
      exit(100);
    }
  }
  free(cmd);
  return;
}

void refresh_function(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		      unsigned long context, char *action, int *Module)
{
  XSetWindowAttributes attributes;
  unsigned long valuemask;

  valuemask = (CWBackPixel);
  attributes.background_pixel = 0;
  attributes.backing_store = NotUseful;
  w = XCreateWindow(dpy, Scr.Root, 0, 0,
		    (unsigned int) Scr.MyDisplayWidth,
		    (unsigned int) Scr.MyDisplayHeight,
		    (unsigned int) 0,
		    CopyFromParent, (unsigned int) CopyFromParent,
		    (Visual *) CopyFromParent, valuemask, &attributes);
  XMapWindow(dpy, w);
  XDestroyWindow(dpy, w);
  XFlush(dpy);
}


void stick_function(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		    unsigned long context, char *action, int *Module)
{
  if (DeferExecution
      (eventp, &w, &tmp_win, &context, SELECT, ButtonRelease)) return;

  if (tmp_win->flags & STICKY) {
    tmp_win->flags &= ~STICKY;
  } else {
    tmp_win->flags |= STICKY;
  }
  BroadcastConfig(M_CONFIGURE_WINDOW, tmp_win);
  SetTitleBar(tmp_win, (Scr.Hilite == tmp_win), True);
}

void wait_func(XEvent * eventp, Window w, FvwmWindow * tmp_win,
	       unsigned long context, char *action, int *Module)
{
  Bool done = False;
  extern FvwmWindow *Tmp_win;

  while (!done) {
    if (My_XNextEvent(dpy, &Event)) {
      DispatchEvent();
      if (Event.type == MapNotify) {
	if ((Tmp_win) && (matchWildcards(action, Tmp_win->name) == True))
	  done = True;
	if ((Tmp_win) && (Tmp_win->class.res_class) &&
	    (matchWildcards(action, Tmp_win->class.res_class) == True))
	  done = True;
	if ((Tmp_win) && (Tmp_win->class.res_name) &&
	    (matchWildcards(action, Tmp_win->class.res_name) == True))
	  done = True;
      }
    }
  }
}


void raise_it_func(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		   unsigned long context, char *action, int *Module)
{
  long val1;
  int val1_unit, n;

  n = GetOneArgument(action, &val1, &val1_unit);

  if (val1 != 0) {
    FocusOn((FvwmWindow *) val1, 0, 1);
    if (((FvwmWindow *) (val1))->flags & ICONIFIED) {
      DeIconify((FvwmWindow *) val1);
      FocusOn((FvwmWindow *) val1, 0, 1);
    }
  }
}


void focus_func(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		unsigned long context, char *action, int *Module)
{
  long val1;
  int val1_unit, n;

  if (DeferExecution
      (eventp, &w, &tmp_win, &context, SELECT, ButtonRelease)) return;

  n = GetOneArgument(action, &val1, &val1_unit);

  if (!n)
    val1 = 1;

  FocusOn(tmp_win, 0, val1);
}


void warp_func(XEvent * eventp, Window w, FvwmWindow * tmp_win,
	       unsigned long context, char *action, int *Module)
{
  int val1_unit, val2_unit, n;
  int val1, val2;

  if (DeferExecution
      (eventp, &w, &tmp_win, &context, SELECT, ButtonRelease)) return;

  n = GetTwoArguments(action, &val1, &val2, &val1_unit, &val2_unit);

  if (n == 2)
    WarpOn(tmp_win, val1, val1_unit, val2, val2_unit);
  else
    WarpOn(tmp_win, 0, 0, 0, 0);
}


void popup_func(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		unsigned long context, char *action, int *Module)
{
  MenuRoot *menu;
  extern int menuFromFrameOrWindowOrTitlebar;
  extern FvwmWindow *ButtonWindow;

  ButtonWindow = tmp_win;
  menu = FindPopup(action);
  if (menu == NULL) {
    fvwm_msg(ERR, "popup_func", "No such menu %s", action);
    return;
  }
  ActiveItem = NULL;
  ActiveMenu = NULL;
  menuFromFrameOrWindowOrTitlebar = FALSE;
  do_menu(menu, 0);
}

void staysup_func(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		  unsigned long context, char *action, int *Module)
{
  MenuRoot *menu;
  extern int menuFromFrameOrWindowOrTitlebar;
  char *default_action = NULL, *menu_name = NULL;
  extern int menu_aborted;

  action = GetNextToken(action, &menu_name);
  GetNextToken(action, &default_action);
  menu = FindPopup(menu_name);
  if (menu == NULL) {
    if (menu_name != NULL) {
      fvwm_msg(ERR, "staysup_func", "No such menu %s", menu_name);
      free(menu_name);
    }
    if (default_action != NULL)
      free(default_action);
    return;
  }
  ActiveItem = NULL;
  ActiveMenu = NULL;
  menuFromFrameOrWindowOrTitlebar = FALSE;

  /* See bottom of windows.c for rationale behind this */
  if (eventp->type == ButtonPress)
    do_menu(menu, 1);
  else
    do_menu(menu, 0);

  if (menu_name != NULL)
    free(menu_name);
  if ((menu_aborted) && (default_action != NULL))
    ExecuteFunction(default_action, tmp_win, eventp, context, *Module);
  if (default_action != NULL)
    free(default_action);
}


void quit_func(XEvent * eventp, Window w, FvwmWindow * tmp_win,
	       unsigned long context, char *action, int *Module)
{
  if (master_pid != getpid())
    kill(master_pid, SIGTERM);
  Done(0, NULL);
}

void quit_screen_func(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		      unsigned long context, char *action, int *Module)
{
  Done(0, NULL);
}

void raiselower_func(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		     unsigned long context, char *action, int *Module)
{
  char *junk, *junkC;
  unsigned long junkN;
  int junkD, method, BoxJunk[4];

  if (DeferExecution
      (eventp, &w, &tmp_win, &context, SELECT, ButtonRelease)) return;
  if (tmp_win == NULL)
    return;

  if ((tmp_win == Scr.LastWindowRaised) || (tmp_win->flags & VISIBLE)) {
    LowerWindow(tmp_win);
    tmp_win->flags &= ~ONTOP;
  } else {
    RaiseWindow(tmp_win);
    if (LookInList
	(Scr.TheList, tmp_win->realname, &tmp_win->class, &junk, &junk,
	 &junkD, &junkD, &junkD, &junkC, &junkC, &junkN, BoxJunk,
	 &method) & STAYSONTOP_FLAG)
      tmp_win->flags |= ONTOP;
    KeepOnTop();
  }
}

void SetEdgeScroll(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		   unsigned long context, char *action, int *Module)
{
  int val1, val2, val1_unit, val2_unit, n;

  n = GetTwoArguments(action, &val1, &val2, &val1_unit, &val2_unit);
  if (n != 2) {
    fvwm_msg(ERR, "SetEdgeScroll", "EdgeScroll requires two arguments");
    return;
  }

  /*
     ** if edgescroll >1000 and < 100000m
     ** wrap at edges of desktop (a "spherical" desktop)
   */
  if (val1 >= 1000) {
    val1 /= 1000;
    Scr.flags |= EdgeWrapX;
  } else {
    Scr.flags &= ~EdgeWrapX;
  }
  if (val2 >= 1000) {
    val2 /= 1000;
    Scr.flags |= EdgeWrapY;
  } else {
    Scr.flags &= ~EdgeWrapY;
  }

  Scr.EdgeScrollX = val1 * val1_unit / 100;
  Scr.EdgeScrollY = val2 * val2_unit / 100;

  checkPanFrames();
}

void SetEdgeResistance(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		       unsigned long context, char *action, int *Module)
{
  int val1, val2, val1_unit, val2_unit, n;

  n = GetTwoArguments(action, &val1, &val2, &val1_unit, &val2_unit);
  if (n != 2) {
    fvwm_msg(ERR, "SetEdgeResistance",
	     "EdgeResistance requires two arguments");
    return;
  }

  Scr.ScrollResistance = val1;
  Scr.MoveResistance = val2;
}

void SetColormapFocus(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		      unsigned long context, char *action, int *Module)
{
  if (strncasecmp(action, "FollowsFocus", 12) == 0) {
    Scr.ColormapFocus = COLORMAP_FOLLOWS_FOCUS;
  } else if (strncasecmp(action, "FollowsMouse", 12) == 0) {
    Scr.ColormapFocus = COLORMAP_FOLLOWS_MOUSE;
  } else {
    fvwm_msg(ERR, "SetColormapFocus",
	     "ColormapFocus requires 1 arg: FollowsFocus or FollowsMouse");
    return;
  }
}

void SetClick(XEvent * eventp, Window w, FvwmWindow * tmp_win,
	      unsigned long context, char *action, int *Module)
{
  long val1;
  int val1_unit, n;

  n = GetOneArgument(action, &val1, &val1_unit);
  if (n != 1) {
    fvwm_msg(ERR, "SetClick", "ClickTime requires 1 argument");
    return;
  }

  Scr.ClickTime = val1;
}

void SetXOR(XEvent * eventp, Window w, FvwmWindow * tmp_win,
	    unsigned long context, char *action, int *Module)
{
  long val1;
  int val1_unit, n;
  XGCValues gcv;
  unsigned long gcm;

  n = GetOneArgument(action, &val1, &val1_unit);
  if (n != 1) {
    fvwm_msg(ERR, "SetXOR", "XORValue requires 1 argument");
    return;
  }
#if 0				/* 1 for XFree86 W32P */
  gcm = GCFunction | GCLineWidth | GCForeground | GCSubwindowMode;
  gcv.function = GXxor;
  gcv.line_width = 0;
  gcv.foreground = val1;
  gcv.subwindow_mode = IncludeInferiors;
#else
  gcm = GCFunction | GCLineWidth | GCFillStyle | GCForeground | GCStipple |
      GCBackground | GCSubwindowMode | GCPlaneMask;
  gcv.function = GXxor;
  gcv.line_width = 1;
  gcv.plane_mask = AllPlanes;
  gcv.stipple = Scr.gray_bitmap;
  gcv.fill_style = FillOpaqueStippled;
  gcv.foreground = val1;
  gcv.subwindow_mode = IncludeInferiors;
#endif
  Scr.DrawGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);
}

void SetOpaque(XEvent * eventp, Window w, FvwmWindow * tmp_win,
	       unsigned long context, char *action, int *Module)
{
  long val1;
  int val1_unit, n;

  n = GetOneArgument(action, &val1, &val1_unit);
  if (n != 1) {
    fvwm_msg(ERR, "SetOpaque", "OpaqueMoveSize requires 1 argument");
    return;
  }

  Scr.OpaqueSize = val1;
}


void SetDeskSize(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		 unsigned long context, char *action, int *Module)
{
  int val1, val2, val1_unit, val2_unit, n;

  n = GetTwoArguments(action, &val1, &val2, &val1_unit, &val2_unit);
  if (n != 2) {
    fvwm_msg(ERR, "SetDeskSize", "DesktopSize requires two arguments");
    return;
  }
  if ((val1_unit != Scr.MyDisplayWidth) ||
      (val2_unit != Scr.MyDisplayHeight)) {
    fvwm_msg(ERR, "SetDeskSize",
	     "DeskTopSize arguments should be unitless");
  }

  Scr.VxMax = val1;
  Scr.VyMax = val2;
  Scr.VxMax = Scr.VxMax * Scr.MyDisplayWidth - Scr.MyDisplayWidth;
  Scr.VyMax = Scr.VyMax * Scr.MyDisplayHeight - Scr.MyDisplayHeight;
  if (Scr.VxMax < 0)
    Scr.VxMax = 0;
  if (Scr.VyMax < 0)
    Scr.VyMax = 0;
  Broadcast(M_NEW_PAGE, 5, Scr.Vx, Scr.Vy, Scr.CurrentDesk, Scr.VxMax,
	    Scr.VyMax, 0, 0);

  checkPanFrames();
}

#ifdef XPM
char *PixmapPath = FVWM_ICONDIR;
void setPixmapPath(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		   unsigned long context, char *action, int *Module)
{
  static char *ptemp = NULL;
  char *tmp;

  if (ptemp == NULL)
    ptemp = PixmapPath;

  if ((PixmapPath != ptemp) && (PixmapPath != NULL))
    free(PixmapPath);
  tmp = stripcpy(action);
  PixmapPath = envDupExpand(tmp, 0);
  free(tmp);
}

#endif

char *IconPath = FVWM_ICONDIR;
void setIconPath(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		 unsigned long context, char *action, int *Module)
{
  static char *ptemp = NULL;
  char *tmp;

  if (ptemp == NULL)
    ptemp = IconPath;

  if ((IconPath != ptemp) && (IconPath != NULL))
    free(IconPath);
  tmp = stripcpy(action);
  IconPath = envDupExpand(tmp, 0);
  free(tmp);
}

#ifdef FVWM_MODULEDIR
char *ModulePath = FVWM_MODULEDIR;
#else
char *ModulePath = FVWMDIR;
#endif
void setModulePath(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		   unsigned long context, char *action, int *Module)
{
  static char *ptemp = NULL;
  char *tmp;

  if (ptemp == NULL)
    ptemp = ModulePath;

  if ((ModulePath != ptemp) && (ModulePath != NULL))
    free(ModulePath);
  tmp = stripcpy(action);
  ModulePath = envDupExpand(tmp, 0);
  free(tmp);
}

void SetMenuColors(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		   unsigned long context, char *action, int *Module)
{
  XGCValues gcv;
  unsigned long gcm;
  char *menufore = NULL, *menuback = NULL, *selfore = NULL, *selback =
      NULL;
  extern char *white, *black;
  FvwmWindow *hilight;

  action = GetNextToken(action, &menufore);
  action = GetNextToken(action, &menuback);
  action = GetNextToken(action, &selfore);
  GetNextToken(action, &selback);
  if (Scr.d_depth > 2) {
    if (menufore != NULL)
      Scr.MenuColors.fore = GetColor(menufore);
    if (menuback != NULL)
      Scr.MenuColors.back = GetColor(menuback);
    if (selfore != NULL)
      Scr.SelColors.fore = GetColor(selfore);
    if (selback != NULL)
      Scr.SelColors.back = GetColor(selback);
    Scr.MenuColors.hilite = GetHilite(Scr.MenuColors.back);
    Scr.MenuColors.shadow = GetShadow(Scr.MenuColors.back);
  } else {
    Scr.MenuColors.back = GetColor(white);
    Scr.MenuColors.fore = GetColor(black);
    Scr.SelColors.back = GetColor(white);
    Scr.SelColors.fore = GetColor(black);
    Scr.MenuColors.hilite = GetColor(white);
    Scr.MenuColors.shadow = GetColor(black);
  }

  gcm =
      GCFunction | GCPlaneMask | GCGraphicsExposures | GCLineWidth |
      GCForeground | GCBackground | GCFillStyle | GCFont;
  gcv.function = GXcopy;
  gcv.plane_mask = AllPlanes;
  gcv.graphics_exposures = False;
  gcv.line_width = 0;
  gcv.fill_style = FillSolid;
  gcv.font = Scr.StdFont.font->fid;

  gcv.foreground = Scr.SelColors.fore;
  gcv.background = Scr.SelColors.back;
  if (Scr.SelGC != NULL)
    XFreeGC(dpy, Scr.SelGC);
  Scr.SelGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

  gcv.foreground = Scr.MenuColors.fore;
  gcv.background = Scr.MenuColors.back;
  if (Scr.MenuGC != NULL)
    XFreeGC(dpy, Scr.MenuGC);
  Scr.MenuGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

  gcv.foreground = Scr.MenuColors.hilite;
  gcv.background = Scr.MenuColors.shadow;
  if (Scr.MenuReliefGC != NULL)
    XFreeGC(dpy, Scr.MenuReliefGC);
  Scr.MenuReliefGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

  gcv.foreground = Scr.MenuColors.shadow;
  gcv.background = Scr.MenuColors.hilite;
  if (Scr.MenuShadowGC != NULL)
    XFreeGC(dpy, Scr.MenuShadowGC);
  Scr.MenuShadowGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

  MakeMenus();

  if (menufore)
    free(menufore);
  if (menuback)
    free(menuback);
  if (selfore)
    free(selfore);
  if (selback)
    free(selback);
}

void SetHiColor(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		unsigned long context, char *action, int *Module)
{
  XGCValues gcv;
  unsigned long gcm;
  char *hifore = NULL, *hiback = NULL;
  extern char *white, *black;
  FvwmWindow *hilight;

  action = GetNextToken(action, &hifore);
  GetNextToken(action, &hiback);
  if (Scr.d_depth > 2) {
    if (hifore != NULL)
      Scr.ActiveTitleColors.fore = GetColor(hifore);
    if (hiback != NULL)
      Scr.ActiveTitleColors.back = GetColor(hiback);
  } else {
    Scr.ActiveTitleColors.back = GetColor(white);
    Scr.ActiveTitleColors.fore = GetColor(black);
  }

  gcm =
      GCFunction | GCPlaneMask | GCGraphicsExposures | GCLineWidth |
      GCForeground | GCBackground | GCFillStyle | GCFont;
  gcv.function = GXcopy;
  gcv.plane_mask = AllPlanes;
  gcv.graphics_exposures = False;
  gcv.line_width = 0;
  gcv.foreground = Scr.ActiveTitleColors.fore;
  gcv.background = Scr.ActiveTitleColors.back;
  gcv.fill_style = FillSolid;
  gcv.font = Scr.StdFont.font->fid;
  if (Scr.HiGC != NULL)
    XFreeGC(dpy, Scr.HiGC);
  Scr.HiGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

  if ((Scr.flags & WindowsCaptured) && (Scr.Hilite != NULL)) {
    hilight = Scr.Hilite;
    SetBorder(Scr.Hilite, False, True, True, None);
    SetBorder(hilight, True, True, True, None);
  }

  if (hifore)
    free(hifore);
  if (hiback)
    free(hiback);
}

void SetStdColor(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		 unsigned long context, char *action, int *Module)
{
  XGCValues gcv;
  unsigned long gcm;
  char *winfore = NULL, *winback = NULL, *stdfore = NULL, *stdback = NULL;
  extern char *white, *black;
  FvwmWindow *hilight;

  action = GetNextToken(action, &winfore);
  action = GetNextToken(action, &winback);
  action = GetNextToken(action, &stdfore);
  GetNextToken(action, &stdback);
  if (Scr.d_depth > 2) {
    if (winfore != NULL)
      Scr.WinColors.fore = GetColor(winfore);
    if (winback != NULL)
      Scr.WinColors.back = GetColor(winback);
    if (stdfore != NULL)
      Scr.TitleColors.fore = GetColor(stdfore);
    if (stdback != NULL)
      Scr.TitleColors.back = GetColor(stdback);
    Scr.WinColors.hilite = GetHilite(Scr.WinColors.back);
    Scr.WinColors.shadow = GetShadow(Scr.WinColors.back);
  } else {
    Scr.TitleColors.fore = GetColor(black);
    Scr.TitleColors.back = GetColor(white);
    Scr.WinColors.fore = GetColor(black);
    Scr.WinColors.back = GetColor(white);
    Scr.WinColors.shadow = GetColor(black);
    Scr.WinColors.hilite = GetColor(white);
  }
  gcm =
      GCFunction | GCPlaneMask | GCGraphicsExposures | GCLineWidth |
      GCForeground | GCBackground | GCFont | GCFillStyle;
  gcv.fill_style = FillSolid;
  gcv.plane_mask = AllPlanes;
  gcv.function = GXcopy;
  gcv.graphics_exposures = False;
  gcv.line_width = 0;
  gcv.font = Scr.StdFont.font->fid;

  gcv.foreground = Scr.WinColors.hilite;
  gcv.background = Scr.WinColors.shadow;
  if (Scr.WinReliefGC != NULL)
    XFreeGC(dpy, Scr.WinReliefGC);
  Scr.WinReliefGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

  gcv.foreground = Scr.WinColors.shadow;
  gcv.background = Scr.WinColors.hilite;
  if (Scr.WinShadowGC != NULL)
    XFreeGC(dpy, Scr.WinShadowGC);
  Scr.WinShadowGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

  gcv.foreground = Scr.WinColors.fore;
  gcv.background = Scr.WinColors.back;
  if (Scr.WinGC != NULL)
    XFreeGC(dpy, Scr.WinGC);
  Scr.WinGC = XCreateGC(dpy, Scr.Root, gcm, &gcv);

  if ((Scr.flags & WindowsCaptured) && (Scr.Hilite != NULL)) {
    hilight = Scr.Hilite;
    SetBorder(Scr.Hilite, False, True, True, None);
    SetBorder(hilight, True, True, True, None);
  }

  if (winfore)
    free(winfore);
  if (winback)
    free(winback);
  if (stdfore)
    free(stdfore);
  if (stdback)
    free(stdback);
}

void SetStickyColor(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		    unsigned long context, char *action, int *Module)
{
  char *fore = NULL, *back = NULL;
  extern char *white, *black;
  FvwmWindow *hilight;

  action = GetNextToken(action, &fore);
  GetNextToken(action, &back);
  if (Scr.d_depth > 2) {
    if (fore != NULL)
      Scr.StickyColors.fore = GetColor(fore);
    if (back != NULL)
      Scr.StickyColors.back = GetColor(back);
  } else {
    Scr.StickyColors.back = Scr.TitleColors.back;
    Scr.StickyColors.fore = Scr.TitleColors.fore;
  }

  if ((Scr.flags & WindowsCaptured) && (Scr.Hilite != NULL)) {
    hilight = Scr.Hilite;
    SetBorder(Scr.Hilite, False, True, True, None);
    SetBorder(hilight, True, True, True, None);
  }

  if (fore)
    free(fore);
  if (back)
    free(back);
}

void SetMenuFont(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		 unsigned long context, char *action, int *Module)
{
  XGCValues gcv;
  unsigned long gcm;
  char *font = NULL;
  extern char *white, *black;
  int wid, hei;

  action = GetNextToken(action, &font);

  if ((font == NULL) ||
      (Scr.StdFont.font = GetFontOrFixed(dpy, font)) == NULL) {
    fprintf(stderr,
	    "[SetMenuFont]: ERROR -- Couldn't load font '%s' or 'fixed'\n",
	    (font == NULL) ? ("NULL") : (font));
    exit(1);
  }
  Scr.StdFont.height =
      Scr.StdFont.font->ascent + Scr.StdFont.font->descent;
  Scr.StdFont.y = Scr.StdFont.font->ascent;
  Scr.EntryHeight = Scr.StdFont.height + HEIGHT_EXTRA;

  gcm = GCFont;
  gcv.font = Scr.StdFont.font->fid;
  if (Scr.MenuGC != NULL)
    XChangeGC(dpy, Scr.MenuGC, gcm, &gcv);
  if (Scr.SelGC != NULL)
    XChangeGC(dpy, Scr.SelGC, gcm, &gcv);
  if (Scr.MenuReliefGC != NULL)
    XChangeGC(dpy, Scr.MenuReliefGC, gcm, &gcv);
  if (Scr.MenuShadowGC != NULL)
    XChangeGC(dpy, Scr.MenuShadowGC, gcm, &gcv);

  if (Scr.SizeWindow != None) {
    Scr.SizeStringWidth = XTextWidth(Scr.StdFont.font,
				     " +8888 x +8888 ", 15);
    wid = Scr.SizeStringWidth + SIZE_HINDENT * 2;
    hei = Scr.StdFont.height + SIZE_VINDENT * 2;

    /* re-position coordinate window (centered) */
    XMoveResizeWindow(dpy, Scr.SizeWindow,
		      Scr.MyDisplayWidth / 2 - wid / 2,
		      Scr.MyDisplayHeight / 2 - hei / 2, wid, hei);
  }

  if (Scr.SizeWindow != None) {
    XSetWindowBackground(dpy, Scr.SizeWindow, Scr.WinColors.back);
  }

  MakeMenus();

  free(font);
}

void LoadIconFont(XEvent * eventp, Window w, FvwmWindow * tmp_win,
		  unsigned long context, char *action, int *Module)
{
  char *font;
  FvwmWindow *tmp;

  action = GetNextToken(action, &font);

  if ((Scr.IconFont.font = GetFontOrFixed(dpy, font)) == NULL) {
    fvwm_msg(ERR, "LoadIconFont", "Couldn't load font '%s' or 'fixed'\n",
	     font);
    free(font);
    return;
  }
  Scr.IconFont.height =
      Scr.IconFont.font->ascent + Scr.IconFont.font->descent;
  Scr.IconFont.y = Scr.IconFont.font->ascent;

  free(font);
  tmp = Scr.FvwmRoot.next;
  while (tmp != NULL) {
    RedoIconName(tmp);

    if (tmp->flags & ICONIFIED) {
      DrawIconWindow(tmp);
    }
    tmp = tmp->next;
  }
}

void LoadWindowFont(XEvent * eventp, Window win, FvwmWindow * tmp_win,
		    unsigned long context, char *action, int *Module)
{
  char *font;
  FvwmWindow *tmp, *hi;
  int x, y, w, h, extra_height;
  XFontStruct *newfont;

  action = GetNextToken(action, &font);

  if ((newfont = GetFontOrFixed(dpy, font)) != NULL) {
    Scr.WindowFont.font = newfont;
    Scr.WindowFont.height =
	Scr.WindowFont.font->ascent + Scr.WindowFont.font->descent;
    Scr.WindowFont.y = Scr.WindowFont.font->ascent;
    extra_height = Scr.TitleHeight;
    Scr.TitleHeight =
	Scr.WindowFont.font->ascent + Scr.WindowFont.font->descent + 3;
    extra_height -= Scr.TitleHeight;
    tmp = Scr.FvwmRoot.next;
    hi = Scr.Hilite;
    while (tmp != NULL) {
      x = tmp->frame_x;
      y = tmp->frame_y;
      w = tmp->frame_width;
      h = tmp->frame_height - extra_height;
      tmp->frame_x = 0;
      tmp->frame_y = 0;
      tmp->frame_height = 0;
      tmp->frame_width = 0;
      SetupFrame(tmp, x, y, w, h, True);
      SetTitleBar(tmp, True, True);
      SetTitleBar(tmp, False, True);
      tmp = tmp->next;
    }
    SetTitleBar(hi, True, True);

  } else {
    fvwm_msg(ERR, "LoadWindowFont", "Couldn't load font '%s' or 'fixed'\n",
	     font);
  }

  free(font);
}

/*****************************************************************************
 * 
 * Changes a button decoration style
 *
 * Rewritten by dtrg to use XPM's rather than a coordinate list 5/5/96
 *
 ****************************************************************************/
void ButtonStyle(XEvent * eventp, Window junk, FvwmWindow * tmp_win,
		 unsigned long context, char *action, int *Module)
{
#ifdef XPM
  int n, button;
  char filename[256];
  XWindowAttributes root_attr;
  XpmAttributes xpm_attributes;
  extern char *PixmapPath;
  char *path = NULL;
  Pixmap junkmaskPixmap;

  n = sscanf(action, "%d %s", &button, filename);
  if (n != 2) {
    fvwm_msg(ERR, "ButtonStyle", "Syntax error");
    return;
  }
  button--;

  if ((button < 0) || (button > 9)) {
    fvwm_msg(ERR, "ButtonStyle", "Button number must be 1..10");
    return;
  }

  path = findIconFile(filename, PixmapPath, R_OK);
  if (path == NULL) {
    fvwm_msg(ERR, "ButtonStyle", "XPM file not found");
    return;
  }

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

/*
  fprintf(stderr, "Loading XPM file %s\n", path);

  if(Scr.button_pixmap[button] != None)
    XFreePixmap(dpy, Scr.button_pixmap[button]);
  if(Scr.button_pmask[button] != None)
    XFreePixmap(dpy, Scr.button_mask[button]);
*/

  if (XpmReadFileToPixmap(dpy, Scr.Root, path,
			  &Scr.button_pixmap[button],
			  &junkmaskPixmap, &xpm_attributes) != XpmSuccess) {
    fvwm_msg(ERR, "ButtonStyle", "Couldn't load XPM file");
    free(path);
    return;
  }

  if ((xpm_attributes.width != 13) || (xpm_attributes.height != 11)) {
    fvwm_msg(ERR, "ButtonStyle", "Warning: XPM file isn't the right size");
    free(path);
    return;
  }
  free(path);
#endif
}

/**************************************************************************
 *
 * Direction = 1 ==> "Next" operation
 * Direction = -1 ==> "Previous" operation 
 *
 **************************************************************************/
FvwmWindow *Circulate(char *action, int Direction, char **restofline)
{
  int l, pass = 0;
  FvwmWindow *fw, *found = NULL;
  char *t, *tstart, *name =
      NULL, *expression, *condition, *prev_condition = NULL;
  char *orig_expr;
  Bool needsIconic = 0;
  Bool needsNormal = 0;
  Bool needsCurrentDesk = 0;
  Bool needsCurrentScreen = 0;
  Bool needsVisible = 0;
  Bool needsInvisible = 0;
  char *AnyWindow = "*";
  Bool useCirculateHit = 0;
  Bool useCirculateHitIcon = 0;

  l = 0;

  if (action == NULL)
    return;

  t = action;
  while (isspace(*t) && (*t != 0))
    t++;
  if (*t == '[') {
    t++;
    tstart = t;

    while ((*t != 0) && (*t != ']')) {
      t++;
      l++;
    }
    if (*t == 0) {
      fvwm_msg(ERR, "Circulate", "Conditionals require closing brace");
      return;
    }

    *restofline = t + 1;

    orig_expr = expression = safemalloc(l + 1);
    strncpy(expression, tstart, l);
    expression[l] = 0;
    expression = GetNextToken(expression, &condition);
    while ((condition != NULL) && (strlen(condition) > 0)) {
      if (strcasecmp(condition, "iconic") == 0)
	needsIconic = 1;
      else if (strcasecmp(condition, "!iconic") == 0)
	needsNormal = 1;
      else if (strcasecmp(condition, "CurrentDesk") == 0)
	needsCurrentDesk = 1;
      else if (strcasecmp(condition, "Visible") == 0)
	needsVisible = 1;
      else if (strcasecmp(condition, "!Visible") == 0)
	needsInvisible = 1;
      else if (strcasecmp(condition, "CurrentScreen") == 0)
	needsCurrentScreen = 1;
      else if (strcasecmp(condition, "CirculateHit") == 0)
	useCirculateHit = 1;
      else if (strcasecmp(condition, "CirculateHitIcon") == 0)
	useCirculateHitIcon = 1;
      else {
	name = condition;
	condition = NULL;
      }
      if (prev_condition)
	free(prev_condition);
      prev_condition = condition;
      expression = GetNextToken(expression, &condition);
    }
    if (prev_condition != NULL)
      free(prev_condition);
    if (orig_expr != NULL)
      free(orig_expr);
  } else
    *restofline = t;

  if (name == NULL)
    name = AnyWindow;

  if (Scr.Focus != NULL) {
    if (Direction == 1)
      fw = Scr.Focus->prev;
    else
      fw = Scr.Focus->next;
  } else
    fw = Scr.FvwmRoot.prev;

  while ((pass < 3) && (found == NULL)) {
    while ((fw != NULL) && (found == NULL) && (fw != &Scr.FvwmRoot)) {
      /* Make CirculateUp and CirculateDown take args. by Y.NOMURA */
      if (((matchWildcards(name, fw->name)) ||
	   (matchWildcards(name, fw->icon_name)) ||
	   (fw->class.res_class &&
	    matchWildcards(name, fw->class.res_class)) ||
	   (fw->class.res_name &&
	    matchWildcards(name, fw->class.res_name))) &&
	  ((useCirculateHit) || !(fw->flags & CirculateSkip)) &&
	  (((useCirculateHitIcon) && (fw->flags & ICONIFIED)) ||
	   !((fw->flags & CirculateSkipIcon) && (fw->flags & ICONIFIED)))
	  && (((!needsIconic) || (fw->flags & ICONIFIED))
	      && ((!needsNormal) || (!(fw->flags & ICONIFIED)))
	      && ((!needsCurrentDesk) || (fw->Desk == Scr.CurrentDesk))
	      && ((!needsVisible) || (fw->flags & VISIBLE))
	      && ((!needsInvisible) || (!(fw->flags & VISIBLE)))
	      && ((!needsCurrentScreen)
		  || ((fw->frame_x < Scr.MyDisplayWidth)
		      && (fw->frame_y < Scr.MyDisplayHeight)
		      && (fw->frame_x + fw->frame_width > 0)
		      && (fw->frame_y + fw->frame_height > 0)))))
	found = fw;
      else {
	if (Direction == 1)
	  fw = fw->prev;
	else
	  fw = fw->next;
      }
    }
    if ((fw == NULL) || (fw == &Scr.FvwmRoot)) {
      if (Direction == 1) {
	/* Go to end of list */
	fw = &Scr.FvwmRoot;
	while ((fw) && (fw->next != NULL)) {
	  fw = fw->next;
	}
      } else {
	/* GO to top of list */
	fw = Scr.FvwmRoot.next;
      }
    }
    pass++;
  }
  if ((name != NULL) && (name != AnyWindow))
    free(name);
  return found;

}

void PrevFunc(XEvent * eventp, Window junk, FvwmWindow * tmp_win,
	      unsigned long context, char *action, int *Module)
{
  FvwmWindow *found;
  char *restofline;

  found = Circulate(action, -1, &restofline);
  if (found != NULL) {
    ExecuteFunction(restofline, found, eventp, C_WINDOW, *Module);
  }

}

void module_zapper(XEvent * eventp, Window junk, FvwmWindow * tmp_win,
		   unsigned long context, char *action, int *Module)
{
  char *condition;

  GetNextToken(action, &condition);
  KillModuleByName(condition);
  free(condition);
}

void NextFunc(XEvent * eventp, Window junk, FvwmWindow * tmp_win,
	      unsigned long context, char *action, int *Module)
{
  FvwmWindow *found;
  char *restofline;

  found = Circulate(action, 1, &restofline);
  if (found != NULL) {
    ExecuteFunction(restofline, found, eventp, C_WINDOW, *Module);
  }

}


void NoneFunc(XEvent * eventp, Window junk, FvwmWindow * tmp_win,
	      unsigned long context, char *action, int *Module)
{
  FvwmWindow *found;
  char *restofline;

  found = Circulate(action, 1, &restofline);
  if (found == NULL) {
    ExecuteFunction(restofline, NULL, eventp, C_ROOT, *Module);
  }
}



/***********************************************************************
 *
 *  Procedure:
 *	Reborder - Removes fvwm border windows
 *
 ************************************************************************/
void Recapture(XEvent * eventp, Window junk, FvwmWindow * tmp_win,
	       unsigned long context, char *action, int *Module)
{
  FvwmWindow *tmp, *next;	/* temp fvwm window structure */
  Window w;
  extern Bool PPosOverride;
  unsigned long data[1];
  extern long isIconicState;
  unsigned char *prop;
  Atom atype;
  int aformat, i;
  unsigned int nchildren;
  unsigned long nitems, bytes_remain;
  Window root, parent, *children;

  if (!XQueryTree(dpy, Scr.Root, &root, &parent, &children, &nchildren))
    return;

  BlackoutScreen();

  PPosOverride = True;
  /* put a border back around all windows */
  XGrabServer(dpy);
  tmp = Scr.FvwmRoot.next;
  for (i = 0; i < nchildren; i++) {
    if (XFindContext(dpy, children[i], FvwmContext,
		     (caddr_t *) & tmp) != XCNOENT) {
      isIconicState = DontCareState;
      if (XGetWindowProperty(dpy, tmp->w, _XA_WM_STATE, 0L, 3L, False,
			     _XA_WM_STATE,
			     &atype, &aformat, &nitems, &bytes_remain,
			     &prop) == Success) {
	if (prop != NULL) {
	  isIconicState = *(long *) prop;
	  XFree(prop);
	}
      }
      next = tmp->next;
      data[0] = (unsigned long) tmp->Desk;
      XChangeProperty(dpy, tmp->w, _XA_WM_DESKTOP, _XA_WM_DESKTOP, 32,
		      PropModeReplace, (unsigned char *) data, 1);

      XSelectInput(dpy, tmp->w, 0);
      w = tmp->w;
      XUnmapWindow(dpy, tmp->frame);
      XUnmapWindow(dpy, w);
      RestoreWithdrawnLocation(tmp, True);
      Destroy(tmp);
      Event.xmaprequest.window = w;
      HandleMapRequestKeepRaised(BlackoutWin);
      tmp = next;
    }
  }
  UnBlackoutScreen();
  isIconicState = DontCareState;	/* needs to be down here instead of above? */
  if (nchildren > 0)
    XFree((char *) children);
  PPosOverride = False;
  KeepOnTop();
  XUngrabServer(dpy);
  XSync(dpy, 0);

}
