/*
 *	sdlc.c -- C component of sdlraster
 *	Copyright (C) 2005 Fred Barnes  <frmb@kent.ac.uk>
 *
 *	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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* NOTE: this assumes a 24/32-bit visual.  Will break if used in 8/16-bit modes */
/* unless SDL transparently maps it away */

/* NOTE:
 *	this now uses CIF to do the connection with occam-pi
 */

/*{{{  general includes*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>

#include "SDL.h"

/*}}}*/
/*{{{  CCSP includes*/
#include "cifccsp.h"
#include "dmem_if.h"

/*}}}*/
/*{{{  events*/
#define OCC_EVENT_UNKNOWN 0
#define OCC_EVENT_EXPOSE 1
#define OCC_EVENT_BUTTON_PRESS 2
#define OCC_EVENT_BUTTON_RELEASE 3
#define OCC_EVENT_KEY_PRESS 4
#define OCC_EVENT_KEY_RELEASE 5
#define OCC_EVENT_QUIT 6
#define OCC_EVENT_MOTION 7
#define OCC_EVENT_CKEY_PRESS 8
#define OCC_EVENT_CKEY_RELEASE 9

/*}}}*/
/*{{{  control keys*/
#define OCC_CKEY_UNKNOWN 0
#define OCC_CKEY_BACKSPACE 1
#define OCC_CKEY_NUMLOCK 2
#define OCC_CKEY_CAPSLOCK 3
#define OCC_CKEY_SCROLLOCK 4
#define OCC_CKEY_RSHIFT 5
#define OCC_CKEY_LSHIFT 6
#define OCC_CKEY_RCTRL 7
#define OCC_CKEY_LCTRL 8
#define OCC_CKEY_RALT 9
#define OCC_CKEY_LALT 10
#define OCC_CKEY_RMETA 11
#define OCC_CKEY_LMETA 12
#define OCC_CKEY_RSUPER 13
#define OCC_CKEY_LSUPER 14
#define OCC_CKEY_SYSREQ 15
#define OCC_CKEY_MENU 16
#define OCC_CKEY_MODE 17
#define OCC_CKEY_UP 18
#define OCC_CKEY_DOWN 19
#define OCC_CKEY_LEFT 20
#define OCC_CKEY_RIGHT 21
#define OCC_CKEY_HOME 22
#define OCC_CKEY_END 23
#define OCC_CKEY_PAGEUP 24
#define OCC_CKEY_PAGEDOWN 25
#define OCC_CKEY_INSERT 26
#define OCC_CKEY_DELETE 27

/*}}}*/
/*{{{  control*/
#define OCC_CTRL_UNKNOWN 0
#define OCC_CTRL_SHUTDOWN 1

/*}}}*/
/*{{{  structure which defines a single raster (SDL window)*/

#define MAX_BUFFERS	32	/* anymore than this is getting silly really.. */

typedef struct TAG_sdlraster {
	struct TAG_sdlraster *prev;
	struct TAG_sdlraster *next;		/* linked list */
	char *dpyname;
	SDL_Surface *screen;			/* main visual surface */
	int width, height;
	int num_buffers;			/* number of buffers */
	SDL_Surface *surfaces[MAX_BUFFERS];
	int buf_tail, buf_count;		/* head, tail and counter for queue */
	unsigned int *bufptr[MAX_BUFFERS];

	SDL_Event *lastevent;			/* pointer to the last event received */
	int shutdown;				/* used to make sure the thing can't
						 * terminate before all its rasters are back
						 */
	unsigned char pshiftstate[4];		/* shift-state including left/right awareness */
	unsigned char shiftstate;		/* used to add modifier masks to button-state */
} sdlraster_t;


/*}}}*/
/*{{{  static variables*/
static int sdl_was_initialised = 0;
static int sdl_window_count = 0;
static sdlraster_t *sdl_listhead = NULL;

/*}}}*/



/*{{{  static void sdlraster_detatch (sdlraster_t *raster)*/
/*
 *	detatches a raster from the linked-list of all rasters
 */
