/* $Id: Events.c,v 1.1 1997/04/15 10:09:31 archer Exp $
 *	EVENTS.C - Handle events in Widget Library
 *
 * Lots of events handled here! System events! Window events! All kind!
 */
#include <Widget/EventsP.h>
#include <Widget/clipP.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif


/**
 ** select() check
 */
#ifndef HAVE_SELECT
#error System without select() are not supported!
#endif


/**
 ** Global variables
 **/
X11_widget	x11__widget_garbage_bin;
fd_set		x11__widget_ifds;
fd_set		x11__widget_ofds;


/**
 ** Private internal variables
 **/
static int		fd_max;
static void		*fd_datas[FD_SETSIZE];
static X11_input	fd_call[FD_SETSIZE];


/**
 ** Internal: MERGE_EXPOSE
 **	Merge two expose events
 **/
static void merge_expose(XExposeEvent *into, XExposeEvent *add)
{
int	a, b;
/*
 * Merge on X axis
 */
	a = into->x + into->width;
	b = add->x  + add->width;
	if (b > a)
		a = b;
	if (into->x > add->x)
		into->x = add->x;
	into->width = a - into->x;
/*
 * Merge on Y axis
 */
	a = into->y + into->height;
	b = add->y  + add->height;
	if (b > a)
		a = b;
	if (into->y > add->y)
		into->y = add->y;
	into->height = a - into->y;
}


/**
 ** X11_EVENT_DISPATCH
 **	Dispatch event to X object
 **/
void x11__event_dispatch(XEvent *event)
{
XEvent		ev2;
Window		w;
X11_widget	o;
x11__handler	c;
/*
 * System events
 */
	switch (event->type) {
	    case SelectionClear:
		x11__clipboard_clear(event);
		return;
	    case SelectionRequest:
		x11__clipboard_request(&event->xselectionrequest);
		return;
/*
 * No one catches these (yet?)
 */
	    case DestroyNotify:
	    case UnmapNotify:
		return;
	}
/*
 * Search for window
 */
	w = event->xany.window;
	for (o = x11__widget_root; o; o = o->list)
		if (o->id == w) {
/*
 * Dispatch on event type
 */
			switch (event->type) {
/*
 * Special events (single handler for all these)
 */
			    case ClientMessage:
			    case FocusIn:
			    case FocusOut:
				if ((c = o->on_special_event) != 0)
					(*c)(o, event);
				return;
/*
 * Ignore spurious
 */
			    case MapNotify:
			    case ReparentNotify:
				return;
/*
 * Window events
 */
			    case ButtonPress:
				if ((c = o->on_button_press) != 0)
					(*c)(o, event);
				return;
			    case ButtonRelease:
				if ((c = o->on_button_release) != 0)
					(*c)(o, event);
				return;
			    case ConfigureNotify:
				if ((c = o->on_resize_window) != 0)
					(*c)(o, event);
				return;
			    case EnterNotify:
				if ((c = o->on_enter_window) != 0)
					(*c)(o, event);
				return;
			    case KeyPress:
				if ((c = o->on_key_press) != 0)
					(*c)(o, event);
				return;
			    case KeyRelease:
				return;
			    case LeaveNotify:
				if ((c = o->on_exit_window) != 0)
					(*c)(o, event);
				return;
			    case MotionNotify:
				if ((c = o->on_motion_notify) != 0)
					(*c)(o, event);
				return;
/*
 * Special for Expose... We might collapse multiple Expose into a single one
 */
			    case Expose:
				while (XCheckTypedWindowEvent(x11__screen, w,
								Expose, &ev2))
					merge_expose(&event->xexpose,
							&ev2.xexpose);
				if (!o->unmapped && (c = o->on_expose) != 0)
					(*c)(o, event);
				return;
/*
 * Not a known/processed event
 */
			}
#ifdef DEBUGGING_X11
printf("Unhandled event %d on window ID 0x%x (\"%s:%s\")\n",event->type,(unsigned)event->xany.window,o->class,o->name);
#endif
			return;
		}
#ifdef DEBUGGING_X11
printf("Event %d on unhandled window ID 0x%x\n",event->type,(unsigned)event->xany.window);
#endif
}


/**
 ** X11_CLOSED_DESCRIPTOR
 **	Stop watching the descriptor
 **/
void X11_ClosedDescriptor(int fd)
{
	fd_call[fd] = 0;
	FD_CLR(fd, &x11__widget_ifds);
	FD_CLR(fd, &x11__widget_ofds);
}


/**
 ** X11_INPUT_DESCRIPTOR
 **	Watch for incoming data
 **/
void X11_InputDescriptor(int fd, X11_input call, void *data)
{
	fd_datas[fd] = data;
	fd_call[fd]  = call;
	FD_SET(fd, &x11__widget_ifds);
	if (fd > fd_max)
		fd_max = fd;
}


/**
 ** X11_OUTPUT_DESCRIPTOR
 **	Watch for incoming data
 **/
void X11_OutputDescriptor(int fd, X11_input call, void *data)
{
	fd_datas[fd] = data;
	fd_call[fd]  = call;
	FD_SET(fd, &x11__widget_ofds);
	if (fd > fd_max)
		fd_max = fd;
}


/**
 ** Internal: VOID_FILTER
 **	Filter any event. Needed because the event mask doesn't include all
 **	events.
 **/
static Bool void_filter(Display *dummy, XEvent *e, XPointer ignore)
{
	return True;
}


/**
 ** X11_WAIT_EVENT
 **	Pick an event. Either during main loop, or in grab loop
 **/
void x11__wait_event(XEvent *e)
{
fd_set	inputs,
	outputs;
int	i;
/*
 * Never leave the loop, until we get an X11 event
 */
	while (1) {
/*
 * Any X11 event? (top priority)
 */
		if (XCheckIfEvent(x11__screen, e, void_filter, 0))
			return;
/*
 * No X11 events pending... go for it...
 */
		inputs  = x11__widget_ifds;
		outputs = x11__widget_ofds;
		i = select(FD_SETSIZE, &inputs, &outputs, (fd_set *)0,
				(struct timeval *)0);
		if (i <= 0)
			continue;
/*
 * Data pending...
 */
		for (i = 0; i <= fd_max; i++)
			if (FD_ISSET(i, &inputs) ||
			    FD_ISSET(i, &outputs)) {
				if (fd_call[i])
					(*fd_call[i])(i, fd_datas[i]);
			}
	}
}


/**
 ** Internal: WIDGET_FREE
 **	Recursively free the widget windows and memory
 **/
static void widget_free(X11_widget o)
{
X11_widget	c, n;
/*
 * Free all our children
 */
	for (c = o->child; c; c = n) {
		n = c->sibling;
		widget_free(c);
	}
/*
 * Release the internal objects
 */
	if (o->id)
		XDestroyWindow(x11__screen, o->id);
/*
 * Release the memory
 */
	free(o);
}


/**
 ** X11_MAIN_LOOP
 **	Application loop
 **/
void X11_MainLoop(void)
{
X11_widget	r;
XEvent		event;
/*
 * Never leave the loop
 */
	while (1) {
/*
 * Drop all 'freed' widgets (linked thru the `events' chain)
 */
		if ((r = x11__widget_garbage_bin) != 0) {
			x11__widget_garbage_bin = r->list;
			widget_free(r);
		}
/*
 * Process event
 */
		x11__wait_event(&event);
		x11__event_dispatch(&event);
	}
}
