/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997, 1998, 1999, 2000  Sam Lantinga

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library 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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    Sam Lantinga
    slouken@libsdl.org
*/

#ifdef SAVE_RCSID
static char rcsid =
 "@(#) $Id: SDL_cgxvideo.c,v 1.6 2002/03/06 11:23:03 slouken Exp $";
#endif

/*
 * CGX based SDL video driver implementation by Gabriele Greco
 * gabriele.greco@aruba.it
 */

/*
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#ifdef MTRR_SUPPORT
#include <asm/mtrr.h>
#include <sys/fcntl.h>
#endif
*/

#include "SDL.h"
#include "SDL_error.h"
#include "SDL_timer.h"
#include "SDL_thread.h"
#include "SDL_video.h"
#include "SDL_mouse.h"
#include "SDL_endian.h"
#include "SDL_sysvideo.h"
#include "SDL_pixels_c.h"
#include "SDL_events_c.h"
#include "SDL_cgxgl_c.h"
#include "SDL_cgxvideo.h"
#include "SDL_cgxwm_c.h"
#include "SDL_amigamouse_c.h"
#include "SDL_amigaevents_c.h"
#include "SDL_cgxmodes_c.h"
#include "SDL_cgximage_c.h"
#include "SDL_cgxyuv_c.h"

/* Initialization/Query functions */
static int CGX_VideoInit(_THIS, SDL_PixelFormat *vformat);
static SDL_Surface *CGX_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags);
static int CGX_ToggleFullScreen(_THIS, int on);
static void CGX_UpdateMouse(_THIS);
static int CGX_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors);
static void CGX_VideoQuit(_THIS);

/* CGX driver bootstrap functions */

struct Library *CyberGfxBase=NULL;
struct IntuitionBase *IntuitionBase=NULL;
struct GfxBase *GfxBase=NULL;

int CGX_SetGamma(_THIS, float red, float green, float blue)
{
    SDL_SetError("Gamma correction not supported");
    return -1;
}

int CGX_GetGamma(_THIS, float red, float green, float blue)
{
    SDL_SetError("Gamma correction not supported");
    return -1;
}

int CGX_SetGammaRamp(_THIS, Uint16 *ramp)
{
#if 0
	Int i, ncolors;
	XColor xcmap[256];

	/* See if actually setting the gamma is supported */
	if ( SDL_Visual->class != DirectColor ) {
	    SDL_SetError("Gamma correction not supported on this visual");
	    return(-1);
	}

	/* Calculate the appropriate palette for the given gamma ramp */
	ncolors = SDL_Visual->map_entries;
	for ( i=0; i<ncolors; ++i ) {
		Uint8 c = (256 * i / ncolors);
		xcmap[i].pixel = SDL_MapRGB(this->screen->format, c, c, c);
		xcmap[i].red   = ramp[0*256+c];
		xcmap[i].green = ramp[1*256+c];
		xcmap[i].blue  = ramp[2*256+c];
		xcmap[i].flags = (DoRed|DoGreen|DoBlue);
	}
	XStoreColors(GFX_Display, SDL_XColorMap, xcmap, ncolors);
	XSync(GFX_Display, False);

	return(0);

#else
    SDL_SetError("Gamma correction not supported on this visual");
    return(-1);

#endif
}

static void DestroyScreen(_THIS)
{
  	if(currently_fullscreen)
	{
		if(this->hidden->dbuffer)
		{
			extern struct MsgPort *safeport,*dispport;

			this->hidden->dbuffer=0;

			if(safeport)
			{
				while(GetMsg(safeport)!=NULL);
				DeleteMsgPort(safeport);
			}
			if(dispport)
			{
				while(GetMsg(dispport)!=NULL);
				DeleteMsgPort(dispport);
			}

			this->hidden->SB[0]->sb_DBufInfo->dbi_SafeMessage.mn_ReplyPort=this->hidden->SB[0]->sb_DBufInfo->dbi_DispMessage.mn_ReplyPort=NULL;
			this->hidden->SB[1]->sb_DBufInfo->dbi_SafeMessage.mn_ReplyPort=this->hidden->SB[1]->sb_DBufInfo->dbi_DispMessage.mn_ReplyPort=NULL;

			if(this->hidden->SB[1])
				FreeScreenBuffer(SDL_Display,this->hidden->SB[1]);
			if(this->hidden->SB[0])
				FreeScreenBuffer(SDL_Display,this->hidden->SB[0]);


			this->hidden->SB[0]=this->hidden->SB[1]=NULL;

			if(SDL_RastPort && SDL_RastPort != &SDL_Display->RastPort)
				free(SDL_RastPort);

			SDL_RastPort=NULL;
		}
		CloseScreen(GFX_Display);
		currently_fullscreen=0;
	}
	else if(GFX_Display)
		UnlockPubScreen(NULL,GFX_Display);

	GFX_Display = NULL;
}

static int CGX_Available(void)
{
	struct Library *l;

	l = OpenLibrary("cybergraphics.library",0L);

	if ( l != NULL ) {
		D(bug("CGX video device AVAILABLE\n"));
		CloseLibrary(l);
	}
	D(else bug("**CGX video device UNAVAILABLE\n"));

	return(l != NULL);
}

static void CGX_DeleteDevice(SDL_VideoDevice *device)
{
	if ( device ) {
		if ( device->hidden ) {
			free(device->hidden);
		}
		if ( device->gl_data ) {
			free(device->gl_data);
		}
		free(device);
	}
}