static void sdlraster_detatch (sdlraster_t *raster)
{
	sdlraster_t *walk, *prev;

	if (!raster) {
		return;
	}
	if (sdl_listhead == raster) {
		sdl_listhead = raster->next;
		raster->next = NULL;
		return;
	}

	for (walk = sdl_listhead->next, prev = sdl_listhead; walk && (walk != raster); prev = walk, walk = walk->next);
	if (!walk) {
		return;
	}
	prev->next = raster->next;
	return;
}
/*}}}*/
/*{{{  static void sdlraster_waitproc (sdlraster_t *raster)*/
/*
 *	this is the blocking sys-call which deals with event capture, used by sdlraster_eventproc
 */
static void sdlraster_waitproc (sdlraster_t *raster, SDL_Event *eventp)
{
	SDL_WaitEvent (eventp);
	raster->lastevent = eventp;

	return;
}
/*}}}*/
/*{{{  static void sdlraster_getkeydata (SDL_keysym *keysym, sdlraster_t *raster, int *evdata)*/
/*
 *	generates occam-data for keyboard event.  also keeps track of modifier states
 */
static void sdlraster_getkeydata (SDL_keysym *keysym, sdlraster_t *raster, int *evdata)
{
	static SDLKey keys[] = {SDLK_BACKSPACE, SDLK_NUMLOCK, SDLK_CAPSLOCK, SDLK_SCROLLOCK, SDLK_RSHIFT, SDLK_LSHIFT, SDLK_RCTRL, SDLK_LCTRL,
				SDLK_RALT, SDLK_LALT, SDLK_RMETA, SDLK_LMETA, SDLK_RSUPER, SDLK_LSUPER, SDLK_SYSREQ, SDLK_MENU,
				SDLK_MODE, SDLK_UP, SDLK_DOWN, SDLK_LEFT, SDLK_RIGHT, SDLK_HOME, SDLK_END, SDLK_PAGEUP,
				SDLK_PAGEDOWN, SDLK_INSERT, SDLK_DELETE};
	static int occkeys[] = {OCC_CKEY_BACKSPACE, OCC_CKEY_NUMLOCK, OCC_CKEY_CAPSLOCK, OCC_CKEY_SCROLLOCK, OCC_CKEY_RSHIFT, OCC_CKEY_LSHIFT, OCC_CKEY_RCTRL, OCC_CKEY_LCTRL,
				OCC_CKEY_RALT, OCC_CKEY_LALT, OCC_CKEY_RMETA, OCC_CKEY_LMETA, OCC_CKEY_RSUPER, OCC_CKEY_LSUPER, OCC_CKEY_SYSREQ, OCC_CKEY_MENU,
				OCC_CKEY_MODE, OCC_CKEY_UP, OCC_CKEY_DOWN, OCC_CKEY_LEFT, OCC_CKEY_RIGHT, OCC_CKEY_HOME, OCC_CKEY_END, OCC_CKEY_PAGEUP,
				OCC_CKEY_PAGEDOWN, OCC_CKEY_INSERT, OCC_CKEY_DELETE};
	static int nkeys = 27;
	int i;

	for (i=0; i<nkeys; i++) {
		if (keysym->sym == keys[i]) {
			/* non-printing keys */
			if (evdata[0] == OCC_EVENT_KEY_PRESS) {
				evdata[0] = OCC_EVENT_CKEY_PRESS;
				/*{{{  set bits if modifier*/
				switch (keys[i]) {
				case SDLK_RSHIFT:
					raster->pshiftstate[0] |= 0x02;
					raster->shiftstate |= 0x10;
					break;
				case SDLK_LSHIFT:
					raster->pshiftstate[0] |= 0x01;
					raster->shiftstate |= 0x10;
					break;
				case SDLK_RCTRL:
					raster->pshiftstate[1] |= 0x02;
					raster->shiftstate |= 0x20;
					break;
				case SDLK_LCTRL:
					raster->pshiftstate[1] |= 0x01;
					raster->shiftstate |= 0x20;
					break;
				case SDLK_RALT:
				case SDLK_MODE:
					raster->pshiftstate[2] |= 0x02;
					raster->shiftstate |= 0x40;
					break;
				case SDLK_LALT:
					raster->pshiftstate[2] |= 0x01;
					raster->shiftstate |= 0x40;
					break;
				case SDLK_RSUPER:
					raster->pshiftstate[3] |= 0x02;
					raster->shiftstate |= 0x80;
					break;
				case SDLK_LSUPER:
					raster->pshiftstate[3] |= 0x01;
					raster->shiftstate |= 0x80;
					break;
				}
				/*}}}*/
			} else {
				evdata[0] = OCC_EVENT_CKEY_RELEASE;
				/*{{{  clear bits if modifier*/
				switch (keys[i]) {
				case SDLK_RSHIFT:
					raster->pshiftstate[0] &= 0x01;
					if (!raster->pshiftstate[0]) {
						raster->shiftstate &= 0xef;
					}
					break;
				case SDLK_LSHIFT:
					raster->pshiftstate[0] &= 0x02;
					if (!raster->pshiftstate[0]) {
						raster->shiftstate &= 0xef;
					}
					break;
				case SDLK_RCTRL:
					raster->pshiftstate[1] &= 0x01;
					if (!raster->pshiftstate[1]) {
						raster->shiftstate &= 0xdf;
					}
					break;
				case SDLK_LCTRL:
					raster->pshiftstate[1] &= 0x02;
					if (!raster->pshiftstate[1]) {
						raster->shiftstate &= 0xdf;
					}
					break;
				case SDLK_RALT:
				case SDLK_MODE:
					raster->pshiftstate[2] &= 0x01;
					if (!raster->pshiftstate[2]) {
						raster->shiftstate &= 0xbf;
					}
					break;
				case SDLK_LALT:
					raster->pshiftstate[2] &= 0x02;
					if (!raster->pshiftstate[2]) {
						raster->shiftstate &= 0xbf;
					}
					break;
				case SDLK_RSUPER:
					raster->pshiftstate[3] &= 0x01;
					if (!raster->pshiftstate[3]) {
						raster->shiftstate &= 0x7f;
					}
					break;
				case SDLK_LSUPER:
					raster->pshiftstate[3] &= 0x02;
					if (!raster->pshiftstate[3]) {
						raster->shiftstate &= 0x7f;
					}
					break;
				}
				/*}}}*/
			}
			evdata[1] = occkeys[i];

			return;
		}
	}
	evdata[1] = (keysym->unicode & 0xffff);

	return;
}
/*}}}*/
/*{{{  static void sdlraster_eventproc (Process *me, sdlraster_t *raster, Channel *killchan, Channel *signal, Process **epp)*/
/*
 *	this process houses the above blocking call
 */
