/* Copyright 1989 GROUPE BULL -- See license conditions in file COPYRIGHT
 * Copyright 1989 Massachusetts Institute of Technology
 */
/***********************\
* 		        *
*  WOOL_OBJECT: Event  *
*  BODY		        *
* 		        *
\***********************/

#include "EXTERN.h"
#include <stdio.h>
#include "wool.h"
#include "wl_atom.h"
#include "wl_pointer.h"
#include "wl_string.h"
#include "wl_number.h"
#include "wl_list.h"
#include "wl_func.h"
#include "gwm.h"
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include "INTERN.h"
#include "wl_event.h"
#include "EXTERN.h"
#include "wl_cursor.h"
#include "wl_fsm.h"

extern WOB_METHOD       MenuClass[], PlugClass[];

/* table of events getting redirected to grabbing wob during a GWM grab
 */

Card32 EventProperties[] = {
/* zero */             0,
/* one */              0,
/* KeyPress */         EPGrabRedirected|EPKeyOrButton|EPXY|EPXYRoot|EPTime,
/* KeyRelease */       EPGrabRedirected|EPKeyOrButton|EPXY|EPXYRoot|EPTime,
/* ButtonPress */      EPGrabRedirected|EPKeyOrButton|EPXY|EPXYRoot|EPTime,
/* ButtonRelease */    EPGrabRedirected|EPKeyOrButton|EPXY|EPXYRoot|EPTime,
/* MotionNotify */     EPGrabRedirected|EPKeyOrButton|EPXY|EPXYRoot|EPTime,
/* EnterNotify */      EPRedirectable|EPCrossing|EPXY|EPXYRoot|EPTime,
/* LeaveNotify */      EPRedirectable|EPCrossing|EPXY|EPXYRoot|EPTime,
/* FocusIn */          EPRedirectable|EPFocus,
/* FocusOut */         EPRedirectable|EPFocus,
/* KeymapNotify */     0,
/* Expose */           0,
/* GraphicsExpose */   0,
/* NoExpose */         0,
/* VisibilityNotify */ 0,
/* CreateNotify */     0,
/* DestroyNotify */    0,
/* UnmapNotify */      0,
/* MapNotify */        0,
/* MapRequest */       0,
/* ReparentNotify */   0,
/* ConfigureNotify */  0,
/* ConfigureRequest */ 0,
/* GravityNotify */    0,
/* ResizeRequest */    0,
/* CirculateNotify */  0,
/* CirculateRequest */ 0,
/* PropertyNotify */   EPTime,
/* SelectionClear */   EPTime,
/* SelectionRequest */ EPTime,
/* SelectionNotify */  EPTime,
/* ColormapNotify */   0,
/* ClientMessage */    0,
/* MappingNotify */    0,
/* LASTEvent */        0,
/* GWMUserEvent */     0
};

/* offset of the Time field of events possessing it */
/* IF THIS GIVES COMPILES ERROR, USE THE FLAG -DNO_STRUCTURE_OFFSETS */
XEvent dummy_evt_ptr;
#define XEventFieldOffset(evt_type, field) \
    ((char *)(&(dummy_evt_ptr.evt_type.field))) - \
    ((char *)(&(dummy_evt_ptr.type)))

int EventTimeFieldOffset
#ifndef NO_STRUCTURE_OFFSETS
[] = {
/* zero */             0,
/* one */              0,
/* KeyPress */         XEventFieldOffset(xkey, time),
/* KeyRelease */       XEventFieldOffset(xkey, time),
/* ButtonPress */      XEventFieldOffset(xbutton, time),
/* ButtonRelease */    XEventFieldOffset(xbutton, time),
/* MotionNotify */     XEventFieldOffset(xmotion, time),
/* EnterNotify */      XEventFieldOffset(xcrossing, time),
/* LeaveNotify */      XEventFieldOffset(xcrossing, time),
/* FocusIn */          0,
/* FocusOut */         0,
/* KeymapNotify */     0,
/* Expose */           0,
/* GraphicsExpose */   0,
/* NoExpose */         0,
/* VisibilityNotify */ 0,
/* CreateNotify */     0,
/* DestroyNotify */    0,
/* UnmapNotify */      0,
/* MapNotify */        0,
/* MapRequest */       0,
/* ReparentNotify */   0,
/* ConfigureNotify */  0,
/* ConfigureRequest */ 0,
/* GravityNotify */    0,
/* ResizeRequest */    0,
/* CirculateNotify */  0,
/* CirculateRequest */ 0,
/* PropertyNotify */   XEventFieldOffset(xproperty, time),
/* SelectionClear */   XEventFieldOffset(xselectionclear, time),
/* SelectionRequest */ XEventFieldOffset(xselectionrequest, time),
/* SelectionNotify */  XEventFieldOffset(xselection, time),
/* ColormapNotify */   0,
/* ClientMessage */    0,
/* MappingNotify */    0,
/* LASTEvent */        0,
/* GWMUserEvent */     0
}
#else
[GWMUserEvent]
#endif /* NO_STRUCTURE_OFFSETS */
;

/* to filter button releases events from the fact that they get the same button
 * pressed as a modifier...
 */
unsigned int ButtonMasks[] = {
    ~0,
    ~Button1Mask,
    ~Button2Mask,
    ~Button3Mask,
    ~Button4Mask,
    ~Button5Mask
    };

/*
 * to built a Event from C
 */

WOOL_Event
wool_event_make(name, x_type, match, release, mask, state, detail)
char *		name;
int		x_type;
int		(*match)();
int		(*release)();
unsigned int	mask, state, detail;
{
    WOOL_Event     object = (WOOL_Event) WLEvent_make(state, detail);

    object -> x_type = x_type;
    object -> match = match;
    object -> release = release;
    object -> mask = mask;
    return (WOOL_Event) WLAtom_set(wool_atom(name), object);
}

WOOL_Event
WLEvent_make(state, detail)
unsigned int	state, detail;
{
    WOOL_Event     object = (WOOL_Event) Malloc(
					       sizeof(struct _WOOL_Event));

    zrt_put(object);
    object -> type = WLEvent;
    object -> state = state;
    object -> detail = detail;
    object -> user_mask = 0;
    object -> flags = 0;
    return object;
}

