/****************************************************************************
 * This module is based on Twm, but has been siginificantly modified 
 * by Rob Nation 
 ****************************************************************************/
/*****************************************************************************/
/**       Copyright 1988 by Evans & Sutherland Computer Corporation,        **/
/**                          Salt Lake City, Utah                           **/
/**  Portions Copyright 1989 by the Massachusetts Institute of Technology   **/
/**                        Cambridge, Massachusetts                         **/
/**                                                                         **/
/**                           All Rights Reserved                           **/
/**                                                                         **/
/**    Permission to use, copy, modify, and distribute this software and    **/
/**    its documentation  for  any  purpose  and  without  fee is hereby    **/
/**    granted, provided that the above copyright notice appear  in  all    **/
/**    copies and that both  that  copyright  notice  and  this  permis-    **/
/**    sion  notice appear in supporting  documentation,  and  that  the    **/
/**    names of Evans & Sutherland and M.I.T. not be used in advertising    **/
/**    in publicity pertaining to distribution of the  software  without    **/
/**    specific, written prior permission.                                  **/
/**                                                                         **/
/**    EVANS & SUTHERLAND AND M.I.T. DISCLAIM ALL WARRANTIES WITH REGARD    **/
/**    TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES  OF  MERCHANT-    **/
/**    ABILITY  AND  FITNESS,  IN  NO  EVENT SHALL EVANS & SUTHERLAND OR    **/
/**    M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL  DAM-    **/
/**    AGES 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.                                     **/
/*****************************************************************************/


/***********************************************************************
 *
 * fvwm event handling
 *
 ***********************************************************************/

#include "../configure.h"

#ifdef ISC
#include <sys/bsdtypes.h>
#endif

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <signal.h>
/* Some people say that AIX and AIXV3 need 3 preceding underscores, other say
 * no. I'll do both */
#if defined ___AIX || defined _AIX || defined __QNX__ || defined ___AIXV3 || defined AIXV3 || defined _SEQUENT_
#include <sys/select.h>
#endif

#include "fvwm.h"
#include <X11/Xatom.h>
#include "menus.h"
#include "misc.h"
#include "parse.h"
#include "screen.h"
#ifdef SHAPE
#include <X11/extensions/shape.h>
#endif /* SHAPE */
#include "module.h"

#ifndef NO_PAGER
extern FvwmWindow *FindCounterpart(Window target);
#endif /* NO_PAGER */

unsigned int mods_used = (ShiftMask | ControlMask | Mod1Mask |
			  Mod2Mask| Mod3Mask| Mod4Mask| Mod5Mask);
extern int menuFromFrameOrWindowOrTitlebar;


int Context = C_NO_CONTEXT;	/* current button press context */
int Button = 0;
FvwmWindow *ButtonWindow;	/* button press window structure */
XEvent Event;			/* the current event */
FvwmWindow *Tmp_win;		/* the current fvwm window */

int last_event_type=0;
Window last_event_window=0;

#ifdef SHAPE
extern int ShapeEventBase;
void HandleShapeNotify(void);
#endif /* SHAPE */

Window PressedW;

/***********************************************************************
 *
 *  Procedure:
 *	DispatchEvent - handle a single X event stored in global var Event
 *
 ************************************************************************/
void DispatchEvent()
{
  Window w = Event.xany.window;

  StashEventTime(&Event);

  if (XFindContext (dpy, w, FvwmContext, (caddr_t *) &Tmp_win) == XCNOENT)
    Tmp_win = NULL;
  last_event_type = Event.type;
  last_event_window = w;

  switch(Event.type)
    {
    case Expose:
      HandleExpose();
      break;
    case DestroyNotify:
      HandleDestroyNotify();
      break;
    case MapRequest:
      HandleMapRequest();
      break;
    case MapNotify:
      HandleMapNotify();
      break;
    case UnmapNotify:
      HandleUnmapNotify();
      break;
    case MotionNotify:
      HandleMotionNotify();
      break;
    case ButtonPress:
      HandleButtonPress();
      break;
    case ButtonRelease:
      HandleButtonRelease();
      break;
    case EnterNotify:
      HandleEnterNotify();
      break;
    case LeaveNotify:
      HandleLeaveNotify();
      break;
    case FocusIn:
      HandleFocusIn();
      break;
    case ConfigureRequest:
      HandleConfigureRequest();
      break;
    case ClientMessage:
      HandleClientMessage();
      break;
    case PropertyNotify:
      HandlePropertyNotify();
      break;
    case KeyPress:
      HandleKeyPress();
      break;
    case VisibilityNotify:
      HandleVisibilityNotify();
      break;
    case ColormapNotify:
      HandleColormapNotify();
      break;
    default:
#ifdef SHAPE
      if(Event.type == (ShapeEventBase + ShapeNotify)) 
	HandleShapeNotify();
#endif /* SHAPE */
      
      break;
    }
  return;
}


/***********************************************************************
 *
 *  Procedure:
 *	HandleEvents - handle X events
 *
 ************************************************************************/
void HandleEvents()
{
  while (TRUE)
    {
      last_event_type = 0;
      if(My_XNextEvent(dpy, &Event))
	{
	  DispatchEvent ();
	}
    }
}

/***********************************************************************
 *
 *  Procedure:
 *	Find the Fvwm context for the Event.
 *
 ************************************************************************/
