/* $Aumix: aumix/src/gpm-xterm.c,v 1.2 2002/03/28 09:37:26 trevor Exp $
 * gpm-xterm.c - pseudo client for non-Linux xterm-only mouse support.
 *               This code has been extracted from libgpm-0.18 and then
 *               took its own way.
 *
 * Copyright (C) 1994, 1995  rubini@linux.it (Alessandro Rubini)
 * Copyright (C) 1994        Janne Kukonlehto
 *
 * This file is part of aumix.
 *
 * Aumix 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.
 *
 * Aumix 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
 * aumix; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
 * Suite 330, Boston, MA 02111-1307, USA.
 */

#include "common.h"
#if defined(HAVE_CURSES) && !defined(HAVE_LIBGPM)
#include "gpm-xterm.h"

#ifdef HAVE_USEKEYPAD
int             gpm_keypad(WINDOW * win, int new)
{
	int             old;
	old = win->_use_keypad;
	win->_use_keypad = new;
	return old;
}
#endif				/* HAVE_USEKEYPAD */

/* This is from liblow.c (libgpm). */
int             gpm_flag = 0, gpm_tried = 0, gpm_fd = -1, gpm_hflag = 0, gpm_zerobased = 0, gpm_visiblepointer = 0, gpm_morekeys = 0;
struct timeval  gpm_timeout = {10, 0};
Gpm_Handler    *gpm_handler = NULL;
void           *gpm_data = NULL;
static int      gpm_convert_event(unsigned char *mdata, Gpm_Event * ePtr);
/* This is from libcurses.c (libgpm). */
int             Gpm_Wgetch(WINDOW * win)
{
	int             flag, result;
	int             fd = STDIN_FILENO;
	static Gpm_Event ev;
	static fd_set   selSet;
/* patched by JD 11/08/1998 */
	static int      nbprevchar = 0, prevchar[MAXNBPREVCHAR];
	static unsigned char mdata[4];
	int             c;
#ifdef HAVE_USEKEYPAD
	int             keypad;
#endif				/* HAVE_USEKEYPAD */

	if (gpm_flag == 0)
		return wgetch(win);
	if (gpm_morekeys && gpm_handler)
		return (*gpm_handler) (&ev, gpm_data);

	gpm_hflag = 0;		/* not generated by handler (default) */

/* JD patch 11/08/1998 */
	if (nbprevchar)		/* if there are some consumed chars */
		return prevchar[--nbprevchar];

	while (1) {
		do {
			FD_ZERO(&selSet);
			FD_SET(fd, &selSet);
			gpm_timeout.tv_sec = SELECT_TIME;
			flag = select(fd + 1, &selSet, (fd_set *) NULL, (fd_set *) NULL, (struct timeval *) NULL);
		}
		while (flag == 0);

		if ((c = wgetch(win)) != 0x1b)
			return c;

		/* escape: go on */
		FD_ZERO(&selSet);
		FD_SET(fd, &selSet);
		if ((flag = select(fd + 1, &selSet, (fd_set *) NULL, (fd_set *) NULL, (struct timeval *) NULL)) == 0)
			return c;
		if ((c = wgetch(win)) != '[') {
			prevchar[nbprevchar++] = c;
			return 0x1B;
		}		/* patched by JD 11/08/1998 */
		/* '[': go on */
		FD_ZERO(&selSet);
		FD_SET(fd, &selSet);
		if ((flag = select(fd + 1, &selSet, (fd_set *) NULL, (fd_set *) NULL, (struct timeval *) NULL)) == 0) {
			prevchar[nbprevchar++] = c;
			return 0x1B;
		}		/* patched by JD 11/08/1998 */
		if ((c = wgetch(win)) != 'M')
/* patched by JD 11/08/1998 Note: prevchar is a LIFO! */
		{
			prevchar[nbprevchar++] = c;
			prevchar[nbprevchar++] = '[';
			return 0x1B;
		}
		/* Now, it surely is a mouse event. */
#ifdef HAVE_USEKEYPAD
		keypad = gpm_keypad(win, FALSE);
#endif				/* HAVE_USEKEYPAD */
		for (c = 0; c < 3; c++)
			mdata[c] = wgetch(win);
#ifdef HAVE_USEKEYPAD
		(void) gpm_keypad(win, keypad);
#endif				/* HAVE_USEKEYPAD */
		gpm_convert_event(mdata, &ev);

		if (gpm_handler && (result = (*gpm_handler) (&ev, gpm_data))) {
			gpm_hflag = 1;
			return result;
		}
	}			/* while(1) */
}
/******* This is from liblow.c (libgpm). */
int             Gpm_Open(Gpm_Connect * conn, int flag)
{
	char           *tty;
	if ((tty = (char *) getenv("TERM")) && !strncmp(tty, "xterm", 5)) {
		if (gpm_tried)
			return gpm_fd;	/* already open */
		gpm_fd = -2;
		GPM_XTERM_ON;
		gpm_flag = 1;
		return gpm_fd;
	}
	return -1;
}