WOOL_OBJECT
WLEvent_print(obj)
WOOL_Event obj;
{
    wool_printf("{EVENT %s", eventtype[obj->x_type]);
    wool_printf(" d=%d", obj->detail);
    wool_printf(" s=%d", obj->state);
    /*print_atom_pointing_to(obj); */
    wool_puts("}");
    return (WOOL_OBJECT) obj;
}

/*
 * List of matching procedures for each event
 */

int 
WLEvent_user(wl_event, evt)
WOOL_Event	wl_event;
Event	evt;
{
    if ((wl_event -> detail == (int) evt -> xany.display)
	|| (wl_event -> detail == (unsigned int) ANY))
	return TRUE;
    else
	return FALSE;
}

int 
WLEvent_propertychange(wl_event, evt)
WOOL_Event	wl_event;
Event	evt;
{
    if (evt -> xproperty.atom == (Atom) wl_event -> detail
	|| (wl_event -> detail == (unsigned int) ANY)) {
	return TRUE;
    } else
	return FALSE;
}

int 
WLEvent_clientmessage(wl_event, evt)
WOOL_Event	wl_event;
Event	evt;
{
    if (evt -> xclient.message_type == (Atom) wl_event -> detail
	|| (wl_event -> detail == (unsigned int) ANY)) {
	return TRUE;
    } else
	return FALSE;
}

int 
WLEvent_property_icon_pixmap_change(wl_event, evt)
WOOL_Event	wl_event;
Event	evt;
{
    if ((evt -> xproperty.atom == (Atom) XA_WM_HINTS) 
	&&
	(SetTarget(TargetWob),
	 TargetWindow -> cached_props -> wm_hints.flags & IconPixmapHint)
	&&
	(!(GWM_backup_of_old_property.wm_hints.flags & IconPixmapHint)
	 ||
	 TargetWindow -> cached_props -> wm_hints.icon_pixmap !=
	 GWM_backup_of_old_property.wm_hints.icon_pixmap))
	return TRUE;
    else
	return FALSE;
}

int 
WLEvent_configurenotify(wl_event, evt)
WOOL_Event	wl_event;
Event	evt;
{
    return TRUE;
}

int 
WLEvent_buttonpress(wl_event, evt)
WOOL_Event	wl_event;
Event	evt;
{
    if (((wl_event -> detail == (unsigned int) ANY) ||
	 (wl_event -> detail == evt -> xbutton.button))
	&&
	((wl_event -> state == (unsigned int) ANY) ||
	 (wl_event -> state == evt -> xbutton.state)))
	return TRUE;
    else
	return FALSE;
}

int 
WLEvent_double_buttonpress(wl_event, evt)
WOOL_Event	wl_event;
Event	evt;
{
    if (((wl_event -> detail == (unsigned int) ANY) ||
	 (wl_event -> detail == evt -> xbutton.button))
	&&
	((wl_event -> state == (unsigned int) ANY) ||
	 (wl_event -> state == evt -> xbutton.state))
	&&
	evt -> xbutton.button == GWM_LastEvent.xbutton.button
	&&
	evt -> xbutton.window == GWM_LastEvent.xbutton.window
	&&
	evt -> xbutton.time <
	(GWM_LastEvent.xbutton.time + GWM_DoubleClickDelay))
	return TRUE;
    else
	return FALSE;
}

int 
WLEvent_buttonrelease(wl_event, evt)
WOOL_Event	wl_event;
Event	evt;
{
    unsigned int butmask = ButtonMasks[evt -> xbutton.button];

    if (((wl_event -> detail == (unsigned int) ANY) ||
	 (wl_event -> detail == evt -> xbutton.button))
	&&
	((wl_event -> state == (unsigned int) ANY) ||
	 ((wl_event -> state & butmask) == (evt -> xbutton.state & butmask)
	  )))
	return TRUE;
    else
	return FALSE;
}

int 
WLEvent_keypress(wl_event, evt)
WOOL_Event	wl_event;
Event	evt;
{
    if (((wl_event -> detail == (unsigned int) ANY) ||
	 (wl_event -> detail == XKeycodeToKeysym(dpy,
				    evt -> xkey.keycode, 0)))
	&&
	((wl_event -> state == (unsigned int) ANY) ||
	 (wl_event -> state == evt -> xkey.state))) {
	return TRUE;
    } else
	return FALSE;
}

int 
WLEvent_keyrelease(wl_event, evt)
WOOL_Event	wl_event;
Event	evt;
{
    if (((wl_event -> detail == (unsigned int) ANY) ||
	 (wl_event -> detail == XKeycodeToKeysym(dpy,
				    evt -> xkey.keycode, 0)))
	&&
	((wl_event -> state == (unsigned int) ANY) ||
	 (wl_event -> state == evt -> xkey.state))) {
	return TRUE;
    } else
	return FALSE;
}

/*
 * enter & leave in the subtree of the window
 */

int
WLEvent_enterwindow(wl_event, evt)
WOOL_Event	wl_event;
Event	evt;
{
    XEvent          report;

    if (evt -> xcrossing.detail != NotifyInferior) {
	/* XSync(dpy, 0); */
	if (!XCheckTypedWindowEvent(dpy, evt -> xcrossing.window,
				    LeaveNotify, &report)
	    || report.xcrossing.detail == NotifyInferior)
	    return TRUE;
	else
	    return FALSE;
    } else
	return FALSE;
}

int
WLEvent_leavewindow(wl_event, evt)
WOOL_Event	wl_event;
Event	evt;
{
    XEvent          report;

    if (evt -> xcrossing.detail != NotifyInferior) {
	/* XSync(dpy, 0); */
	if (!XCheckTypedWindowEvent(dpy, evt -> xcrossing.window,
				    EnterNotify, &report)
	    || report.xcrossing.detail == NotifyInferior)
	    return TRUE;
	else
	    return FALSE;
    } else
	return FALSE;
}