int GetContext(FvwmWindow *t, XEvent *e, Window *w)
{
  int Context,i;
  
  if(!t)
    return C_ROOT; 
  
  Context = C_NO_CONTEXT;
  *w= e->xany.window;
  
  if(*w == Scr.NoFocusWin)
    return C_ROOT;
  
  /* Since key presses and button presses are grabbed in the frame
   * when we have re-parented windows, we need to find out the real
   * window where the event occured */
  if((e->type == KeyPress)&&(e->xkey.subwindow != None))
    *w = e->xkey.subwindow;
  
  if((e->type == ButtonPress)&&(e->xbutton.subwindow != None)&&
     ((e->xbutton.subwindow == t->w)||(e->xbutton.subwindow == t->Parent)))
    *w = e->xbutton.subwindow;
  
  if (*w == Scr.Root)
    Context = C_ROOT;
  if (t)
    {
      if (*w == t->title_w)
	Context = C_TITLE;
      if ((*w == t->w)||(*w == t->Parent))
	Context = C_WINDOW;
      if (*w == t->icon_w)
	Context = C_ICON;
      if (*w == t->icon_pixmap_w)
	Context = C_ICON;
      if (*w == t->frame)
	Context = C_SIDEBAR;
      for(i=0;i<4;i++)
	if(*w == t->corners[i])
	  {
	    Context = C_FRAME;
	    Button = i;
	  }
      for(i=0;i<4;i++)
	if(*w == t->sides[i])
	  {
	    Context = C_SIDEBAR;
	    Button = i;
	  }
      for(i=0;i<Scr.nr_left_buttons;i++)
	{
	  if(*w == t->left_w[i])
	    {
	      Context = (1<<i)*C_L1;
	      Button = i;
	    }
	}
      for(i=0;i<Scr.nr_right_buttons;i++)
	{
	  if(*w == t->right_w[i])
	    {
	      Context = (1<<i)*C_R1;
	      Button = i;
	    }
	}
    }
  return Context;
}

/***********************************************************************
 *
 *  Procedure:
 *	HandleFocusIn - handles focus in events
 *
 ************************************************************************/
void HandleFocusIn()
{
  XEvent d;
  Window w;
  
  w= Event.xany.window;
  while(XCheckTypedEvent(dpy,FocusIn,&d))
    {
      w = d.xany.window;
    }
  if (XFindContext (dpy, w, FvwmContext, (caddr_t *) &Tmp_win) == XCNOENT)
    {
      Tmp_win = NULL;
    }
  
  if(!Tmp_win)
    {
      SetBorder(Scr.Hilite,False,True,True,None);
      Broadcast(M_FOCUS_CHANGE,3,0,0,0,0,0,0,0);
    }
  else if(Tmp_win != Scr.Hilite)
    {
      SetBorder(Tmp_win,True,True,True,None);
      Broadcast(M_FOCUS_CHANGE,3,Tmp_win->w,
		Tmp_win->frame,(unsigned long)Tmp_win,0,0,0,0);
    }
}

/***********************************************************************
 *
 *  Procedure:
 *	HandleKeyPress - key press event handler
 *
 ************************************************************************/
void HandleKeyPress()
{
  FuncKey *key;
  unsigned int modifier;
  Window dummy;
  
  Context = GetContext(Tmp_win,&Event,&dummy);

  modifier = (Event.xkey.state & mods_used);
  for (key = Scr.FuncKeyRoot.next; key != NULL; key = key->next)
    {
      ButtonWindow = Tmp_win;
      /* Here's a real hack - some systems have two keys with the
       * same keysym and different keycodes. This converts all
       * the cases to one keycode. */
      Event.xkey.keycode = 
	XKeysymToKeycode(dpy,XKeycodeToKeysym(dpy,Event.xkey.keycode,0));
      if ((key->keycode == Event.xkey.keycode) &&
	  ((key->mods == (modifier&(~LockMask)))||
	   (key->mods == AnyModifier)) &&
	  (key->cont & Context))
	{
	  ExecuteFunction(key->func, key->action, Event.xany.window,Tmp_win,
			  &Event,Context,key->val1,key->val2,
			  key->val1_unit,key->val2_unit,
			  key->menu,-1);
	  return;
	}
    }
  
  /* if we get here, no function key was bound to the key.  Send it
   * to the client if it was in a window we know about.
   */
  if (Tmp_win)
    {
      if(Event.xkey.window != Tmp_win->w)
	{
	  Event.xkey.window = Tmp_win->w;
	  XSendEvent(dpy, Tmp_win->w, False, KeyPressMask, &Event);
	}
    }
  ButtonWindow = NULL;
}


/***********************************************************************
 *
 *  Procedure:
 *	HandlePropertyNotify - property notify event handler
 *
 ***********************************************************************/
#define MAX_NAME_LEN 200L		/* truncate to this many */
#define MAX_ICON_NAME_LEN 200L		/* ditto */