static void sdlraster_eventproc (Process *me, sdlraster_t *raster, Channel *killchan, Channel *signal, Process **epp)
{
	SDL_Event event;

	while (*epp == me) {
		raster->lastevent = NULL;
		ChanInit (killchan);
		BLOCKING_KILLCALLN (sdlraster_waitproc, killchan, raster, &event);
		if (raster->lastevent) {
			int evdata[2];

			switch (event.type) {
			case SDL_VIDEOEXPOSE:
				evdata[0] = OCC_EVENT_EXPOSE;
				evdata[1] = 0;
				break;
			case SDL_MOUSEBUTTONDOWN:
				evdata[0] = OCC_EVENT_BUTTON_PRESS;
				evdata[1] = ((event.button.x & 0xfff) << 20) | ((event.button.y & 0xfff) << 8) | (raster->shiftstate & 0xf0) | (event.button.button & 0x0f);
				break;
			case SDL_MOUSEBUTTONUP:
				evdata[0] = OCC_EVENT_BUTTON_RELEASE;
				evdata[1] = ((event.button.x & 0xfff) << 20) | ((event.button.y & 0xfff) << 8) | (raster->shiftstate & 0xf0) | (event.button.button & 0x0f);
				break;
			case SDL_KEYDOWN:
				evdata[0] = OCC_EVENT_KEY_PRESS;
				sdlraster_getkeydata (&event.key.keysym, raster, evdata);
				break;
			case SDL_KEYUP:
				evdata[0] = OCC_EVENT_KEY_RELEASE;
				sdlraster_getkeydata (&event.key.keysym, raster, evdata);
				break;
			case SDL_QUIT:
				evdata[0] = OCC_EVENT_QUIT;
				evdata[1] = 0;
				break;
			case SDL_MOUSEMOTION:
				evdata[0] = OCC_EVENT_MOTION;
				evdata[1] = ((event.motion.x & 0xfff) << 20) | ((event.motion.y & 0xfff) << 8) | (raster->shiftstate & 0xf0) | (event.motion.state & 0x0f);
				break;
			default:
				evdata[0] = OCC_EVENT_UNKNOWN;
				evdata[1] = 0;
				break;
			}
			ChanOut (signal, &(evdata[0]), 2 * sizeof(int));
		} else {
			*epp = NULL;
		}
	}
	return;
}
/*}}}*/
/*{{{  static void sdlraster_iredraw (sdlraster_t *raster, int bufnum)*/
/*
 *	helper function to redraw a raster (called in thread context)
 */