static SDL_VideoDevice *CGX_CreateDevice(int devindex)
{
	SDL_VideoDevice *device;

	/* Initialize all variables that we clean on shutdown */
	device = (SDL_VideoDevice *)malloc(sizeof(SDL_VideoDevice));
	if ( device ) {
		memset(device, 0, (sizeof *device));
		device->hidden = (struct SDL_PrivateVideoData *)
				malloc((sizeof *device->hidden));
		device->gl_data = (struct SDL_PrivateGLData *)
				malloc((sizeof *device->gl_data));
	}
	if ( (device == NULL) || (device->hidden == NULL) ||
	                         (device->gl_data == NULL) ) {
		D(bug("Unable to create video device!\n"));
		SDL_OutOfMemory();
		CGX_DeleteDevice(device);
		return(0);
	}
	memset(device->hidden, 0, sizeof(*device->hidden));
	memset(device->gl_data, 0, sizeof(*device->gl_data));

	/* Set the driver flags */
	device->handles_any_size = 1;

	/* Set the function pointers */
	device->VideoInit = CGX_VideoInit;
	device->ListModes = CGX_ListModes;
	device->SetVideoMode = CGX_SetVideoMode;
	device->ToggleFullScreen = CGX_ToggleFullScreen;
	device->UpdateMouse = CGX_UpdateMouse;
#ifdef XFREE86_XV
	device->CreateYUVOverlay = X11_CreateYUVOverlay;
#endif
	device->SetColors = CGX_SetColors;
	device->UpdateRects = NULL;
	device->VideoQuit = CGX_VideoQuit;
	device->AllocHWSurface = CGX_AllocHWSurface;
	device->CheckHWBlit = CGX_CheckHWBlit;
	device->FillHWRect = CGX_FillHWRect;
	device->SetHWColorKey = CGX_SetHWColorKey;
	device->SetHWAlpha = NULL;
	device->LockHWSurface = CGX_LockHWSurface;
	device->UnlockHWSurface = CGX_UnlockHWSurface;
	device->FlipHWSurface = CGX_FlipHWSurface;
	device->FreeHWSurface = CGX_FreeHWSurface;
	device->SetGamma = CGX_SetGamma;
	device->GetGamma = CGX_GetGamma;
	device->SetGammaRamp = CGX_SetGammaRamp;
	device->GetGammaRamp = NULL;
#ifdef HAVE_OPENGL
	device->GL_LoadLibrary = CGX_GL_LoadLibrary;
	device->GL_GetProcAddress = CGX_GL_GetProcAddress;
	device->GL_GetAttribute = CGX_GL_GetAttribute;
	device->GL_MakeCurrent = CGX_GL_MakeCurrent;
	device->GL_SwapBuffers = CGX_GL_SwapBuffers;
#endif
	device->SetIcon = CGX_SetIcon;
	device->SetCaption = CGX_SetCaption;
	device->IconifyWindow = NULL; /* CGX_IconifyWindow; */
	device->GrabInput = NULL /* CGX_GrabInput*/;
	device->GetWMInfo = CGX_GetWMInfo;
	device->FreeWMCursor = amiga_FreeWMCursor;
	device->CreateWMCursor = amiga_CreateWMCursor;
	device->ShowWMCursor = amiga_ShowWMCursor;
	device->WarpWMCursor = amiga_WarpWMCursor;
	device->CheckMouseMode = amiga_CheckMouseMode;
	device->InitOSKeymap = amiga_InitOSKeymap;
	device->PumpEvents = amiga_PumpEvents;

	device->free = CGX_DeleteDevice;

	return device;
}

VideoBootStrap CGX_bootstrap = {
	"CGX", "AmigaOS CyberGraphics", CGX_Available, CGX_CreateDevice
};

Uint32 MakeBitMask(_THIS,int type,int format,int *bpp)
{
	D(if(type==0)bug("REAL pixel format: "));

	if(this->hidden->depth==*bpp)
	{

	switch(format)
    	{
		case PIXFMT_LUT8:
			D(if(type==0)bug("LUT8\n"));
			return 0;
		case PIXFMT_BGR15:
		case PIXFMT_RGB15PC:
			switch(type)
			{
				case 0:
					D(bug("RGB15PC/BGR15\n"));
					return 31;
				case 1:
					return 992;
				case 2:
					return 31744;
			}
		case PIXFMT_RGB15:
		case PIXFMT_BGR15PC:
			switch(type)
			{
				case 0:
					D(bug("RGB15/BGR15PC\n"));
					return 31744;
				case 1:
					return 992;
				case 2:
					return 31;
			}
		case PIXFMT_BGR16PC:
		case PIXFMT_RGB16:
			switch(type)
			{
				case 0:
					D(bug("RGB16PC\n"));
					return 63488;
				case 1:
					return 2016;
				case 2:
					return 31;
			}
		case PIXFMT_BGR16:
		case PIXFMT_RGB16PC:
			switch(type)
			{
				case 0:
					D(bug("RGB16PC/BGR16\n"));
					return 31;
				case 1:
					return 2016;
				case 2:
					return 63488;
			}

		case PIXFMT_RGB24:
			switch(type)
			{
				case 0:
					D(bug("RGB24/BGR24\n"));
					return 0xff0000;
				case 1:
					return 0xff00;
				case 2:
					return 0xff;
			}
		case PIXFMT_BGR24:
			switch(type)
			{
				case 0:
					D(bug("BGR24\n"));
					return 0xff;
				case 1:
					return 0xff00;
				case 2:
					return 0xff0000;
			}
		case PIXFMT_ARGB32:
			switch(type)
			{
				case 0:
					D(bug("ARGB32\n"));
					return 0xff0000;
				case 1:
					return 0xff00;
				case 2:
					return 0xff;
			}
		case PIXFMT_BGRA32:
			switch(type)
			{
				case 0:
					D(bug("BGRA32\n"));
					return 0xff00;
				case 1:
					return 0xff0000;
				case 2:
					return 0xff000000;
			}
		case PIXFMT_RGBA32:
			switch(type)
			{
				case 0:
					D(bug("RGBA32\n"));
					return 0xff000000;
				case 1:
					return 0xff0000;
				case 2:
					return 0xff00;
			}
		default:
			D(bug("Unknown pixel format! Default to 24bit\n"));
			return (Uint32) (255<<(type*8));
	}
	}
	else
	{
		D(if(type==0)bug("DIFFERENT from screen.\nAllocated screen format: "));

		switch(*bpp)
		{
			case 32:
				D(if(type==0) bug("RGBA32\n"));
				switch(type)
				{
					case 0:
						return 0xff000000;
					case 1:
						return 0xff0000;
					case 2:
						return 0xff00;
				}
				break;
			case 24:
use_truecolor:
				switch(type)
				{
					case 0:
						D(bug("RGB24\n"));
						return 0xff0000;
					case 1:
						return 0xff00;
					case 2:
						return 0xff;
				}
			case 16:
			case 15:
				D(if(type==0) bug("Not supported, switching to 24bit!\n"));
				*bpp=24;
				goto use_truecolor;
				break;
			default:
				D(if(type==0)bug("This is a chunky display\n"));
// For chunky display mask is always 0;
				return 0;
		}
	}
	return 0;
}

