/* steali.c -- install and remove C-language input handler
 * Copyright (c) 1988, I and I Computing, and Commodore-Amiga, Inc.
 *
 *
 * Executables based on this information may be used in software
 * for Commodore Amiga computers.  All other rights reserved.
 *
 * This information is provided "as is"; no warranties are made.
 * All use is at your own risk, and no liability or responsibility is assumed.
 */

#include "sysall.h"

#define TEST 0

#if TEST

/* Test Input Handler
 * this fellow runs as the input.device task.  His context is set
 * up so that he can access global data and other functions,
 * but it must be remembered that no DOS functions may be used.
 */
struct InputEvent *
customHandler(input_event)
struct InputEvent *input_event;
{
	extern long int shared_count;	/* global data	*/
	struct InputEvent *ie;

	/* count 'em	*/

	for (ie = input_event; ie; ie = ie->ie_NextEvent)
	{
		shared_count++;
	}

	return (input_event);
}

/* indicator variable shared by my task and my input handler */
long int	shared_count = 0;

main()
{
	LONG installInputHandler();

	printf("try to install my handler\n");

	/* install my input handler at priority greater than Intuition */
	installInputHandler( customHandler, 51);

	/* wait for something to happen	*/
	printf("handler installed, press rtc:");
	fflush(stdout);
	getchar();

	/* remove my fellow	*/
	extractInputHandler();

	printf("there were %ld input events counted by our handler\n", shared_count);
}

#endif	TEST

struct MsgPort		*indev_port = NULL;
struct IOStdReq		*indev_req =  NULL;
struct Interrupt	handler_interrupt;

/* 
 * opens input.device, installs an input handler
 * that calls c_handler in a C friendly way
 *
 * NOTE: returns WaitIO() return value, which is
 * 0 if OK.
 */
LONG
installInputHandler( c_handler, priority)
struct InputEvent	*(*c_handler)();
int					priority;
{
	LONG	handlerInterface();		/* asm-C interface code	*/

	indev_port = CreatePort( NULL, 0L );
	indev_req =  CreateStdIO( indev_port );

	OpenDevice( "input.device", 0L, indev_req, 0L );

	/* set up interrupt struct, use C-handler address as the data	*/
	handler_interrupt.is_Code = (VOID (*)()) handlerInterface;
	handler_interrupt.is_Data = (APTR) c_handler;
	handler_interrupt.is_Node.ln_Pri = priority;

	indev_req->io_Command = IND_ADDHANDLER;
	indev_req->io_Data =	(APTR) &handler_interrupt;

	/* install the sucker	*/
	SendIO( indev_req );
	return ( WaitIO( indev_req ) );
}

/*
 * better be sure that installInputHandler() worked and is
 * in effect.
 */
extractInputHandler()
{
	indev_req->io_Command = IND_REMHANDLER;

	SendIO( indev_req );
	WaitIO( indev_req );

	CloseDevice( indev_req );		/* close input.device	*/
	DeleteStdIO( indev_req );
	DeletePort( indev_port );
}

/*
 * what follows is an assembly language fragment necessary (perhaps
 * more than necessary) to call a small model Aztec C program from
 * the input device.
 *
 * Aztec C has a different register convention than the system code,
 * and small model requires that a base address pointer be set
 * up in register A4.  Also, the parameter to an input handler
 * must be pushed on the stack for a C routine to use it.
 */

#asm
	cseg
	public	_handlerInterface
	public	_geta4

	; ---	NEVER FORGET that this runs as the input.device task

_handlerInterface:
	; ---	pointers to linked list of input events in a0, is_Data in a1
	; ---	is_Data is simply the address of the C handler

	; ---	protect everything necessary (and more?)
	movem.l	d2/d3/d4-d7/a2-a6,-(sp)
	jsr		_geta4		; set up context for handler (Aztec C function)
	move.l	a0,-(sp)	; push input event list
	jsr		(a1)		; jump to real C handler
	addq.l	#4,a7		; pop argument
	movem.l	(sp)+,d2/d3/d4-d7/a2-a6

	rts
#endasm