void HandlePropertyNotify()
{
  char *prop = NULL;
  Atom actual = None;
  int actual_format;
  unsigned long nitems, bytesafter;
  
  if ((!Tmp_win)||(XGetGeometry(dpy, Tmp_win->w, &JunkRoot, &JunkX, &JunkY,
				&JunkWidth, &JunkHeight, &JunkBW, &JunkDepth) == 0))
    return;
  
  switch (Event.xproperty.atom) 
    {
    case XA_WM_NAME:
      if (XGetWindowProperty (dpy, Tmp_win->w, Event.xproperty.atom, 0L, 
			      MAX_NAME_LEN, False, XA_STRING, &actual,
			      &actual_format, &nitems, &bytesafter,
			      (unsigned char **) &prop) != Success ||
	  actual == None)
	return;
      if (!prop) prop = NoName;
      free_window_names (Tmp_win, True, False);
      
      Tmp_win->name = prop;
      BroadcastName(M_WINDOW_NAME,Tmp_win->w,Tmp_win->frame,
		    (unsigned long)Tmp_win,Tmp_win->name);
      
      /* fix the name in the title bar */
      if(!(Tmp_win->flags & ICONIFIED))
	SetTitleBar(Tmp_win,(Scr.Hilite==Tmp_win),True);
      
      /*
       * if the icon name is NoName, set the name of the icon to be
       * the same as the window 
       */
      if (Tmp_win->icon_name == NoName) 
	{
	  Tmp_win->icon_name = Tmp_win->name;
	  BroadcastName(M_ICON_NAME,Tmp_win->w,Tmp_win->frame,
			(unsigned long)Tmp_win,Tmp_win->icon_name);
	  RedoIconName(Tmp_win);
	}
      break;
      
    case XA_WM_ICON_NAME:
      if (XGetWindowProperty (dpy, Tmp_win->w, Event.xproperty.atom, 0, 
			      MAX_ICON_NAME_LEN, False, XA_STRING, &actual,
			      &actual_format, &nitems, &bytesafter,
			      (unsigned char **) &prop) != Success ||
	  actual == None)
	return;
      if (!prop) prop = NoName;
      free_window_names (Tmp_win, False, True);
      Tmp_win->icon_name = prop;
      BroadcastName(M_ICON_NAME,Tmp_win->w,Tmp_win->frame,
		    (unsigned long)Tmp_win,Tmp_win->icon_name);
      RedoIconName(Tmp_win);
      break;
      
    case XA_WM_HINTS:
      if (Tmp_win->wmhints) 
	XFree ((char *) Tmp_win->wmhints);
      Tmp_win->wmhints = XGetWMHints(dpy, Event.xany.window);

      if(Tmp_win->wmhints == NULL)
	return;

      if((Tmp_win->wmhints->flags & IconPixmapHint)||
	 (Tmp_win->wmhints->flags & IconWindowHint))
	if(Tmp_win->icon_bitmap_file == Scr.DefaultIcon)
	  Tmp_win->icon_bitmap_file = (char *)0;
      
      if((Tmp_win->wmhints->flags & IconPixmapHint)||
         (Tmp_win->wmhints->flags & IconWindowHint))
	{
	  if (!(Tmp_win->flags & SUPPRESSICON))
	    {
              if (Tmp_win->icon_w)
                XDestroyWindow(dpy,Tmp_win->icon_w);
	      XDeleteContext(dpy, Tmp_win->icon_w, FvwmContext);
	      if(Tmp_win->flags & ICON_OURS)
		{
		  if(Tmp_win->icon_pixmap_w != None)
		    {
		      XDestroyWindow(dpy,Tmp_win->icon_pixmap_w);
		      XDeleteContext(dpy, Tmp_win->icon_pixmap_w, FvwmContext);
		    }
		}
	      else 
		XUnmapWindow(dpy,Tmp_win->icon_pixmap_w);
	    }
          Tmp_win->icon_w = None;
          Tmp_win->icon_pixmap_w = None;
	  Tmp_win->iconPixmap = (Window)NULL;
	  if(Tmp_win->flags & ICONIFIED)
	    {
	      Tmp_win->flags &= ~ICONIFIED;
	      Tmp_win->flags &= ~ICON_UNMAPPED;
	      CreateIconWindow(Tmp_win,Tmp_win->icon_x_loc,Tmp_win->icon_y_loc);
	      Broadcast(M_ICONIFY,7,Tmp_win->w,Tmp_win->frame,
			(unsigned long)Tmp_win,
			Tmp_win->icon_x_loc,
			Tmp_win->icon_y_loc,
			Tmp_win->icon_w_width,
			Tmp_win->icon_w_height);
	      BroadcastConfig(M_CONFIGURE_WINDOW,Tmp_win);
	      
	      if (!(Tmp_win->flags & SUPPRESSICON))
		{
		  LowerWindow(Tmp_win);
		  AutoPlace(Tmp_win);
		  if(Tmp_win->Desk == Scr.CurrentDesk)
		    {
                      if(Tmp_win->icon_w)
                        XMapWindow(dpy, Tmp_win->icon_w);
		      if(Tmp_win->icon_pixmap_w != None)
			XMapWindow(dpy, Tmp_win->icon_pixmap_w);
		    }
		}
	      Tmp_win->flags |= ICONIFIED;
	      DrawIconWindow(Tmp_win);
	    }
	}
      break;
      
    case XA_WM_NORMAL_HINTS:
      GetWindowSizeHints (Tmp_win);
      BroadcastConfig(M_CONFIGURE_WINDOW,Tmp_win);
      break;
      
    default:
      if(Event.xproperty.atom == _XA_WM_PROTOCOLS)
	FetchWmProtocols (Tmp_win);
      else if (Event.xproperty.atom == _XA_WM_COLORMAP_WINDOWS)
	{
	  FetchWmColormapWindows (Tmp_win);	/* frees old data */
	  ReInstallActiveColormap();
	}
      else if(Event.xproperty.atom == _XA_WM_STATE)
	{
	  if((Scr.flags & ClickToFocus)&&(Tmp_win == Scr.Focus)&&
	     (Tmp_win != NULL))
	    {
	      Scr.Focus = NULL;
	      SetFocus(Tmp_win->w,Tmp_win);
	      ClearCirculatedFlag();
	    }
	}
      break;
    }
}


/***********************************************************************
 *
 *  Procedure:
 *	HandleClientMessage - client message event handler
 *
 ************************************************************************/
void HandleClientMessage()
{
  XEvent button;
  
  if ((Event.xclient.message_type == _XA_WM_CHANGE_STATE)&&
      (Tmp_win)&&(Event.xclient.data.l[0]==IconicState)&&
      !(Tmp_win->flags & ICONIFIED))
    {
      XQueryPointer( dpy, Scr.Root, &JunkRoot, &JunkChild,
		    &(button.xmotion.x_root),
		    &(button.xmotion.y_root),
		    &JunkX, &JunkY, &JunkMask);
      button.type = 0;
      ExecuteFunction(F_ICONIFY, NULLSTR, Event.xany.window,
		      Tmp_win, &button, C_FRAME,0,0, 0,0,
		      (MenuRoot *)0,-1);
    }
}

/***********************************************************************
 *
 *  Procedure:
 *	HandleExpose - expose event handler
 *
 ***********************************************************************/