static int CGX_VideoInit(_THIS, SDL_PixelFormat *vformat)
{
	int i;
	struct Library *RTGBase;

	D(bug("VideoInit... Opening libraries\n"));

	if(!IntuitionBase) {
		if( !(IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",39L))) {
			SDL_SetError("Couldn't open intuition V39+");
			return -1;
		}
	}

	if(!GfxBase) {
		if( !(GfxBase=(struct GfxBase *)OpenLibrary("graphics.library",39L))) {
			SDL_SetError("Couldn't open graphics V39+");
			return -1;
		}
	}

	if(!CyberGfxBase) {
		if( !(CyberGfxBase=OpenLibrary("cybergraphics.library",40L))) {
			SDL_SetError("Couldn't open cybergraphics.");
			return(-1);
		}
	}

	if(RTGBase=OpenLibrary("libs:picasso96/rtg.library",0L)) {
		extern int use_picasso96;

		CloseLibrary(RTGBase);
		use_picasso96=1;
	}

	D(bug("Library intialized, locking screen...\n"));

	SDL_Display = LockPubScreen(NULL);

	if ( SDL_Display == NULL ) {
		D(bug("Cannot lock display...\n"));
		SDL_SetError("Couldn't lock the display");
		return(-1);
	}

	D(bug("Checking if we are using a CGX native display...\n"));

	if(!IsCyberModeID(GetVPModeID(&SDL_Display->ViewPort)))
	{
		Uint32 okid=BestCModeIDTags(CYBRBIDTG_NominalWidth,SDL_Display->Width,
				CYBRBIDTG_NominalHeight,SDL_Display->Height,
				CYBRBIDTG_Depth,8,
				TAG_DONE);

		D(bug("Default visual is not CGX native!\n"));

		UnlockPubScreen(NULL,SDL_Display);

		GFX_Display=NULL;

		if(okid!=INVALID_ID)
		{
			GFX_Display=OpenScreenTags(NULL,
									SA_Width,SDL_Display->Width,
									SA_Height,SDL_Display->Height,
									SA_Depth,8,SA_Quiet,TRUE,
									SA_ShowTitle,FALSE,
									SA_DisplayID,okid,
									TAG_DONE);
		}

		if(!GFX_Display)
		{
			SDL_SetError("Unable to open a suited CGX display");
			return -1;
		}
		else SDL_Display=GFX_Display;

	}
	else GFX_Display = SDL_Display;


	/* See whether or not we need to swap pixels */

	swap_pixels = 0;

// Non e' detto che sia cosi' pero', alcune schede potrebbero gestire i modi in modo differente

	if ( SDL_BYTEORDER == SDL_LIL_ENDIAN ) {
		swap_pixels = 1;
	}

	D(bug("Before GetVideoModes....\n"));

	/* Get the available video modes */
	if(CGX_GetVideoModes(this) < 0)
	    return -1;

	/* Determine the default screen depth:
	   Use the default visual (or at least one with the same depth) */

	for(i = 0; i < this->hidden->nvisuals; i++)
	    if(this->hidden->visuals[i].depth == GetCyberMapAttr(SDL_Display->RastPort.BitMap,CYBRMATTR_DEPTH))
					break;
	if(i == this->hidden->nvisuals) {
	    /* default visual was useless, take the deepest one instead */
	    i = 0;
	}
	SDL_Visual = this->hidden->visuals[i].visual;

//	SDL_XColorMap = SDL_DisplayColormap;

	this->hidden->depth = this->hidden->visuals[i].depth;
	D(bug("Init: Setting screen depth to: %ld\n",this->hidden->depth));
	vformat->BitsPerPixel = this->hidden->visuals[i].depth; /* this->hidden->visuals[i].bpp; */

	{
		int form;
		APTR handle;
		struct DisplayInfo info;

		if(!(handle=FindDisplayInfo(this->hidden->visuals[i].visual)))
		{
			D(bug("Unable to get visual info...\n"));
			return -1;
		}

		if(!GetDisplayInfoData(handle,(char *)&info,sizeof(struct DisplayInfo),DTAG_DISP,NULL)) {
			D(bug("Unable to get visual info data...\n"));
			return -1;
		}

		form=GetCyberIDAttr(CYBRIDATTR_PIXFMT,SDL_Visual);

// In this case I use makebitmask in a way that I'm sure I'll get PIXFMT pixel mask

		if ( vformat->BitsPerPixel > 8 )
		{
			vformat->Rmask = MakeBitMask(this,0,form,&this->hidden->depth);
	  		vformat->Gmask = MakeBitMask(this,1,form,&this->hidden->depth);
		  	vformat->Bmask = MakeBitMask(this,2,form,&this->hidden->depth);
		}
	}

	/* See if we have been passed a window to use */
/*	SDL_windowid = getenv("SDL_WINDOWID"); */
	SDL_windowid=NULL;

	/* Create the blank cursor */
	SDL_BlankCursor = AllocMem(16,MEMF_CHIP|MEMF_CLEAR);

	/* Fill in some window manager capabilities */
	this->info.wm_available = 1;
	this->info.blit_hw = 1;
	this->info.blit_hw_CC = 1;
	this->info.blit_sw = 1;
	this->info.blit_fill = 1;
	this->info.video_mem=2000000; // Not always true but almost any Amiga card has this memory!

	this->hidden->same_format=0;
	SDL_RastPort=&SDL_Display->RastPort;
	/* We're done! */
	D(bug("End of CGX_VideoInit\n"));

	return(0);
}

void CGX_DestroyWindow(_THIS, SDL_Surface *screen)
{
	D(bug("Destroy Window...\n"));

	if ( ! SDL_windowid ) {
		/* Hide the managed window */
		int was_fullscreen=0;

		/* Clean up OpenGL */
		if ( screen ) {
		screen->flags &= ~(SDL_OPENGL|SDL_OPENGLBLIT);
		}

		if ( screen && (screen->flags & SDL_FULLSCREEN) ) {
			was_fullscreen=1;
			screen->flags &= ~SDL_FULLSCREEN;
//			CGX_LeaveFullScreen(this); tolto x crash
		}

		/* Destroy the output window */
		if ( SDL_Window ) {
			CloseWindow(SDL_Window);
			SDL_Window=NULL;
		}

		/* Free the colormap entries */
		if ( SDL_XPixels ) {
			int numcolors;
			unsigned long pixel;

			if(this->screen->format&&this->hidden->depth==8&&!was_fullscreen)
			{
				numcolors = 1<<this->screen->format->BitsPerPixel;

				if(numcolors>256)
					numcolors=256;

				if(!was_fullscreen&&this->hidden->depth==8)
				{
					for ( pixel=0; pixel<numcolors; pixel++ )
					{
						if(SDL_XPixels[pixel]>=0)
							ReleasePen(GFX_Display->ViewPort.ColorMap,SDL_XPixels[pixel]);
					}
				}
			}
			free(SDL_XPixels);
			SDL_XPixels = NULL;
		}
	}
}

static void CGX_SetSizeHints(_THIS, int w, int h, Uint32 flags)
{
	if ( flags & SDL_RESIZABLE ) {
		WindowLimits(SDL_Window, 32, 32,4096,4096);
	} else {
		WindowLimits(SDL_Window, w,h,w,h);
	}
	if ( flags & SDL_FULLSCREEN ) {
		flags&=~SDL_RESIZABLE;
	} else if ( getenv("SDL_VIDEO_CENTERED") ) {
		int display_w, display_h;

		display_w = SDL_Display->Width;
		display_h = SDL_Display->Height;
		ChangeWindowBox(SDL_Window,(display_w - w - SDL_Window->BorderLeft-SDL_Window->BorderRight)/2,
					(display_h - h - SDL_Window->BorderTop-SDL_Window->BorderBottom)/2,
					w+SDL_Window->BorderLeft+SDL_Window->BorderRight,
					h+SDL_Window->BorderTop+SDL_Window->BorderBottom);
	}
}

int CGX_CreateWindow(_THIS, SDL_Surface *screen,
			    int w, int h, int bpp, Uint32 flags)
{
#if 0
	int i, depth;
	Uint32 vis;
#endif
	D(bug("CGX_CreateWindow\n"));

	/* If a window is already present, destroy it and start fresh */
	if ( SDL_Window ) {
		CGX_DestroyWindow(this, screen);
	}

	/* See if we have been given a window id */
	if ( SDL_windowid ) {
		SDL_Window = (struct Window *)atol(SDL_windowid);
	} else {
		SDL_Window = 0;
	}

	/* find out which visual we are going to use */
#if 0
/* questo l'ho spostato nell'apertura dello schermo, in quanto su Amiga le finestre
   hanno il pixel mode degli schermi.
 */
	/*if ( flags & SDL_OPENGL ) {
		SDL_SetError("OpenGL not supported by the Amiga SDL!");
		return -1;
	}
	else {*/
		for ( i = 0; i < this->hidden->nvisuals; i++ ) {
			if ( this->hidden->visuals[i].depth == bpp ) /* era .depth */
				break;
		}
		if ( i == this->hidden->nvisuals ) {
			SDL_SetError("No matching visual for requested depth");
			return -1;	/* should never happen */
		}
		vis = this->hidden->visuals[i].visual;
		depth = this->hidden->visuals[i].depth;
//	}
	SDL_Visual = vis;
	this->hidden->depth = depth;
	D(bug("Setting screen depth to: %ld\n",this->hidden->depth));
#endif

	/* Allocate the new pixel format for this video mode */
	{
		Uint32 form;
		APTR handle;
		struct DisplayInfo info;

		if(!(handle=FindDisplayInfo(SDL_Visual)))
			return -1;

		if(!GetDisplayInfoData(handle,(char *)&info,sizeof(struct DisplayInfo),DTAG_DISP,NULL))
			return -1;

		form=GetCyberIDAttr(CYBRIDATTR_PIXFMT,SDL_Visual);

		if(flags&SDL_HWSURFACE)
		{
			if(bpp!=this->hidden->depth)
			{
				bpp=this->hidden->depth;
				D(bug("Accel forces bpp to be equal (%ld)\n",bpp));
			}
		}

		D(bug("BEFORE screen allocation: bpp:%ld (real:%ld)\n",bpp,this->hidden->depth));

/* With this call if needed I'll revert the wanted bpp to a bpp best suited for the display, actually occurs
   only with requested format 15/16bit and display format != 15/16bit
 */

		if ( ! SDL_ReallocFormat(screen, bpp,
				MakeBitMask(this,0,form,&bpp), MakeBitMask(this,1,form,&bpp), MakeBitMask(this,2,form,&bpp), 0) )
			return -1;

		D(bug("AFTER screen allocation: bpp:%ld (real:%ld)\n",bpp,this->hidden->depth));

	}

	/* Create the appropriate colormap */
/*
	if ( SDL_XColorMap != SDL_DisplayColormap ) {
		XFreeColormap(SDL_Display, SDL_XColorMap);
	}
*/
	if ( GetCyberMapAttr(SDL_Display->RastPort.BitMap,CYBRMATTR_PIXFMT)==PIXFMT_LUT8 || bpp==8 ) {
	    int ncolors,i;
	    D(bug("XPixels palette allocation...\n"));

	    /* Allocate the pixel flags */

	    if(bpp==8)
		ncolors=256;
	    else
		ncolors = 1 << screen->format->BitsPerPixel;

	    SDL_XPixels = (Sint32 *)malloc(ncolors * sizeof(Sint32));

	    if(SDL_XPixels == NULL) {
		SDL_OutOfMemory();
		return -1;
	    }


	    for(i=0;i<ncolors;i++)
		    SDL_XPixels[i]=-1;

	    /* always allocate a private colormap on non-default visuals */
	    if(bpp==8)
		flags |= SDL_HWPALETTE;

	    if ( flags & SDL_HWPALETTE )
			screen->flags |= SDL_HWPALETTE;
	} 

	/* resize the (possibly new) window manager window */

	/* Create (or use) the X11 display window */

	if ( !SDL_windowid ) {
			if( flags & SDL_FULLSCREEN )
			{
				SDL_Window = OpenWindowTags(NULL,WA_Width,w,WA_Height,h,
											WA_Flags,WFLG_ACTIVATE|WFLG_RMBTRAP|WFLG_BORDERLESS|WFLG_BACKDROP|WFLG_REPORTMOUSE,
											WA_IDCMP,IDCMP_RAWKEY|IDCMP_MOUSEBUTTONS|IDCMP_MOUSEMOVE,
											WA_CustomScreen,(ULONG)SDL_Display,
											TAG_DONE);

				D(bug("Opening backdrop window %ldx%ld on display %lx!\n",w,h,SDL_Display));
			}
			else
			{
				/* Create GimmeZeroZero window when OpenGL is used */
				unsigned long gzz = FALSE;
				if( flags & SDL_OPENGL ) {
					gzz = TRUE;
				}

				SDL_Window = OpenWindowTags(NULL,WA_InnerWidth,w,WA_InnerHeight,h,
											WA_Flags,WFLG_REPORTMOUSE|WFLG_ACTIVATE|WFLG_RMBTRAP | ((flags&SDL_NOFRAME) ? 0 : (WFLG_DEPTHGADGET|WFLG_CLOSEGADGET|WFLG_DRAGBAR | ((flags&SDL_RESIZABLE) ? WFLG_SIZEGADGET|WFLG_SIZEBBOTTOM : 0))),
											WA_IDCMP,IDCMP_RAWKEY|IDCMP_CLOSEWINDOW|IDCMP_MOUSEBUTTONS|IDCMP_NEWSIZE|IDCMP_MOUSEMOVE,
											WA_PubScreen,(ULONG)SDL_Display,
											WA_GimmeZeroZero, gzz,
														TAG_DONE);
				D(bug("Opening WB window of size: %ldx%ld!\n",w,h));
			}

		if(!SDL_Window)
			return -1;
	}

	this->hidden->BytesPerPixel=GetCyberMapAttr(SDL_Window->RPort->BitMap,CYBRMATTR_BPPIX);

	if(screen->flags & SDL_DOUBLEBUF)
	{
		if(SDL_RastPort=malloc(sizeof(struct RastPort)))
		{
			InitRastPort(SDL_RastPort);
			SDL_RastPort->BitMap=this->hidden->SB[1]->sb_BitMap;
		}
		else
			return -1;
	}
	else SDL_RastPort=SDL_Window->RPort;

	if(flags&SDL_HWSURFACE)
		screen->flags|=SDL_HWSURFACE;

	if( !SDL_windowid ) {
	    CGX_SetSizeHints(this, w, h, flags);
		current_w = w;
		current_h = h;
	}

	/* Set our colormaps when not setting a GL mode */
/*
	if ( ! (flags & SDL_OPENGL) ) {
		XSetWindowColormap(SDL_Display, SDL_Window, SDL_XColorMap);
	}
*/

	/* Map them both and go fullscreen, if requested */
	if ( ! SDL_windowid ) {
		if ( flags & SDL_FULLSCREEN ) {
			screen->flags |= SDL_FULLSCREEN;
			currently_fullscreen=1;
//			CGX_EnterFullScreen(this); Ci siamo gia'!
		} else {
			screen->flags &= ~SDL_FULLSCREEN;
		}
	}
	screen->w = w;
	screen->h = h;
	screen->pitch = SDL_CalculatePitch(screen);
	CGX_ResizeImage(this, screen, flags);

	/* Make OpenGL Context if needed*/
	if(flags & SDL_OPENGL) {
		if(this->gl_data->gl_active == 0) {
			if(CGX_GL_Init(this) < 0)
				return -1;
			else
				screen->flags |= SDL_OPENGL;
		}
		else {
			if(CGX_GL_Update(this) < 0)
				return -1;
			else
				screen->flags |= SDL_OPENGL;
		}
	}
}

int CGX_ResizeWindow(_THIS,
			SDL_Surface *screen, int w, int h, Uint32 flags)
{
	D(bug("CGX_ResizeWindow\n"));

	if ( ! SDL_windowid ) {
		/* Resize the window manager window */
		CGX_SetSizeHints(this, w, h, flags);
		current_w = w;
		current_h = h;

		ChangeWindowBox(SDL_Window,SDL_Window->LeftEdge,SDL_Window->TopEdge, w+SDL_Window->BorderLeft+SDL_Window->BorderRight,
					h+SDL_Window->BorderTop+SDL_Window->BorderBottom);

		screen->w = w;
		screen->h = h;
		screen->pitch = SDL_CalculatePitch(screen);
		CGX_ResizeImage(this, screen, flags);
	}
	return(0);
}

static SDL_Surface *CGX_SetVideoMode(_THIS, SDL_Surface *current,
				int width, int height, int bpp, Uint32 flags)
{
	Uint32 saved_flags;
	int needcreate=0;

	D(bug("CGX_SetVideoMode current:%lx\n",current));

	/* Lock the event thread, in multi-threading environments */
	SDL_Lock_EventThread();

// Check if the window needs to be closed or can be resized

	if( (flags&SDL_FULLSCREEN) || (current && current->flags&SDL_FULLSCREEN && !(flags&SDL_FULLSCREEN)))
		needcreate=1;

// Check if we need to close an already existing videomode...

	if(current && current->flags&SDL_FULLSCREEN && !(flags&SDL_FULLSCREEN)) {
		unsigned long i;
		D(bug("Destroying image, window & screen!\n"));

		CGX_DestroyImage(this,current);
		CGX_DestroyWindow(this,current);
		DestroyScreen(this);
		GFX_Display=SDL_Display=LockPubScreen(NULL);

		bpp=this->hidden->depth=GetCyberMapAttr(SDL_Display->RastPort.BitMap,CYBRMATTR_DEPTH);

		for ( i = 0; i < this->hidden->nvisuals; i++ ) {
			if ( this->hidden->visuals[i].depth == bpp ) /* era .depth */
				break;
		}
		if ( i == this->hidden->nvisuals ) {
			SDL_SetError("No matching visual for requested depth");
			return NULL;	/* should never happen */
		}
		SDL_Visual = this->hidden->visuals[i].visual;

		D(bug("Setting screen depth to: %ld\n",this->hidden->depth));

	}
	/* Check the combination of flags we were passed */
	if ( flags & SDL_FULLSCREEN ) {
		int i;

		/* Clear fullscreen flag if not supported */
		if ( SDL_windowid ) {
			flags &= ~SDL_FULLSCREEN;
		}
		else if(current && current->flags&SDL_FULLSCREEN ) {
			if(current->w!=width ||
				current->h!=height ||
				(this->hidden && this->hidden->depth!=bpp))
			{
				D(bug("Deleting previous window...\n"));
				CGX_DestroyImage(this,current);
				CGX_DestroyWindow(this,current);
				DestroyScreen(this);
				goto buildnewscreen;
			}
		}
		else
buildnewscreen:
		{
			Uint32 okid=BestCModeIDTags(CYBRBIDTG_NominalWidth,width,
				CYBRBIDTG_NominalHeight,height,
				CYBRBIDTG_Depth,bpp,
				TAG_DONE);

			GFX_Display=NULL;

			D(bug("Opening screen...\n"));

			if(okid!=INVALID_ID)
				GFX_Display=OpenScreenTags(NULL,
								SA_Width,width,
								SA_Height,height,
								SA_Quiet,TRUE,SA_ShowTitle,FALSE,
								SA_Depth,bpp,
								SA_DisplayID,okid,
								TAG_DONE);

			if(!GFX_Display) {
				GFX_Display=SDL_Display;
				flags &= ~SDL_FULLSCREEN;
				flags &= ~SDL_DOUBLEBUF;
			}
			else {
				UnlockPubScreen(NULL,SDL_Display);
				SDL_Display=GFX_Display;
	
				D(bug("Screen opened.\n"));

				if(flags&SDL_DOUBLEBUF) {
					int ok=0;
					D(bug("Start of DBuffering allocations...\n"));

					if(this->hidden->SB[0]=AllocScreenBuffer(SDL_Display,NULL,SB_SCREEN_BITMAP)) {

						if(this->hidden->SB[1]=AllocScreenBuffer(SDL_Display,NULL,0L)) {
							extern struct MsgPort *safeport,*dispport;

							safeport=CreateMsgPort();
							dispport=CreateMsgPort();

							if(!safeport || !dispport) {
								if(safeport) {
									DeleteMsgPort(safeport);
									safeport=NULL;
								}
								if(dispport) {
									DeleteMsgPort(dispport);
									dispport=NULL;
								}
								FreeScreenBuffer(SDL_Display,this->hidden->SB[0]);
								FreeScreenBuffer(SDL_Display,this->hidden->SB[1]);
							}
							else {
								extern ULONG safe_sigbit,disp_sigbit;
								int i;

								safe_sigbit=1L<< safeport->mp_SigBit;
								disp_sigbit=1L<< dispport->mp_SigBit;

								for(i=0;i<2;i++) {
									this->hidden->SB[i]->sb_DBufInfo->dbi_SafeMessage.mn_ReplyPort=safeport;
									this->hidden->SB[i]->sb_DBufInfo->dbi_DispMessage.mn_ReplyPort=dispport;
								}

								ok=1;
								D(bug("Dbuffering enabled!\n"));
								this->hidden->dbuffer=1;
								current->flags|=SDL_DOUBLEBUF;
							}
						}
						else {
							FreeScreenBuffer(SDL_Display,this->hidden->SB[1]);
							this->hidden->SB[0]=NULL;
						}
					}

					if(!ok)
						flags&=~SDL_DOUBLEBUF;
				}
			}

			if(GetCyberMapAttr(SDL_Display->RastPort.BitMap,CYBRMATTR_DEPTH)==bpp)
				this->hidden->same_format=1;
		}

		bpp=this->hidden->depth=GetCyberMapAttr(SDL_Display->RastPort.BitMap,CYBRMATTR_DEPTH);
		D(bug("Setting screen depth to: %ld\n",this->hidden->depth));

		for ( i = 0; i < this->hidden->nvisuals; i++ )
			if ( this->hidden->visuals[i].depth == bpp ) /* era .depth */
				break;

		if ( i == this->hidden->nvisuals ) {
			SDL_SetError("No matching visual for requested depth");
			return NULL;	/* should never happen */
		}
		SDL_Visual = this->hidden->visuals[i].visual;

	}

	/* Set up the X11 window */
	saved_flags = current->flags;

	if (SDL_Window && (saved_flags&SDL_OPENGL) == (flags&SDL_OPENGL)
	    && bpp == current->format->BitsPerPixel && !needcreate) {
		if (CGX_ResizeWindow(this, current, width, height, flags) < 0) {
			current = NULL;
			goto done;
		}
	} else {
		if (CGX_CreateWindow(this,current,width,height,bpp,flags) < 0) {
			current = NULL;
			goto done;
		}
	}

#if 0
	/* Set up the new mode framebuffer */
	if ( ((current->w != width) || (current->h != height)) ||
             ((saved_flags&SDL_OPENGL) != (flags&SDL_OPENGL)) ) {
		current->w = width;
		current->h = height;
		current->pitch = SDL_CalculatePitch(current);
		CGX_ResizeImage(this, current, flags);
	}
#endif

	current->flags |= (flags&SDL_RESIZABLE); // Resizable only if the user asked it

  done:
	/* Release the event thread */
	SDL_Unlock_EventThread();

	/* We're done! */
	return(current);
}

static int CGX_ToggleFullScreen(_THIS, int on)
{
	Uint32 event_thread;

	/* Don't switch if we don't own the window */
	if ( SDL_windowid ) {
		return(0);
	}

	/* Don't lock if we are the event thread */
	event_thread = SDL_EventThreadID();
	if ( event_thread && (SDL_ThreadID() == event_thread) ) {
		event_thread = 0;
	}
	if ( event_thread ) {
		SDL_Lock_EventThread();
	}
	if ( on ) {
		this->screen->flags |= SDL_FULLSCREEN;
		CGX_EnterFullScreen(this);
	} else {
		this->screen->flags &= ~SDL_FULLSCREEN;
		CGX_LeaveFullScreen(this);
	}

	CGX_RefreshDisplay(this);
	if ( event_thread ) {
		SDL_Unlock_EventThread();
	}

	SDL_ResetKeyboard();

	return(1);
}

static void SetSingleColor(Uint32 fmt, unsigned char r, unsigned char g, unsigned char b, unsigned char *c)
{
	switch(fmt)
	{
		case PIXFMT_BGR15:
		case PIXFMT_RGB15PC:
			{
				Uint16 *t=(Uint16 *)c;
				*t=(r>>3) | ((g>>3)<<5) | ((b>>3)<<10) ;
			}
			break;
		case PIXFMT_RGB15:
		case PIXFMT_BGR15PC:
			{
				Uint16 *t=(Uint16 *)c;
				*t=(b>>3) | ((g>>3)<<5) | ((r>>3)<<10) ;
			}
			break;
		case PIXFMT_BGR16PC:
		case PIXFMT_RGB16:
			{
				Uint16 *t=(Uint16 *)c;
				*t=(b>>3) | ((g>>2)<<5) | ((r>>3)<<11) ;
			}
			break;
		case PIXFMT_BGR16:
		case PIXFMT_RGB16PC:
			{
				Uint16 *t=(Uint16 *)c;
				*t=(r>>3) | ((g>>2)<<5) | ((b>>3)<<11) ;
			}
			break;
		case PIXFMT_RGB24:
			c[0]=r;
			c[1]=g;
			c[2]=b;
			c[3]=0;
			break;
		case PIXFMT_BGR24:
			c[0]=b;
			c[1]=g;
			c[2]=r;
			c[3]=0;
			break;
		case PIXFMT_ARGB32:
			c[0]=0;
			c[1]=r;
			c[2]=g;
			c[3]=b;
			break;
		case PIXFMT_BGRA32:
			c[0]=b;
			c[1]=g;
			c[2]=r;
			c[3]=0;
			break;
		case PIXFMT_RGBA32:
			c[0]=r;
			c[1]=g;
			c[2]=b;
			c[3]=0;
			break;

		default:
			D(bug("Error, SetSingleColor with PIXFMT %ld!\n",fmt));
	}
}

/* Update the current mouse state and position */
static void CGX_UpdateMouse(_THIS)
{
	/* Lock the event thread, in multi-threading environments */
	SDL_Lock_EventThread();

	if(currently_fullscreen)
	{
			SDL_PrivateAppActive(1, SDL_APPMOUSEFOCUS);
			SDL_PrivateMouseMotion(0, 0, SDL_Display->MouseX, SDL_Display->MouseY);
	}
	else
	{
		if(	SDL_Display->MouseX>=(SDL_Window->LeftEdge+SDL_Window->BorderLeft) && SDL_Display->MouseX<(SDL_Window->LeftEdge+SDL_Window->Width-SDL_Window->BorderRight) &&
			SDL_Display->MouseY>=(SDL_Window->TopEdge+SDL_Window->BorderLeft) && SDL_Display->MouseY<(SDL_Window->TopEdge+SDL_Window->Height-SDL_Window->BorderBottom)
			)
		{
			SDL_PrivateAppActive(1, SDL_APPMOUSEFOCUS);
			SDL_PrivateMouseMotion(0, 0, SDL_Display->MouseX-SDL_Window->LeftEdge-SDL_Window->BorderLeft,
										SDL_Display->MouseY-SDL_Window->TopEdge-SDL_Window->BorderTop);
		}
		else
		{
			SDL_PrivateAppActive(0, SDL_APPMOUSEFOCUS);
		}
	}
	SDL_Unlock_EventThread();
}

static int CGX_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors)
{
	int      i;

	/* Check to make sure we have a colormap allocated */

	/* It's easy if we have a hidden colormap */
	if ( (this->screen->flags & SDL_HWPALETTE) && currently_fullscreen )
	{
		ULONG  xcmap[256*3+2];

		xcmap[0]=(ncolors<<16);
		xcmap[0]+=firstcolor;

//		D(bug("Setting %ld colors on an HWPALETTE screen\n",ncolors));

		for ( i=0; i<ncolors; i++ ) {
			xcmap[i*3+1] = colors[i+firstcolor].r<<24;
			xcmap[i*3+2] = colors[i+firstcolor].g<<24;
			xcmap[i*3+3] = colors[i+firstcolor].b<<24;
		}
		xcmap[ncolors*3+1]=0;
		LoadRGB32(&GFX_Display->ViewPort,xcmap);
	} else {
// XPixels are not needed on 8bit screen with hwpalette
		unsigned long pixel;

		if ( SDL_XPixels == NULL ) {
			D(bug("SetColors without colormap!"));
			return(0);
		}

		if(this->hidden->depth==8)
		{
// In this case I have to unalloc and realloc the full palette
			D(bug("Obtaining %ld colors on the screen\n",ncolors));

		/* Free existing allocated colors */
			for ( pixel=0; pixel<this->screen->format->palette->ncolors; ++pixel ) {
				if(SDL_XPixels[pixel]>=0)
					ReleasePen(GFX_Display->ViewPort.ColorMap,SDL_XPixels[pixel]);
			}

		/* Try to allocate all the colors */
			for ( i=0; i<this->screen->format->palette->ncolors; ++i ) {
				SDL_XPixels[i]=ObtainBestPenA(GFX_Display->ViewPort.ColorMap,colors[i].r<<24,colors[i].g<<24,colors[i].b<<24,NULL);
			}
		}
		else
		{
#ifndef USE_CGX_WRITELUTPIXEL
			Uint32 fmt;
			D(bug("Preparing a conversion pixel table...\n"));

			fmt=GetCyberMapAttr(SDL_Display->RastPort.BitMap,CYBRMATTR_PIXFMT);

			for(i=0;i<ncolors;i++)
			{
				SetSingleColor(fmt,colors[firstcolor+i].r,colors[firstcolor+i].g,colors[firstcolor+i].b,(unsigned char *)&SDL_XPixels[firstcolor+i]);
			}
#else
//			D(bug("Executing XPixel(%lx) remapping: (from %ld, %ld colors) first: r%ld g%ld b%ld\n",SDL_XPixels,firstcolor,ncolors,colors[firstcolor].r,colors[firstcolor].g,colors[firstcolor].b));
			for(i=0;i<ncolors;i++)
				SDL_XPixels[i+firstcolor]=(colors[firstcolor+i].r<<16)+(colors[firstcolor+i].g<<8)+colors[firstcolor+i].b;
#endif
		}
	}

// Actually it cannot fail!

	return 1;
}

