/* -*-c-*- */
/* This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/* ---------------------------- included header files ---------------------- */

#define FEVENT_C
#define FEVENT_PRIVILEGED_ACCESS
#include "config.h"
#include "libs/fvwmlib.h"
#include <X11/Xlib.h>
#include "FEvent.h"
#undef FEVENT_C
#undef FEVENT_PRIVILEGED_ACCESS

#include <stdio.h>

#include "libs/ftime.h"

/* ---------------------------- local definitions -------------------------- */

/* ---------------------------- local macros ------------------------------- */

/* ---------------------------- imports ------------------------------------ */

/* ---------------------------- included code files ------------------------ */

/* ---------------------------- local types -------------------------------- */

typedef struct
{
	Bool (*predicate) (Display *display, XEvent *event, XPointer arg);
	XPointer arg;
	XEvent event;
	Bool found;
} fev_check_peek_args;

/* ---------------------------- forward declarations ----------------------- */

/* ---------------------------- local variables ---------------------------- */

static XEvent fev_event;
static XEvent fev_event_old;
/* until Xlib does this for us */
Time fev_last_timestamp = CurrentTime;

/* ---------------------------- exported variables (globals) --------------- */

/* ---------------------------- local functions ---------------------------- */

/* Records the time of the last processed event. */
static void fev_update_last_timestamp(const XEvent *ev)
{
	Time new_timestamp = CurrentTime;

	switch (ev->type)
	{
	case KeyPress:
	case KeyRelease:
		new_timestamp = ev->xkey.time;
		break;
	case ButtonPress:
	case ButtonRelease:
		new_timestamp = ev->xbutton.time;
		break;
	case MotionNotify:
		new_timestamp = ev->xmotion.time;
		break;
	case EnterNotify:
	case LeaveNotify:
		new_timestamp = ev->xcrossing.time;
		break;
	case PropertyNotify:
		new_timestamp = ev->xproperty.time;
		break;
	case SelectionClear:
		new_timestamp = ev->xselectionclear.time;
		break;
	case SelectionRequest:
		new_timestamp = ev->xselectionrequest.time;
		break;
	case SelectionNotify:
		new_timestamp = ev->xselection.time;
		break;
	default:
		return;
	}
	/* Only update if the new timestamp is later than the old one, or
	 * if the new one is from a time at least 30 seconds earlier than the
	 * old one (in which case the system clock may have changed) */
	if (new_timestamp > fev_last_timestamp ||
	    fev_last_timestamp - new_timestamp > CLOCK_SKEW_MS)
	{
		fev_last_timestamp = new_timestamp;
	}

	return;
}

static Bool fev_check_peek_pred(
	Display *display, XEvent *event, XPointer arg)
{
	fev_check_peek_args *cpa = (fev_check_peek_args *)arg;

	if (cpa->found == True)
	{
		return False;
	}
	cpa->found = cpa->predicate(display, event, cpa->arg);
	if (cpa->found == True)
	{
		cpa->event = *event;
	}

	return False;
}

/* ---------------------------- interface functions (privileged access) ----- */

void fev_copy_last_event(XEvent *dest)
{
	*dest = fev_event;

	return;
}

XEvent *fev_get_last_event_address(void)
{
	return &fev_event;
}

/* ---------------------------- interface functions (normal_access) -------- */

Time fev_get_evtime(void)
{
	return fev_last_timestamp;
}

Bool fev_get_evpos_or_query(
	Display *dpy, Window w, const XEvent *e, int *ret_x, int *ret_y)
{
	Window JunkW;
	int JunkC;
	unsigned int JunkM;
	Bool rc;
	int type;

	type = (e != NULL) ? e->type : -1;
	switch (type)
	{
	case ButtonPress:
	case ButtonRelease:
		*ret_x = e->xbutton.x_root;
		*ret_y = e->xbutton.y_root;
		return True;
	case KeyPress:
	case KeyRelease:
		*ret_x = e->xkey.x_root;
		*ret_y = e->xkey.y_root;
		return True;
	case EnterNotify:
	case LeaveNotify:
		*ret_x = e->xcrossing.x_root;
		*ret_y = e->xcrossing.y_root;
		return True;
	case MotionNotify:
		if (e->xmotion.same_screen == True)
		{
			*ret_x = e->xmotion.x_root;
			*ret_y = e->xmotion.y_root;
		}
		else
		{
			/* pointer is on different screen */
			*ret_x = 0;
			*ret_y = 0;
		}
		return True;
	default:
		rc = FQueryPointer(
			dpy, w, &JunkW, &JunkW, ret_x, ret_y, &JunkC, &JunkC,
			&JunkM);
		if (rc == False)
		{
			/* pointer is on a different screen */
			*ret_x = 0;
			*ret_y = 0;
		}
		return rc;
	}
}