void HandleExpose()
{
  if (Event.xexpose.count != 0)
    return;
  
  if (Tmp_win)
    {
#ifndef NO_PAGER
      if((Tmp_win->w == Scr.Pager_w)||
	 (Tmp_win->w == Scr.CPagerWin))
	{
	  ReallyRedrawPager();
	}
#endif
      if ((Event.xany.window == Tmp_win->title_w))
	{
	  SetTitleBar(Tmp_win,(Scr.Hilite == Tmp_win),False);
	}
      else
	{
	  SetBorder(Tmp_win,(Scr.Hilite == Tmp_win),True,True,Event.xany.window);
	}
    }
#ifndef NO_PAGER
  else
    {
      if(FindCounterpart(Event.xany.window))
	ReallyRedrawPager();
    }
#endif
  return;
}



/***********************************************************************
 *
 *  Procedure:
 *	HandleDestroyNotify - DestroyNotify event handler
 *
 ***********************************************************************/
void HandleDestroyNotify()
{
  Destroy(Tmp_win);
}




/***********************************************************************
 *
 *  Procedure:
 *	HandleMapRequest - MapRequest event handler
 *
 ************************************************************************/
void HandleMapRequest()
{
  extern long isIconicState;
  
  Event.xany.window = Event.xmaprequest.window;
  
  if(XFindContext(dpy, Event.xany.window, FvwmContext, 
		  (caddr_t *)&Tmp_win)==XCNOENT)
    Tmp_win = NULL;
  
  XFlush(dpy);
  
  /* If the window has never been mapped before ... */
  if(!Tmp_win)
    {
      /* Add decorations. */
      Tmp_win = AddWindow(Event.xany.window);
      if (Tmp_win == NULL)
	return;
    }

  /* If it's not merely iconified, and we have hints, use them. */
  if (!(Tmp_win->flags & ICONIFIED))
    {
      int state;
      
      if(Tmp_win->wmhints && (Tmp_win->wmhints->flags & StateHint))
	state = Tmp_win->wmhints->initial_state;
      else
	state = NormalState;
      
      if(Tmp_win->flags & STARTICONIC)
	state = IconicState;

      if(isIconicState != DontCareState) 
	state = isIconicState;

      XGrabServer (dpy);
      switch (state) 
	{
	case DontCareState:
	case NormalState:
	case InactiveState:
	default:
	  if (Tmp_win->Desk == Scr.CurrentDesk)
	    {
	      XMapWindow(dpy, Tmp_win->w);
	      XMapWindow(dpy, Tmp_win->frame);
	      Tmp_win->flags |= MAP_PENDING;
	      SetMapStateProp(Tmp_win, NormalState);
	      if(Scr.flags & ClickToFocus)
		{
		  SetFocus(Tmp_win->w,Tmp_win);
		  ClearCirculatedFlag();
		}
	    }
	  else
	    {
	      XMapWindow(dpy, Tmp_win->w);
	      SetMapStateProp(Tmp_win, NormalState);
	    }
	  break;
	  
	case IconicState:
	  Iconify(Tmp_win, 0, 0);
	  break;
	}
      XSync(dpy,0);
      XUngrabServer(dpy);
    }
  /* If no hints, or currently an icon, just "deiconify" */
  else
    {
      DeIconify(Tmp_win);
    }
  KeepOnTop();
}


/***********************************************************************
 *
 *  Procedure:
 *	HandleMapNotify - MapNotify event handler
 *
 ***********************************************************************/
void HandleMapNotify()
{
  if (!Tmp_win)
    {
      return;
    }

  /*
   * Need to do the grab to avoid race condition of having server send
   * MapNotify to client before the frame gets mapped; this is bad because
   * the client would think that the window has a chance of being viewable
   * when it really isn't.
   */
  XGrabServer (dpy);
  if (Tmp_win->icon_w)
    XUnmapWindow(dpy, Tmp_win->icon_w);
  if(Tmp_win->icon_pixmap_w != None)
    XUnmapWindow(dpy, Tmp_win->icon_pixmap_w);
  XMapSubwindows(dpy, Tmp_win->frame);

  if(Tmp_win->Desk == Scr.CurrentDesk)
    {
      XMapWindow(dpy, Tmp_win->frame);
    }

  if(Tmp_win->flags & ICONIFIED)
    Broadcast(M_DEICONIFY,3,Tmp_win->w,Tmp_win->frame,
	      (unsigned long)Tmp_win,0,0,0,0);
  else
    {
      Broadcast(M_MAP,3,Tmp_win->w,Tmp_win->frame,
		(unsigned long)Tmp_win,0,0,0,0);
    }
  
  if(Scr.flags & ClickToFocus)
    {
      SetFocus(Tmp_win->w,Tmp_win);
      ClearCirculatedFlag();
    }
  if((!(Tmp_win->flags &(BORDER|TITLE)))&&(Tmp_win->boundary_width <2))
    {
      SetBorder(Tmp_win,False,True,True,Tmp_win->frame);
    }
  XSync(dpy,0);
  XUngrabServer (dpy);
  XFlush (dpy);
  Tmp_win->flags |= MAPPED;
  Tmp_win->flags &= ~MAP_PENDING;
  Tmp_win->flags &= ~ICONIFIED;
  Tmp_win->flags &= ~ICON_UNMAPPED;
  KeepOnTop();
}


/***********************************************************************
 *
 *  Procedure:
 *	HandleUnmapNotify - UnmapNotify event handler
 *
 ************************************************************************/