int
WLEvent_enterwindow_nograb(wl_event, evt)
WOOL_Event	wl_event;
Event	evt;
{
    XEvent          report;

    if (evt -> xcrossing.detail != NotifyInferior) {
	/* XSync(dpy, 0); */
	if ((!XCheckTypedWindowEvent(dpy, evt -> xcrossing.window,
				    LeaveNotify, &report)
	    || report.xcrossing.detail == NotifyInferior)
	    && evt -> xcrossing.mode != NotifyGrab
	    && evt -> xcrossing.mode != NotifyUngrab)
	    return TRUE;
	else
	    return FALSE;
    } else
	return FALSE;
}

int
WLEvent_leavewindow_nograb(wl_event, evt)
WOOL_Event	wl_event;
Event	evt;
{
    XEvent          report;

    if (evt -> xcrossing.detail != NotifyInferior) {
	/* XSync(dpy, 0); */
	if ((!XCheckTypedWindowEvent(dpy, evt -> xcrossing.window,
				    EnterNotify, &report)
	    || report.xcrossing.detail == NotifyInferior)
	    && evt -> xcrossing.mode != NotifyGrab
	    && evt -> xcrossing.mode != NotifyUngrab)
	    return TRUE;
	else
	    return FALSE;
    } else
	return FALSE;
}

/* Focus management
 */

int
WLEvent_focusin(wl_event, evt)
WOOL_Event	wl_event;
Event	evt;
{
    if (evt -> xfocus.detail == NotifyNonlinearVirtual
	 || TargetWob -> type == PlugClass) {
	return TRUE;
    } else
	return FALSE;
}

int
WLEvent_focusout(wl_event, evt)
WOOL_Event	wl_event;
Event	evt;
{
    if (evt -> xfocus.detail == NotifyNonlinearVirtual
	 || TargetWob -> type == PlugClass) {
	return TRUE;
    } else
	return FALSE;
}


/* visibility management
 */

int
WLEvent_visibility_partially_obscured(wl_event, evt)
WOOL_Event      wl_event;
Event   evt;
{
    return (evt -> xvisibility.state == VisibilityPartiallyObscured);
}

int
WLEvent_visibility_fully_obscured(wl_event, evt)
WOOL_Event      wl_event;
Event   evt;
{
    return (evt -> xvisibility.state == VisibilityFullyObscured);
}

int
WLEvent_visibility_unobscured(wl_event, evt)
WOOL_Event      wl_event;
Event   evt;
{
    return (evt -> xvisibility.state == VisibilityUnobscured);
}

/*
 * list of release procedures
 */

int
WLEvent_button_release(wl_event, evt)
WOOL_Event      wl_event;
Event   evt;
{
    wait_for_button_release(evt -> xbutton.button);
    return 0;
}

int
WLEvent_key_release(wl_event, evt)
WOOL_Event      wl_event;
Event   evt;
{
    wait_for_key_release(evt -> xkey.keycode);
    return 0;
}

/* mapping of window
 */

int
WLEvent_map_notify(wl_event, evt)
WOOL_Event      wl_event;
Event   evt;
{
    return TRUE;
}

/*
 * functions to make the different events
 */

WOOL_Event
WLEvent_user_make(data)
WOOL_Atom	data;
{
    WOOL_Event      event;

    if (data -> type == WLNumber && (((WOOL_Number) data) -> number == ANY))
	event = WLEvent_make(0, ANY);
    else
	event = WLEvent_make(0, (unsigned int)data);
    event -> x_type = GWMUserEvent;
    event -> match = WLEvent_user;
    event -> release = NULL;
    event -> mask = 0;
    event -> user_mask = 1;
    return event;
}

WOOL_Event
WLEvent_propertychange_make(name)
WOOL_String	name;
{
    WOOL_Event      event = WLEvent_make(0, 0);
    WOOL_Pointer    x_atom;
    Atom            property_name;

    event -> x_type = PropertyNotify;
    event -> match = WLEvent_propertychange;
    event -> release =  NULL;
    event -> mask = PropertyChangeMask;
    if (is_a_string(name)) {
	if (wool_self_pointer_make(name -> string, '\030', &x_atom)) {
	    property_name = *(x_atom -> ptr);
	} else {
	    property_name = XInternAtom(dpy, name -> string, False);
	    *(x_atom -> ptr) = property_name;
	}
    } else {
	property_name = (Atom) ANY;
    }
    event -> detail = (unsigned int) property_name;    
    return event;
}

WOOL_Event
WLEvent_clientmessage_make(name)
WOOL_String	name;
{
    WOOL_Event      event = WLEvent_make(0, 0);
    WOOL_Pointer    x_atom;
    Atom            property_name;

    event -> x_type = ClientMessage;
    event -> match = WLEvent_clientmessage;
    event -> release =  NULL;
    event -> mask = 0;
    must_be_string(name, 0);
    if (wool_self_pointer_make(name -> string, '\030', &x_atom)) {
	property_name = *(x_atom -> ptr);
    } else {
	property_name = XInternAtom(dpy, name -> string, False);
	*(x_atom -> ptr) = property_name;
    }
    event -> detail = (unsigned int) property_name;    
    return event;
}

WOOL_Event
WLEvent_button_make(button, modifier)
WOOL_Number	modifier;
WOOL_Number	button;
{
    WOOL_Event      event = WLEvent_make(modifier -> number,
		  (button -> number == AnyButton ? ANY : button -> number));

    event -> x_type = ButtonPress;
    event -> match = WLEvent_buttonpress;
    event -> release =  WLEvent_button_release;
    event -> mask = ButtonPressMask | ButtonReleaseMask;
    return event;
}

WOOL_Event
WLEvent_buttonpress_make(button, modifier)
WOOL_Number	modifier;
WOOL_Number	button;
{
    WOOL_Event      event = WLEvent_button_make(button, modifier);

    event -> release = NULL;
    return event;
}