static void sdlraster_iredraw (sdlraster_t *raster, int bufnum)
{
	SDL_Rect rect;

	rect.x = 0;
	rect.y = 0;
	rect.w = raster->width;
	rect.h = raster->height;

	if (SDL_MUSTLOCK (raster->surfaces[bufnum])) {
		SDL_UnlockSurface (raster->surfaces[bufnum]);
	}
	SDL_BlitSurface (raster->surfaces[bufnum], NULL, raster->screen, &rect);
	SDL_UpdateRect (raster->screen, 0, 0, 0, 0);

	return;
}
/*}}}*/
/*{{{  static void sdlraster_destroysdl (sdlraster_t *raster)*/
/*
 *	this destroys a raster, but doesn't free the raster structure itself
 */
static void sdlraster_destroysdl (sdlraster_t *raster)
{
	int i;

	for (i=0; i<raster->num_buffers; i++) {
		SDL_FreeSurface (raster->surfaces[i]);
		raster->surfaces[i] = NULL;
	}
	SDL_FreeSurface (raster->screen);
	raster->screen = NULL;

	return;
}
/*}}}*/
/*{{{  static void sdlraster_mainproc (Process *me, sdlraster_t *raster, Channel *rin, Channel *rout, Channel *evout, Channel *ctlin)*/
/*
 *	this is the SDLRaster main-process: produces a single raster window on the display
 *	frees the sdlraster_t structure before returning
 */