void HandleUnmapNotify()
{
  int dstx, dsty;
  Window dumwin;
  XEvent dummy;
  extern FvwmWindow *colormap_win;

  /*
   * The July 27, 1988 ICCCM spec states that a client wishing to switch
   * to WithdrawnState should send a synthetic UnmapNotify with the
   * event field set to (pseudo-)root, in case the window is already
   * unmapped (which is the case for fvwm for IconicState).  Unfortunately,
   * we looked for the FvwmContext using that field, so try the window
   * field also.
   */
  if (!Tmp_win)
    {
      Event.xany.window = Event.xunmap.window;
      if (XFindContext(dpy, Event.xany.window,
		       FvwmContext, (caddr_t *)&Tmp_win) == XCNOENT)
	Tmp_win = NULL;
    }
  
  if(!Tmp_win)
    return;

  if(Tmp_win ==  Scr.Hilite)
    Scr.Hilite = NULL;

  if(Scr.PreviousFocus == Tmp_win)
    Scr.PreviousFocus = NULL;

  if((Tmp_win == Scr.Focus)&&(Scr.flags & ClickToFocus))
    {
      if(Tmp_win->next)
	SetFocus(Tmp_win->next->w,Tmp_win->next);
      else
	SetFocus(Scr.NoFocusWin,NULL);
    }
  
  if(Scr.Focus == Tmp_win)
    SetFocus(Scr.NoFocusWin,NULL);

  if(Tmp_win == Scr.pushed_window)
    Scr.pushed_window = NULL;

  ClearCirculatedFlag();
  if(Tmp_win == colormap_win)
    colormap_win = NULL;

  if ((!(Tmp_win->flags & MAPPED)&&!(Tmp_win->flags&ICONIFIED)))
    {
      return;
    }
  
  XGrabServer(dpy);
  
  if(XCheckTypedWindowEvent (dpy, Event.xunmap.window, DestroyNotify,&dummy)) 
    {
      Destroy(Tmp_win);
      XUngrabServer (dpy);
      return;
    } 

  /*
   * The program may have unmapped the client window, from either
   * NormalState or IconicState.  Handle the transition to WithdrawnState.
   *
   * We need to reparent the window back to the root (so that fvwm exiting 
   * won't cause it to get mapped) and then throw away all state (pretend 
   * that we've received a DestroyNotify).
   */
  if (XTranslateCoordinates (dpy, Event.xunmap.window, Scr.Root,
			     0, 0, &dstx, &dsty, &dumwin)) 
    {
      XEvent ev;
      Bool reparented;
      
      reparented = XCheckTypedWindowEvent (dpy, Event.xunmap.window, 
					   ReparentNotify, &ev);
      SetMapStateProp (Tmp_win, WithdrawnState);
      if (reparented) 
	{
	  if (Tmp_win->old_bw)
	    XSetWindowBorderWidth (dpy, Event.xunmap.window, Tmp_win->old_bw);
	  if((!(Tmp_win->flags & SUPPRESSICON))&&
	     (Tmp_win->wmhints && (Tmp_win->wmhints->flags & IconWindowHint)))
	    XUnmapWindow (dpy, Tmp_win->wmhints->icon_window);
	} 
      else
	{
	  RestoreWithdrawnLocation (Tmp_win,False);
	}
      XRemoveFromSaveSet (dpy, Event.xunmap.window);
      XSelectInput (dpy, Event.xunmap.window, NoEventMask);
      Destroy(Tmp_win);		/* do not need to mash event before */
      /*
       * Flush any pending events for the window.
       */
      /* Bzzt! it could be about to re-map */
/*      while(XCheckWindowEvent(dpy, Event.xunmap.window,
			      StructureNotifyMask | PropertyChangeMask |
			      ColormapChangeMask | VisibilityChangeMask |
			      EnterWindowMask | LeaveWindowMask, &dummy));
      */
    } /* else window no longer exists and we'll get a destroy notify */
  XUngrabServer (dpy);
  
  XFlush (dpy);
}


/***********************************************************************
 *
 *  Procedure:
 *	HandleMotionNotify - MotionNotify event handler
 *
 **********************************************************************/
void HandleMotionNotify()
{
#ifndef NO_PAGER
  extern Bool EnablePagerRedraw;
  
  /* here is the code for dragging the viewport around within the pager.*/
  if((Tmp_win)&&(Tmp_win->w == Scr.Pager_w)&&
     (!(Tmp_win->flags & ICONIFIED)))
    {
      if(Event.xmotion.state == Button3MotionMask)
	{
	  EnablePagerRedraw = False;	  
	  SwitchPages(FALSE,FALSE);
	}
    }
#endif
}

/***********************************************************************
 *
 *  Procedure:
 *	HandleButtonPress - ButtonPress event handler
 *
 ***********************************************************************/