Bool fev_set_evpos(XEvent *e, int x, int y)
{
	switch (e->type)
	{
	case ButtonPress:
	case ButtonRelease:
		e->xbutton.x_root = x;
		e->xbutton.y_root = y;
		return True;
	case KeyPress:
	case KeyRelease:
		e->xkey.x_root = x;
		e->xkey.y_root = y;
		return True;
	case MotionNotify:
		if (e->xmotion.same_screen == True)
		{
			e->xmotion.x_root = x;
			e->xmotion.y_root = y;
			return True;
		}
		break;
	default:
		break;
	} /* switch */

	return False;
}

void fev_fake_event(XEvent *ev)
{
	fev_event_old = fev_event;
	fev_event = *ev;
	/* don't update the last timestamp here; the triggering event has
	 * already done this */

	return;
}

void *fev_save_event(void)
{
	XEvent *ev;

	ev = (XEvent *)safemalloc(sizeof(XEvent));
	*ev = fev_event;

	return ev;
}

void fev_restore_event(void *ev)
{
	fev_event = *(XEvent *)ev;
	free(ev);

	return;
}

void fev_make_null_event(XEvent *ev, Display *dpy)
{
	memset(ev, 0, sizeof(*ev));
	ev->xany.serial = fev_event.xany.serial;
	ev->xany.display = dpy;

	return;
}

void fev_get_last_event(XEvent *ev)
{
	*ev = fev_event;

	return;
}

/* ---------------------------- X event replacements ----------------------- */

XTimeCoord *FGetMotionEvents(
	Display *display, Window w, Time start, Time stop, int *nevents_return)
{
	XTimeCoord *rc;

	rc = XGetMotionEvents(display, w, start, stop, nevents_return);

	return rc;
}

int FAllowEvents(
	Display *display, int event_mode, Time time)
{
	int rc;

	rc = XAllowEvents(display, event_mode, time);

	return rc;
}

Bool FCheckIfEvent(
	Display *display, XEvent *event_return,
	Bool (*predicate) (Display *display, XEvent *event, XPointer arg),
	XPointer arg)
{
	Bool rc;
	XEvent new_ev;

	rc = XCheckIfEvent(display, &new_ev, predicate, arg);
	if (rc == True)
	{
		fev_event_old = fev_event;
		fev_event = new_ev;
		*event_return = fev_event;
		fev_update_last_timestamp(event_return);
	}

	return rc;
}

Bool FCheckMaskEvent(
	Display *display, long event_mask, XEvent *event_return)
{
	Bool rc;
	XEvent new_ev;

	rc = XCheckMaskEvent(display, event_mask, &new_ev);
	if (rc == True)
	{
		fev_event_old = fev_event;
		fev_event = new_ev;
		*event_return = fev_event;
		fev_update_last_timestamp(event_return);
	}

	return rc;
}

Bool FCheckPeekIfEvent(
	Display *display, XEvent *event_return,
	Bool (*predicate) (Display *display, XEvent *event, XPointer arg),
	XPointer arg)
{
	XEvent dummy;
	fev_check_peek_args cpa;

	cpa.predicate = predicate;
	cpa.arg = arg;
	cpa.found = False;
	XCheckIfEvent(display, &dummy, fev_check_peek_pred, (char *)&cpa);
	if (cpa.found == True)
	{
		*event_return = cpa.event;
		fev_update_last_timestamp(event_return);
	}

	return cpa.found;
}

Bool FCheckTypedEvent(
	Display *display, int event_type, XEvent *event_return)
{
	Bool rc;
	XEvent new_ev;

	rc = XCheckTypedEvent(display, event_type, &new_ev);
	if (rc == True)
	{
		fev_event_old = fev_event;
		fev_event = new_ev;
		*event_return = fev_event;
		fev_update_last_timestamp(event_return);
	}

	return rc;
}

Bool FCheckTypedWindowEvent(
	Display *display, Window w, int event_type, XEvent *event_return)
{
	Bool rc;
	XEvent new_ev;

	rc = XCheckTypedWindowEvent(display, w, event_type, &new_ev);
	if (rc == True)
	{
		fev_event_old = fev_event;
		fev_event = new_ev;
		*event_return = fev_event;
		fev_update_last_timestamp(event_return);
	}

	return rc;
}