/* Note:  If we are terminated, this could be called in the middle of
   another SDL video routine -- notably UpdateRects.
*/
static void CGX_VideoQuit(_THIS)
{
	/* Shutdown everything that's still up */
	/* The event thread should be done, so we can touch SDL_Display */
	D(bug("CGX_VideoQuit\n"));

	if ( SDL_Display != NULL ) {
		/* Clean up OpenGL */
		if(this->gl_data->gl_active == 1) {
			CGX_GL_Quit(this);
		}
		/* Start shutting down the windows */
		D(bug("Destroying image...\n"));
		CGX_DestroyImage(this, this->screen);
		D(bug("Destroying window...\n"));
		CGX_DestroyWindow(this, this->screen);
// Otherwise SDL_VideoQuit will try to free it!
		SDL_VideoSurface=NULL;

		CGX_FreeVideoModes(this);

		/* Free that blank cursor */
		if ( SDL_BlankCursor != NULL ) {
			FreeMem(SDL_BlankCursor,16);
			SDL_BlankCursor = NULL;
		}

		/* Close the X11 graphics connection */
		this->hidden->same_format=0;

		D(bug("Destroying screen...\n"));

		if ( GFX_Display != NULL )
			DestroyScreen(this);

		/* Close the X11 display connection */
		SDL_Display = NULL;

		/* Unload GL library after X11 shuts down */
	}

	D(bug("Closing libraries...\n"));

	if( CyberGfxBase) {
		CloseLibrary(CyberGfxBase);
		CyberGfxBase=NULL;
	}

	if (IntuitionBase) {
		CloseLibrary((struct Library *)IntuitionBase);
		IntuitionBase=NULL;
	}
	if (GfxBase) {
		CloseLibrary((struct Library *)GfxBase);
		GfxBase=NULL;
	}

	if ( this->screen && (this->screen->flags & SDL_HWSURFACE) ) {
		/* Direct screen access, no memory buffer */
		this->screen->pixels = NULL;
	}
	D(bug("End of CGX_VideoQuit.\n"));

}

