/******************************************************************************
FILE
    SqViewMegaPixel.m
DESCRIPTION
	NeXTstep user Interface for Squeak.
	This view is a subclass of SqView that map the whole NeXT MegaPixel Display
	screen. It is faster than SqView since it directly access the video buffer,
	but it's activated only in full screen mode.
AUTHOR
	<PJB> Pascal J. Bourguignon
MODIFICATIONS
	1998/07/03 <PJB> Creation.
LEGAL
	Copyright Pascal J. Bourguignon 1998 - 1998

	This program is free software; you can redistribute it and/or
	modify it under the terms of the version 2 of the GNU General Public 
	License as published by the Free Software Foundation.
	
	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 
	hereafter for more details.
******************************************************************************/
#import "SqViewMegaPixel.h"
#import "SqApplication.h"
#import <string.h>

#ifdef mc68000
#define SqViewMegaPixel_isPossible	
#else
#undef SqViewMegaPixel_isPossible	
#endif


#ifdef SqViewMegaPixel_isPossible
#import <bsd/dev/m68k/video.h>
#endif
#import <driverkit/displayDefs.h> // shamelessly reusing IODisplayInfo struct.
	

#define debug(a)


	
@implementation SqViewMegaPixel

	static IODisplayInfo*   displayInfo=0;