Bool FCheckWindowEvent(
	Display *display, Window w, long event_mask, XEvent *event_return)
{
	Bool rc;
	XEvent new_ev;

	rc = XCheckWindowEvent(display, w, event_mask, &new_ev);
	if (rc == True)
	{
		fev_event_old = fev_event;
		fev_event = new_ev;
		*event_return = fev_event;
		fev_update_last_timestamp(event_return);
	}

	return rc;
}

int FEventsQueued(
	Display *display, int mode)
{
	int rc;

	rc = XEventsQueued(display, mode);

	return rc;
}

int FIfEvent(
	Display *display, XEvent *event_return,
	Bool (*predicate) (Display *display, XEvent *event, XPointer arg),
	XPointer arg)
{
	int rc;

	fev_event_old = fev_event;
	rc = XIfEvent(display, &fev_event, predicate, arg);
	*event_return = fev_event;
	fev_update_last_timestamp(event_return);

	return rc;
}

int FMaskEvent(
	Display *display, long event_mask, XEvent *event_return)
{
	int rc;

	fev_event_old = fev_event;
	rc = XMaskEvent(display, event_mask, &fev_event);
	*event_return = fev_event;
	fev_update_last_timestamp(event_return);

	return rc;
}

int FNextEvent(
	Display *display, XEvent *event_return)
{
	int rc;

	fev_event_old = fev_event;
	rc = XNextEvent(display, &fev_event);
	*event_return = fev_event;
	fev_update_last_timestamp(event_return);

	return rc;
}

int FPeekEvent(
	Display *display, XEvent *event_return)
{
	int rc;

	rc = XPeekEvent(display, event_return);
	if (rc == True)
	{
		fev_update_last_timestamp(event_return);
	}

	return rc;
}

int FPeekIfEvent(
	Display *display, XEvent *event_return,
	Bool (*predicate) (Display *display, XEvent *event, XPointer arg),
	XPointer arg)
{
	int rc;

	rc = XPeekIfEvent(display, event_return, predicate, arg);
	if (rc == True)
	{
		fev_update_last_timestamp(event_return);
	}

	return rc;
}

int FPending(
	Display *display)
{
	int rc;

	rc = XPending(display);

	return rc;
}

int FPutBackEvent(
	Display *display, XEvent *event)
{
	int rc;

	rc = XPutBackEvent(display, event);
	fev_event = fev_event_old;

	return rc;
}

int FQLength(
	Display *display)
{
	int rc;

	rc = XQLength(display);

	return rc;
}

Bool FQueryPointer(
	Display *display, Window w, Window *root_return, Window *child_return,
	int *root_x_return, int *root_y_return, int *win_x_return,
	int *win_y_return, unsigned int *mask_return)
{
	Bool rc;

	rc = XQueryPointer(
		display, w, root_return, child_return, root_x_return,
		root_y_return, win_x_return, win_y_return, mask_return);

	return rc;
}

Status FSendEvent(
	Display *display, Window w, Bool propagate, long event_mask,
	XEvent *event_send)
{
	Status rc;

	rc = XSendEvent(display, w, propagate, event_mask, event_send);

	return rc;
}

int FWarpPointer(
	Display *display, Window src_w, Window dest_w, int src_x, int src_y,
	unsigned int src_width, unsigned int src_height, int dest_x, int dest_y)
{
	int rc;

	rc = XWarpPointer(
		display, src_w, dest_w, src_x, src_y, src_width, src_height,
		dest_x, dest_y);

	return rc;
}

int FWarpPointerUpdateEvpos(
	XEvent *ev, Display *display, Window src_w, Window dest_w, int src_x,
	int src_y, unsigned int src_width, unsigned int src_height,
	int dest_x, int dest_y)
{
	int rc;

	rc = XWarpPointer(
		display, src_w, dest_w, src_x, src_y, src_width, src_height,
		dest_x, dest_y);
	if (ev != NULL && dest_w == DefaultRootWindow(display))
	{
		fev_set_evpos(ev, dest_x, dest_y);
	}

	return rc;
}

int FWindowEvent(
	Display *display, Window w, long event_mask, XEvent *event_return)
{
	int rc;

	fev_event_old = fev_event;
	rc = XWindowEvent(display, w, event_mask, &fev_event);
	*event_return = fev_event;
	fev_update_last_timestamp(event_return);

	return rc;
}


syntax highlighted by Code2HTML, v. 0.9.1