int             Gpm_Close(void)
{
	if (gpm_fd == -2)	/* xterm */
		GPM_XTERM_OFF;
	gpm_tried = 0;
	gpm_fd = -1;
	return 0;
}

int             Gpm_Getc(FILE * f)
{
	int             flag, result;
	static Gpm_Event ev;
	int             fd = fileno(f);
	static int      count;
	static struct timeval to = {0, DELAY_MS * 1000};
	static fd_set   selSet;
/* patched by JD 11/08/1998 */
	static int      nbprevchar = 0, prevchar[MAXNBPREVCHAR];
	static unsigned char mdata[4];
	int             c;
	/* Hmm... I must be sure it is unbuffered. */
	if (!(count++))
		setvbuf(f, NULL, _IONBF, 0);

	if (!gpm_flag)
		return fgetc(f);

	/* If the handler asked to provide more keys, give them back. */
	if (gpm_morekeys && gpm_handler)
		return (*gpm_handler) (&ev, gpm_data);
	gpm_hflag = 0;

/* patched by JD 11/08/1998 */
	if (nbprevchar)		/* if there are some consumed chars */
		return prevchar[--nbprevchar];

	while (1) {
		do {
			FD_ZERO(&selSet);
			FD_SET(fd, &selSet);
			gpm_timeout.tv_sec = SELECT_TIME;
			flag = select(fd + 1, &selSet, (fd_set *) NULL, (fd_set *) NULL, &to);
		}
		while (flag == 0);

		if ((c = fgetc(f)) != 0x1b)
			return c;

		/* escape: go on */
		FD_ZERO(&selSet);
		FD_SET(fd, &selSet);
		to.tv_usec = DELAY_MS * 1000;
		if ((flag = select(fd + 1, &selSet, (fd_set *) NULL, (fd_set *) NULL, &to)) == 0)
			return c;
		if ((c = fgetc(f)) != '[')
/* patched by JD 11/08/1998 */
		{
			prevchar[nbprevchar++] = c;
			return 0x1B;
		}
/*      {ungetc(c,stdin); return 0x1B;} */

		/* '[': go on */
		FD_ZERO(&selSet);
		FD_SET(fd, &selSet);
		to.tv_usec = DELAY_MS * 1000;
		if ((flag = select(fd + 1, &selSet, (fd_set *) NULL, (fd_set *) NULL, &to)) == 0)
/* patched by JD 11/08/1998 */
		{
			prevchar[nbprevchar++] = c;
			return 0x1B;
		}
/*	{ungetc(c,f); return 0x1B;}	*/
		if ((c = fgetc(f)) != 'M')
/* patched by JD 11/08/1998 Note: prevchar is a LIFO! */
		{
			prevchar[nbprevchar++] = c;
			prevchar[nbprevchar++] = '[';
			return 0x1B;
		}
		/* Now, it surely is a mouse event. */
		for (c = 0; c < 3; c++)
			mdata[c] = fgetc(f);
		gpm_convert_event(mdata, &ev);

		if (gpm_handler && (result = (*gpm_handler) (&ev, gpm_data))) {
			gpm_hflag = 1;
			return result;
		}
	}			/* while(1) */
}

int             Gpm_Repeat(int msec)
{
	struct timeval  to = {0, 0};
	fd_set          selSet;
	int             fd = STDIN_FILENO;
	to.tv_usec = msec * 1000;
	FD_ZERO(&selSet);
	FD_SET(fd, &selSet);
	return (select(fd + 1, &selSet, (fd_set *) NULL, (fd_set *) NULL, &to) == 0);
}
/* This is the real protocol conversion. */
static int      gpm_convert_event(unsigned char *mdata, Gpm_Event * ePtr)
{
	static struct timeval tv1 = {0, 0}, tv2;
	/*
	 * The clicks variable has the following meaning: 0 means button 1 is down; 1 means button 2 is down; 2 means button 3 is down; 3 means all buttons are up.
	 */
	static int      clicks = 0;
	int             c;
	c = mdata[0] - 32;

	if (c == 3) {
		ePtr->type = GPM_UP | (GPM_SINGLE << clicks);
		ePtr->buttons = 0;
		GET_TIME(tv1);
		clicks = 0;
	} else {
		ePtr->type = GPM_DOWN;
		GET_TIME(tv2);
		if (tv1.tv_sec && (DIF_TIME(tv1, tv2) < 250)) {	/* 250 ms for double click */
			clicks++;
			clicks %= 3;
		} else
			clicks = 0;

		switch (c) {
		case 0:
			ePtr->buttons |= GPM_B_LEFT;
			break;
		case 1:
			ePtr->buttons |= GPM_B_MIDDLE;
			break;
		case 2:
			ePtr->buttons |= GPM_B_RIGHT;
		default:
			break;
		}
	}
	/* Coordinates are 33-based.  Transform them to 1-based. */
	ePtr->x = mdata[1] - 32;
	ePtr->y = mdata[2] - 32;
	return 0;
}
#endif				/* defined(HAVE_CURSES) && !defined(HAVE_LIBGPM) */