/*
	int             width;
	int             height;
	int             totalWidth;
	int             rowBytes; 
	int             refreshRate;   // unused here.
	void*           frameBuffer; 
	IOBitsPerPixel  bitsPerPixel;  // assumed to be IO_2BitsPerPixel.
	IOColorSpace    colorSpace;    // assumed to be IO_OneIsBlackColorSpace.
	IOPixelEncoding pixelEncoding; // assumed to be "KK".
	unsigned int    flags;         // unused here.
	void*           parameters;    // unused here.
*/


	+initialize
	{
#ifdef SqViewMegaPixel_isPossible
		static IODisplayInfo    displayInfoRec;
		int fd=open("/dev/vid0",O_RDWR,0);
		int vid_base=0;
		if(fd>=0){
			if(ioctl(fd,DKIOCGADDR,&vid_base)<0){
				vid_base=0;
			}
			close(fd); // May be we should not close it now?
		}
		if(vid_base==0){
			displayInfo=0;
		}else{
			displayInfoRec.width=VIDEO_W;
			displayInfoRec.height=VIDEO_H;
			/*
				We cannot use VIDEO_MW because dma_chip is only available
				to kernel code. We should do a loadable driver to access and
				publish it. May be these data are available thru the
				DKIOCGFBINFO ioctl, but then, it's not documented anywhere.
				That's the joys of proprietary software. LONG LIFE GNU & LINUX!
			*/
			displayInfoRec.totalWidth=VIDEO_W;
			displayInfoRec.rowBytes=VIDEO_W/NPPB;
			displayInfoRec.refreshRate=0;
			displayInfoRec.frameBuffer=(void*)vid_base;
			displayInfoRec.bitsPerPixel=IO_2BitsPerPixel;
			displayInfoRec.colorSpace=IO_OneIsWhiteColorSpace;
			strcpy(displayInfoRec.pixelEncoding,"KK");
			displayInfoRec.flags=0;
			displayInfoRec.parameters=0;
			displayInfo=&(displayInfoRec);
		}
#else
		displayInfo=0;
#endif
		return(self);
	}//initialize;
	

	
	+(BOOL)thereIsTheSlightestChanceOfHavingAMegaPixelDisplay
	{
#ifdef SqViewMegaPixel_isPossible
		/*
			Since there is no documented way to learn what kind of 
			buffer /dev/vid0:DKIOCGADDR will return, let's be extremely
			conservative.
		*/
		const NXScreen* screens;
		int             screen_count;
		
		if(displayInfo==0){
			return(NO);
		}
		[NXApp getScreens:&screens count:&screen_count];
		if(screen_count>1){
			// Ok, one of them may be a MegaPixel Display, but in doubt...
			return(NO);
		}
		if(screens[0].depth!=NX_TwoBitGrayDepth){
			return(NO);
		}
		if((screens[0].screenBounds.size.width!=displayInfo->width)
		||(screens[0].screenBounds.size.height!=displayInfo->height)){
			return(NO);
		}
		// Ok, let's reluctantly return YES.
		return(YES);
#else
		return(NO);
#endif
	}//thereIsTheSlightestChanceOfHavingAMegaPixelDisplay;
	


	-(id)initFrame:(const NXRect*)frameRect
	{
		self=[super initFrame:frameRect];
		if(self!=0){
			megaPixelIsActive=NO;
		}
		return(self);
	}//initFrame:;
	
	
	-(void)setColorEntry:(int)i red:(int)r green:(int)g blue:(int)b
	{
		[super setColorEntry:i red:r green:g blue:b];
		mp_colorMap2Gray[i]=3-colorMap2Gray[i];
	}//setColorEntry:red:green:blue:;
	
	
	-(void)initializeSqueakColorMap
	{
		mp_Two_to_Two=(unsigned char*)malloc(sizeof(unsigned char)*256);
		mp_colorMap2Gray=(unsigned char*)malloc(sizeof(unsigned char)*256);

		{
			unsigned char map[]={
				/*transparent:*/  0x01,
				/*black:*/        0x03,
				/*white:*/        0x00,
				/*half gray:*/    0x02};
			int i,j,k,l;
			for(i=0;i<4;i++){
				for(j=0;j<4;j++){
					for(k=0;k<4;k++){
						for(l=0;l<4;l++){
							mp_Two_to_Two[(i<<6)|(j<<4)|(k<<2)|l]=
								(map[i]<<6)|(map[j]<<4)|(map[k]<<2)|map[l];
						}
					}
				}
			}
		}

		[super initializeSqueakColorMap];
	}//initializeSqueakColorMap;
	
	
	-(id)free
	{
		return([super free]);
	}//free;
	

	-(void)redisplayAfterUnhide
	{
		if(megaPixelIsActive){
			[self updateWholeDisplay];
		}else{
			[super redisplayAfterUnhide];
		}
	}//redisplayAfterUnhide;
	
	
	-(int)ioForceDisplayUpdate
	{
		if(!megaPixelIsActive){
			return([super ioForceDisplayUpdate]);
		}
		[NXApp ioProcessEvents];
		return(0);
	}//ioForceDisplayUpdate;
	
	
	-(int)ioSetFullScreen:(int)fullScreen
	{
		[super ioSetFullScreen:fullScreen];
		if(displayInfo!=0){
			megaPixelIsActive=isFullScreen;
		}else{
			megaPixelIsActive=NO;
		}
		return(0);
	}//ioSetFullScreen:;
	


	-(void)copyPixels_1_to_2:(void*)srcBits
			srcPixelWidth:(int)srcPixelWidth
			left:(int)left right:(int)right 
			top:(int)top bottom:(int)bottom
	{
		debug(int dummy=fprintf(stderr,"%s\n",sel_getName(_cmd));\
			int fummy=fflush(stderr);)
		static unsigned char One_to_Two[16]={
			0x00,0x03,0x0c,0x0f,
			0x30,0x33,0x3c,0x3f,
			0xc0,0xc3,0xcc,0xcf,
			0xf0,0xf3,0xfc,0xff
		};
		unsigned char*      sBits=srcBits;
		unsigned char*      dBits=displayInfo->frameBuffer;
		int                 dRow;
		int                 sRowSlots;
		int                 minY;
		int                 maxY;
		int                 minX;
		int                 maxX;
		int                 srcY;
		
		if((srcPixelWidth%8)!=0){
			fprintf(stderr,"It would be better to have srcPixelWidth%%8==0 "
					"instead of %d.\n",srcPixelWidth);
			return;
		}
		
		sRowSlots=srcPixelWidth/8;
		left=left&(~7);
		right=(right+7)&(~7);
		
		minY=top*sRowSlots;
		maxY=bottom*sRowSlots;
		minX=left/8;
		maxX=right/8;
		
		PShidecursor();NXPing();
		dBits=dBits+(top*displayInfo->rowBytes)+(left/4); // assuming 2-bpp.
		dRow=0;
		for(srcY=minY;srcY<maxY;srcY+=sRowSlots){
			int maxXY=srcY+maxX;
			int srcX;
			int i=dRow;
			for(srcX=srcY+minX;srcX<maxXY;srcX++){
				unsigned char src=sBits[srcX];
				dBits[i++]=One_to_Two[src>>4];
				dBits[i++]=One_to_Two[src&0xf];
			}
			dRow+=displayInfo->rowBytes;
		}
		
		PSshowcursor();
	}//copyPixels_1_to_2:srcPixelWidth:left:right:top:bottom:;

	
	-(void)copyPixels_2_to_2:(void*)srcBits
			srcPixelWidth:(int)srcPixelWidth
			left:(int)left right:(int)right 
			top:(int)top bottom:(int)bottom
	{
		debug(int dummy=fprintf(stderr,"%s\n",sel_getName(_cmd));\
			int fummy=fflush(stderr);)
		unsigned char*      sBits=srcBits;
		unsigned char*      dBits=displayInfo->frameBuffer;
		int                 dRow;
		unsigned char*      map=mp_Two_to_Two;
		int                 sRowSlots;
		int                 minY;
		int                 maxY;
		int                 minX;
		int                 maxX;
		int                 srcY;
		
		if((srcPixelWidth%4)!=0){
			fprintf(stderr,"It would be better to have srcPixelWidth%%4==0 "
					"instead of %d.\n",srcPixelWidth);
			return;
		}
		
		sRowSlots=srcPixelWidth/4;
		left=left&(~3);
		right=(right+3)&(~3);


		minY=top*sRowSlots;
		maxY=bottom*sRowSlots;
		minX=left/4;
		maxX=right/4;
		
		PShidecursor();NXPing();
		dBits=dBits+(top*displayInfo->rowBytes)+(left/4); // assuming 2-bpp.
		dRow=0;
		for(srcY=minY;srcY<maxY;srcY+=sRowSlots){
			int maxXY=srcY+maxX;
			int srcX;
			int i=dRow;
			for(srcX=srcY+minX;srcX<maxXY;srcX++){
				dBits[i++]=map[sBits[srcX]];
			}
			dRow+=displayInfo->rowBytes;
		}
		PSshowcursor();
	}//copyPixels_2_to_2:srcPixelWidth:left:right:top:bottom:;

	
	-(void)copyPixels_8_to_2:(void*)srcBits
			srcPixelWidth:(int)srcPixelWidth
			left:(int)left right:(int)right 
			top:(int)top bottom:(int)bottom
	{
		debug(int dummy=fprintf(stderr,"%s\n",sel_getName(_cmd));\
			int fummy=fflush(stderr);)
		unsigned char*      sBits=srcBits;
		unsigned char*      dBits=displayInfo->frameBuffer;
		int                 dRow;
		unsigned char*      map=mp_colorMap2Gray;
		int                 sRowSlots;
		int                 minY;
		int                 maxY;
		int                 minX;
		int                 maxX;
		int                 srcY;
		
		sRowSlots=srcPixelWidth;
		left=left&(~3);
		right=(right+3)&(~3);

		minY=top*sRowSlots;
		maxY=bottom*sRowSlots;
		minX=left;
		maxX=right;
		
		PShidecursor();NXPing();
		dBits=dBits+(top*displayInfo->rowBytes)+(left/4); // assuming 2-bpp.
		dRow=0;
		for(srcY=minY;srcY<maxY;srcY+=sRowSlots){
			int maxXY=srcY+maxX;
			int srcX;
			int i=dRow;
			for(srcX=srcY+minX;srcX<maxXY;srcX+=4){
				dBits[i++]=(map[sBits[srcX]]<<6)
						|(map[sBits[srcX+1]]<<4)
						|(map[sBits[srcX+2]]<<2)
						|(map[sBits[srcX+3]]);
			}
			dRow+=displayInfo->rowBytes;
		}
		PSshowcursor();
	}//copyPixels_8_to_2:srcPixelWidth:left:right:top:bottom:;

	
	-(int)ioShowDisplayBitsIndex:(int)bInd 
			width:(int)w height:(int)h depth:(int)d 
			affectedLeft:(int)l affectedRight:(int)r 
			affectedTop:(int)t affectedBottom:(int)b
	{
		debug(int dummy=fprintf(stderr,"%s\n",sel_getName(_cmd));\
			int fummy=fflush(stderr);)
		debug(int dumm2=fprintf(stderr,"%22x%6d%7d%6d%13d%14d%12d%15d\n",\
			bitsIndex,width,height,depth,\
			left,right,top,bottom);)

		if(!megaPixelIsActive){
			return([super ioShowDisplayBitsIndex:bInd
					width:w height:h depth:d
					affectedLeft:l affectedRight:r
					affectedTop:t affectedBottom:b]);
		}
		
		if(![[self window]isVisible]){
			return(0);
		}
		
		if((r<=l)||(b<=t)){
			return(1);
		}
		
		lastBitsIndex=bInd;
		lastWidth=w;
		lastHeight=h;
		lastDepth=d;
		
		NX_DURING
			switch(d){
			case 1:
				[self copyPixels_1_to_2:(void*)bInd srcPixelWidth:w
						left:l right:r top:t bottom:b];
				break;
			case 2:
				[self copyPixels_2_to_2:(void*)bInd srcPixelWidth:w
						left:l right:r top:t bottom:b];
				break;
			case 8:
				[self copyPixels_8_to_2:(void*)bInd srcPixelWidth:w
						left:l right:r top:t bottom:b];
				break;
			default:
				[super ioShowDisplayBitsIndex:bInd
						width:w height:h depth:d
						affectedLeft:l affectedRight:r
						affectedTop:t affectedBottom:b];
			}
		NX_HANDLER
			fprintf(stderr,"An exception is caught in [%s %s]: "
					"code=%d data1=0x%x data2=0x%x\n",
					"SqViewMegaPixel",sel_getName(_cmd),NXLocalHandler.code,
					(unsigned int)NXLocalHandler.data1,
					(unsigned int)NXLocalHandler.data2);
		NX_ENDHANDLER

		return(0);
	}//ioShowDisplayBitsIndex:width:affectedLeft:affectedRight:...;

@end // SqViewMegaPixel.

/*** SqViewMegaPixel.m / Thu Aug 27 23:20:49 MET 1998 / PJB ***/

