/* -*- mode: C; mode: fold; -*- */
#include "config.h"
#include "jed-feat.h"

#include <stdio.h>
#include <dos.h>

#if JED_HAS_MULTICLICK
#include <sys/types.h>
#include <time.h>

static int JX_MultiClick_Time = 5;     /* 5/10 of sec */
#endif

static int Mouse_X, Mouse_Y;

static void generate_press (int x, int y, int type, int but) /*{{{*/
{
   char buf[3];
   unsigned char b, s;
   JMouse_Type jm;
   int id;
#if JED_HAS_MULTICLICK
   clock_t t;
   static clock_t last_press_time;
   static unsigned int clicks;
   static unsigned int last_button;
   
   t = clock ();
   
   t = t / (CLOCKS_PER_SEC / 10);	       /* clocks per 1/10 sec */
   
   if (type == JMOUSE_DOWN)
     {
	if ((last_button == but)
	    && (last_press_time + JX_MultiClick_Time > t))
	  {
	     clicks++;
	     if (clicks == 2) 
	       type = JMOUSE_DOUBLE_CLICK;
	     else
	       type = JMOUSE_TRIPLE_CLICK;
	  }
	else
	  {
	     clicks = 1;
	     last_button = but;
	  }
	last_press_time = t;
     }
   else if ((clicks > 1) && (last_button == but))
     {
	/* Last was a multi-click.  Ignore this event. */
	type = JMOUSE_IGNORE_EVENT;
     }
#endif
   
   Mouse_X = jm.x = x;
   Mouse_Y = jm.y = y;
   
   b = but & 0xF;
   s = but >> 4;
   
   if (b == 1) jm.button = JMOUSE_BUTTON_1;	       /* left */
   else if (b == 2) jm.button = JMOUSE_BUTTON_3;    /* right */
   else jm.button = JMOUSE_BUTTON_2;		       /* middle */
   
   if (s & 0x8) 		       /* alt key--- use as middle */
     {
	jm.button = JMOUSE_BUTTON_2;
     }
   
   if (s & 0x3)	jm.state = JMOUSE_SHIFT;
   else if (s & 0x4) jm.state = JMOUSE_CTRL;
   else jm.state = 0;

   jm.type = type;

   if (-1 == (id = jed_mouse_add_event (&jm)))
     return;

   buf[0] = 27; buf[1] = 0; buf[2] = (char) id;
   
   ungetkey_string (buf, 3);
}

/*}}}*/

/* Mouse routines for the ibmpc */
static int Mouse_Hidden = 1;
static int Last_Mouse_Hidden = 1;

static void show_mouse (int show) /*{{{*/
{
   union REGS r;
   
   if (show) 
     {
	if (Mouse_Hidden == 0) return;
	r.x.ax = 1;
     }
   else 
     {
	if (Mouse_Hidden) return;
	r.x.ax = 2;
     }
   int86 (0x33, &r, &r);
   Mouse_Hidden = !show;
}

/*}}}*/

static void pc_close_mouse () /*{{{*/
{
   show_mouse (0);
}

/*}}}*/

static int pc_get_mouse_event (void) /*{{{*/
{
   union REGS r;
   static int last_press;
   short x, y;
   
   /* return 0; */
   if (last_press)
     {
	show_mouse (1);
	r.x.ax = 6;
	r.x.bx = (last_press & 0xF) - 1;
	int86 (0x33, &r, &r);
	if (r.x.bx)
	  {
	     r.x.cx = r.x.cx / 8 + 1;
	     r.x.dx = r.x.dx / 8 + 1;
	     generate_press (r.x.cx, r.x.dx, JMOUSE_UP, last_press);
	     last_press = 0;
	     return 1;
	  }
     }
	
   r.x.ax = 3;
   int86 (0x33, &r, &r);
   x = r.x.cx;
   y = r.x.dx;
   
#if 0
   /* Check motion counters */
   r.x.ax = 0xB;
   int86 (0x33, &r, &r);	
#endif
   if (last_press)
     {
#if 0
	y += (short) (int) (r.x.dx);
#endif
	
	x = x / 8 + 1;
	y = y / 8 + 1;
	
	if ((Mouse_X == x) && (Mouse_Y == y)) return 0;
	generate_press (x, y, JMOUSE_DRAG, last_press);
	return 1;
     }
   
   x = x / 8 + 1;
   y = y / 8 + 1;
   
   /* It looks like we are looking for a press. */
   if ((Mouse_X != x) || (Mouse_Y != y))
     {
	/* mouse moved so show it. */
	show_mouse (1);
     }
   Mouse_X = x;
   Mouse_Y = y;
   if (r.x.bx)
     {
	if (r.x.bx & 1) last_press = 1;   /* left */
	else if (r.x.bx & 2) last_press = 2;   /* right */
	else if (r.x.bx & 4) last_press = 3;	       /* middle */
	else return 0;
	
	/* Find shift key status */
#ifdef __GO32__
	r.h.ah = 0x12;
	int86 (0x16, &r, &r);
	last_press |= (r.x.ax & 0xF) << 4;
#else
	last_press |= (*(unsigned char far *) 0x417) << 4;
#endif
	generate_press (x, y, JMOUSE_DOWN, last_press);
	return 1;
     }
   return 0;
}