void HandleButtonPress()
{
#ifndef NO_PAGER
  extern Bool EnablePagerRedraw;
#endif
  unsigned int modifier;
  MouseButton *MouseEntry;
  char *Action;
  Window x;
  int LocalContext;
  
  /* click to focus stuff goes here */
  if((Scr.flags & ClickToFocus)&&(Tmp_win != Scr.Ungrabbed)&&
     ((Event.xbutton.state&
       (ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) == 0))
    {
      if(Tmp_win)
	{
	  SetFocus(Tmp_win->w,Tmp_win);
	  ClearCirculatedFlag();
       	  if(Scr.AutoRaiseDelay > 0)
	    {
	      SetTimer(Scr.AutoRaiseDelay);
	    }
	  else 
	    {
#ifdef CLICKY_MODE_1
	      if((Event.xany.window != Tmp_win->w)&&
		 (Event.xbutton.subwindow != Tmp_win->w)&&
		 (Event.xany.window != Tmp_win->Parent)&&
		 (Event.xbutton.subwindow != Tmp_win->Parent))
#endif
		{
		  if(Scr.AutoRaiseDelay == 0)
		    RaiseWindow(Tmp_win);
		}
	    }
	  
	  KeepOnTop();

	  /* Why is this here? Seems to cause breakage with
	   * non-focusing windows! */
	  if(!(Tmp_win->flags & ICONIFIED))
	    {
	      XSync(dpy,0);
	      XAllowEvents(dpy,ReplayPointer,CurrentTime);
	      XSync(dpy,0);
	      return;
	    }

	}
    }
  XSync(dpy,0);
  XAllowEvents(dpy,ReplayPointer,CurrentTime);
  XSync(dpy,0);
  
  /* here is the code for moving windows in the pager.*/
#ifndef NO_PAGER
  if((Tmp_win)&&(Tmp_win->w == Event.xbutton.window)&&
     (Tmp_win->w == Scr.Pager_w)&&(Event.xbutton.button == Button2))
    {
      PagerMoveWindow();
      return;
    }
  if((Tmp_win)&&(Tmp_win->w == Scr.Pager_w)&&
     (!(Tmp_win->flags & ICONIFIED))&&
     (Event.xbutton.button == Button3)&&
     (Event.xany.window == Scr.Pager_w))
    {
#ifndef NO_PAGER
      EnablePagerRedraw = False;	  
#endif
      SwitchPages(FALSE,FALSE);
    }
#endif
  
  Context = GetContext(Tmp_win,&Event, &PressedW);
  LocalContext = Context;
  x= PressedW;
  if(Context == C_TITLE)
    SetTitleBar(Tmp_win,(Scr.Hilite == Tmp_win),False);
  else
    SetBorder(Tmp_win,(Scr.Hilite == Tmp_win),True,True,PressedW);	
  
  ButtonWindow = Tmp_win;
  
  /* we have to execute a function or pop up a menu
   */
  
  modifier = (Event.xbutton.state & mods_used);
  /* need to search for an appropriate mouse binding */
  MouseEntry = Scr.MouseButtonRoot;
  while(MouseEntry != (MouseButton *)0)
    {
      if(((MouseEntry->Button == Event.xbutton.button)||(MouseEntry->Button == 0))&&
	 (MouseEntry->Context & Context)&&
	 ((MouseEntry->Modifier == AnyModifier)||
	  (MouseEntry->Modifier == (modifier& (~LockMask)))))
	{
	  /* got a match, now process it */
	  if (MouseEntry->func != (int)NULL)
	    {
	      Action = MouseEntry->item ? MouseEntry->item->action : NULL;
	      ExecuteFunction(MouseEntry->func, Action, Event.xany.window, 
			      Tmp_win, &Event, Context,MouseEntry->val1,
			      MouseEntry->val2,
			      MouseEntry->val1_unit,MouseEntry->val2_unit,
			      MouseEntry->menu,-1);
	      break;
	    }
	}
      MouseEntry = MouseEntry->NextButton;
    }
  PressedW = None;
  if(LocalContext!=C_TITLE)
    SetBorder(ButtonWindow,(Scr.Hilite == ButtonWindow),True,True,x);
  else
    SetTitleBar(ButtonWindow,(Scr.Hilite==ButtonWindow),False);
  ButtonWindow = NULL;
}

/***********************************************************************
 *
 *  Procedure:
 *	HandleButtonRelease - ButtonRelease event handler
 *
 ***********************************************************************/
void HandleButtonRelease()
{
#ifndef NO_PAGER
  extern Bool EnablePagerRedraw;
  if((Tmp_win)&&(Event.xany.window == Scr.Pager_w)&&
     (!(Tmp_win->flags & ICONIFIED)))
    {
      switch(Event.xbutton.button)
	{
	default:
	case Button1:
	  SwitchPages(TRUE,TRUE);
	  break;
	case Button3:
	  EnablePagerRedraw = True;
	  SwitchPages(FALSE,FALSE);
	  RedrawPager();
	  break;
	}
    }
#endif
}


/***********************************************************************
 *
 *  Procedure:
 *	HandleEnterNotify - EnterNotify event handler
 *
 ************************************************************************/
void HandleEnterNotify()
{
  XEnterWindowEvent *ewp = &Event.xcrossing;
  XEvent d;
  
  /* look for a matching leaveNotify which would nullify this enterNotify */
  if(XCheckTypedWindowEvent (dpy, ewp->window, LeaveNotify, &d))
    {
      StashEventTime(&d);
      if((d.xcrossing.mode==NotifyNormal)&&
	 (d.xcrossing.detail!=NotifyInferior))
	return;
    }

/* an EnterEvent in one of the PanFrameWindows activates the Paging */
#ifndef NON_VIRTUAL
  if (ewp->window==Scr.PanFrameTop.win 
      || ewp->window==Scr.PanFrameLeft.win
      || ewp->window==Scr.PanFrameRight.win 
      || ewp->window==Scr.PanFrameBottom.win ) 
    {
      int delta_x=0, delta_y=0;
      /* this was in the HandleMotionNotify before, HEDU */
      HandlePaging(Scr.EdgeScrollX,Scr.EdgeScrollY,
                   &Event.xcrossing.x_root,&Event.xcrossing.y_root,
                   &delta_x,&delta_y,True);
      return;
    }
#endif /* NON_VIRTUAL */
 
  if(Event.xany.window == Scr.Root)
    {
      if((!(Scr.flags & ClickToFocus))&&(!(Scr.flags & SloppyFocus)))
	{
	  SetFocus(Scr.NoFocusWin,NULL);
	  ClearCirculatedFlag();
	}
      InstallWindowColormaps(NULL);
      return;
    }
  
  /* make sure its for one of our windows */
  if (!Tmp_win) 
    return;
  
  if(!(Scr.flags & ClickToFocus))
    {
      if(Scr.Focus != Tmp_win)
	{
	  if((Scr.AutoRaiseDelay > 0)&&(!(Tmp_win->flags & VISIBLE)))
	    SetTimer(Scr.AutoRaiseDelay);
	  SetFocus(Tmp_win->w,Tmp_win);
	  ClearCirculatedFlag();
	}
      else
	SetFocus(Tmp_win->w,Tmp_win);
    }
  if((!(Tmp_win->flags & ICONIFIED))&&(Event.xany.window == Tmp_win->w))
    InstallWindowColormaps(Tmp_win);
  else
    InstallWindowColormaps(NULL);
  return;
}


/***********************************************************************
 *
 *  Procedure:
 *	HandleLeaveNotify - LeaveNotify event handler
 *
 ************************************************************************/
void HandleLeaveNotify()
{
  /* If we leave the root window, then we're really moving
   * another screen on a multiple screen display, and we
   * need to de-focus and unhighlight to make sure that we
   * don't end up with more than one highlighted window at a time */
  if(Event.xcrossing.window == Scr.Root)
    {
      if(Event.xcrossing.mode == NotifyNormal)
	{
	  if (Event.xcrossing.detail != NotifyInferior) 
	    {
	      if(Scr.Focus != NULL)
		{
		  SetFocus(Scr.NoFocusWin,NULL);
		}
	      if(Scr.Hilite != NULL)
		SetBorder(Scr.Hilite,False,True,True,None);
	    }
	}
    }
}


/***********************************************************************
 *
 *  Procedure:
 *	HandleConfigureRequest - ConfigureRequest event handler
 *
 ************************************************************************/
void HandleConfigureRequest()
{
  XWindowChanges xwc;
  unsigned long xwcm;
  int x, y, width, height;
  XConfigureRequestEvent *cre = &Event.xconfigurerequest;
  
  /*
   * Event.xany.window is Event.xconfigurerequest.parent, so Tmp_win will
   * be wrong
   */
  Event.xany.window = cre->window;	/* mash parent field */
  if (XFindContext (dpy, cre->window, FvwmContext, (caddr_t *) &Tmp_win) ==
      XCNOENT)
    Tmp_win = NULL;
  
  /*
   * According to the July 27, 1988 ICCCM draft, we should ignore size and
   * position fields in the WM_NORMAL_HINTS property when we map a window.
   * Instead, we'll read the current geometry.  Therefore, we should respond
   * to configuration requests for windows which have never been mapped.
   */
  if (!Tmp_win || (Tmp_win->icon_w == cre->window))
    {
      xwcm = cre->value_mask & 
	(CWX | CWY | CWWidth | CWHeight | CWBorderWidth);
      xwc.x = cre->x;
      xwc.y = cre->y;
      if((Tmp_win)&&((Tmp_win->icon_w == cre->window)))
	{
	  Tmp_win->icon_xl_loc = cre->x;
	  Tmp_win->icon_x_loc = cre->x + 
	    (Tmp_win->icon_w_width - Tmp_win->icon_p_width)/2;
	  Tmp_win->icon_y_loc = cre->y - Tmp_win->icon_p_height;
	  if(!(Tmp_win->flags & ICON_UNMAPPED))
	    Broadcast(M_ICON_LOCATION,7,Tmp_win->w,Tmp_win->frame,
		      (unsigned long)Tmp_win,
		      Tmp_win->icon_x_loc,Tmp_win->icon_y_loc,
		      Tmp_win->icon_w_width, 
		      Tmp_win->icon_w_height + Tmp_win->icon_p_height);
	}
      xwc.width = cre->width;
      xwc.height = cre->height;
      xwc.border_width = cre->border_width;
      XConfigureWindow(dpy, Event.xany.window, xwcm, &xwc);
      
      if(Tmp_win)
	{
	  xwc.x = Tmp_win->icon_x_loc;
	  xwc.y = Tmp_win->icon_y_loc - Tmp_win->icon_p_height;
	  xwcm = cre->value_mask & (CWX | CWY);
	  if(Tmp_win->icon_pixmap_w != None)
	    XConfigureWindow(dpy, Tmp_win->icon_pixmap_w, xwcm, &xwc);
	  xwc.x = Tmp_win->icon_x_loc;
	  xwc.y = Tmp_win->icon_y_loc;
	  xwcm = cre->value_mask & (CWX | CWY);
	  if(Tmp_win->icon_w != None)
	    XConfigureWindow(dpy, Tmp_win->icon_w, xwcm, &xwc);
	}
      return;
    }
  
  if (cre->value_mask & CWStackMode) 
    {
      FvwmWindow *otherwin;
      
      xwc.sibling = (((cre->value_mask & CWSibling) &&
		      (XFindContext (dpy, cre->above, FvwmContext,
				     (caddr_t *) &otherwin) == XCSUCCESS))
		     ? otherwin->frame : cre->above);
      xwc.stack_mode = cre->detail;
      XConfigureWindow (dpy, Tmp_win->frame,
			cre->value_mask & (CWSibling | CWStackMode), &xwc);
    }
  
#ifdef SHAPE
  {
    int xws, yws, xbs, ybs;
    unsigned wws, hws, wbs, hbs;
    int boundingShaped, clipShaped;
    
    XShapeQueryExtents (dpy, Tmp_win->w,&boundingShaped, &xws, &yws, &wws,
			&hws,&clipShaped, &xbs, &ybs, &wbs, &hbs);
    Tmp_win->wShaped = boundingShaped;
  }
#endif /* SHAPE */
  
  /* Don't modify frame_XXX fields before calling SetupWindow! */
  x = Tmp_win->frame_x;
  y = Tmp_win->frame_y;
  width = Tmp_win->frame_width;
  height = Tmp_win->frame_height;
  
  /* for restoring */  
  if (cre->value_mask & CWBorderWidth) 
    {
      Tmp_win->old_bw = cre->border_width; 
    }
  /* override even if border change */
  
  if (cre->value_mask & CWX)
    x = cre->x - Tmp_win->boundary_width - Tmp_win->bw;
  if (cre->value_mask & CWY) 
    y = cre->y - Tmp_win->boundary_width - Tmp_win->title_height - Tmp_win->bw;
  if (cre->value_mask & CWWidth)
    width = cre->width + 2*Tmp_win->boundary_width;
  if (cre->value_mask & CWHeight) 
    height = cre->height+Tmp_win->title_height+2*Tmp_win->boundary_width;
  
  /*
   * SetupWindow (x,y) are the location of the upper-left outer corner and
   * are passed directly to XMoveResizeWindow (frame).  The (width,height)
   * are the inner size of the frame.  The inner width is the same as the 
   * requested client window width; the inner height is the same as the
   * requested client window height plus any title bar slop.
   */
  SetupFrame (Tmp_win, x, y, width, height,FALSE);
  KeepOnTop();
  
#ifndef NO_PAGER
  RedrawPager();
#endif
}

/***********************************************************************
 *
 *  Procedure:
 *      HandleShapeNotify - shape notification event handler
 *
 ***********************************************************************/
#ifdef SHAPE
void HandleShapeNotify (void)
{
  XShapeEvent *sev = (XShapeEvent *) &Event;
  
  if (!Tmp_win)
    return;
  if (sev->kind != ShapeBounding)
    return;
  Tmp_win->wShaped = sev->shaped;
  SetShape(Tmp_win,Tmp_win->frame_width);
}
#endif  /* SHAPE*/

/***********************************************************************
 *
 *  Procedure:
 *	HandleVisibilityNotify - record fully visible windows for
 *      use in the RaiseLower function and the OnTop type windows.
 *
 ************************************************************************/
void HandleVisibilityNotify()
{
  XVisibilityEvent *vevent = (XVisibilityEvent *) &Event;
  
  if(Tmp_win)
    {
      if(vevent->state == VisibilityUnobscured)
	Tmp_win->flags |= VISIBLE;
      else
	Tmp_win->flags &= ~VISIBLE;
      
      /* For the most part, we'll raised partially obscured ONTOP windows
       * here. The exception is ONTOP windows that are obscured by
       * other ONTOP windows, which are raised in KeepOnTop(). This
       * complicated set-up saves us from continually re-raising
       * every on top window */
      if(((vevent->state == VisibilityPartiallyObscured)||
	  (vevent->state == VisibilityFullyObscured))&&
	 (Tmp_win->flags&ONTOP)&&(Tmp_win->flags & RAISED))
	{
	  RaiseWindow(Tmp_win);
	  Tmp_win->flags &= ~RAISED;
	}
    }
}


/**************************************************************************
 * 
 * For auto-raising windows, this routine is called
 *
 *************************************************************************/
volatile int alarmed;
void enterAlarm(int nonsense)
{
  alarmed = True;
  signal(SIGALRM, enterAlarm);
}


/***************************************************************************
 *
 * Waits for next X event, or for an auto-raise timeout.
 *
 ****************************************************************************/
int My_XNextEvent(Display *dpy, XEvent *event)
{
  extern int fd_width, x_fd;
  struct itimerval value;
  fd_set in_fdset,out_fdset;
  Window child;
  Window targetWindow;
  int i,count;
  int retval;

  /* Do this prior to the select() call, in case the timer already expired,
   * in which case the select would never return. */
  if(alarmed)
    {
      alarmed = False;
      XQueryPointer(dpy,Scr.Root,&JunkRoot,&child,&JunkX,&JunkY,&JunkX, 
		    &JunkY,&JunkMask );
      if((Scr.Focus != NULL)&&(child == Scr.Focus->frame))
	{
	  if(!(Scr.Focus->flags & VISIBLE))
	    {
	      RaiseWindow(Scr.Focus);
	      KeepOnTop();
#ifndef NO_PAGER
	      RedrawPager();
#endif
	    }
	}
      return 0;
    }
  
#ifndef HAVE_GETITIMER
  value.it_value.tv_usec = 0;
  value.it_value.tv_sec = 0;
#else
  getitimer(ITIMER_REAL,&value);
#endif
  
  FD_ZERO(&in_fdset);
  FD_SET(x_fd,&in_fdset);
  FD_ZERO(&out_fdset);
  for(i=0; i<npipes; i++)
    {
      if(readPipes[i]>=0)
	{
	  FD_SET(readPipes[i], &in_fdset);
	}
    }

  for(i=0; i<npipes; i++)
    {
      if(pipeQueue[i]!= NULL)
	{
	  FD_SET(writePipes[i], &out_fdset);
	}
    }
  

  /* Do this IMMEDIATELY prior to select, to prevent any nasty
   * queued up X events from just hanging around waiting to be
   * flushed */
  XFlush(dpy);
  if(XPending(dpy))
    {
      XNextEvent(dpy,event);
      StashEventTime(event);
      return 1;
    }

  /* Zap all those zombies! */
  /* If we get to here, then there are no X events waiting to be processed.
   * Just take a moment to check for dead children. */
  ReapChildren();

  XFlush(dpy);
  if((value.it_value.tv_usec != 0)||
     (value.it_value.tv_sec != 0))
#ifdef __hpux
    retval=select(fd_width,(int *)&in_fdset, 0, 0, &value.it_value);
#else
    retval=select(fd_width,&in_fdset, 0, 0, &value.it_value);
#endif
  else
#ifdef __hpux
    retval=select(fd_width,(int *)&in_fdset, (int *)&out_fdset,
		  0, NULL);
#else
    retval=select(fd_width,&in_fdset, &out_fdset, 0, NULL);
#endif
  
  /* Check for module input. */
  for(i=0;i<npipes;i++)
    {
      if(readPipes[i] >= 0)
	{
	  if((retval>0)&&(FD_ISSET(readPipes[i], &in_fdset)))
	    {
	      if((count = 
		  read(readPipes[i],&targetWindow, sizeof(Window))) >0)
		{
		  HandleModuleInput(targetWindow,i);
		}
	      if(count <= 0)
		{
		  KillModule(i,10);
		}
	    }
	}
      if(writePipes[i] >= 0)
	{
	  if((retval>0)&&(FD_ISSET(writePipes[i], &out_fdset)))
	    {
	      FlushQueue(i);
	    }
	}
    }
  return 0;
}