WOOL_Event
WLEvent_double_button_make(button, modifier)
WOOL_Number	modifier;
WOOL_Number	button;
{
    WOOL_Event      event = WLEvent_make(modifier -> number,
		  (button -> number == AnyButton ? ANY : button -> number));

    event -> x_type = ButtonPress;
    event -> match = WLEvent_double_buttonpress;
    event -> release =  WLEvent_button_release;
    event -> mask = ButtonPressMask | ButtonReleaseMask;
    return event;
}

WOOL_Event
WLEvent_double_buttonpress_make(button, modifier)
WOOL_Number	modifier;
WOOL_Number	button;
{
    WOOL_Event      event = WLEvent_double_button_make(button, modifier);

    event -> release = NULL;
    return event;
}

WOOL_Event
WLEvent_buttonrelease_make(button, modifier)
WOOL_Number	modifier;
WOOL_Number	button;
{
    WOOL_Event      event =
    WLEvent_button_make(button,
			WLNumber_make(((unsigned long) modifier -> number) |
	  (((unsigned long) Button1Mask) << (button -> number - Button1))));

    event -> x_type = ButtonRelease;
    event -> match = WLEvent_buttonrelease;
    event -> release = NULL;
    return event;
}

WOOL_Event
WLEvent_key_make(key, modifier)
WOOL_Number	modifier;
WOOL_Number	key;
{
    WOOL_Event      event;
    KeySym          keysym;

    must_be_number(modifier, 1);
    if (key -> type == WLNumber)
	keysym = key -> number;
    else {
	if ((keysym = XStringToKeysym(((WOOL_String) key) -> string))
	    == NoSymbol) {
	    wool_printf("unknown key name: \"%s\"\n", 
			((WOOL_String) key) -> string);
	    keysym = (KeySym) UNDEFINED_KEYCODE;
	    }
    }
    event = WLEvent_make(modifier -> number, keysym);
    event -> x_type = KeyPress;
    event -> match = WLEvent_keypress;
    event -> release = WLEvent_key_release;
    event -> mask = KeyPressMask | KeyReleaseMask;
    return event;
}

WOOL_Event
WLEvent_keypress_make(key, modifier)
WOOL_Number	modifier;
WOOL_Number	key;
{
    WOOL_Event      event = WLEvent_key_make(key, modifier);

    event -> release = NULL;
    return event;
}

WOOL_Event
WLEvent_keyrelease_make(key, modifier)
WOOL_Number	modifier;
WOOL_Number	key;
{
    WOOL_Event      event = WLEvent_key_make(key, modifier);

    event -> x_type = KeyRelease;
    event -> match = WLEvent_keyrelease;
    event -> release = NULL;
    return event;
}

/*
 * Initialize all the events
 */

void
wool_all_events_make()
{
    grab_queue_init();

    wool_atom_with_numeric_value_make("any", ANY);
    wool_atom_with_numeric_value_make("alone", 0);
    wool_atom_with_numeric_value_make("with-button-1", Button1Mask);
    wool_atom_with_numeric_value_make("with-button-2", Button2Mask);
    wool_atom_with_numeric_value_make("with-button-3", Button3Mask);
    wool_atom_with_numeric_value_make("with-button-4", Button4Mask);
    wool_atom_with_numeric_value_make("with-button-5", Button5Mask);
    wool_atom_with_numeric_value_make("with-shift", ShiftMask);
    wool_atom_with_numeric_value_make("with-lock", LockMask);
    wool_atom_with_numeric_value_make("with-control", ControlMask);
    wool_atom_with_numeric_value_make("with-alt", Mod1Mask);
    wool_atom_with_numeric_value_make("with-modifier-1", Mod1Mask);
    wool_atom_with_numeric_value_make("with-modifier-2", Mod2Mask);
    wool_atom_with_numeric_value_make("with-modifier-3", Mod3Mask);
    wool_atom_with_numeric_value_make("with-modifier-4", Mod4Mask);
    wool_atom_with_numeric_value_make("with-modifier-5", Mod5Mask);

    wool_subr_make(WLSubr, WLEvent_buttonpress_make, "buttonpress", 2);
    wool_subr_make(WLSubr, WLEvent_keypress_make, "keypress", 2);
    wool_subr_make(WLSubr, WLEvent_button_make, "button", 2);
    wool_subr_make(WLSubr, WLEvent_key_make, "key", 2);
    wool_subr_make(WLSubr, WLEvent_buttonrelease_make, "buttonrelease", 2);
    wool_subr_make(WLSubr, WLEvent_keyrelease_make, "keyrelease", 2);
    wool_subr_make(WLSubr, WLEvent_user_make, "user-event", 1);
    wool_subr_make(WLSubr, WLEvent_propertychange_make, "property-change", 1);
    wool_subr_make(WLSubr, WLEvent_clientmessage_make, "client-message", 1);
    wool_subr_make(WLSubr, WLEvent_double_buttonpress_make,
		   "double-buttonpress", 2);
    wool_subr_make(WLSubr, WLEvent_double_button_make, "double-button", 2);
    wool_event_make("name-change", PropertyNotify, WLEvent_propertychange,
    NULL, PropertyChangeMask, 0, XA_WM_NAME);
    wool_event_make("window-icon-pixmap-change", PropertyNotify,
		    WLEvent_property_icon_pixmap_change, NULL,
		    PropertyChangeMask, 0, XA_WM_HINTS);
    wool_event_make("geometry-change", ConfigureNotify,
    WLEvent_configurenotify, NULL, SubstructureNotifyMask, 0, 0);
    wool_event_make("enter-window", EnterNotify, WLEvent_enterwindow, NULL,
		    EnterWindowMask, 0, 0);
    wool_event_make("leave-window", LeaveNotify, WLEvent_leavewindow, NULL,
		    LeaveWindowMask, 0, 0);
    wool_event_make("enter-window-not-from-grab", EnterNotify,
		    WLEvent_enterwindow_nograb, NULL,
		    EnterWindowMask, 0, 0);
    wool_event_make("leave-window-not-from-grab", LeaveNotify,
		    WLEvent_leavewindow_nograb, NULL,
		    LeaveWindowMask, 0, 0);
    wool_event_make("focus-in", FocusIn, WLEvent_focusin, NULL,
		    FocusChangeMask, 0, 0);
    wool_event_make("focus-out", FocusOut, WLEvent_focusout, NULL,
		    FocusChangeMask, 0, 0);
    wool_event_make("map-notify", MapNotify, WLEvent_map_notify, NULL,
		    StructureNotifyMask, 0, 0);
    wool_event_make("visibility-unobscured", VisibilityNotify,
		    WLEvent_visibility_unobscured,
		    NULL, VisibilityChangeMask, 0, 0);
    wool_event_make("visibility-partially-obscured", VisibilityNotify,
		    WLEvent_visibility_partially_obscured,
		    NULL, VisibilityChangeMask, 0, 0);
    wool_event_make("visibility-fully-obscured", VisibilityNotify,
		    WLEvent_visibility_fully_obscured,
		    NULL, VisibilityChangeMask, 0, 0);
}