/*}}}*/

static void move_mouse (int x, int y) /*{{{*/
{
   union REGS r;
   r.x.ax = 4;
   r.x.cx = 8 * (x - 1);
   r.x.dx = 8 * (y - 1);
   int86 (0x33, &r, &r);
}

/*}}}*/

static void pc_update_open_hook (void) /*{{{*/
{
   Last_Mouse_Hidden = Mouse_Hidden;
   if (Mouse_Hidden == 0) show_mouse (0);
}

/*}}}*/

static int Warp_Pending;
static void pc_update_close_hook (void) /*{{{*/
{
   if (Last_Mouse_Hidden == 0) show_mouse (1);
   if (Warp_Pending) move_mouse (Screen_Col, Screen_Row);
   Warp_Pending = 0;
}

/*}}}*/

static void warp_pointer (void) /*{{{*/
{
   Warp_Pending = 1;
}

/*}}}*/

static char *CutBuffer;
static int CutBuffer_Len;

static int insert_cutbuffer (void) /*{{{*/
{
   CHECK_READ_ONLY
   if (CutBuffer == NULL) return 0;
   if (CutBuffer_Len) ins_chars ((unsigned char *) CutBuffer, CutBuffer_Len);
   return CutBuffer_Len;
}

/*}}}*/

static void region_to_cutbuffer (void) /*{{{*/
{
   int nbytes;
   
   if (CutBuffer != NULL)
     {
	SLFREE (CutBuffer);
     }
   
   CutBuffer = make_buffer_substring(&nbytes);
   CutBuffer_Len = nbytes;
}

/*}}}*/

static SLang_Name_Type gpm_mouse_table[] = /*{{{*/
{
   MAKE_INTRINSIC(".x_warp_pointer", warp_pointer, VOID_TYPE, 0),
   MAKE_INTRINSIC(".x_insert_cutbuffer", insert_cutbuffer, INT_TYPE, 0),
   /* Prototype: Integer x_insert_cut_buffer ();
    * Inserts cutbuffer into the current buffer and returns the number
    * of characters inserted.
    */
   MAKE_INTRINSIC(".x_copy_region_to_cutbuffer", region_to_cutbuffer, VOID_TYPE, 0),
   /* Prototype: Void x_copy_region_to_cutbuffer();
    */
   SLANG_END_TABLE
};

/*}}}*/

void (*X_Close_Mouse_Hook)(void);
static int pc_open_mouse () /*{{{*/
{
   union REGS r;
   int rows, cols, scanlines;
   static int not_first_time;

   X_Close_Mouse_Hook = NULL;
   JMouse_Event_Hook = NULL;
   JMouse_Hide_Mouse_Hook = NULL;
   X_Update_Open_Hook = NULL;
   X_Update_Close_Hook = NULL;
#ifndef __GO32__
   if (getvect (0x33) == NULL) return -1;
#endif
   /* Now see if the mouse is present */
   r.x.ax = 0x21;
   int86 (0x33, &r, &r);
   if (r.x.ax == 0x21) return -1;
   
   if ((scanlines = get_term_dimensions (&cols, &rows)) <= 0)
     scanlines = 8;
   
   r.x.ax = 7;
   r.x.cx = 0;
   r.x.dx = 8 * cols - 1;
   int86 (0x33, &r, &r);
   
   r.x.ax = 8;
   r.x.cx = 0;
   r.x.dx = scanlines * rows - 1;
   int86 (0x33, &r, &r);
   
   move_mouse (cols / 2 + 1, rows / 2 + 1);
   
   if (not_first_time == 0)
     {
	if (!SLang_add_table(gpm_mouse_table, "Mouse")
	    || !SLdefine_for_ifdef("MOUSE")) return -1;
	
	not_first_time = 1;
     }
   
   JMouse_Hide_Mouse_Hook = show_mouse;
   X_Close_Mouse_Hook = pc_close_mouse;
   JMouse_Event_Hook = pc_get_mouse_event;
   X_Update_Open_Hook = pc_update_open_hook;
   X_Update_Close_Hook = pc_update_close_hook;
   return 1;
}

/*}}}*/

int (*X_Open_Mouse_Hook)(void) = pc_open_mouse;