static void sdlraster_mainproc (Process *me, sdlraster_t *raster, Channel *rin, Channel *rout, Channel *evout, Channel *ctlin)
{
	int i;
	Channel *palist[6];
	Channel dummy0, dummy1, dummy2;
	int got_event = 0;
	unsigned int cbuf[3];
	Process *eventproc;
	Channel killepchan;
	Channel eventsig;
	int evdata[2];
	int running = 1;

	ChanInit (&dummy0);
	ChanInit (&dummy1);
	ChanInit (&dummy2);

	/* just do this forever.. */
	palist[0] = rin;
	palist[1] = raster->buf_count ? rout : &dummy0;
	palist[2] = got_event ? evout : &dummy1;
	palist[3] = ctlin;
	palist[4] = &eventsig;
	palist[5] = NULL;

	/* start the event collector */
	ChanInit (&killepchan);
	ChanInit (&eventsig);
	eventproc = ProcAlloc (sdlraster_eventproc, 65536, 4, raster, &killepchan, &eventsig, &eventproc);
	ProcFork (eventproc);

	while (running) {
		palist[1] = (raster->buf_count && !raster->shutdown) ? rout : &dummy0;
		palist[2] = got_event ? evout : &dummy1;
		palist[3] = !raster->shutdown ? ctlin : &dummy2;

		i = ProcAltList (palist);
#if 0
EXTERNAL_CALLN (fprintf, stderr, "sdlraster_mainproc(): ALT complete: i = %d\n", i);
#endif
		switch (i) {
			/*{{{  0 -- rin (raster in)*/
			/* FIXME: if rasters are given back in the wrong order, odd things may happen! */
		case 0:
			ChanMInN (rin, cbuf, 3);
			for (i=0; (i<raster->num_buffers) && (raster->bufptr[i] != (unsigned int *)(cbuf[0])); i++);
			if (i == raster->num_buffers) {
				EXTERNAL_CALLN (fprintf, stderr, "KRoC: SDLRaster (%s): not one of my buffers (0x%8.8x  (%d,%d))\n", cbuf[0], (int)cbuf[1], (int)cbuf[2]);
				/* FIXME: fatal ? */
			} else {
				EXTERNAL_CALLN (sdlraster_iredraw, raster, i);

				raster->buf_count++;

				/* see if were were stuck shutting-down */
				if ((raster->shutdown == 1) && (raster->buf_count == raster->num_buffers)) {
					/* all back now, so can shut-down */
					int result;

					running = 0;
					raster->shutdown = 2;
					eventproc = NULL;
					BLOCKING_CALL_KILL (&killepchan, &result);
					/* control channel still blocked */
					ChanXEnd (ctlin);
				}
			}
			break;
			/*}}}*/
			/*{{{  1 -- rout (raster out)*/
		case 1:
			cbuf[0] = (unsigned int)raster->bufptr[raster->buf_tail];
			cbuf[1] = (unsigned int)raster->height;
			cbuf[2] = (unsigned int)raster->width;

			if (SDL_MUSTLOCK (raster->surfaces[raster->buf_tail])) {
				EXTERNAL_CALLN (SDL_LockSurface, raster->surfaces[raster->buf_tail]);
			}
			raster->buf_tail = (raster->buf_tail + 1) % raster->num_buffers;
			raster->buf_count--;

			ChanMOutN (rout, cbuf, 3);
			break;
			/*}}}*/
			/*{{{  2 -- evout (events out)*/
		case 2:
#if 0
EXTERNAL_CALLN (fprintf, stderr, "sdlraster_mainproc(): ALT complete: i = %d\n", i);
#endif
			/* just output what we have */
			ChanOut (evout, &(evdata[0]), 2 * sizeof (int));
			got_event = 0;
			break;
			/*}}}*/
			/*{{{  3 -- ctlin (control in)*/
		case 3:
			ChanXIn (ctlin, &i, sizeof (int));
			switch (i) {
			case OCC_CTRL_SHUTDOWN:
				if (raster->buf_count == raster->num_buffers) {
					/* all back, so can shutdown safely */
					int result;

					running = 0;
					raster->shutdown = 2;
					eventproc = NULL;
					BLOCKING_CALL_KILL (&killepchan, &result);
					ChanXEnd (ctlin);
				} else {
					/* can't shutdown just now, so hold this communication */
					raster->shutdown = 1;
				}
				break;
			default:
				break;
			}
			break;
			/*}}}*/
			/*{{{  4 -- eventsig (from eventproc)*/
		case 4:
#if 0
EXTERNAL_CALLN (fprintf, stderr, "sdlraster_mainproc(): ALT complete: i = %d\n", i);
#endif
			/* overwrite here */
			ChanIn (&eventsig, &(evdata[0]), 2 * sizeof (int));
			got_event = 1;
			break;
			/*}}}*/
		}
	}

	/* destroy the raster */
	EXTERNAL_CALLN (sdlraster_destroysdl, raster);

	return;
}
/*}}}*/
/*{{{  static void real_sdlraster_init (int *raddr, char *dispid, int idlen, int width, int height, int nbuffers, Channel *raster_in, Channel *raster_out, Channel *events, Channel *control)*/
/*
 *	allocates a new sdlraster CIF process, returns the address for it to be started from occam
 */