/*
 * wait for release of button n
 */

int
LookForButtonRelease(display, evt, arg)
Display *display;
XEvent *evt;
char *arg;
{
    int n = (int)arg;
    if ((evt -> type == ButtonRelease) && (evt -> xbutton.button == n))
	return True;
    else
	return False;
}

wait_for_button_release(n)
int n;
{
    XEvent	    button_event;

    XIfEvent(dpy, &button_event, LookForButtonRelease, (char *)n);
}

/*
 * wait for release of key of code n
 */

int
LookForKeyRelease(display, evt, arg)
Display *display;
XEvent *evt;
char *arg;
{
    if ((evt -> type == KeyRelease) && (evt -> xkey.keycode == (int)arg))
	return True;
    else
	return False;
}

wait_for_key_release(n)
int n;
{
    XEvent	    button_event;

    XIfEvent(dpy, &button_event, LookForKeyRelease, (char *)n);
}

/* grab management
 */

WOOL_OBJECT
set_grab(wob)
Wob	wob;
{
    WOOL_Cursor     cursor;
    int             status;

    if (wob -> type == MenuClass)
	cursor = (WOOL_Cursor) wob -> cursor;
    else
	cursor =
	    (WOOL_Cursor) WOOL_send(WOOL_eval, WA_cursor, (WA_cursor));
    if ((status =
	 XGrabPointer(dpy, wob -> hook, True,
		      LegalPointerEventMask(((WOOL_Fsm) wob -> fsm) -> mask),
		      GrabModeAsync, GrabModeAsync,
		      (GWM_confine_grabs ? wob -> hook : None),
		      (cursor == (WOOL_Cursor) NIL ? None : cursor -> cursor),
		      CurrentTime))
	!= GrabSuccess)
	return (WOOL_OBJECT) WLNumber_make(status);
    if (GWM_grab_keyboard_also)
	if ((status = XGrabKeyboard(dpy, wob -> hook, True,
				 GrabModeAsync, GrabModeAsync, CurrentTime))
	    != GrabSuccess) {
	    XUngrabPointer(dpy, CurrentTime);
	    return (WOOL_OBJECT) WLNumber_make(status);
	} else {
	    GWM_KeyboardGrabbed = wob;
	}
    if (GWM_GrabServer)
	XGrabServer(dpy);
    GWM_ServerGrabbed = wob;
    return NIL;
}

WOOL_OBJECT
remove_grab(wob)
Wob	wob;
{
    if (!wob || wob == GWM_ServerGrabbed) {
	if (GWM_ServerGrabbed)
	  XUngrabPointer(dpy, CurrentTime);
	if (GWM_KeyboardGrabbed)
	  XUngrabKeyboard(dpy, CurrentTime);
	if (GWM_GrabServer)
	  XUngrabServer(dpy);
	XSync(dpy, 0);
	GWM_ServerGrabbed = GWM_KeyboardGrabbed = NULL;
	FlushQueuedGrabEvents();
    }
    return NIL;
}

WOOL_OBJECT
wool_set_grab(argc, argv)
int	argc;
WOOL_Number	argv[];
{
    GWM_GrabChildEvents = 0;
    switch (argc) {
    case 0:
	return set_grab(TargetWob);
    case 2:
	GWM_GrabChildEvents = 1;
    }
    must_be_number(argv[0], 0);
    return set_grab(argv[0] -> number);
}

WOOL_OBJECT
wool_remove_grab(argc, argv)
int	argc;
WOOL_Number	argv[];
{
    if (!argc)
	return remove_grab(0);
    must_be_number(argv[0], 0);
    return remove_grab(argv[0] -> number);
}

/* replay back event in a grab
 * if arg != NIL, replay key event
 */

WOOL_OBJECT
wool_ungrab_and_replay_event(key)
WOOL_OBJECT	key;
{
    if (key == NIL)
	XAllowEvents(dpy, ReplayPointer, CurrentTime);
    else
	XAllowEvents(dpy, ReplayKeyboard, CurrentTime);
    return NIL;
}

/* makes a button event replayable */

WOOL_OBJECT
wool_makes_replayable(event)
WOOL_Event 	event;
{
    must_be_event(event, 0);
    event -> flags |= FREEZE_ON_GRABS;
    return (WOOL_OBJECT) event;
}

/* allows event processing after a freeze due to a replayable event
 */

WOOL_OBJECT
wool_allow_events()
{
    XAllowEvents(dpy, AsyncPointer, CurrentTime);
    XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
    return NIL;
}

/* last events of some type
 */

WOOL_OBJECT
wool_last_key_struck()
{
    char            s[81];
    int             l;

    if ((TriggeringEvent -> type == KeyPress ||
	 TriggeringEvent -> type == KeyRelease)
	&& (l = XLookupString(&TriggeringEvent->xkey, s, 80, 0, 0))) {
	s[l] = '\0';
	return (WOOL_OBJECT) WLString_make(s);
    } else
	return NIL;
}

WOOL_OBJECT
wool_last_user_event()
{
    if (TriggeringEvent -> type == GWMUserEvent)
	return (WOOL_OBJECT) TriggeringEvent -> xany.display;
    else
	return wool_error("last event%s was not an user event", "");
}

