/*
 * PC/HW routine collection v1.3 for DOS/DJGPP
 *
 *  Copyright (C) 2002 - Borca Daniel
 *  Email : dborca@yahoo.com
 *  Web   : http://www.geocities.com/dborca
 */


#include <dpmi.h>
#include <sys/exceptn.h>
#include <sys/segments.h>

#include "pc_hw.h"



#define PC_CUTE_WHEEL 1 /* CuteMouse WheelAPI */

#define MOUSE_STACK_SIZE 16384

#define CLEAR_MICKEYS() \
        do { \
            __asm __volatile ("movw $0xb, %%ax; int $0x33":::"%eax", "%ecx", "%edx"); \
            ox = oy = 0; \
        } while (0)

extern void mouse_wrap (void);
extern int mouse_wrap_end[];

static MFUNC mouse_func;
static long mouse_callback;
static __dpmi_regs mouse_regs;

static volatile struct {
       volatile int x, y, z, b;
} pc_mouse;

static int minx = 0;
static int maxx = 319;
static int miny = 0;
static int maxy = 199;
static int minz = 0;
static int maxz = 255;

static int sx = 2;
static int sy = 2;

static int emulat3 = FALSE;

static int ox, oy;


static void mouse (__dpmi_regs *r)
{
 int nx = (signed short)r->x.si / sx;
 int ny = (signed short)r->x.di / sy;
 int dx = nx - ox;
 int dy = ny - oy;
#if PC_CUTE_WHEEL
 int dz = (signed char)r->h.bh;
#endif
 ox = nx;
 oy = ny;

 pc_mouse.b = r->h.bl;
 pc_mouse.x = MID(minx, pc_mouse.x + dx, maxx);
 pc_mouse.y = MID(miny, pc_mouse.y + dy, maxy);
#if PC_CUTE_WHEEL
 pc_mouse.z = MID(minz, pc_mouse.z + dz, maxz);
#endif

 if (emulat3) {
    if ((pc_mouse.b&3)==3) {
       pc_mouse.b = 4;
    }
 }

 if (mouse_func) {
    mouse_func(pc_mouse.x, pc_mouse.y, pc_mouse.z, pc_mouse.b);
 }
} ENDOFUNC(mouse)

void pc_remove_mouse (void)
{
 if (mouse_callback) {
    pc_clexit(pc_remove_mouse);
    __asm("\n\
		movl	%%edx, %%ecx	\n\
		shrl	$16, %%ecx	\n\
		movw	$0x0304, %%ax	\n\
		int	$0x31		\n\
		movw	$0x000c, %%ax	\n\
		xorl	%%ecx, %%ecx	\n\
		int	$0x33		\n\
    "::"d"(mouse_callback):"%eax", "%ecx");

    mouse_callback = 0;

    free((void *)(mouse_wrap_end[0] - MOUSE_STACK_SIZE));
 }
}