static void real_sdlraster_init (int *raddr, char *dispid, int idlen, int width, int height, int nbuffers, Channel *raster_in, Channel *raster_out, Channel *events, Channel *control)
{
	sdlraster_t *raster = dmem_alloc (sizeof (sdlraster_t));
	Process *p;
	int i;

	/*{{{  prep raster structure*/
	raster->next = NULL;
	raster->dpyname = dmem_alloc (idlen + 1);
	if (idlen) {
		memcpy (raster->dpyname, dispid, idlen);
	}
	raster->dpyname[idlen] = '\0';
	raster->screen = NULL;
	raster->width = width;
	raster->height = height;
	raster->lastevent = NULL;
	raster->shutdown = 0;
	raster->shiftstate = 0;


	/*}}}*/
	/*{{{  initialise SDL, set video mode*/
	if (!sdl_was_initialised) {
		/* display name seems not to be required with SDL -- must just take it from the environment..! */
		if (SDL_Init (SDL_INIT_VIDEO)) {
			fprintf (stderr, "KRoC: SDLRaster (%s): failed to initialise SDL: %s\n", raster->dpyname, SDL_GetError ());
			goto out_error_0;
		}
		sdl_was_initialised = 1;
	}

	raster->screen = SDL_SetVideoMode (raster->width, raster->height, 0, SDL_HWSURFACE);
	if (!raster->screen) {
		fprintf (stderr, "KRoC: SDLRaster (%s): failed to create surface: %s\n", raster->dpyname, SDL_GetError ());
		goto out_error_1;
	}
	sdl_window_count++;

	/*}}}*/
	/*{{{  setup buffers, other SDL initialisation*/
	if (nbuffers > MAX_BUFFERS) {
		raster->num_buffers = MAX_BUFFERS;
	} else {
		raster->num_buffers = nbuffers;
	}
	raster->buf_tail = 0;
	raster->buf_count = 0;

	for (i=0; i<MAX_BUFFERS; i++) {
		raster->surfaces[i] = NULL;
		raster->bufptr[i] = NULL;
	}

	/* create raster buffers */
	for (i=0; i<raster->num_buffers; i++) {
		Uint32 rmask, gmask, bmask, amask;

#if SDL_BYTEORDER == SDL_BIG_ENDIAN
		rmask = 0xff000000;
		gmask = 0x00ff0000;
		bmask = 0x0000ff00;
		amask = 0x000000ff;
#else
		rmask = 0x000000ff;
		gmask = 0x0000ff00;
		bmask = 0x00ff0000;
		amask = 0xff000000;
#endif
		// raster->surfaces[i] = SDL_CreateRGBSurface (SDL_SWSURFACE, width, height, 32,
		//		rmask, gmask, bmask, amask);
		raster->surfaces[i] = SDL_DisplayFormat (raster->screen);
		if (!raster->surfaces[i]) {
			int j;

			for (j=0; j<i; j++) {
				SDL_FreeSurface (raster->surfaces[j]);
				raster->surfaces[j] = NULL;
			}
			fprintf (stderr, "real_sdlraster_init(): failed to create surface: %s\n", SDL_GetError ());
			goto out_error_2;
		}
	}
	for (i=0; i<raster->num_buffers; i++) {
		SDL_LockSurface (raster->surfaces[i]);
		raster->bufptr[i] = (unsigned int *)raster->surfaces[i]->pixels;
		SDL_UnlockSurface (raster->surfaces[i]);
	}

	/* enable UNICODE for keyboard input */
	SDL_EnableUNICODE (1);

	/*}}}*/

	raster->buf_count = raster->num_buffers;

	p = ProcAlloc (sdlraster_mainproc, 65536, 5, raster, raster_in, raster_out, events, control);
	*raddr = (int)p;

	/*{{{  add to linked-list*/
	raster->next = sdl_listhead;
	sdl_listhead = raster;
	/*}}}*/

	return;
	/* error paths out here */
out_error_2:
	SDL_FreeSurface (raster->screen);
	raster->screen = NULL;
	sdl_window_count--;
out_error_1:
	if (!sdl_window_count) {
		/* no windows left, shutdown SDL */
		SDL_Quit ();
		sdl_was_initialised = 0;
	}
out_error_0:
	dmem_release (raster);
	*raddr = 0;
	return;
}
/*}}}*/
/*{{{  static void real_sdlraster_free (int addr)*/
/*
 *	frees an SDLRaster CIF process
 */
static void real_sdlraster_free (int addr)
{
	Process *p = (Process *)addr;

	ProcAllocClean (p);
	return;
}
/*}}}*/
/*{{{  static void real_sdlraster_clear (unsigned int *ptr, int dim1, int dim2)*/
/*
 *	clears the given raster
 */
static void real_sdlraster_clear (unsigned int *ptr, int dim1, int dim2)
{
	unsigned int *data = ptr;
	unsigned int i, c;

#if 0
fprintf (stderr, "real_sdlraster_clear(): ptr=0x%8.8x, dim1=%d, dim2=%d\n", (unsigned int)ptr, dim1, dim2);
#endif
	c = dim1 * dim2;
	for (i=0; i<c; i++) {
		*data = 0;
		data++;
	}
	return;
}
/*}}}*/
/*{{{  static void real_sdlraster_update (unsigned int *ptr, int dim1, int dim2)*/
/*
 *	re-draws one of the buffers (called from the application for efficiency)
 */