Event
last_key_or_button_event(evt)
Event	evt;
{
    if (event_is_key_or_button(*TriggeringEvent))
	return TriggeringEvent;
    else
	return &GWM_LastEvent;
}

/*
 * returns the coords of the click relative to the client as a list
 * (logical-x logical-y x y) for the current window's client
 */

#ifdef NOBASEDIMS
#ifdef PBaseSize
#undef PBaseSize
#endif
#define PBaseSize PMinSize
#define base_width min_width
#define base_height min_height
#endif /* NOBASEDIMS */


WOOL_OBJECT
wool_logical_coords()
{
    int             x=0 , y=0;
    Window          child;
    WOOL_List       wl_list = wool_list_make(6);
    Event           evt = last_key_or_button_event(TriggeringEvent);
    XSizeHints     *normal_hints =
	&(TargetWindow -> cached_props -> normal_hints);

    if (TargetWindow -> client)
	XTranslateCoordinates(dpy, evt -> xbutton.root,
			      TargetWindow -> client, evt -> xbutton.x_root,
			      evt -> xbutton.y_root, &x, &y, &child);
    increase_reference(wl_list -> list[0] = (WOOL_OBJECT) WLNumber_make(
        (x - (normal_hints -> flags & PBaseSize ?
	      normal_hints -> base_width : 0))
	 / (normal_hints -> flags & PResizeInc ?
	    normal_hints -> width_inc : 1)));
    increase_reference(wl_list -> list[1] = (WOOL_OBJECT) WLNumber_make(
        (y - (normal_hints -> flags & PBaseSize ?
	      normal_hints -> base_height : 0))
	 / (normal_hints -> flags & PResizeInc ?
	    normal_hints -> height_inc : 1)));
    increase_reference(wl_list -> list[2] = (WOOL_OBJECT) WLNumber_make(x));
    increase_reference(wl_list -> list[3] = (WOOL_OBJECT) WLNumber_make(y));

    XTranslateCoordinates(dpy, evt -> xbutton.root,
                              TargetWindow -> hook, evt -> xbutton.x_root,
                              evt -> xbutton.y_root, &x, &y, &child);
    increase_reference(wl_list -> list[4] = (WOOL_OBJECT) WLNumber_make(x));
    increase_reference(wl_list -> list[5] = (WOOL_OBJECT) WLNumber_make(y));

    return (WOOL_OBJECT) wl_list;
}

int EventMasks[]={0,0,
	KeyPressMask,
	KeyReleaseMask,
	ButtonPressMask,
	ButtonReleaseMask};

WOOL_OBJECT
wool_resend_event(wob)
WOOL_Number	wob;
{
    Window          window;
    Event	    evt = last_key_or_button_event(TriggeringEvent);

    if (wob = (WOOL_Number) NIL)
	window = TargetWindow -> client;
    else
	window = ((Wob) wob) -> hook;
    if (window) {
	evt -> xany.window = window;
	XSendEvent(dpy, TargetWindow -> client, False,
		   EventMasks[evt->type], evt);
	XFlush(dpy);
    }
    return (WOOL_OBJECT) NIL;
}

WOOL_OBJECT
wool_send_keycode_to_client(keycode, mod)
WOOL_Number	keycode, mod;
{
    XEvent event;

    fill_x_key_event(&event, keycode -> number, mod -> number);
    XSendEvent(dpy, TargetWindow -> client, False, KeyPressMask, &event);
    fill_x_key_event(&event, keycode -> number, mod -> number);
    event.type = KeyRelease;
    XSendEvent(dpy, TargetWindow -> client, False, KeyReleaseMask, &event);
    return (WOOL_OBJECT) keycode;
}

WOOL_OBJECT
wool_send_key_to_client(key, mod)
WOOL_Number	key, mod;
{
    XEvent event;
    char           *p = 0;
    int             keysym, keycode;
    int modifier;

    must_be_number(mod, 1);
    if (key -> type != WLNumber) {
	must_be_string(key, 0);
	p = ((WOOL_String) key) -> string;
	if (!(*p))
	    return NIL;
    }
    do {				/* we MUST re-init event each time */
	modifier = mod -> number;
	if (key -> type == WLNumber)
	    keysym = key -> number;
	else {
	    keysym = *p++;
	}
	if (!(keycode = XKeysymToKeycode(dpy, keysym)))
	    return wool_error("unknown keysym for this server: 0x%x", keysym);

	/* add shift automatically for shifted chars */
	/* We should do same thing for control chars & meta... */
	if (keysym != XKeycodeToKeysym(dpy, keycode, 0)) {
	    if (keysym == XKeycodeToKeysym(dpy, keycode, 1)) {
		modifier |= ShiftMask;
	    }
	}

	fill_x_key_event(&event, keycode, modifier);
	XSendEvent(dpy, TargetWindow -> client, False, KeyPressMask, &event);
	fill_x_key_event(&event, keycode, modifier);
	event.type = KeyRelease;
	XSendEvent(dpy, TargetWindow -> client, False, KeyReleaseMask,
		   &event);
    } while (p && *p);
    return (WOOL_OBJECT) key;
}

fill_x_key_event(evt, keycode, modifier)
XKeyPressedEvent *evt;
int keycode, modifier;
{
    evt -> type = KeyPress;
    evt -> display = dpy;
    evt -> window =  evt -> subwindow = TargetWindow -> client;
    evt -> root = Context->root;
    evt -> time = CurrentTime;
    evt -> x = evt -> y = evt -> x_root = evt -> y_root = 0;
    evt -> same_screen = 1;
    evt -> keycode = keycode;
    evt -> state = modifier;
}

/* same with button: button modifier x y
 */

fill_x_button_event(evt, button, modifier, x, y, x_root, y_root, child)
XButtonEvent *evt;
int button, modifier, x, y, x_root, y_root;
Window child;
{
    evt -> type = ButtonPress;
    evt -> display = dpy;
    evt -> window = child;
    evt -> subwindow = 0;
    evt -> root = Context->root;
    evt -> time = CurrentTime;
    evt -> x = x;
    evt -> y = y;
    evt -> x_root = x_root;
    evt -> y_root = y_root;
    evt -> same_screen = 1;
    evt -> button = button;
    evt -> state = modifier;
}