int pc_install_mouse (void)
{
 int buttons;

 /* fail if already call-backed */
 if (mouse_callback) {
    return 0;
 }

 /* reset mouse and get status */
 __asm("\n\
		xorl	%%eax, %%eax	\n\
		int	$0x33		\n\
		andl	%%ebx, %%eax	\n\
		movl	%%eax, %0	\n\
 ":"=g" (buttons)::"%eax", "%ebx");
 if (!buttons) {
    return 0;
 }

 /* lock wrapper */
 LOCKDATA(mouse_func);
 LOCKDATA(mouse_callback);
 LOCKDATA(mouse_regs);
 LOCKDATA(pc_mouse);
 LOCKDATA(minx);
 LOCKDATA(maxx);
 LOCKDATA(miny);
 LOCKDATA(maxy);
 LOCKDATA(minz);
 LOCKDATA(maxz);
 LOCKDATA(sx);
 LOCKDATA(sy);
 LOCKDATA(emulat3);
 LOCKDATA(ox);
 LOCKDATA(oy);
 LOCKFUNC(mouse);
 LOCKFUNC(mouse_wrap);

 mouse_wrap_end[1] = __djgpp_ds_alias;
 /* grab a locked stack */
 if ((mouse_wrap_end[0] = (int)pc_malloc(MOUSE_STACK_SIZE)) == NULL) {
    return 0;
 }

 /* try to hook a call-back */
 __asm("\n\
		pushl	%%ds		\n\
		pushl	%%es		\n\
		movw	$0x0303, %%ax	\n\
		pushl	%%ds		\n\
		pushl	%%cs		\n\
		popl	%%ds		\n\
		popl	%%es		\n\
		int	$0x31		\n\
		popl	%%es		\n\
		popl	%%ds		\n\
		jc	0f		\n\
		shll	$16, %%ecx	\n\
		movw	%%dx, %%cx	\n\
		movl	%%ecx, %0	\n\
	0:				\n\
 ":"=g"(mouse_callback)
  :"S" (mouse_wrap), "D"(&mouse_regs)
  :"%eax", "%ecx", "%edx");
 if (!mouse_callback) {
    free((void *)mouse_wrap_end[0]);
    return 0;
 }

 /* adjust stack */
 mouse_wrap_end[0] += MOUSE_STACK_SIZE;

 /* install the handler */
 mouse_regs.x.ax = 0x000c;
#if PC_CUTE_WHEEL
 mouse_regs.x.cx = 0x7f | 0x80;
#else
 mouse_regs.x.cx = 0x7f;
#endif
 mouse_regs.x.dx = mouse_callback&0xffff;
 mouse_regs.x.es = mouse_callback>>16;
 __dpmi_int(0x33, &mouse_regs);

 CLEAR_MICKEYS();

 emulat3 = buttons<3;
 pc_atexit(pc_remove_mouse);
 return buttons;
}

MFUNC pc_install_mouse_handler (MFUNC handler)
{
 MFUNC old;

 if (!mouse_callback && !pc_install_mouse()) {
    return NULL;
 }

 old = mouse_func;
 mouse_func = handler;
 return old;
}

void pc_mouse_area (int x1, int y1, int x2, int y2)
{
 minx = x1;
 maxx = x2;
 miny = y1;
 maxy = y2;
}

void pc_mouse_speed (int xspeed, int yspeed)
{
 DISABLE();

 sx = MAX(1, xspeed);
 sy = MAX(1, yspeed);

 ENABLE();
}

int pc_query_mouse (int *x, int *y, int *z)
{
 *x = pc_mouse.x;
 *y = pc_mouse.y;
 *z = pc_mouse.z;
 return pc_mouse.b;
}

void pc_warp_mouse (int x, int y)
{
 CLEAR_MICKEYS();

 pc_mouse.x = MID(minx, x, maxx);
 pc_mouse.y = MID(miny, y, maxy);

 if (mouse_func) {
    mouse_func(pc_mouse.x, pc_mouse.y, pc_mouse.z, pc_mouse.b);
 }
}

/* Hack alert:
 * `mouse_wrap_end' actually holds the
 * address of stack in a safe data selector.
 */
__asm("\n\
		.text				\n\
		.p2align 5,,31			\n\
		.global	_mouse_wrap		\n\
_mouse_wrap:					\n\
		cld				\n\
		lodsl				\n\
		movl	%eax, %es:42(%edi)	\n\
		addw	$4, %es:46(%edi)	\n\
		pushl	%es			\n\
		movl	%ss, %ebx		\n\
		movl	%esp, %esi		\n\
		lss	%cs:_mouse_wrap_end, %esp\n\
		pushl	%ss			\n\
		pushl	%ss			\n\
		popl	%es			\n\
		popl	%ds			\n\
		movl	___djgpp_dos_sel, %fs	\n\
		pushl	%fs			\n\
		popl	%gs			\n\
		pushl	%edi			\n\
		call	_mouse			\n\
		popl	%edi			\n\
		movl	%ebx, %ss		\n\
		movl	%esi, %esp		\n\
		popl	%es			\n\
		iret				\n\
		.global	_mouse_wrap_end		\n\
_mouse_wrap_end:.long	0, 0");
