[comp.sources.x] v09i010: TWM with a virtual root window, Part09/09

toml@marvin.Solbourne.COM (Tom LaStrange) (08/30/90)

Submitted-by: toml@marvin.Solbourne.COM (Tom LaStrange)
Posting-number: Volume 9, Issue 10
Archive-name: tvtwm/part09

#! /bin/sh
# This is a shell archive, meaning:
# 1.  Remove everything above the #! /bin/sh line.
# 2.  Save the resulting test in a file
# 3.  Execute the file with /bin/sh (not csh) to create the files:
#
#events.c
#
# Created by toml () on Wed Aug 29 08:43:43 MDT 1990
#
if test -f 'events.c'
then
    echo shar: will not over-write existing file "events.c"
else
echo extracting "events.c"
sed 's/^X//' >events.c <<'SHAR_EOF'
X/*****************************************************************************/
X/**       Copyright 1988 by Evans & Sutherland Computer Corporation,        **/
X/**                          Salt Lake City, Utah                           **/
X/**  Portions Copyright 1989 by the Massachusetts Institute of Technology   **/
X/**                        Cambridge, Massachusetts                         **/
X/**                                                                         **/
X/**                           All Rights Reserved                           **/
X/**                                                                         **/
X/**    Permission to use, copy, modify, and distribute this software and    **/
X/**    its documentation  for  any  purpose  and  without  fee is hereby    **/
X/**    granted, provided that the above copyright notice appear  in  all    **/
X/**    copies and that both  that  copyright  notice  and  this  permis-    **/
X/**    sion  notice appear in supporting  documentation,  and  that  the    **/
X/**    names of Evans & Sutherland and M.I.T. not be used in advertising    **/
X/**    in publicity pertaining to distribution of the  software  without    **/
X/**    specific, written prior permission.                                  **/
X/**                                                                         **/
X/**    EVANS & SUTHERLAND AND M.I.T. DISCLAIM ALL WARRANTIES WITH REGARD    **/
X/**    TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES  OF  MERCHANT-    **/
X/**    ABILITY  AND  FITNESS,  IN  NO  EVENT SHALL EVANS & SUTHERLAND OR    **/
X/**    M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL  DAM-    **/
X/**    AGES OR  ANY DAMAGES WHATSOEVER  RESULTING FROM LOSS OF USE, DATA    **/
X/**    OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER    **/
X/**    TORTIOUS ACTION, ARISING OUT OF OR IN  CONNECTION  WITH  THE  USE    **/
X/**    OR PERFORMANCE OF THIS SOFTWARE.                                     **/
X/*****************************************************************************/
X
X
X/***********************************************************************
X *
X * $XConsortium: events.c,v 1.148 90/03/22 18:52:48 jim Exp $
X *
X * twm event handling
X *
X * 17-Nov-87 Thomas E. LaStrange		File created
X *
X ***********************************************************************/
X
X#if !defined(lint) && !defined(SABER)
Xstatic char RCSinfo[]=
X"$XConsortium: events.c,v 1.148 90/03/22 18:52:48 jim Exp $";
X#endif
X
X#include <stdio.h>
X#include "twm.h"
X#include <X11/Xatom.h>
X#include "add_window.h"
X#include "menus.h"
X#include "events.h"
X#include "resize.h"
X#include "parse.h"
X#include "gram.h"
X#include "util.h"
X#include "screen.h"
X#include "iconmgr.h"
X#include "version.h"
X#include "vdt.h"
X
Xextern int iconifybox_width, iconifybox_height;
Xextern unsigned int mods_used;
X
X#define MAX_X_EVENT 256
Xevent_proc EventHandler[MAX_X_EVENT]; /* event handler jump table */
Xchar *Action;
Xint Context = C_NO_CONTEXT;	/* current button press context */
XTwmWindow *ButtonWindow;	/* button press window structure */
XXEvent ButtonEvent;		/* button press event */
XXEvent Event;			/* the current event */
XTwmWindow *Tmp_win;		/* the current twm window */
X
XWindow DragWindow;		/* variables used in moving windows */
XWindow DragVirtual;		/* variables used in moving windows */
Xint origDragX;
Xint origDragY;
Xint DragX;
Xint DragY;
Xint DragWidth;
Xint DragHeight;
Xint CurrentDragX;
Xint CurrentDragY;
X
Xint FromVirtualDesktop = False;
X
Xstatic int enter_flag;
Xstatic int ColortableThrashing;
Xstatic TwmWindow *enter_win, *raise_win;
X
XScreenInfo *FindScreenInfo();
Xint ButtonPressed = -1;
Xint Cancel = FALSE;
X
Xvoid HandleCreateNotify();
Xvoid HandleReparentNotify();
X
X#ifdef SHAPE
Xvoid HandleShapeNotify ();
Xextern int ShapeEventBase, ShapeErrorBase;
X#endif
X
Xvoid AutoRaiseWindow (tmp)
X    TwmWindow *tmp;
X{
X    RaiseFrame(tmp);
X    XSync (dpy, 0);
X    enter_win = NULL;
X    enter_flag = TRUE;
X    raise_win = tmp;
X}
X
Xvoid SetRaiseWindow (tmp)
X    TwmWindow *tmp;
X{
X    enter_flag = TRUE;
X    enter_win = NULL;
X    raise_win = tmp;
X    XSync (dpy, 0);
X}
X
X
X/***********************************************************************
X *
X *  Procedure:
X *	InitEvents - initialize the event jump table
X *
X ***********************************************************************
X */
X
Xvoid
XInitEvents()
X{
X    int i;
X
X
X    ResizeWindow = NULL;
X    DragWindow = NULL;
X    enter_flag = FALSE;
X    enter_win = raise_win = NULL;
X
X    for (i = 0; i < MAX_X_EVENT; i++)
X	EventHandler[i] = HandleUnknown;
X
X    EventHandler[Expose] = HandleExpose;
X    EventHandler[CreateNotify] = HandleCreateNotify;
X    EventHandler[ReparentNotify] = HandleReparentNotify;
X    EventHandler[DestroyNotify] = HandleDestroyNotify;
X    EventHandler[MapRequest] = HandleMapRequest;
X    EventHandler[MapNotify] = HandleMapNotify;
X    EventHandler[UnmapNotify] = HandleUnmapNotify;
X    EventHandler[MotionNotify] = HandleMotionNotify;
X    EventHandler[ButtonRelease] = HandleButtonRelease;
X    EventHandler[ButtonPress] = HandleButtonPress;
X    EventHandler[EnterNotify] = HandleEnterNotify;
X    EventHandler[LeaveNotify] = HandleLeaveNotify;
X    EventHandler[ConfigureRequest] = HandleConfigureRequest;
X    EventHandler[ClientMessage] = HandleClientMessage;
X    EventHandler[PropertyNotify] = HandlePropertyNotify;
X    EventHandler[KeyPress] = HandleKeyPress;
X    EventHandler[ColormapNotify] = HandleColormapNotify;
X    EventHandler[VisibilityNotify] = HandleVisibilityNotify;
X#ifdef SHAPE
X    if (HasShape)
X	EventHandler[ShapeEventBase+ShapeNotify] = HandleShapeNotify;
X#endif
X}
X
X
XTime lastTimestamp = CurrentTime;	/* until Xlib does this for us */
X
XBool StashEventTime (ev)
X    register XEvent *ev;
X{
X    switch (ev->type) {
X      case KeyPress:
X      case KeyRelease:
X	lastTimestamp = ev->xkey.time;
X	return True;
X      case ButtonPress:
X      case ButtonRelease:
X	lastTimestamp = ev->xbutton.time;
X	return True;
X      case MotionNotify:
X	lastTimestamp = ev->xmotion.time;
X	return True;
X      case EnterNotify:
X      case LeaveNotify:
X	lastTimestamp = ev->xcrossing.time;
X	return True;
X      case PropertyNotify:
X	lastTimestamp = ev->xproperty.time;
X	return True;
X      case SelectionClear:
X	lastTimestamp = ev->xselectionclear.time;
X	return True;
X      case SelectionRequest:
X	lastTimestamp = ev->xselectionrequest.time;
X	return True;
X      case SelectionNotify:
X	lastTimestamp = ev->xselection.time;
X	return True;
X    }
X    return False;
X}
X
X
X/*
X * WindowOfEvent - return the window about which this event is concerned; this
X * window may not be the same as XEvent.xany.window (the first window listed
X * in the structure).
X */
XWindow WindowOfEvent (e)
X    XEvent *e;
X{
X    /*
X     * Each window subfield is marked with whether or not it is the same as
X     * XEvent.xany.window or is different (which is the case for some of the
X     * notify events).
X     */
X    switch (e->type) {
X      case KeyPress:
X      case KeyRelease:  return e->xkey.window;			     /* same */
X      case ButtonPress:
X      case ButtonRelease:  return e->xbutton.window;		     /* same */
X      case MotionNotify:  return e->xmotion.window;		     /* same */
X      case EnterNotify:
X      case LeaveNotify:  return e->xcrossing.window;		     /* same */
X      case FocusIn:
X      case FocusOut:  return e->xfocus.window;			     /* same */
X      case KeymapNotify:  return e->xkeymap.window;		     /* same */
X      case Expose:  return e->xexpose.window;			     /* same */
X      case GraphicsExpose:  return e->xgraphicsexpose.drawable;	     /* same */
X      case NoExpose:  return e->xnoexpose.drawable;		     /* same */
X      case VisibilityNotify:  return e->xvisibility.window;	     /* same */
X      case CreateNotify:  return e->xcreatewindow.window;	     /* DIFF */
X      case DestroyNotify:  return e->xdestroywindow.window;	     /* DIFF */
X      case UnmapNotify:  return e->xunmap.window;		     /* DIFF */
X      case MapNotify:  return e->xmap.window;			     /* DIFF */
X      case MapRequest:  return e->xmaprequest.window;		     /* DIFF */
X      case ReparentNotify:  return e->xreparent.window;		     /* DIFF */
X      case ConfigureNotify:  return e->xconfigure.window;	     /* DIFF */
X      case ConfigureRequest:  return e->xconfigurerequest.window;    /* DIFF */
X      case GravityNotify:  return e->xgravity.window;		     /* DIFF */
X      case ResizeRequest:  return e->xresizerequest.window;	     /* same */
X      case CirculateNotify:  return e->xcirculate.window;	     /* DIFF */
X      case CirculateRequest:  return e->xcirculaterequest.window;    /* DIFF */
X      case PropertyNotify:  return e->xproperty.window;		     /* same */
X      case SelectionClear:  return e->xselectionclear.window;	     /* same */
X      case SelectionRequest: return e->xselectionrequest.requestor;  /* DIFF */
X      case SelectionNotify:  return e->xselection.requestor;	     /* same */
X      case ColormapNotify:  return e->xcolormap.window;		     /* same */
X      case ClientMessage:  return e->xclient.window;		     /* same */
X      case MappingNotify:  return None;
X    }
X    return None;
X}
X
X/***********************************************************************
X *
X *  Procedure:
X *	DispatchEvent - handle a single X event stored in global var Event
X *
X ***********************************************************************
X */
XBool DispatchEvent ()
X{
X    Window w = Event.xany.window;
X    StashEventTime (&Event);
X
X    if (XFindContext (dpy, w, TwmContext, (caddr_t *) &Tmp_win) == XCNOENT)
X      Tmp_win = NULL;
X
X    if (XFindContext (dpy, w, ScreenContext, (caddr_t *)&Scr) == XCNOENT) {
X	Scr = FindScreenInfo (WindowOfEvent (&Event));
X    }
X
X    if (!Scr) return False;
X
X    if (Event.type >= 0 && Event.type < MAX_X_EVENT) {
X	(*EventHandler[Event.type])();
X    }
X
X    return True;
X}
X
X
X/***********************************************************************
X *
X *  Procedure:
X *	HandleEvents - handle X events
X *
X ***********************************************************************
X */
X
Xvoid
XHandleEvents()
X{
X    while (TRUE)
X    {
X	if (ResizeWindow && !XPending(dpy) )
X	{
X	    Event.xany.window = ResizeWindow;
X	    XQueryPointer( dpy, Event.xany.window,
X		&(Event.xmotion.root), &JunkChild,
X		&(Event.xmotion.x_root), &(Event.xmotion.y_root),
X		&(Event.xmotion.x), &(Event.xmotion.y),
X		&JunkMask);
X	    XFindContext(dpy, Event.xany.window, ScreenContext, (caddr_t *)&Scr);
X
X	    (*EventHandler[MotionNotify])();
X	}
X	else
X	{
X	    if (enter_flag && !QLength(dpy)) {
X		if (enter_win && enter_win != raise_win) {
X		    AutoRaiseWindow (enter_win);  /* sets enter_flag T */
X		} else {
X		    enter_flag = FALSE;
X		}
X	    }
X	    if (ColortableThrashing && !QLength(dpy) && Scr) {
X		InstallWindowColormaps(ColormapNotify, (TwmWindow *) NULL);
X	    }
X	    WindowMoved = FALSE;
X	    XNextEvent(dpy, &Event);
X	    (void) DispatchEvent ();
X	}
X    }
X}
X
X
X/***********************************************************************
X *
X *  Procedure:
X *	HandleColormapNotify - colormap notify event handler
X *
X * This procedure handles both a client changing its own colormap, and
X * a client explicitly installing its colormap itself (only the window
X * manager should do that, so we must set it correctly).
X *
X ***********************************************************************
X */
X
Xvoid
XHandleColormapNotify()
X{
X    XColormapEvent *cevent = (XColormapEvent *) &Event;
X    ColormapWindow *cwin, **cwins;
X    TwmColormap *cmap;
X    int i, j, n, number_cwins;
X    extern TwmColormap *CreateTwmColormap();
X
X    if (XFindContext(dpy, cevent->window, ColormapContext, (caddr_t *)&cwin) == XCNOENT)
X	return;
X    cmap = cwin->colormap;
X
X    if (cevent->new)
X    {
X	if (XFindContext(dpy, cevent->colormap, ColormapContext,
X			 (caddr_t *)&cwin->colormap) == XCNOENT)
X	    cwin->colormap = CreateTwmColormap(cevent->colormap);
X	else
X	    cwin->colormap->refcnt++;
X
X	cmap->refcnt--;
X
X	if (cevent->state == ColormapUninstalled)
X	    cmap->state &= ~CM_INSTALLED;
X	else
X	    cmap->state |= CM_INSTALLED;
X
X	if (cmap->state & CM_INSTALLABLE)
X	    InstallWindowColormaps(ColormapNotify, (TwmWindow *) NULL);
X
X	if (cmap->refcnt == 0)
X	{
X	    XDeleteContext(dpy, cmap->c, ColormapContext);
X	    free((char *) cmap);
X	}
X
X	return;
X    }
X
X    if (cevent->state == ColormapUninstalled &&
X	(cmap->state & CM_INSTALLABLE))
X    {
X	if (!(cmap->state & CM_INSTALLED))
X	    return;
X	cmap->state &= ~CM_INSTALLED;
X
X	if (!ColortableThrashing)
X	{
X	    ColortableThrashing = TRUE;
X	    XSync(dpy, 0);
X	}
X
X	if (cevent->serial >= Scr->cmapInfo.first_req)
X	{
X	    number_cwins = Scr->cmapInfo.cmaps->number_cwins;
X
X	    /*
X	     * Find out which colortables collided.
X	     */
X
X	    cwins = Scr->cmapInfo.cmaps->cwins;
X	    for (i = j = -1, n = 0;
X		 (i == -1 || j == -1) && n < number_cwins;
X		 n++)
X	    {
X		if (i == -1 && cwins[n] == cwin)
X		{
X		    i = n;
X		    continue;
X		}
X
X		if (j == -1 &&
X		    cwins[n]->colormap->install_req == cevent->serial)
X		{
X		    j = n;
X		    continue; /* for symetry and later added code */
X		}
X	    }
X
X	    /* need if test in case client was fooling arround w/
X	     * XInstallColormap() or XUninstallColormap()
X	     */
X	    if (i != -1 && j != -1)
X	    {
X		/* lower diagonal index calculation */
X		if (i > j)
X		    n = i*(i-1)/2 + j;
X		else
X		    n = j*(j-1)/2 + i;
X		Scr->cmapInfo.cmaps->scoreboard[n] = 1;
X	    } else {
X		InstallWindowColormaps(ColormapNotify, (TwmWindow *) NULL);
X	    }
X	}
X    }
X
X    else if (cevent->state == ColormapUninstalled)
X	cmap->state &= ~CM_INSTALLED;
X
X    else if (cevent->state == ColormapInstalled)
X	cmap->state |= CM_INSTALLED;
X}
X
X/***********************************************************************
X *
X *  Procedure:
X *	HandleVisibilityNotify - visibility notify event handler
X *
X * This routine keeps track of visibility events so that colormap
X * installation can keep the maximum number of useful colormaps
X * installed at one time.
X *
X ***********************************************************************
X */
X
Xvoid
XHandleVisibilityNotify()
X{
X    XVisibilityEvent *vevent = (XVisibilityEvent *) &Event;
X    ColormapWindow *cwin;
X    TwmColormap *cmap;
X
X    if (XFindContext(dpy, vevent->window, ColormapContext, (caddr_t *)&cwin) == XCNOENT)
X	return;
X    
X    /*
X     * when Saber complains about retreiving an <int> from an <unsigned int>
X     * just type "touch vevent->state" and "cont"
X     */
X    cmap = cwin->colormap;
X    if ((cmap->state & CM_INSTALLABLE) &&
X	vevent->state != cwin->visibility &&
X	(vevent->state == VisibilityFullyObscured ||
X	 cwin->visibility == VisibilityFullyObscured) &&
X	cmap->w == cwin->w) {
X	cwin->visibility = vevent->state;
X	InstallWindowColormaps(VisibilityNotify, (TwmWindow *) NULL);
X    } else
X	cwin->visibility = vevent->state;
X}
X
X/***********************************************************************
X *
X *  Procedure:
X *	HandleKeyPress - key press event handler
X *
X ***********************************************************************
X */
X
Xvoid
XHandleKeyPress()
X{
X    FuncKey *key;
X    int len;
X    unsigned int modifier;
X
X    if (InfoLines) XUnmapWindow(dpy, Scr->InfoWindow);
X    Context = C_NO_CONTEXT;
X
X    if (Event.xany.window == Scr->Root)
X	Context = C_ROOT;
X    if (Tmp_win)
X    {
X	if (Event.xany.window == Tmp_win->title_w)
X	    Context = C_TITLE;
X	if (Event.xany.window == Tmp_win->w ||
X	    Event.xany.window == Tmp_win->virtualWindow)
X	    Context = C_WINDOW;
X	if (Event.xany.window == Tmp_win->icon_w ||
X	    Event.xany.window == Tmp_win->virtualIcon)
X	    Context = C_ICON;
X	if (Event.xany.window == Tmp_win->frame)
X	    Context = C_FRAME;
X	if (Tmp_win->list && Event.xany.window == Tmp_win->list->w)
X	    Context = C_ICONMGR;
X	if (Tmp_win->list && Event.xany.window == Tmp_win->list->icon)
X	    Context = C_ICONMGR;
X    }
X
X    modifier = (Event.xkey.state & mods_used);
X    for (key = Scr->FuncKeyRoot.next; key != NULL; key = key->next)
X    {
X	if (key->keycode == Event.xkey.keycode &&
X	    key->mods == modifier &&
X	    (key->cont == Context || key->cont == C_NAME))
X	{
X	    /* weed out the functions that don't make sense to execute
X	     * from a key press 
X	     */
X	    if (key->func == F_MOVE || key->func == F_RESIZE)
X		return;
X
X	    if (key->cont != C_NAME)
X	    {
X		ExecuteFunction(key->func, key->action, Event.xany.window,
X		    Tmp_win, &Event, Context, FALSE);
X		XUngrabPointer(dpy, CurrentTime);
X		return;
X	    }
X	    else
X	    {
X		int matched = FALSE;
X		len = strlen(key->win_name);
X
X		/* try and match the name first */
X		for (Tmp_win = Scr->TwmRoot.next; Tmp_win != NULL;
X		    Tmp_win = Tmp_win->next)
X		{
X		    if (!strncmp(key->win_name, Tmp_win->name, len))
X		    {
X			matched = TRUE;
X			ExecuteFunction(key->func, key->action, Tmp_win->frame,
X			    Tmp_win, &Event, C_FRAME, FALSE);
X			XUngrabPointer(dpy, CurrentTime);
X		    }
X		}
X
X		/* now try the res_name */
X		if (!matched)
X		for (Tmp_win = Scr->TwmRoot.next; Tmp_win != NULL;
X		    Tmp_win = Tmp_win->next)
X		{
X		    if (!strncmp(key->win_name, Tmp_win->class.res_name, len))
X		    {
X			matched = TRUE;
X			ExecuteFunction(key->func, key->action, Tmp_win->frame,
X			    Tmp_win, &Event, C_FRAME, FALSE);
X			XUngrabPointer(dpy, CurrentTime);
X		    }
X		}
X
X		/* now try the res_class */
X		if (!matched)
X		for (Tmp_win = Scr->TwmRoot.next; Tmp_win != NULL;
X		    Tmp_win = Tmp_win->next)
X		{
X		    if (!strncmp(key->win_name, Tmp_win->class.res_class, len))
X		    {
X			matched = TRUE;
X			ExecuteFunction(key->func, key->action, Tmp_win->frame,
X			    Tmp_win, &Event, C_FRAME, FALSE);
X			XUngrabPointer(dpy, CurrentTime);
X		    }
X		}
X		if (matched)
X		    return;
X	    }
X	}
X    }
X
X    /* if we get here, no function key was bound to the key.  Send it
X     * to the client if it was in a window we know about.
X     */
X    if (Tmp_win)
X    {
X        if (Event.xany.window == Tmp_win->icon_w ||
X	    Event.xany.window == Tmp_win->virtualWindow ||
X	    Event.xany.window == Tmp_win->virtualIcon ||
X	    Event.xany.window == Tmp_win->frame ||
X	    Event.xany.window == Tmp_win->title_w ||
X	    (Tmp_win->list && (Event.xany.window == Tmp_win->list->w)))
X        {
X            Event.xkey.window = Tmp_win->w;
X            XSendEvent(dpy, Tmp_win->w, False, KeyPressMask, &Event);
X        }
X    }
X
X}
X
Xstatic void free_window_names (tmp, nukefull, nukename, nukeicon)
X    TwmWindow *tmp;
X    Bool nukefull, nukename, nukeicon;
X{
X/*
X * XXX - are we sure that nobody ever sets these to another constant (check
X * twm windows)?
X */
X    if (tmp->name == tmp->full_name) nukefull = False;
X    if (tmp->icon_name == tmp->name) nukename = False;
X
X#define isokay(v) ((v) && (v) != NoName)
X    if (nukefull && isokay(tmp->full_name)) XFree (tmp->full_name);
X    if (nukename && isokay(tmp->name)) XFree (tmp->name);
X    if (nukeicon && isokay(tmp->icon_name)) XFree (tmp->icon_name);
X#undef isokay
X    return;
X}
X
Xvoid free_cwins (tmp)
X    TwmWindow *tmp;
X{
X    int i;
X    TwmColormap *cmap;
X
X    if (tmp->cmaps.number_cwins) {
X	for (i = 0; i < tmp->cmaps.number_cwins; i++) {
X	     if (--tmp->cmaps.cwins[i]->refcnt == 0) {
X		cmap = tmp->cmaps.cwins[i]->colormap;
X		if (--cmap->refcnt == 0) {
X		    XDeleteContext(dpy, cmap->c, ColormapContext);
X		    free((char *) cmap);
X		}
X		XDeleteContext(dpy, tmp->cmaps.cwins[i]->w, ColormapContext);
X		free((char *) tmp->cmaps.cwins[i]);
X	    }
X	}
X	free((char *) tmp->cmaps.cwins);
X	if (tmp->cmaps.number_cwins > 1) {
X	    free(tmp->cmaps.scoreboard);
X	    tmp->cmaps.scoreboard = NULL;
X	}
X	tmp->cmaps.number_cwins = 0;
X    }
X}
X
X/***********************************************************************
X *
X *  Procedure:
X *	HandlePropertyNotify - property notify event handler
X *
X ***********************************************************************
X */
X
Xvoid
XHandlePropertyNotify()
X{
X    char *prop = NULL;
X    Atom actual = None;
X    int actual_format;
X    unsigned long nitems, bytesafter;
X    unsigned long valuemask;		/* mask for create windows */
X    XSetWindowAttributes attributes;	/* attributes for create windows */
X    Pixmap pm;
X    long supplied;
X
X    /* watch for standard colormap changes */
X    if (Event.xproperty.window == Scr->Root) {
X	XStandardColormap *maps = NULL;
X	int nmaps;
X
X	switch (Event.xproperty.state) {
X	  case PropertyNewValue:
X	    if (XGetRGBColormaps (dpy, Scr->Root, &maps, &nmaps, 
X				  Event.xproperty.atom)) {
X		/* if got one, then replace any existing entry */
X		InsertRGBColormap (Event.xproperty.atom, maps, nmaps, True);
X	    }
X	    return;
X
X	  case PropertyDelete:
X	    RemoveRGBColormap (Event.xproperty.atom);
X	    return;
X	}
X    }
X
X    if (!Tmp_win) return;		/* unknown window */
X
X#define MAX_NAME_LEN 200L		/* truncate to this many */
X#define MAX_ICON_NAME_LEN 200L		/* ditto */
X
X    switch (Event.xproperty.atom) {
X      case XA_WM_NAME:
X	if (XGetWindowProperty (dpy, Tmp_win->w, Event.xproperty.atom, 0L, 
X				MAX_NAME_LEN, False, XA_STRING, &actual,
X				&actual_format, &nitems, &bytesafter,
X				(unsigned char **) &prop) != Success ||
X	    actual == None)
X	  return;
X	if (!prop) prop = NoName;
X	free_window_names (Tmp_win, True, True, False);
X
X	Tmp_win->full_name = prop;
X	Tmp_win->name = prop;
X
X	Tmp_win->name_width = XTextWidth (Scr->TitleBarFont.font,
X					  Tmp_win->name,
X					  strlen (Tmp_win->name));
X
X	SetupWindow (Tmp_win, Tmp_win->frame_x, Tmp_win->frame_y,
X		     Tmp_win->frame_width, Tmp_win->frame_height, -1);
X
X	if (Tmp_win->title_w) XClearArea(dpy, Tmp_win->title_w, 0,0,0,0, True);
X
X	/*
X	 * if the icon name is NoName, set the name of the icon to be
X	 * the same as the window 
X	 */
X	if (Tmp_win->icon_name == NoName) {
X	    Tmp_win->icon_name = Tmp_win->name;
X	    RedoIconName();
X	}
X	break;
X
X      case XA_WM_ICON_NAME:
X	if (XGetWindowProperty (dpy, Tmp_win->w, Event.xproperty.atom, 0, 
X				MAX_ICON_NAME_LEN, False, XA_STRING, &actual,
X				&actual_format, &nitems, &bytesafter,
X				(unsigned char **) &prop) != Success ||
X	    actual == None)
X	  return;
X	if (!prop) prop = NoName;
X	free_window_names (Tmp_win, False, False, True);
X	Tmp_win->icon_name = prop;
X
X	RedoIconName();
X	break;
X
X      case XA_WM_HINTS:
X	if (Tmp_win->wmhints) XFree ((char *) Tmp_win->wmhints);
X	Tmp_win->wmhints = XGetWMHints(dpy, Event.xany.window);
X
X	if (Tmp_win->wmhints && (Tmp_win->wmhints->flags & WindowGroupHint))
X	  Tmp_win->group = Tmp_win->wmhints->window_group;
X
X	if (!Tmp_win->forced && Tmp_win->wmhints &&
X	    Tmp_win->wmhints->flags & IconWindowHint) {
X	    if (Tmp_win->icon_w) {
X	    	int icon_x, icon_y;
X
X		/*
X		 * There's already an icon window.
X		 * Try to find out where it is; if we succeed, move the new
X		 * window to where the old one is.
X		 */
X		if (XGetGeometry (dpy, Tmp_win->icon_w, &JunkRoot, &icon_x,
X		  &icon_y, &JunkWidth, &JunkHeight, &JunkBW, &JunkDepth)) {
X		    /*
X		     * Move the new icon window to where the old one was.
X		     */
X		    XMoveWindow(dpy, Tmp_win->wmhints->icon_window, icon_x,
X		      icon_y);
X		}
X
X		/*
X		 * If the window is iconic, map the new icon window.
X		 */
X		if (Tmp_win->icon)
X		    XMapWindow(dpy, Tmp_win->wmhints->icon_window);
X
X		/*
X		 * Now, if the old window isn't ours, unmap it, otherwise
X		 * just get rid of it completely.
X		 */
X		if (Tmp_win->icon_not_ours)
X		    XUnmapWindow(dpy, Tmp_win->icon_w);
X		else
X		    XDestroyWindow(dpy, Tmp_win->icon_w);
X
X		/*
X		 * The new icon window isn't our window, so note that fact
X		 * so that we don't treat it as ours.
X		 */
X		Tmp_win->icon_not_ours = TRUE;
X
X		/*
X		 * Now make the new window the icon window for this window,
X		 * and set it up to work as such (select for key presses
X		 * and button presses/releases, set up the contexts for it,
X		 * and define the cursor for it).
X		 */
X		Tmp_win->icon_w = Tmp_win->wmhints->icon_window;
X		XSelectInput (dpy, Tmp_win->icon_w,
X		  KeyPressMask | ButtonPressMask | ButtonReleaseMask);
X		XSaveContext(dpy, Tmp_win->icon_w, TwmContext, (caddr_t)Tmp_win);
X		XSaveContext(dpy, Tmp_win->icon_w, ScreenContext, (caddr_t)Scr);
X		XDefineCursor(dpy, Tmp_win->icon_w, Scr->IconCursor);
X		if (Tmp_win->virtualIcon) {
X		    XGetGeometry (dpy, Tmp_win->icon_w, &JunkRoot, &icon_x,
X			&icon_y, &JunkWidth, &JunkHeight, &JunkBW, &JunkDepth);
X		    ResizeVirtual(Tmp_win->virtualIcon, JunkWidth, JunkHeight);
X		}
X	    }
X	}
X
X	if (Tmp_win->icon_w && !Tmp_win->forced && Tmp_win->wmhints &&
X	    (Tmp_win->wmhints->flags & IconPixmapHint)) {
X	    if (!XGetGeometry (dpy, Tmp_win->wmhints->icon_pixmap, &JunkRoot,
X			       &JunkX, &JunkY, (unsigned int *)&Tmp_win->icon_width, 
X			       (unsigned int *)&Tmp_win->icon_height, &JunkBW, &JunkDepth)) {
X		return;
X	    }
X
X	    pm = XCreatePixmap (dpy, Scr->Root, Tmp_win->icon_width,
X				Tmp_win->icon_height, Scr->d_depth);
X
X	    FB(Tmp_win->iconc.fore, Tmp_win->iconc.back);
X	    XCopyPlane(dpy, Tmp_win->wmhints->icon_pixmap, pm,
X		Scr->NormalGC,
X		0,0, Tmp_win->icon_width, Tmp_win->icon_height, 0, 0, 1 );
X
X	    valuemask = CWBackPixmap;
X	    attributes.background_pixmap = pm;
X
X	    if (Tmp_win->icon_bm_w)
X		XDestroyWindow(dpy, Tmp_win->icon_bm_w);
X
X	    Tmp_win->icon_bm_w =
X	      XCreateWindow (dpy, Tmp_win->icon_w, 0, 0,
X			     (unsigned int) Tmp_win->icon_width,
X			     (unsigned int) Tmp_win->icon_height,
X			     (unsigned int) 0, Scr->d_depth,
X			     (unsigned int) CopyFromParent, Scr->d_visual,
X			     valuemask, &attributes);
X
X	    XFreePixmap (dpy, pm);
X	    RedoIconName();
X	}
X	break;
X
X      case XA_WM_NORMAL_HINTS:
X	if (XGetWMNormalHints (dpy, Tmp_win->w, &Tmp_win->hints, &supplied) &&
X	    !(supplied & PWinGravity))
X	  SimulateWinGravity (Tmp_win);
X	break;
X
X      default:
X	if (Event.xproperty.atom == _XA_WM_COLORMAP_WINDOWS) {
X	    FetchWmColormapWindows (Tmp_win);	/* frees old data */
X	    break;
X	} else if (Event.xproperty.atom == _XA_WM_PROTOCOLS) {
X	    FetchWmProtocols (Tmp_win);
X	    break;
X	}
X	break;
X    }
X}
X
X/***********************************************************************
X *
X *  Procedure:
X *	RedoIconName - procedure to re-position the icon window and name
X *
X ***********************************************************************
X */
X
XRedoIconName()
X{
X    int x, y;
X
X    if (Tmp_win->list)
X    {
X	/* let the expose event cause the repaint */
X	XClearArea(dpy, Tmp_win->list->w, 0,0,0,0, True);
X
X	if (Scr->SortIconMgr)
X	    SortIconManager(Tmp_win->list->iconmgr);
X    }
X
X    if (Tmp_win->icon_w == NULL)
X	return;
X
X    if (Tmp_win->icon_not_ours)
X	return;
X
X    Tmp_win->icon_w_width = XTextWidth(Scr->IconFont.font,
X	Tmp_win->icon_name, strlen(Tmp_win->icon_name));
X
X    Tmp_win->icon_w_width += 6;
X    if (Tmp_win->icon_w_width < Tmp_win->icon_width)
X    {
X	Tmp_win->icon_x = (Tmp_win->icon_width - Tmp_win->icon_w_width)/2;
X	Tmp_win->icon_x += 3;
X	Tmp_win->icon_w_width = Tmp_win->icon_width;
X    }
X    else
X    {
X	Tmp_win->icon_x = 3;
X    }
X
X    if (Tmp_win->icon_w_width == Tmp_win->icon_width)
X	x = 0;
X    else
X	x = (Tmp_win->icon_w_width - Tmp_win->icon_width)/2;
X
X    y = 0;
X
X    Tmp_win->icon_w_height = Tmp_win->icon_height + Scr->IconFont.height + 4;
X    Tmp_win->icon_y = Tmp_win->icon_height + Scr->IconFont.height;
X
X    XResizeWindow(dpy, Tmp_win->icon_w, Tmp_win->icon_w_width,
X	Tmp_win->icon_w_height);
X    ResizeVirtual(Tmp_win->virtualIcon, Tmp_win->icon_w_width, Tmp_win->icon_w_height);
X    if (Tmp_win->icon_bm_w)
X    {
X	XMoveWindow(dpy, Tmp_win->icon_bm_w, x, y);
X	XMapWindow(dpy, Tmp_win->icon_bm_w);
X    }
X    if (Tmp_win->icon)
X    {
X	XClearArea(dpy, Tmp_win->icon_w, 0, 0, 0, 0, True);
X    }
X}
X
X/***********************************************************************
X *
X *  Procedure:
X *	HandleClientMessage - client message event handler
X *
X ***********************************************************************
X */
X
Xvoid
XHandleClientMessage()
X{
X    if (Event.xclient.message_type == _XA_WM_CHANGE_STATE)
X    {
X	if (Tmp_win != NULL)
X	{
X	    if (Event.xclient.data.l[0] == IconicState && !Tmp_win->icon)
X	    {
X		XEvent button;
X
X		XQueryPointer( dpy, Scr->Root, &JunkRoot, &JunkChild,
X			      &(button.xmotion.x_root),
X			      &(button.xmotion.y_root),
X			      &JunkX, &JunkY, &JunkMask);
X
X		ExecuteFunction(F_ICONIFY, NULLSTR, Event.xany.window,
X		    Tmp_win, &button, FRAME, FALSE);
X		XUngrabPointer(dpy, CurrentTime);
X	    }
X	}
X    }
X}
X
X
X/***********************************************************************
X *
X *  Procedure:
X *	HandleExpose - expose event handler
X *
X ***********************************************************************
X */
X
Xvoid
XHandleExpose()
X{
X    MenuRoot *tmp;
X    static void flush_expose();
X
X    if (Event.xany.window == Scr->Panner)
X    {
X	HandlePannerExpose(&Event);
X	return;
X    }
X
X    if (XFindContext(dpy, Event.xany.window, MenuContext, (caddr_t *)&tmp) == 0)
X    {
X	PaintMenu(tmp, &Event);
X	return;
X    }
X
X    if (Event.xexpose.count != 0)
X	return;
X
X    if (Event.xany.window == Scr->InfoWindow && InfoLines)
X    {
X	int i;
X	int height;
X
X	FBF(Scr->DefaultC.fore, Scr->DefaultC.back,
X	    Scr->DefaultFont.font->fid);
X
X	height = Scr->DefaultFont.height+2;
X	for (i = 0; i < InfoLines; i++)
X	{
X	    XDrawString(dpy, Scr->InfoWindow, Scr->NormalGC,
X		5, (i*height) + Scr->DefaultFont.y, Info[i], strlen(Info[i]));
X	}
X	flush_expose (Event.xany.window);
X    } 
X    else if (Tmp_win != NULL)
X    {
X	if (Event.xany.window == Tmp_win->title_w)
X	{
X	    FBF(Tmp_win->title.fore, Tmp_win->title.back,
X		Scr->TitleBarFont.font->fid);
X
X	    XDrawString (dpy, Tmp_win->title_w, Scr->NormalGC,
X			 Scr->TBInfo.titlex, Scr->TitleBarFont.y, 
X			 Tmp_win->name, strlen(Tmp_win->name));
X	    flush_expose (Event.xany.window);
X	}
X	else if (Event.xany.window == Tmp_win->icon_w)
X	{
X	    FBF(Tmp_win->iconc.fore, Tmp_win->iconc.back,
X		Scr->IconFont.font->fid);
X
X	    XDrawString (dpy, Tmp_win->icon_w,
X		Scr->NormalGC,
X		Tmp_win->icon_x, Tmp_win->icon_y,
X		Tmp_win->icon_name, strlen(Tmp_win->icon_name));
X	    flush_expose (Event.xany.window);
X	    return;
X	} else if (Tmp_win->titlebuttons) {
X	    int i;
X	    Window w = Event.xany.window;
X	    register TBWindow *tbw;
X	    int nb = Scr->TBInfo.nleft + Scr->TBInfo.nright;
X
X	    for (i = 0, tbw = Tmp_win->titlebuttons; i < nb; i++, tbw++) {
X		if (w == tbw->window) {
X		    register TitleButton *tb = tbw->info;
X
X		    FB(Tmp_win->title.fore, Tmp_win->title.back);
X		    XCopyPlane (dpy, tb->bitmap, w, Scr->NormalGC,
X				tb->srcx, tb->srcy, tb->width, tb->height,
X				tb->dstx, tb->dsty, 1);
X		    flush_expose (w);
X		    return;
X		}
X	    }
X	}
X	if (Tmp_win->list) {
X	    if (Event.xany.window == Tmp_win->list->w)
X	    {
X		FBF(Tmp_win->list->fore, Tmp_win->list->back,
X		    Scr->IconManagerFont.font->fid);
X		XDrawString (dpy, Event.xany.window, Scr->NormalGC, 
X		    iconmgr_textx, Scr->IconManagerFont.y+4,
X		    Tmp_win->icon_name, strlen(Tmp_win->icon_name));
X		DrawIconManagerBorder(Tmp_win->list);
X		flush_expose (Event.xany.window);
X		return;
X	    }
X	    if (Event.xany.window == Tmp_win->list->icon)
X	    {
X		FB(Tmp_win->list->fore, Tmp_win->list->back);
X		XCopyPlane(dpy, Scr->siconifyPm, Tmp_win->list->icon,
X		    Scr->NormalGC,
X		    0,0, iconifybox_width, iconifybox_height, 0, 0, 1);
X		flush_expose (Event.xany.window);
X		return;
X	    }
X	} 
X    }
X}
X
Xstatic void remove_window_from_ring (tmp)
X    TwmWindow *tmp;
X{
X    TwmWindow *prev = tmp->ring.prev, *next = tmp->ring.next;
X
X    if (enter_win == tmp) {
X	enter_flag = FALSE;
X	enter_win = NULL;
X    }
X    if (raise_win == Tmp_win) raise_win = NULL;
X
X    /*
X     * 1. Unlink window
X     * 2. If window was only thing in ring, null out ring
X     * 3. If window was ring leader, set to next (or null)
X     */
X    if (prev) prev->ring.next = next;
X    if (next) next->ring.prev = prev;
X    if (Scr->Ring == tmp) 
X      Scr->Ring = (next != tmp ? next : (TwmWindow *) NULL);
X
X    if (!Scr->Ring || Scr->RingLeader == tmp) Scr->RingLeader = Scr->Ring;
X}
X
X/***********************************************************************
X *
X *  Procedure:
X *	HandleDestroyNotify - DestroyNotify event handler
X *
X ***********************************************************************
X */
X
Xvoid
XHandleDestroyNotify()
X{
X    int i;
X
X    /*
X     * Warning, this is also called by HandleUnmapNotify; if it ever needs to
X     * look at the event, HandleUnmapNotify will have to mash the UnmapNotify
X     * into a DestroyNotify.
X     */
X
X    if (Tmp_win == NULL)
X	return;
X
X    if (Tmp_win == Scr->Focus)
X    {
X	FocusOnRoot();
X    }
X    XDeleteContext(dpy, Tmp_win->w, TwmContext);
X    XDeleteContext(dpy, Tmp_win->w, ScreenContext);
X    XDeleteContext(dpy, Tmp_win->frame, TwmContext);
X    XDeleteContext(dpy, Tmp_win->frame, ScreenContext);
X    if (Tmp_win->icon_w)
X    {
X	XDeleteContext(dpy, Tmp_win->icon_w, TwmContext);
X	XDeleteContext(dpy, Tmp_win->icon_w, ScreenContext);
X    }
X    if (Tmp_win->title_height)
X    {
X	int nb = Scr->TBInfo.nleft + Scr->TBInfo.nright;
X	XDeleteContext(dpy, Tmp_win->title_w, TwmContext);
X	XDeleteContext(dpy, Tmp_win->title_w, ScreenContext);
X	if (Tmp_win->hilite_w)
X	{
X	    XDeleteContext(dpy, Tmp_win->hilite_w, TwmContext);
X	    XDeleteContext(dpy, Tmp_win->hilite_w, ScreenContext);
X	}
X	if (Tmp_win->titlebuttons) {
X	    for (i = 0; i < nb; i++) {
X		XDeleteContext (dpy, Tmp_win->titlebuttons[i].window,
X				TwmContext);
X		XDeleteContext (dpy, Tmp_win->titlebuttons[i].window,
X				ScreenContext);
X	    }
X        }
X    }
X
X    if (Scr->cmapInfo.cmaps == &Tmp_win->cmaps)
X	InstallWindowColormaps(DestroyNotify, &Scr->TwmRoot);
X
X    if (Tmp_win->virtualWindow)
X    {
X	XDeleteContext(dpy, Tmp_win->virtualWindow, TwmContext);
X	XDeleteContext(dpy, Tmp_win->virtualWindow, VirtualContext);
X	XDeleteContext(dpy, Tmp_win->virtualWindow, ScreenContext);
X	XDestroyWindow(dpy, Tmp_win->virtualWindow);
X    }
X    if (Tmp_win->virtualIcon)
X    {
X	XDeleteContext(dpy, Tmp_win->virtualIcon, TwmContext);
X	XDeleteContext(dpy, Tmp_win->virtualIcon, VirtualContext);
X	XDeleteContext(dpy, Tmp_win->virtualIcon, ScreenContext);
X	XDestroyWindow(dpy, Tmp_win->virtualIcon);
X    }
X    /*
X     * TwmWindows contain the following pointers
X     * 
X     *     1.  full_name
X     *     2.  name
X     *     3.  icon_name
X     *     4.  wmhints
X     *     5.  class.res_name
X     *     6.  class.res_class
X     *     7.  list
X     *     8.  iconmgrp
X     *     9.  cwins
X     *     10. titlebuttons
X     *     11. window ring
X     */
X    if (Tmp_win->gray) XFreePixmap (dpy, Tmp_win->gray);
X
X    XDestroyWindow(dpy, Tmp_win->frame);
X    if (Tmp_win->icon_w) {
X	XDestroyWindow(dpy, Tmp_win->icon_w);
X	IconDown (Tmp_win);
X    }
X    RemoveIconManager(Tmp_win);					/* 7 */
X    Tmp_win->prev->next = Tmp_win->next;
X    if (Tmp_win->next != NULL)
X	Tmp_win->next->prev = Tmp_win->prev;
X    if (Tmp_win->auto_raise) Scr->NumAutoRaises--;
X
X    free_window_names (Tmp_win, True, True, True);		/* 1, 2, 3 */
X    if (Tmp_win->wmhints)					/* 4 */
X      XFree ((char *)Tmp_win->wmhints);
X    if (Tmp_win->class.res_name && Tmp_win->class.res_name != NoName)  /* 5 */
X      XFree ((char *)Tmp_win->class.res_name);
X    if (Tmp_win->class.res_class && Tmp_win->class.res_class != NoName) /* 6 */
X      XFree ((char *)Tmp_win->class.res_class);
X    free_cwins (Tmp_win);				/* 9 */
X    if (Tmp_win->titlebuttons)					/* 10 */
X      free ((char *) Tmp_win->titlebuttons);
X    remove_window_from_ring (Tmp_win);				/* 11 */
X
X    free((char *)Tmp_win);
X}
X
Xvoid
XHandleCreateNotify()
X{
X#ifdef DEBUG_EVENTS
X    fprintf(stderr, "CreateNotify w = 0x%x\n", Event.xcreatewindow.window);
X    fflush(stderr);
X    XBell(dpy, 0);
X    XSync(dpy, 0);
X#endif
X
X    /* OI clients will actually create windows on the virtual desktop window,
X     * we need to save these just in case we get killed without being able
X     * to clean things up
X     */
X    if (Event.xcreatewindow.parent == Scr->VirtualDesktop)
X	XAddToSaveSet(dpy, Event.xcreatewindow.window);
X
X}
X
Xvoid
XHandleReparentNotify()
X{
X    /* OI clients will actually create windows on the virtual desktop window,
X     * we need to save these just in case we get killed without being able
X     * to clean things up
X     */
X    if (Event.xreparent.event == Scr->VirtualDesktop) {
X	if (Event.xreparent.parent == Scr->VirtualDesktop)
X	    XAddToSaveSet(dpy, Event.xreparent.window);
X	else
X	    XRemoveFromSaveSet(dpy, Event.xreparent.window);
X    }
X}
X
X
X/***********************************************************************
X *
X *  Procedure:
X *	HandleMapRequest - MapRequest event handler
X *
X ***********************************************************************
X */
X
Xvoid
XHandleMapRequest()
X{
X    int stat;
X    int zoom_save;
X    Window parent;
X
X    parent = Event.xmaprequest.parent;
X    Event.xany.window = Event.xmaprequest.window;
X    stat = XFindContext(dpy, Event.xany.window, TwmContext, (caddr_t *)&Tmp_win);
X    if (stat == XCNOENT)
X	Tmp_win = NULL;
X
X    /* If the window has never been mapped before ... */
X    if (Tmp_win == NULL)
X    {
X	/* Add decorations. */
X	if (parent == Scr->VirtualDesktop)
X	    FromVirtualDesktop == True;
X	Tmp_win = AddWindow(Event.xany.window, FALSE, (IconMgr *) NULL);
X	FromVirtualDesktop == False;
X	if (Tmp_win == NULL)
X	    return;
X    }
X    else
X    {
X	/*
X	 * If the window has been unmapped by the client, it won't be listed
X	 * in the icon manager.  Add it again, if requested.
X	 */
X	if (Tmp_win->list == NULL)
X	    (void) AddIconManager (Tmp_win);
X    }
X
X    /* If it's not merely iconified, and we have hints, use them. */
X    if ((! Tmp_win->icon) &&
X	Tmp_win->wmhints && (Tmp_win->wmhints->flags & StateHint))
X    {
X	int state;
X	Window icon;
X
X	/* use WM_STATE if enabled */
X	if (!(RestartPreviousState && GetWMState(Tmp_win->w, &state, &icon) &&
X	      (state == NormalState || state == IconicState)))
X	  state = Tmp_win->wmhints->initial_state;
X
X	switch (state) 
X	{
X	    case DontCareState:
X	    case NormalState:
X	    case ZoomState:
X	    case InactiveState:
X		MapFrame(Tmp_win);
X		SetMapStateProp(Tmp_win, NormalState);
X		SetRaiseWindow (Tmp_win);
X		break;
X
X	    case IconicState:
X		zoom_save = Scr->DoZoom;
X		Scr->DoZoom = FALSE;
X		Iconify(Tmp_win, 0, 0);
X		Scr->DoZoom = zoom_save;
X		break;
X	}
X    }
X    /* If no hints, or currently an icon, just "deiconify" */
X    else
X    {
X	DeIconify(Tmp_win);
X	SetRaiseWindow (Tmp_win);
X    }
X}
X
Xvoid SimulateMapRequest (w)
X    Window w;
X{
X    Event.xmaprequest.window = w;
X    HandleMapRequest ();
X}
X
X
X/***********************************************************************
X *
X *  Procedure:
X *	HandleMapNotify - MapNotify event handler
X *
X ***********************************************************************
X */
X
Xvoid
XHandleMapNotify()
X{
X    if (Tmp_win == NULL)
X	return;
X
X    /*
X     * Need to do the grab to avoid race condition of having server send
X     * MapNotify to client before the frame gets mapped; this is bad because
X     * the client would think that the window has a chance of being viewable
X     * when it really isn't.
X     */
X    XGrabServer (dpy);
X    if (Tmp_win->icon_w)
X	UnmapIcon(Tmp_win);
X    if (Tmp_win->title_w)
X	XMapSubwindows(dpy, Tmp_win->title_w);
X    XMapSubwindows(dpy, Tmp_win->frame);
X    if (Scr->Focus != Tmp_win && Tmp_win->hilite_w)
X	XUnmapWindow(dpy, Tmp_win->hilite_w);
X
X    MapFrame(Tmp_win);
X    XUngrabServer (dpy);
X    XFlush (dpy);
X    Tmp_win->mapped = TRUE;
X    Tmp_win->icon = FALSE;
X    Tmp_win->icon_on = FALSE;
X}
X
X
X
X/***********************************************************************
X *
X *  Procedure:
X *	HandleUnmapNotify - UnmapNotify event handler
X *
X ***********************************************************************
X */
X
Xvoid
XHandleUnmapNotify()
X{
X    int dstx, dsty;
X    Window dumwin;
X
X    /*
X     * The July 27, 1988 ICCCM spec states that a client wishing to switch
X     * to WithdrawnState should send a synthetic UnmapNotify with the
X     * event field set to (pseudo-)root, in case the window is already
X     * unmapped (which is the case for twm for IconicState).  Unfortunately,
X     * we looked for the TwmContext using that field, so try the window
X     * field also.
X     */
X    if (Tmp_win == NULL)
X    {
X	Event.xany.window = Event.xunmap.window;
X	if (XFindContext(dpy, Event.xany.window,
X	    TwmContext, (caddr_t *)&Tmp_win) == XCNOENT)
X	    Tmp_win = NULL;
X    }
X
X    if (Tmp_win == NULL || Event.xunmap.window == Tmp_win->frame ||
X	Event.xunmap.window == Tmp_win->icon_w ||
X	(!Tmp_win->mapped && !Tmp_win->icon))
X	return;
X
X    /*
X     * The program may have unmapped the client window, from either
X     * NormalState or IconicState.  Handle the transition to WithdrawnState.
X     *
X     * We need to reparent the window back to the root (so that twm exiting 
X     * won't cause it to get mapped) and then throw away all state (pretend 
X     * that we've received a DestroyNotify).
X     */
X
X    XGrabServer (dpy);
X    if (XTranslateCoordinates (dpy, Event.xunmap.window, Tmp_win->attr.root,
X			       0, 0, &dstx, &dsty, &dumwin)) {
X	SetMapStateProp (Tmp_win, WithdrawnState);
X	XReparentWindow (dpy, Event.xunmap.window, Tmp_win->attr.root,
X			 dstx, dsty);
X	RestoreWithdrawnLocation (Tmp_win);
X	XRemoveFromSaveSet (dpy, Event.xunmap.window);
X	HandleDestroyNotify ();		/* do not need to mash event before */
X    } /* else window no longer exists and we'll get a destroy notify */
X    XUngrabServer (dpy);
X    XFlush (dpy);
X}
X
X/***********************************************************************
X *
X *  Procedure:
X *	HandleMotionNotify - MotionNotify event handler
X *
X ***********************************************************************
X */
X
Xvoid
XHandleMotionNotify()
X{
X    if (Event.xany.window == Scr->Panner)
X    {
X	HandlePannerMotionNotify(&Event);
X	return;
X    }
X    if (ResizeWindow != NULL)
X    {
X	XFindContext(dpy, ResizeWindow, TwmContext, (caddr_t *)&Tmp_win);
X	DoResize(Event.xmotion.x_root, Event.xmotion.y_root, Tmp_win);
X    }
X}
X
X/***********************************************************************
X *
X *  Procedure:
X *	HandleButtonRelease - ButtonRelease event handler
X *
X ***********************************************************************
X */
X
Xvoid
XHandleButtonRelease()
X{
X    int xl, xr, yt, yb, w, h;
X    unsigned mask;
X
X    if (Event.xany.window == Scr->Panner)
X    {
X	HandlePannerButtonRelease(&Event);
X	return;
X    }
X    if (DragWindow != None)
X    {
X	MoveOutline(Scr->Root, 0, 0, 0, 0, 0, 0);
X
X	XFindContext(dpy, DragWindow, TwmContext, (caddr_t *)&Tmp_win);
X	if (DragWindow == Tmp_win->frame)
X	{
X	    xl = Event.xbutton.x_root - DragX - Tmp_win->frame_bw;
X	    yt = Event.xbutton.y_root - DragY - Tmp_win->frame_bw;
X	    w = DragWidth + 2 * Tmp_win->frame_bw;
X	    h = DragHeight + 2 * Tmp_win->frame_bw;
X	}
X	else
X	{
X	    xl = Event.xbutton.x_root - DragX - BW;
X	    yt = Event.xbutton.y_root - DragY - BW;
X	    w = DragWidth + 2 * BW;
X	    h = DragHeight + 2 * BW;
X	}
X
X	if (ConstMove)
X	{
X	    if (ConstMoveDir == MOVE_HORIZ)
X		yt = ConstMoveY;
X
X	    if (ConstMoveDir == MOVE_VERT)
X		xl = ConstMoveX;
X
X	    if (ConstMoveDir == MOVE_NONE)
X	    {
X		yt = ConstMoveY;
X		xl = ConstMoveX;
X	    }
X	}
X	
X	if (Scr->DontMoveOff && MoveFunction != F_FORCEMOVE)
X	{
X	    xr = xl + w;
X	    yb = yt + h;
X
X	    if (xl < 0)
X		xl = 0;
X	    if (xr > Scr->MyDisplayWidth)
X		xl = Scr->MyDisplayWidth - w;
X
X	    if (yt < 0)
X		yt = 0;
X	    if (yb > Scr->MyDisplayHeight)
X		yt = Scr->MyDisplayHeight - h;
X	}
X
X	CurrentDragX = xl;
X	CurrentDragY = yt;
X	if (DragWindow == Tmp_win->frame)
X	    SetupWindow (Tmp_win, xl, yt,
X			 Tmp_win->frame_width, Tmp_win->frame_height, -1);
X	else {
X	    XMoveWindow (dpy, DragWindow, xl, yt);
X	    if (DragVirtual)
X		XMoveWindow(dpy, DragVirtual, xl/Scr->PannerScale, yt/Scr->PannerScale);
X	}
X
X	if (!Scr->NoRaiseMove/* && !Scr->OpaqueMove*/) {    /* opaque already did */
X	    XRaiseWindow(dpy, DragWindow);
X	    if (DragVirtual)
X		XRaiseWindow(dpy, DragVirtual);
X	}
X
X	if (!Scr->OpaqueMove)
X	    UninstallRootColormap();
X	else
X	    XSync(dpy, 0);
X
X	if (Scr->NumAutoRaises) {
X	    enter_flag = TRUE;
X	    enter_win = NULL;
X	    raise_win = ((DragWindow == Tmp_win->frame && !Scr->NoRaiseMove)
X			 ? Tmp_win : NULL);
X	}
X
X	DragWindow = NULL;
X	DragVirtual = NULL;
X	ConstMove = FALSE;
X    }
X
X    if (ResizeWindow != NULL)
X    {
X	EndResize();
X    }
X
X    if (ActiveMenu != NULL && RootFunction == NULL)
X    {
X	if (ActiveItem != NULL)
X	{
X	    Action = ActiveItem->action;
X	    if (ActiveItem->func == F_MOVE ||
X		ActiveItem->func == F_FORCEMOVE)
X		    ButtonPressed = -1;
X	    ExecuteFunction(ActiveItem->func, ActiveItem->action,
X		ButtonWindow ? ButtonWindow->frame : NULL,
X		ButtonWindow, &ButtonEvent, Context, TRUE);
X	    Context = C_NO_CONTEXT;
X	    ButtonWindow = NULL;
X
X	    /* if we are not executing a defered command, then take down the
X	     * menu
X	     */
X	    if (RootFunction == NULL)
X	    {
X		PopDownMenu();
X	    }
X	}
X	else
X	    PopDownMenu();
X    }
X
X    mask = (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask);
X    switch (Event.xbutton.button)
X    {
X	case Button1: mask &= ~Button1Mask; break;
X	case Button2: mask &= ~Button2Mask; break;
X	case Button3: mask &= ~Button3Mask; break;
X	case Button4: mask &= ~Button4Mask; break;
X	case Button5: mask &= ~Button5Mask; break;
X    }
X
X    if (RootFunction != NULL ||
X	ResizeWindow != None ||
X	DragWindow != None)
X	ButtonPressed = -1;
X
X    if (RootFunction == NULL &&
X	(Event.xbutton.state & mask) == 0 &&
X	DragWindow == None &&
X	ResizeWindow == None)
X    {
X	XUngrabPointer(dpy, CurrentTime);
X	XUngrabServer(dpy);
X	XFlush(dpy);
X	EventHandler[EnterNotify] = HandleEnterNotify;
X	EventHandler[LeaveNotify] = HandleLeaveNotify;
X	ButtonPressed = -1;
X	if (DownIconManager)
X	{
X	    DownIconManager->down = FALSE;
X	    if (Scr->Highlight) DrawIconManagerBorder(DownIconManager);
X	    DownIconManager = NULL;
X	}
X	Cancel = FALSE;
X    }
X}
X
X
Xstatic do_menu (menu, w)
X    MenuRoot *menu;			/* menu to pop up */
X    Window w;				/* invoking window or None */
X{
X    int x = Event.xbutton.x_root;
X    int y = Event.xbutton.y_root;
X    Bool center;
X
X    if (!Scr->NoGrabServer)
X	XGrabServer(dpy);
X    if (w) {
X	int h = Scr->TBInfo.width - Scr->TBInfo.border;
X	Window child;
X
X	(void) XTranslateCoordinates (dpy, w, Scr->Root, 0, h, &x, &y, &child);
X	center = False;
X    } else {
X	center = True;
X    }
X    if (PopUpMenu (menu, x, y, center)) {
X	UpdateMenu();
X    } else {
X	XBell (dpy, 0);
X    }
X}
X
X
X/***********************************************************************
X *
X *  Procedure:
X *	HandleButtonPress - ButtonPress event handler
X *
X ***********************************************************************
X */
Xvoid
XHandleButtonPress()
X{
X    unsigned int modifier;
X    Cursor cur;
X
X    /* pop down the menu, if any */
X    if (ActiveMenu != NULL)
X	PopDownMenu();
X
X    if (InfoLines) {
X	XUnmapWindow(dpy, Scr->InfoWindow);
X	InfoLines = 0;
X    }
X    XSync(dpy, 0);			/* XXX - remove? */
X
X    if (Event.xany.window == Scr->Panner)
X    {
X	HandlePannerButtonPress(&Event);
X	return;
X    }
X
X    if (XFindContext (dpy, Event.xany.window, VirtualContext, (caddr_t *) &Tmp_win) != XCNOENT)
X    {
X	HandlePannerMove(&Event, Tmp_win);
X	return;
X    }
X    if (ButtonPressed != -1)
X    {
X	/* we got another butt press in addition to one still held
X	 * down, we need to cancel the operation we were doing
X	 */
X	Cancel = TRUE;
X	CurrentDragX = origDragX;
X	CurrentDragY = origDragY;
X	if (Scr->OpaqueMove && DragWindow != None) {
X	    XMoveWindow (dpy, DragWindow, origDragX, origDragY);
X	} else {
X	    MoveOutline(Scr->Root, 0, 0, 0, 0, 0, 0);
X	}
X	XUnmapWindow(dpy, Scr->SizeWindow);
X	if (!Scr->OpaqueMove)
X	    UninstallRootColormap();
X	ResizeWindow = None;
X	DragWindow = None;
X	cur = LeftButt;
X	if (Event.xbutton.button == Button2)
X	    cur = MiddleButt;
X	else if (Event.xbutton.button >= Button3)
X	    cur = RightButt;
X
X	XGrabPointer(dpy, Scr->Root, True,
X	    ButtonReleaseMask | ButtonPressMask,
X	    GrabModeAsync, GrabModeAsync,
X	    Scr->Root, cur, CurrentTime);
X
X	return;
X    }
X    else
X	ButtonPressed = Event.xbutton.button;
X
X    if (ResizeWindow != None ||
X	DragWindow != None  ||
X	ActiveMenu != NULL)
X	return;
X
X    /* check the title bar buttons */
X    if (Tmp_win && Tmp_win->title_height && Tmp_win->titlebuttons)
X    {
X	register int i;
X	register TBWindow *tbw;
X	int nb = Scr->TBInfo.nleft + Scr->TBInfo.nright;
X
X	for (i = 0, tbw = Tmp_win->titlebuttons; i < nb; i++, tbw++) {
X	    if (Event.xany.window == tbw->window) {
X		if (tbw->info->func == F_MENU) {
X		    ButtonEvent = Event;
X		    ButtonWindow = Tmp_win;
X		    do_menu (tbw->info->menuroot, tbw->window);
X		} else {
X		    ExecuteFunction (tbw->info->func, tbw->info->action,
X				     Event.xany.window, Tmp_win, &Event,
X				     C_TITLE, FALSE);
X		}
X		return;
X	    }
X	}
X    }
X
X    Context = C_NO_CONTEXT;
X
X    if (Event.xany.window == Scr->Root)
X	Context = C_ROOT;
X    if (Tmp_win)
X    {
X	if (Tmp_win->list && RootFunction != NULL &&
X	    (Event.xany.window == Tmp_win->list->w ||
X		Event.xany.window == Tmp_win->list->icon))
X	{
X	    Tmp_win = Tmp_win->list->iconmgr->twm_win;
X	    XTranslateCoordinates(dpy, Event.xany.window, Tmp_win->w,
X		Event.xbutton.x, Event.xbutton.y, 
X		&JunkX, &JunkY, &JunkChild);
X
X	    Event.xbutton.x = JunkX;
X	    Event.xbutton.y = JunkY - Tmp_win->title_height;
X	    Event.xany.window = Tmp_win->w;
X	    Context = C_WINDOW;
X	}
X	else if (Event.xany.window == Tmp_win->title_w)
X	{
X	    Context = C_TITLE;
X	}
X	else if (Event.xany.window == Tmp_win->w)
X	    Context = C_WINDOW;
X	else if (Event.xany.window == Tmp_win->icon_w)
X	{
X	    Context = C_ICON;
X	}
X	else if (Event.xany.window == Tmp_win->frame)
X	    Context = C_FRAME;
X	else if (Tmp_win->list &&
X	    (Event.xany.window == Tmp_win->list->w ||
X		Event.xany.window == Tmp_win->list->icon))
X	{
X	    Tmp_win->list->down = TRUE;
X	    if (Scr->Highlight) DrawIconManagerBorder(Tmp_win->list);
X	    DownIconManager = Tmp_win->list;
X	    Context = C_ICONMGR;
X	}
X    }
X
X    /* this section of code checks to see if we were in the middle of
X     * a command executed from a menu
X     */
X    if (RootFunction != NULL)
X    {
X	if (Event.xany.window == Scr->Root)
X	{
X	    Window child, last_child, found_child;
X	    /* if the window was the Root, we don't know for sure it
X	     * it was the root.  We must check to see if it happened to be
X	     * inside of a client that was getting button press events.
X	     */
X
X	    Tmp_win = NULL;
X	    child = Scr->Root;
X	    do {
X		last_child = child;
X		XTranslateCoordinates(dpy, Scr->Root, last_child,
X		    Event.xbutton.x, Event.xbutton.y,
X		    &JunkX, &JunkY, &child);
X		if (child)
X		    if (XFindContext(dpy, child, TwmContext, (caddr_t *)&Tmp_win) != XCNOENT)
X			found_child = child;
X	    } while (child != None);
X
X	    if (!Tmp_win)
X	    {
X		RootFunction = NULL;
X		XBell(dpy, 0);
X		return;
X	    }
X
X	    /* if the window was one of the small virtual windows, the user
X	     * probably meant to move the panner
X	     */
X	    if (found_child == Tmp_win->virtualWindow ||
X		found_child == Tmp_win->virtualIcon)
X	    {
X		XFindContext(dpy, Scr->Panner, TwmContext, (caddr_t *)&Tmp_win);
X	    }
X
X	    Event.xany.window = Tmp_win->w;
X	    XTranslateCoordinates(dpy, Scr->Root, Event.xany.window,
X		Event.xbutton.x, 
X		Event.xbutton.y, 
X		&JunkX, &JunkY, &JunkChild);
X
X	    Event.xbutton.x = JunkX;
X	    Event.xbutton.y = JunkY;
X	    Context = C_WINDOW;
X	}
X
X	ExecuteFunction(RootFunction, Action, Event.xany.window,
X	    Tmp_win, &Event, Context, FALSE);
X
X	RootFunction = NULL;
X	return;
X    }
X
X    ButtonEvent = Event;
X    ButtonWindow = Tmp_win;
X
X    /* if we get to here, we have to execute a function or pop up a 
X     * menu
X     */
X    modifier = (Event.xbutton.state & mods_used);
X
X    if (Context == C_NO_CONTEXT)
X	return;
X
X    RootFunction = NULL;
X    if (Scr->Mouse[Event.xbutton.button][Context][modifier].func == F_MENU)
X    {
X	do_menu (Scr->Mouse[Event.xbutton.button][Context][modifier].menu,
X		 (Window) None);
X    }
X    else if (Scr->Mouse[Event.xbutton.button][Context][modifier].func != NULL)
X    {
X	Action = Scr->Mouse[Event.xbutton.button][Context][modifier].item ?
X	    Scr->Mouse[Event.xbutton.button][Context][modifier].item->action : NULL;
X	ExecuteFunction(Scr->Mouse[Event.xbutton.button][Context][modifier].func,
X	    Action, Event.xany.window, Tmp_win, &Event, Context, FALSE);
X    }
X    else if (Scr->DefaultFunction.func != NULL)
X    {
X	if (Scr->DefaultFunction.func == F_MENU)
X	{
X	    do_menu (Scr->DefaultFunction.menu, (Window) None);
X	}
X	else
X	{
X	    Action = Scr->DefaultFunction.item ?
X		Scr->DefaultFunction.item->action : NULL;
X	    ExecuteFunction(Scr->DefaultFunction.func, Action,
X	       Event.xany.window, Tmp_win, &Event, Context, FALSE);
X	}
X    }
X}
X
X
X/***********************************************************************
X *
X *  Procedure:
X *	HENQueueScanner - EnterNotify event q scanner
X *
X *	Looks at the queued events and determines if any matching
X *	LeaveNotify events or EnterEvents deriving from the
X *	termination of a grab are behind this event to allow
X *	skipping of unnecessary processing.
X *
X ***********************************************************************
X */
X
Xtypedef struct HENScanArgs {
X    Window w;		/* Window we are currently entering */
X    Bool leaves;	/* Any LeaveNotifies found for this window */
X    Bool inferior;	/* Was NotifyInferior the mode for LeaveNotify */
X    Bool enters;	/* Any EnterNotify events with NotifyUngrab */
X} HENScanArgs;
X
X/* ARGSUSED*/
Xstatic Bool
XHENQueueScanner(dpy, ev, args)
X    Display *dpy;
X    XEvent *ev;
X    char *args;
X{
X    if (ev->type == LeaveNotify) {
X	if (ev->xcrossing.window == ((HENScanArgs *) args)->w &&
X	    ev->xcrossing.mode == NotifyNormal) {
X	    ((HENScanArgs *) args)->leaves = True;
X	    /*
X	     * Only the last event found matters for the Inferior field.
X	     */
X	    ((HENScanArgs *) args)->inferior =
X		(ev->xcrossing.detail == NotifyInferior);
X	}
X    } else if (ev->type == EnterNotify) {
X	if (ev->xcrossing.mode == NotifyUngrab)
X	    ((HENScanArgs *) args)->enters = True;
X    }
X
X    return (False);
X}
X
X/***********************************************************************
X *
X *  Procedure:
X *	HandleEnterNotify - EnterNotify event handler
X *
X ***********************************************************************
X */
X
Xvoid
XHandleEnterNotify()
X{
X    MenuRoot *mr;
X    XEnterWindowEvent *ewp = &Event.xcrossing;
X    HENScanArgs scanArgs;
X    XEvent dummy;
X
X    /*
X     * if we aren't in the middle of menu processing
X     */
X    if (!ActiveMenu) {
X	/*
X	 * We're not interested in pseudo Enter/Leave events generated
X	 * from grab initiations.
X	 */
X	if (ewp->mode == NotifyGrab)
X	    return;
X
X	/*
X	 * Scan for Leave and Enter Notify events to see if we can avoid some
X	 * unnecessary processing.
X	 */
X	scanArgs.w = ewp->window;
X	scanArgs.leaves = scanArgs.enters = False;
X	(void) XCheckIfEvent(dpy, &dummy, HENQueueScanner, (char *) &scanArgs);
X
X	/*
X	 * if entering root window, restore twm default colormap so that 
X	 * titlebars are legible
X	 */
X	if (ewp->window == Scr->Root) {
X	    if (!scanArgs.leaves && !scanArgs.enters)
X		InstallWindowColormaps(EnterNotify, &Scr->TwmRoot);
X	    return;
X	}
X
X	/*
X	 * if we have an event for a specific one of our windows
X	 */
X	if (Tmp_win) {
X	    /*
X	     * If currently in PointerRoot mode (indicated by FocusRoot), then
X	     * focus on this window
X	     */
X	    if (Scr->FocusRoot && (!scanArgs.leaves || scanArgs.inferior)) {
X		if (Tmp_win->list) ActiveIconManager(Tmp_win->list);
X		if (Tmp_win->mapped) {
X		    /*
X		     * unhighlight old focus window
X		     */
X		    if (Scr->Focus &&
X			Scr->Focus != Tmp_win && Tmp_win->hilite_w)
X		      XUnmapWindow(dpy, Scr->Focus->hilite_w);
X
X		    /*
X		     * If entering the frame or the icon manager, then do 
X		     * "window activation things":
X		     *
X		     *     1.  turn on highlight window (if any)
X		     *     2.  install frame colormap
X		     *     3.  set frame and highlight window (if any) border
X		     *     4.  focus on client window to forward typing
X		     *     5.  send WM_TAKE_FOCUS if requested
X		     */
X		    if (ewp->window == Tmp_win->frame ||
X			(Tmp_win->list && ewp->window == Tmp_win->list->w)) {
X			if (Tmp_win->hilite_w)				/* 1 */
X			  XMapWindow (dpy, Tmp_win->hilite_w);
X			if (!scanArgs.leaves && !scanArgs.enters)
X			    InstallWindowColormaps (EnterNotify,	/* 2 */
X						    &Scr->TwmRoot);
X			SetBorder (Tmp_win, True);			/* 3 */
X			if (Tmp_win->title_w && Scr->TitleFocus)	/* 4 */
X			  SetFocus (Tmp_win);
X			if (Tmp_win->protocols & DoesWmTakeFocus)	/* 5 */
X			  SendTakeFocusMessage (Tmp_win, ewp->time);
X			Scr->Focus = Tmp_win;
X		    } else if (ewp->window == Tmp_win->w) {
X			/*
X			 * If we are entering the application window, install
X			 * its colormap(s).
X			 */
X			if (!scanArgs.leaves || scanArgs.inferior)
X			    InstallWindowColormaps(EnterNotify, Tmp_win);
X		    }
X		}			/* end if Tmp_win->mapped */
X	    }				/* end if FocusRoot */
X	    /*
X	     * If this window is to be autoraised, mark it so
X	     */
X	    if (Tmp_win->auto_raise) {
X		enter_win = Tmp_win;
X		if (enter_flag == FALSE) AutoRaiseWindow (Tmp_win);
X	    } else if (enter_flag && raise_win == Tmp_win)
X	      enter_win = Tmp_win;
X	    /*
X	     * set ring leader
X	     */
X	    if (Tmp_win->ring.next && (!enter_flag || raise_win == enter_win))
X	      Scr->RingLeader = Tmp_win;
X	    XSync (dpy, 0);
X	    return;
X	}				/* end if Tmp_win */
X    }					/* end if !ActiveMenu */
X
X    /*
X     * Find the menu that we are dealing with now; punt if unknown
X     */
X    if (XFindContext (dpy, ewp->window, MenuContext, (caddr_t *)&mr) != XCSUCCESS) return;
X
X    mr->entered = TRUE;
X    if (ActiveMenu && mr == ActiveMenu->prev && RootFunction == NULL) {
X	if (Scr->Shadow) XUnmapWindow (dpy, ActiveMenu->shadow);
X	XUnmapWindow (dpy, ActiveMenu->w);
X	ActiveMenu->mapped = UNMAPPED;
X	UninstallRootColormap ();
X	if (ActiveItem) ActiveItem->state = 0;
X	ActiveItem = NULL;
X	ActiveMenu = mr;
X	MenuDepth--;
X    }
X    return;
X}
X
X
X/***********************************************************************
X *
X *  Procedure:
X *	HLNQueueScanner - LeaveNotify event q scanner
X *
X *	Looks at the queued events and determines if any
X *	EnterNotify events are behind this event to allow
X *	skipping of unnecessary processing.
X *
X ***********************************************************************
X */
X
Xtypedef struct HLNScanArgs {
X    Window w;		/* The window getting the LeaveNotify */
X    Bool enters;	/* Any EnterNotify event at all */
X    Bool matches;	/* Any matching EnterNotify events */
X} HLNScanArgs;
X
X/* ARGSUSED*/
Xstatic Bool
XHLNQueueScanner(dpy, ev, args)
X    Display *dpy;
X    XEvent *ev;
X    char *args;
X{
X    if (ev->type == EnterNotify && ev->xcrossing.mode != NotifyGrab) {
X	((HLNScanArgs *) args)->enters = True;
X	if (ev->xcrossing.window == ((HLNScanArgs *) args)->w)
X	    ((HLNScanArgs *) args)->matches = True;
X    }
X
X    return (False);
X}
X
X/***********************************************************************
X *
X *  Procedure:
X *	HandleLeaveNotify - LeaveNotify event handler
X *
X ***********************************************************************
X */
X
Xvoid
XHandleLeaveNotify()
X{
X    HLNScanArgs scanArgs;
X    XEvent dummy;
X
X    if (Tmp_win != NULL)
X    {
X	Bool inicon;
X
X	/*
X	 * We're not interested in pseudo Enter/Leave events generated
X	 * from grab initiations and terminations.
X	 */
X	if (Event.xcrossing.mode != NotifyNormal)
X	    return;
X
X	inicon = (Tmp_win->list &&
X		  Tmp_win->list->w == Event.xcrossing.window);
X
X	if (Scr->RingLeader && Scr->RingLeader == Tmp_win &&
X	    (Event.xcrossing.detail != NotifyInferior &&
X	     Event.xcrossing.window != Tmp_win->w)) {
X	    if (!inicon) {
X		if (Tmp_win->mapped) {
X		    Tmp_win->ring.cursor_valid = False;
X		} else {
X		    Tmp_win->ring.cursor_valid = True;
X		    Tmp_win->ring.curs_x = (Event.xcrossing.x_root -
X					    Tmp_win->frame_x);
X		    Tmp_win->ring.curs_y = (Event.xcrossing.y_root -
X					    Tmp_win->frame_y);
X		}
X	    }
X	    Scr->RingLeader = (TwmWindow *) NULL;
X	}
X	if (Scr->FocusRoot) {
X
X	    if (Event.xcrossing.detail != NotifyInferior) {
X
X		/*
X		 * Scan for EnterNotify events to see if we can avoid some
X		 * unnecessary processing.
X		 */
X		scanArgs.w = Event.xcrossing.window;
X		scanArgs.enters = scanArgs.matches = False;
X		(void) XCheckIfEvent(dpy, &dummy, HLNQueueScanner,
X				     (char *) &scanArgs);
X
X		if ((Event.xcrossing.window == Tmp_win->frame &&
X			!scanArgs.matches) || inicon) {
X		    if (Tmp_win->list) NotActiveIconManager(Tmp_win->list);
X		    if (Tmp_win->hilite_w)
X		      XUnmapWindow (dpy, Tmp_win->hilite_w);
X		    SetBorder (Tmp_win, False);
X		    if (Scr->TitleFocus ||
X			Tmp_win->protocols & DoesWmTakeFocus)
X		      SetFocus ((TwmWindow *) NULL);
X		    Scr->Focus = NULL;
X		} else if (Event.xcrossing.window == Tmp_win->w &&
X				!scanArgs.enters) {
X		    InstallWindowColormaps (LeaveNotify, &Scr->TwmRoot);
X		}
X	    }
X	}
X	XSync (dpy, 0);
X	return;
X    }
X}
X
X
X/***********************************************************************
X *
X *  Procedure:
X *	HandleConfigureRequest - ConfigureRequest event handler
X *
X ***********************************************************************
X */
X
Xvoid
XHandleConfigureRequest()
X{
X    XWindowChanges xwc;
X    unsigned long xwcm;
X    int x, y, width, height, bw;
X    int gravx, gravy;
X    XConfigureRequestEvent *cre = &Event.xconfigurerequest;
X
X#ifdef DEBUG_EVENTS
X    fprintf(stderr, "ConfigureRequest\n");
X    if (cre->value_mask & CWX)
X	fprintf(stderr, "  x = %d\n", cre->x);
X    if (cre->value_mask & CWY)
X	fprintf(stderr, "  y = %d\n", cre->y);
X    if (cre->value_mask & CWWidth)
X	fprintf(stderr, "  width = %d\n", cre->width);
X    if (cre->value_mask & CWHeight)
X	fprintf(stderr, "  height = %d\n", cre->height);
X    if (cre->value_mask & CWSibling)
X	fprintf(stderr, "  above = 0x%x\n", cre->above);
X    if (cre->value_mask & CWStackMode)
X	fprintf(stderr, "  stack = %d\n", cre->detail);
X#endif
X
X    /*
X     * Event.xany.window is Event.xconfigurerequest.parent, so Tmp_win will
X     * be wrong
X     */
X    Event.xany.window = cre->window;	/* mash parent field */
X    if (XFindContext (dpy, cre->window, TwmContext, (caddr_t *) &Tmp_win) ==
X	XCNOENT)
X      Tmp_win = NULL;
X
X
X    /*
X     * According to the July 27, 1988 ICCCM draft, we should ignore size and
X     * position fields in the WM_NORMAL_HINTS property when we map a window.
X     * Instead, we'll read the current geometry.  Therefore, we should respond
X     * to configuration requests for windows which have never been mapped.
X     */
X    if (!Tmp_win) {
X	xwcm = cre->value_mask & 
X	    (CWX | CWY | CWWidth | CWHeight | CWBorderWidth);
X	xwc.x = cre->x;
X	xwc.y = cre->y;
X	xwc.width = cre->width;
X	xwc.height = cre->height;
X	xwc.border_width = cre->border_width;
X	XConfigureWindow(dpy, Event.xany.window, xwcm, &xwc);
X	return;
X    }
X
X    if ((cre->value_mask & CWStackMode) && Tmp_win->stackmode) {
X	TwmWindow *otherwin;
X
X	xwc.sibling = (((cre->value_mask & CWSibling) &&
X			(XFindContext (dpy, cre->above, TwmContext,
X				       (caddr_t *) &otherwin) == XCSUCCESS))
X		       ? otherwin->frame : cre->above);
X	xwc.stack_mode = cre->detail;
X	XConfigureWindow (dpy, Tmp_win->frame, 
X			  cre->value_mask & (CWSibling | CWStackMode), &xwc);
X    }
X
X
X    /* Don't modify frame_XXX fields before calling SetupWindow! */
X    x = Tmp_win->frame_x;
X    y = Tmp_win->frame_y;
X    width = Tmp_win->frame_width;
X    height = Tmp_win->frame_height;
X    bw = Tmp_win->frame_bw;
X
X    /*
X     * Section 4.1.5 of the ICCCM states that the (x,y) coordinates in the
X     * configure request are for the upper-left outer corner of the window.
X     * This means that we need to adjust for the additional title height as
X     * well as for any border width changes that we decide to allow.  The
X     * current window gravity is to be used in computing the adjustments, just
X     * as when initially locating the window.  Note that if we do decide to 
X     * allow border width changes, we will need to send the synthetic 
X     * ConfigureNotify event.
X     */
X    GetGravityOffsets (Tmp_win, &gravx, &gravy);
X
X    if (cre->value_mask & CWBorderWidth) {
X	int bwdelta = cre->border_width - Tmp_win->old_bw;  /* posit growth */
X	if (bwdelta && Scr->ClientBorderWidth) {  /* if change allowed */
X	    x += gravx * bwdelta;	/* change default values only */
X	    y += gravy * bwdelta;	/* ditto */
X	    bw = cre->border_width;
X	    if (Tmp_win->title_height) height += bwdelta;
X	    x += (gravx < 0) ? bwdelta : -bwdelta;
X	    y += (gravy < 0) ? bwdelta : -bwdelta;
X	}
X	Tmp_win->old_bw = cre->border_width;  /* for restoring */
X    }
X
X    if (cre->value_mask & CWX) {	/* override even if border change */
X	x = cre->x - bw;
X    }
X    if (cre->value_mask & CWY) {
X	y = cre->y - ((gravy < 0) ? 0 : Tmp_win->title_height) - bw;
X    }
X
X    if (cre->value_mask & CWWidth) {
X	width = cre->width;
X    }
X    if (cre->value_mask & CWHeight) {
X	height = cre->height + Scr->TitleHeight + bw;
X    }
X
X    if (width != Tmp_win->frame_width || height != Tmp_win->frame_height)
X	Tmp_win->zoomed = ZOOM_NONE;
X
X    /*
X     * SetupWindow (x,y) are the location of the upper-left outer corner and
X     * are passed directly to XMoveResizeWindow (frame).  The (width,height)
X     * are the inner size of the frame.  The inner width is the same as the 
X     * requested client window width; the inner height is the same as the
X     * requested client window height plus any title bar slop.
X     */
X    SetupWindow (Tmp_win, x, y, width, height, bw);
X}
X
X#ifdef SHAPE
X/***********************************************************************
X *
X *  Procedure:
X *	HandleShapeNotify - shape notification event handler
X *
X ***********************************************************************
X */
Xvoid
XHandleShapeNotify ()
X{
X    XShapeEvent	    *sev = (XShapeEvent *) &Event;
X
X    if (Tmp_win == NULL)
X	return;
X    if (sev->kind != ShapeBounding)
X	return;
X    Tmp_win->wShaped = sev->shaped;
X    SetFrameShape (Tmp_win);
X}
X#endif
X
X/***********************************************************************
X *
X *  Procedure:
X *	HandleUnknown - unknown event handler
X *
X ***********************************************************************
X */
X
Xvoid
XHandleUnknown()
X{
X#ifdef DEBUG_EVENTS
X    fprintf(stderr, "type = %d\n", Event.type);
X#endif
X}
X
X/***********************************************************************
X *
X *  Procedure:
X *	Transient - checks to see if the window is a transient
X *
X *  Returned Value:
X *	TRUE	- window is a transient
X *	FALSE	- window is not a transient
X *
X *  Inputs:
X *	w	- the window to check
X *
X ***********************************************************************
X */
X
Xint
XTransient(w)
X    Window w;
X{
X    Window propw;
X
X    return (XGetTransientForHint(dpy, w, &propw));
X}
X
X/***********************************************************************
X *
X *  Procedure:
X *	FindScreenInfo - get ScreenInfo struct associated with a given window
X *
X *  Returned Value:
X *	ScreenInfo struct
X *
X *  Inputs:
X *	w	- the window
X *
X ***********************************************************************
X */
X
XScreenInfo *
XFindScreenInfo(w)
X    Window w;
X{
X    XWindowAttributes attr;
X    int scrnum;
X
X    attr.screen = NULL;
X    if (XGetWindowAttributes(dpy, w, &attr)) {
X	for (scrnum = 0; scrnum < NumScreens; scrnum++) {
X	    if (ScreenList[scrnum] != NULL &&
X		(ScreenOfDisplay(dpy, ScreenList[scrnum]->screen) ==
X		 attr.screen))
X	      return ScreenList[scrnum];
X	}
X    }
X
X    return NULL;
X}
X
X
Xstatic void flush_expose (w)
X    Window w;
X{
X    XEvent dummy;
X
X    while (XCheckTypedWindowEvent (dpy, w, Expose, &dummy)) ;
X}
X
X
X/***********************************************************************
X *
X *  Procedure:
X *	InstallWindowColormaps - install the colormaps for one twm window
X *
X *  Inputs:
X *	type	- type of event that caused the installation
X *	tmp	- for a subset of event types, the address of the
X *		  window structure, whose colormaps are to be installed.
X *
X ***********************************************************************
X */
X
XInstallWindowColormaps (type, tmp)
X    int type;
X    TwmWindow *tmp;
X{
X    int i, j, n, number_cwins, state;
X    ColormapWindow **cwins, *cwin, **maxcwin = NULL;
X    TwmColormap *cmap;
X    char *row, *scoreboard;
X
X    switch (type) {
X    case EnterNotify:
X    case LeaveNotify:
X    case DestroyNotify:
X    default:
X	/* Save the colormap to be loaded for when force loading of
X	 * root colormap(s) ends.
X	 */
X	Scr->cmapInfo.pushed_window = tmp;
X	/* Don't load any new colormap if root colormap(s) has been
X	 * force loaded.
X	 */
X	if (Scr->cmapInfo.root_pushes)
X	    return;
X	/* Don't reload the currend window colormap list.
X	 */
X	if (Scr->cmapInfo.cmaps == &tmp->cmaps)
X	    return;
X	if (Scr->cmapInfo.cmaps)
X	    for (i = Scr->cmapInfo.cmaps->number_cwins,
X		 cwins = Scr->cmapInfo.cmaps->cwins; i-- > 0; cwins++)
X		(*cwins)->colormap->state &= ~CM_INSTALLABLE;
X	Scr->cmapInfo.cmaps = &tmp->cmaps;
X	break;
X    
X    case PropertyNotify:
X    case VisibilityNotify:
X    case ColormapNotify:
X	break;
X    }
X
X    number_cwins = Scr->cmapInfo.cmaps->number_cwins;
X    cwins = Scr->cmapInfo.cmaps->cwins;
X    scoreboard = Scr->cmapInfo.cmaps->scoreboard;
X
X    ColortableThrashing = FALSE; /* in case installation aborted */
X
X    state = CM_INSTALLED;
X
X    for (i = n = 0; i < number_cwins; i++) {
X	cwin = cwins[i];
X	cmap = cwin->colormap;
X	cmap->state |= CM_INSTALLABLE;
X	cmap->state &= ~CM_INSTALL;
X	cmap->w = cwin->w;
X	if (cwin->visibility != VisibilityFullyObscured &&
X	    n < Scr->cmapInfo.maxCmaps) {
X	    row = scoreboard + (i*(i-1)/2);
X	    for (j = 0; j < i; j++)
X		if (row[j] && (cwins[j]->colormap->state & CM_INSTALL))
X		    break;
X	    if (j != i)
X		continue;
X	    n++;
X	    maxcwin = &cwins[i];
X	    state &= (cmap->state & CM_INSTALLED);
X	    cmap->state |= CM_INSTALL;
X	}
X    }
X
X    Scr->cmapInfo.first_req = NextRequest(dpy);
X
X    for ( ; n > 0; maxcwin--) {
X	cmap = (*maxcwin)->colormap;
X	if (cmap->state & CM_INSTALL) {
X	    cmap->state &= ~CM_INSTALL;
X	    if (!(state & CM_INSTALLED)) {
X		cmap->install_req = NextRequest(dpy);
X		XInstallColormap(dpy, cmap->c);
X	    }
X	    cmap->state |= CM_INSTALLED;
X	    n--;
X	}
X    }
X}
X
X/***********************************************************************
X *
X *  Procedures:
X *	<Uni/I>nstallRootColormap - Force (un)loads root colormap(s)
X *
X *	   These matching routines provide a mechanism to insure that
X *	   the root colormap(s) is installed during operations like
X *	   rubber banding or menu display that require colors from
X *	   that colormap.  Calls may be nested arbitrarily deeply,
X *	   as long as there is one UninstallRootColormap call per
X *	   InstallRootColormap call.
X *
X *	   The final UninstallRootColormap will cause the colormap list
X *	   which would otherwise have be loaded to be loaded, unless
X *	   Enter or Leave Notify events are queued, indicating some
X *	   other colormap list would potentially be loaded anyway.
X ***********************************************************************
X */
X
XInstallRootColormap()
X{
X    TwmWindow *tmp;
X    if (Scr->cmapInfo.root_pushes == 0) {
X	/*
X	 * The saving and restoring of cmapInfo.pushed_window here
X	 * is a slimy way to remember the actual pushed list and
X	 * not that of the root window.
X	 */
X	tmp = Scr->cmapInfo.pushed_window;
X	InstallWindowColormaps(0, &Scr->TwmRoot);
X	Scr->cmapInfo.pushed_window = tmp;
X    }
X    Scr->cmapInfo.root_pushes++;
X}
X
X
X/* ARGSUSED*/
Xstatic Bool
XUninstallRootColormapQScanner(dpy, ev, args)
X    Display *dpy;
X    XEvent *ev;
X    char *args;
X{
X    if (!*args)
X	if (ev->type == EnterNotify) {
X	    if (ev->xcrossing.mode != NotifyGrab)
X		*args = 1;
X	} else if (ev->type == LeaveNotify) {
X	    if (ev->xcrossing.mode == NotifyNormal)
X		*args = 1;
X	}
X
X    return (False);
X}
X
XUninstallRootColormap()
X{
X    char args;
X    XEvent dummy;
X
X    if (Scr->cmapInfo.root_pushes)
X	Scr->cmapInfo.root_pushes--;
X    
X    if (!Scr->cmapInfo.root_pushes) {
X	/*
X	 * If we have subsequent Enter or Leave Notify events,
X	 * we can skip the reload of pushed colormaps.
X	 */
X	XSync (dpy, 0);
X	args = 0;
X	(void) XCheckIfEvent(dpy, &dummy, UninstallRootColormapQScanner, &args);
X
X	if (!args)
X	    InstallWindowColormaps(0, Scr->cmapInfo.pushed_window);
X    }
X}
X
X
X#ifdef TRACE
Xdumpevent (e)
X    XEvent *e;
X{
X    char *name = NULL;
X
X    switch (e->type) {
X      case KeyPress:  name = "KeyPress"; break;
X      case KeyRelease:  name = "KeyRelease"; break;
X      case ButtonPress:  name = "ButtonPress"; break;
X      case ButtonRelease:  name = "ButtonRelease"; break;
X      case MotionNotify:  name = "MotionNotify"; break;
X      case EnterNotify:  name = "EnterNotify"; break;
X      case LeaveNotify:  name = "LeaveNotify"; break;
X      case FocusIn:  name = "FocusIn"; break;
X      case FocusOut:  name = "FocusOut"; break;
X      case KeymapNotify:  name = "KeymapNotify"; break;
X      case Expose:  name = "Expose"; break;
X      case GraphicsExpose:  name = "GraphicsExpose"; break;
X      case NoExpose:  name = "NoExpose"; break;
X      case VisibilityNotify:  name = "VisibilityNotify"; break;
X      case CreateNotify:  name = "CreateNotify"; break;
X      case DestroyNotify:  name = "DestroyNotify"; break;
X      case UnmapNotify:  name = "UnmapNotify"; break;
X      case MapNotify:  name = "MapNotify"; break;
X      case MapRequest:  name = "MapRequest"; break;
X      case ReparentNotify:  name = "ReparentNotify"; break;
X      case ConfigureNotify:  name = "ConfigureNotify"; break;
X      case ConfigureRequest:  name = "ConfigureRequest"; break;
X      case GravityNotify:  name = "GravityNotify"; break;
X      case ResizeRequest:  name = "ResizeRequest"; break;
X      case CirculateNotify:  name = "CirculateNotify"; break;
X      case CirculateRequest:  name = "CirculateRequest"; break;
X      case PropertyNotify:  name = "PropertyNotify"; break;
X      case SelectionClear:  name = "SelectionClear"; break;
X      case SelectionRequest:  name = "SelectionRequest"; break;
X      case SelectionNotify:  name = "SelectionNotify"; break;
X      case ColormapNotify:  name = "ColormapNotify"; break;
X      case ClientMessage:  name = "ClientMessage"; break;
X      case MappingNotify:  name = "MappingNotify"; break;
X    }
X
X    if (name) {
X	printf ("event:  %s, %d remaining\n", name, QLength(dpy));
X    } else {
X	printf ("unknown event %d, %d remaining\n", e->type, QLength(dpy));
X    }
X}
X#endif /* TRACE */
X
SHAR_EOF
if test 73881 -ne "`wc -c < events.c`"
then
    echo shar: error transmitting "events.c" '(should have been 73881 characters)'
fi
fi
# end of shell archive
exit 0

dan
----------------------------------------------------
O'Reilly && Associates   argv@sun.com / argv@ora.com
Opinions expressed reflect those of the author only.