/* look which window would receive button event
 */

Window
WindowGettingButtonEvent(w, x, y)
Window w;
int x,y;
{
    int x2, y2;
    Window child, w2 = w;
    XWindowAttributes wa;

 find_window:
    XTranslateCoordinates(dpy, w, w2, x, y, &x2, &y2, &child);
    if (child) {
	x = x2;
	y = y2;
	w = w2;
	w2 = child;
	goto find_window;
    }
    x = x2;
    y = y2;
    w = w2;

 find_listener:
    XGetWindowAttributes(dpy, w, &wa);
    if (!(wa.all_event_masks & (ButtonPressMask | ButtonReleaseMask))) {
	Window d1, d2, *d3, parent;
	unsigned int d4;
	
	XQueryTree(dpy, w, &d1, &parent, &d3, &d4);
	XFreeN(d3);
	if (parent) {
	    w = parent;
	    goto find_listener;
	}
    }
    return w;
}

WOOL_OBJECT
wool_send_button_to_client(argc, argv)
int	argc;
WOOL_Number	argv[];
{
    XEvent event;
    int button, modifier, x, y, x2, y2, x_root, y_root, i;
    Window child, dummy;

    if (argc != 4)
	return wool_error(BAD_NUMBER_OF_ARGS, argc);
    for (i = 0; i < argc; i++)
	must_be_number(argv[i], i);
    button = argv[0] -> number;
    modifier = argv[1] -> number;
    x = argv[2] -> number;
    y = argv[3] -> number;

    XTranslateCoordinates(dpy, TargetWindow -> client, Context -> root, x, y,
			  &x_root, &y_root, &child);

    child = WindowGettingButtonEvent(TargetWindow -> client, x, y);
    x2 =x, y2 =y;
    XTranslateCoordinates(dpy, TargetWindow -> client, child, x2, y2,
			  &x, &y, &dummy);

    fill_x_button_event(&event, button, modifier, x, y, x_root, y_root, child);
    XSendEvent(dpy, child, False, ButtonPressMask, &event);
    fill_x_button_event(&event, button, modifier, x, y, x_root, y_root, child);
    event.type = ButtonRelease;
    XSendEvent(dpy, child, False, ButtonReleaseMask, &event);
    return (WOOL_OBJECT) argv[0];
}

/* management of the enter/leave on grabs for menus
 * these functions are used
 * - just before sending event in GWM_ProcessEvents (gwm.c)
 * 	    if (IsNotGrabDiscarded(&evt)) {
 * 		send-event...
 * 	    }
 * - in remove_grab (at the end) for FlushQueuedGrabEvents
 * 
 * - in wool_all_events_make for grab_queue_init
 */

static XEvent *grab_queue;
static int     grab_queue_start;
static int     grab_queue_size;
static int     grab_queue_limit;

grab_queue_init()
{
    grab_queue_size = 0;
    grab_queue_limit = 1;
    grab_queue = (XEvent *) Malloc(grab_queue_limit * sizeof(XEvent));
}

int
IsNotGrabDiscarded(evt)
Event	evt;
{
    if (((evt -> type == LeaveNotify)
	 && (evt -> xcrossing.mode == NotifyGrab))
	|| ((evt -> type == FocusOut)
	    && (evt -> xfocus.mode == NotifyGrab))) {
	if (grab_queue_size == grab_queue_limit) {
	    grab_queue_limit *= 2;
	    grab_queue = (XEvent *) Realloc(grab_queue, grab_queue_limit
					    * sizeof(XEvent));
	}
	bcopy(evt, &grab_queue[grab_queue_size++], sizeof(XEvent));
	return FALSE;
    } else {
	if (((evt -> type == EnterNotify)
	 && (evt -> xcrossing.mode == NotifyUngrab))
	|| ((evt -> type == FocusIn)
	    && (evt -> xfocus.mode == NotifyUngrab))) {
	    return TRUE;
	} else {
	    return FALSE;
	}
    }
}

FlushQueuedGrabEvents()
{
    Wob             wob;

    SAVE_EVENT_CONTEXT;
    while (grab_queue_start < grab_queue_size) {
	if (wob = (Wob) LookUpWob(grab_queue[grab_queue_start].xany.window)) {
	    WOOL_send(WOOL_process_event, wob,
		      (wob, &grab_queue[grab_queue_start]));
	}
	grab_queue_start++;
    }
    RESTORE_EVENT_CONTEXT;
    grab_queue_size = 0;
    grab_queue_start = 0;
}


/*
 * recuperates parameters of the triggering event
 */

#define field_of_event(evt, event_property, etype, field) \
    if(TriggeringEvent->type > 1 && TriggeringEvent->type < LASTEvent && \
       EventProperties[TriggeringEvent->type] & event_property) \
	return (WOOL_OBJECT) WLNumber_make(((etype *) evt) -> field); \
    else return (WOOL_OBJECT) WLNumber_make(0);

WOOL_OBJECT
wool_get_triggering_event_data()
{
    if (TriggeringEvent->type == ClientMessage)
    {
	

	switch (((XClientMessageEvent *) TriggeringEvent)->format)
	{
	case 8:
	    return ((WOOL_OBJECT) WLString_make(
			((XClientMessageEvent *) TriggeringEvent)->data.b));
	case 16:
	    {
		WOOL_Number ListArgv[10];
		int i;
		
		for (i=0;i<10;i++)
		{
		    ListArgv[i] = WLNumber_make(
			((XClientMessageEvent *) TriggeringEvent)->data.s[i]);
		}
		return ((WOOL_OBJECT) wool_list_make_from_evaluated_array(10,
						   (WOOL_OBJECT *) ListArgv));
	    }
	case 32:
	    {
		WOOL_Number ListArgv[5];
		int i;
		
		for (i=0;i<5;i++)
		{
		    ListArgv[i] = WLNumber_make(
			((XClientMessageEvent *) TriggeringEvent)->data.l[i]);
		}
		return ((WOOL_OBJECT) wool_list_make_from_evaluated_array(5,
						   (WOOL_OBJECT *) ListArgv));
	    }
	}
    }
    return wool_error("internal error, event was not of good type%s", "");
}