static void real_sdlraster_update (unsigned int *ptr, int dim1, int dim2)
{
	sdlraster_t *raster = NULL;
	SDL_Rect rect;
	int bufnum;

	/* search for the raster */
	bufnum = -1;
	for (raster = sdl_listhead; raster; raster = raster->next) {
		int i;

		for (i=0; (i<raster->num_buffers) && (raster->bufptr[i] != ptr); i++);
		if (i < raster->num_buffers) {
			bufnum = i;
			break;
		}
	}

	if (!raster || (bufnum < 0)) {
		return;
	}

	rect.x = 0, rect.y = 0;
	rect.w = raster->width, rect.h = raster->height;

	if (SDL_MUSTLOCK (raster->surfaces[bufnum])) {
		SDL_UnlockSurface (raster->surfaces[bufnum]);
	}
	SDL_BlitSurface (raster->surfaces[bufnum], NULL, raster->screen, &rect);
	SDL_UpdateRect (raster->screen, 0, 0, 0, 0);
#if 0
fprintf (stderr, "real_sdlraster_update(): updated raster and flushed display..\n");
#endif
	if (SDL_MUSTLOCK (raster->surfaces[bufnum])) {
		SDL_LockSurface (raster->surfaces[bufnum]);
	}
	return;
}
/*}}}*/
/*{{{  static void real_sdlraster_update_buf (int bufnum)*/
/*
 *	re-draws one of the buffers (called from the application for efficiency)
 */
static void real_sdlraster_update_buf (int bufnum)
{
	sdlraster_t *raster = NULL;
	SDL_Rect rect;

	/* only works on the first raster */
	if (!sdl_listhead) {
		return;
	}
	raster = sdl_listhead;
	if ((bufnum < 0) || (bufnum >= raster->num_buffers)) {
		return;
	}

	rect.x = 0, rect.y = 0;
	rect.w = raster->width, rect.h = raster->height;

	if (SDL_MUSTLOCK (raster->surfaces[bufnum])) {
		SDL_UnlockSurface (raster->surfaces[bufnum]);
	}
	SDL_BlitSurface (raster->surfaces[bufnum], NULL, raster->screen, &rect);
	SDL_UpdateRect (raster->screen, 0, 0, 0, 0);
#if 0
fprintf (stderr, "real_sdlraster_update_buf(): updated raster and flushed display..\n");
#endif
	if (SDL_MUSTLOCK (raster->surfaces[bufnum])) {
		SDL_LockSurface (raster->surfaces[bufnum]);
	}
	return;
}
/*}}}*/




/*{{{  occam interfaces*/
/*
 *	#PRAGMA EXTERNAL "PROC C.sdlraster.init (RESULT INT raddr, VAL []BYTE dispname,
 *				VAL INT width, height, nbuffers, CHAN RASTER in?, out!,
 *				CHAN [2]INT events!, CHAN INT control?) = 0"
 *	#PRAGMA EXTERNAL "PROC C.sdlraster.free (VAL INT addr) = 0"
 *	#PRAGMA EXTERNAL "PROC C.sdlraster.clear ([][]INT r) = 0"
 *	#PRAGMA EXTERNAL "PROC C.sdlraster.update ([][]INT r) = 0"
 */
void _sdlraster_init (int *w) { real_sdlraster_init ((int *)(w[0]), (char *)(w[1]), (int)(w[2]), (int)(w[3]), (int)(w[4]), (int)(w[5]), (Channel *)(w[6]), (Channel *)(w[7]), (Channel *)(w[8]), (Channel *)(w[9])); }
void _sdlraster_free (int *w) { real_sdlraster_free ((int)(w[0])); }
void _sdlraster_clear (int *w) { real_sdlraster_clear ((unsigned int *)(w[0]), (int)(w[1]), (int)(w[2])); }
void _sdlraster_update (int *w) { real_sdlraster_update ((unsigned int *)(w[0]), (int)(w[1]), (int)(w[2])); }
void _sdlraster_update_buf (int *w) { real_sdlraster_update_buf ((int)(w[0])); }


/*}}}*/