WOOL_OBJECT
wool_get_triggering_event_state()
{
    field_of_event(TriggeringEvent, EPKeyOrButton, XButtonEvent, state);
}

WOOL_OBJECT
wool_get_triggering_event_code()
{
    field_of_event(TriggeringEvent, EPKeyOrButton, XButtonEvent, button);
}

WOOL_OBJECT
wool_get_triggering_event_x()
{
    field_of_event(TriggeringEvent, EPXYRoot, XButtonEvent, x_root);
}

WOOL_OBJECT
wool_get_triggering_event_y()
{
    field_of_event(TriggeringEvent, EPXYRoot, XButtonEvent, y_root);
}

WOOL_OBJECT
wool_get_triggering_event_x_relative()
{
    field_of_event(TriggeringEvent, EPXY, XButtonEvent, x);
}

WOOL_OBJECT
wool_get_triggering_event_y_relative()
{
    field_of_event(TriggeringEvent, EPXY, XButtonEvent, y);
}

WOOL_OBJECT
wool_get_triggering_event_time()
{
    field_of_event(TriggeringEvent, EPXY, XButtonEvent, time);
}

/* used to know if current event was due to a grab */

WOOL_OBJECT
wool_event_was_due_to_a_grab()
{
    WOOL_Atom             result = (WOOL_Atom) NIL;

    if (event_is_crossing(*TriggeringEvent)) {
	if (TriggeringEvent -> xcrossing.mode == NotifyGrab)
	    result = WA_grab;
	else if (TriggeringEvent -> xcrossing.mode == NotifyUngrab)
	    result = WA_ungrab;
    } else if (event_is_focus(*TriggeringEvent)) {
	if (TriggeringEvent -> xfocus.mode == NotifyGrab)
	    result = WA_grab;
	else if (TriggeringEvent -> xfocus.mode == NotifyUngrab)
	    result = WA_ungrab;
    }
    return (WOOL_OBJECT) result;
}

/********************\
* 		     *
* Tracing of events  *
* 		     *
\********************/

#ifdef DEBUG

char *FocusDetailText[] = {
    "NotifyAncestor",		/* 0 */
    "NotifyVirtual",		/* 1 */
    "NotifyInferior",		/* 2 */
    "NotifyNonlinear",		/* 3 */
    "NotifyNonlinearVirtual",	/* 4 */
    "NotifyPointer",		/* 5 */
    "NotifyPointerRoot",	/* 6 */
    "NotifyDetailNone"		/* 7 */
};

char *FocusModeText[] = {
    "NotifyNormal",            /* 0 */
    "NotifyGrab",              /* 1 */
    "NotifyUngrab",            /* 2 */
    "NotifyWhileGrabbed"       /* 3 */
};

GWM_prettyprint_fsm_event(wob, evt, text, state, newstate)
Wob	wob;
Event           evt;
char	*text;
int state, newstate;
{
    fprintf(stderr, "%s %s (0x%lx/0x%lx): ",
	    text, ((WOOL_Atom) wob -> type[0])-> p_name,
	    wob, wob -> hook);
    if (state || newstate) {
	fprintf(stderr, " (s%d", state);
	if (state != newstate) {
	    fprintf(stderr, "->%d", newstate);
	}
	fprintf(stderr, ")");
    }
    GWM_prettyprint_event(" ", evt, "\n");
}

GWM_prettyprint_event(tag, evt, end)
    char *tag;
    Event evt;
    char *end;
{
    fputs(tag, stderr);
    fprintf(stderr, "%s ", eventtype[evt -> type]); 
    switch (evt->type) {
    case GWMUserEvent:
	fprintf(stderr, "%s",
	    ((WOOL_String) (evt->xany.display))->string);
	break;
    case FocusIn:
    case FocusOut:
	fprintf(stderr, "w 0x%x, detail %s, mode %s",
	    evt->xfocus.window,
	    FocusDetailText[evt->xfocus.detail],
	    FocusModeText[evt->xfocus.mode]);
	break;
    case EnterNotify:
    case LeaveNotify:
	fprintf(stderr, "w 0x%x, detail %s, mode %s",
	    evt->xcrossing.window,
	    FocusDetailText[evt->xcrossing.detail],
	    FocusModeText[evt->xcrossing.mode]);
	break;
    case PropertyNotify:
	fprintf(stderr, "w 0x%x, %s",
	    evt->xproperty.window,
	    XGetAtomName(dpy, evt->xproperty.atom));
	break;
    case ReparentNotify:
	fprintf(stderr, "w 0x%x to 0x%x %s",
	    evt->xreparent.window, evt->xreparent.parent,
		(evt->xreparent.override_redirect ? "(Override)":""));
	break;

    case ConfigureNotify:
	fprintf(stderr, "event 0x%x window 0x%x x %d y %d w %d h %d bw %d above 0x%x\n", evt->xconfigure.event, evt->xconfigure.window, evt->xconfigure.x, evt->xconfigure.y, evt->xconfigure.width, evt->xconfigure.height, evt->xconfigure.border_width, evt->xconfigure.above);
	break;

    case ConfigureRequest:
	fprintf(stderr, "parent 0x%x window 0x%x x %d y %d w %d h %d bw %d above 0x%x detail %d mask %d\n", evt->xconfigurerequest.parent, evt->xconfigurerequest.window, evt->xconfigurerequest.x, evt->xconfigurerequest.y, evt->xconfigurerequest.width, evt->xconfigurerequest.height, evt->xconfigurerequest.border_width, evt->xconfigurerequest.above, evt->xconfigurerequest.detail, evt->xconfigurerequest.value_mask);
	break;

    default:
	fprintf(stderr, "w 0x%x", evt->xany.window);
    }
    fputs(end, stderr);    
}

#endif /* DEBUG */
