Home | History | Annotate | Download | only in maccommon
      1 /*
      2     SDL - Simple DirectMedia Layer
      3     Copyright (C) 1997-2006 Sam Lantinga
      4 
      5     This library is free software; you can redistribute it and/or
      6     modify it under the terms of the GNU Lesser General Public
      7     License as published by the Free Software Foundation; either
      8     version 2.1 of the License, or (at your option) any later version.
      9 
     10     This library is distributed in the hope that it will be useful,
     11     but WITHOUT ANY WARRANTY; without even the implied warranty of
     12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13     Lesser General Public License for more details.
     14 
     15     You should have received a copy of the GNU Lesser General Public
     16     License along with this library; if not, write to the Free Software
     17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     18 
     19     Sam Lantinga
     20     slouken (at) libsdl.org
     21 */
     22 #include "SDL_config.h"
     23 
     24 #if defined(__APPLE__) && defined(__MACH__)
     25 #include <Carbon/Carbon.h>
     26 #elif TARGET_API_MAC_CARBON && (UNIVERSAL_INTERFACES_VERSION > 0x0335)
     27 #include <Carbon.h>
     28 #else
     29 #include <Windows.h>
     30 #include <Strings.h>
     31 #endif
     32 
     33 #if SDL_MACCLASSIC_GAMMA_SUPPORT
     34 #include <Devices.h>
     35 #include <Files.h>
     36 #include <MacTypes.h>
     37 #include <QDOffscreen.h>
     38 #include <Quickdraw.h>
     39 #include <Video.h>
     40 #endif
     41 
     42 #include "SDL_stdinc.h"
     43 #include "SDL_macwm_c.h"
     44 
     45 void Mac_SetCaption(_THIS, const char *title, const char *icon)
     46 {
     47 	/* Don't convert C to P string in place, because it may be read-only */
     48 	Str255		ptitle; /* MJS */
     49 	ptitle[0] = strlen (title);
     50 	SDL_memcpy(ptitle+1, title, ptitle[0]); /* MJS */
     51 	if (SDL_Window)
     52 		SetWTitle(SDL_Window, ptitle); /* MJS */
     53 }
     54 
     55 #if SDL_MACCLASSIC_GAMMA_SUPPORT
     56 /*
     57  * ADC Gamma Ramp support...
     58  *
     59  * Mac Gamma Ramp code was originally from sample code provided by
     60  *  Apple Developer Connection, and not written specifically for SDL:
     61  * "Contains: Functions to enable Mac OS device gamma adjustments using 3 channel 256 element 8 bit gamma ramps
     62  *  Written by: Geoff Stahl (ggs)
     63  *  Copyright: Copyright (c) 1999 Apple Computer, Inc., All Rights Reserved
     64  *  Disclaimer: You may incorporate this sample code into your applications without
     65  *              restriction, though the sample code has been provided "AS IS" and the
     66  *              responsibility for its operation is 100% yours.  However, what you are
     67  *              not permitted to do is to redistribute the source as "DSC Sample Code"
     68  *              after having made changes. If you're going to re-distribute the source,
     69  *              we require that you make it clear in the source that the code was
     70  *              descended from Apple Sample Code, but that you've made changes."
     71  * (The sample code has been integrated into this file, and thus is modified from the original Apple sources.)
     72  */
     73 
     74 typedef struct recDeviceGamma											/* storage for device handle and gamma table */
     75 {
     76 	GDHandle hGD;												/* handle to device */
     77 	GammaTblPtr pDeviceGamma;									/* pointer to device gamma table */
     78 } recDeviceGamma;
     79 typedef recDeviceGamma * precDeviceGamma;
     80 
     81 typedef struct recSystemGamma											/* storage for system devices and gamma tables */
     82 {
     83 	short numDevices;											/* number of devices */
     84 	precDeviceGamma * devGamma;									/* array of pointers to device gamma records */
     85 } recSystemGamma;
     86 typedef recSystemGamma * precSystemGamma;
     87 
     88 static Ptr CopyGammaTable (GammaTblPtr pTableGammaIn)
     89 {
     90 	GammaTblPtr		pTableGammaOut = NULL;
     91 	short			tableSize, dataWidth;
     92 
     93 	if (pTableGammaIn)												/* if there is a table to copy  */
     94 	{
     95 		dataWidth = (pTableGammaIn->gDataWidth + 7) / 8;			/* number of bytes per entry */
     96 		tableSize = sizeof (GammaTbl) + pTableGammaIn->gFormulaSize +
     97 					(pTableGammaIn->gChanCnt * pTableGammaIn->gDataCnt * dataWidth);
     98 		pTableGammaOut = (GammaTblPtr) NewPtr (tableSize);			/* allocate new table */
     99 		if (pTableGammaOut)
    100 			BlockMove( (Ptr)pTableGammaIn, (Ptr)pTableGammaOut, tableSize);	/* move everything */
    101 	}
    102 	return (Ptr)pTableGammaOut;										/* return whatever we allocated, could be NULL */
    103 }
    104 
    105 static OSErr GetGammaTable (GDHandle hGD, GammaTblPtr * ppTableGammaOut)
    106 {
    107 	VDGammaRecord   DeviceGammaRec;
    108 	CntrlParam		cParam;
    109 	OSErr			err;
    110 
    111 	cParam.ioCompletion = NULL;										/* set up control params */
    112 	cParam.ioNamePtr = NULL;
    113 	cParam.ioVRefNum = 0;
    114 	cParam.ioCRefNum = (**hGD).gdRefNum;
    115 	cParam.csCode = cscGetGamma;									/* Get Gamma commnd to device */
    116 	*(Ptr *)cParam.csParam = (Ptr) &DeviceGammaRec;					/* record for gamma */
    117 
    118 	err = PBStatusSync( (ParmBlkPtr)&cParam );						/* get gamma */
    119 
    120 	*ppTableGammaOut = (GammaTblPtr)(DeviceGammaRec.csGTable);		/* pull table out of record */
    121 
    122 	return err;
    123 }
    124 
    125 static Ptr GetDeviceGamma (GDHandle hGD)
    126 {
    127 	GammaTblPtr		pTableGammaDevice = NULL;
    128 	GammaTblPtr		pTableGammaReturn = NULL;
    129 	OSErr			err;
    130 
    131 	err = GetGammaTable (hGD, &pTableGammaDevice);					/* get a pointer to the devices table */
    132 	if ((noErr == err) && pTableGammaDevice)						/* if succesful */
    133 		pTableGammaReturn = (GammaTblPtr) CopyGammaTable (pTableGammaDevice); /* copy to global */
    134 
    135 	return (Ptr) pTableGammaReturn;
    136 }
    137 
    138 static void DisposeGammaTable (Ptr pGamma)
    139 {
    140 	if (pGamma)
    141 		DisposePtr((Ptr) pGamma);									/* get rid of it */
    142 }
    143 
    144 static void DisposeSystemGammas (Ptr* ppSystemGammas)
    145 {
    146 	precSystemGamma pSysGammaIn;
    147 	if (ppSystemGammas)
    148 	{
    149 		pSysGammaIn = (precSystemGamma) *ppSystemGammas;
    150 		if (pSysGammaIn)
    151 		{
    152 			short i;
    153 			for (i = 0; i < pSysGammaIn->numDevices; i++)		/* for all devices */
    154 				if (pSysGammaIn->devGamma [i])						/* if pointer is valid */
    155 				{
    156 					DisposeGammaTable ((Ptr) pSysGammaIn->devGamma [i]->pDeviceGamma); /* dump gamma table */
    157 					DisposePtr ((Ptr) pSysGammaIn->devGamma [i]);					   /* dump device info */
    158 				}
    159 			DisposePtr ((Ptr) pSysGammaIn->devGamma);				/* dump device pointer array		 */
    160 			DisposePtr ((Ptr) pSysGammaIn);							/* dump system structure */
    161 			*ppSystemGammas = NULL;
    162 		}
    163 	}
    164 }
    165 
    166 static Boolean GetDeviceGammaRampGD (GDHandle hGD, Ptr pRamp)
    167 {
    168 	GammaTblPtr		pTableGammaTemp = NULL;
    169 	long 			indexChan, indexEntry;
    170 	OSErr			err;
    171 
    172 	if (pRamp)															/* ensure pRamp is allocated */
    173 	{
    174 		err = GetGammaTable (hGD, &pTableGammaTemp);					/* get a pointer to the current gamma */
    175 		if ((noErr == err) && pTableGammaTemp)							/* if successful */
    176 		{
    177 			/* fill ramp */
    178 			unsigned char * pEntry = (unsigned char *) &pTableGammaTemp->gFormulaData + pTableGammaTemp->gFormulaSize; /* base of table */
    179 			short bytesPerEntry = (pTableGammaTemp->gDataWidth + 7) / 8; /* size, in bytes, of the device table entries */
    180 			short shiftRightValue = pTableGammaTemp->gDataWidth - 8; 	 /* number of right shifts device -> ramp */
    181 			short channels = pTableGammaTemp->gChanCnt;
    182 			short entries = pTableGammaTemp->gDataCnt;
    183 			if (3 == channels)											/* RGB format */
    184 			{															/* note, this will create runs of entries if dest. is bigger (not linear interpolate) */
    185 				for (indexChan = 0; indexChan < channels; indexChan++)
    186 					for (indexEntry = 0; indexEntry < 256; indexEntry++)
    187 						*((unsigned char *) pRamp + (indexChan * 256) + indexEntry) =
    188 						  *(pEntry + indexChan * entries * bytesPerEntry + indexEntry * entries * bytesPerEntry / 256) >> shiftRightValue;
    189 			}
    190 			else														/* single channel format */
    191 			{
    192 				for (indexChan = 0; indexChan < 768; indexChan += 256)	/* repeat for all 3 channels (step by ramp size) */
    193 					for (indexEntry = 0; indexEntry < 256; indexEntry++) /* for all entries set vramp value */
    194 						*((unsigned char *) pRamp + indexChan + indexEntry) =
    195 						  *(pEntry + indexEntry * entries * bytesPerEntry / 256) >> shiftRightValue;
    196 			}
    197 			return true;
    198 		}
    199 	}
    200 	return false;
    201 }
    202 
    203 static Ptr GetSystemGammas (void)
    204 {
    205 	precSystemGamma pSysGammaOut;									/* return pointer to system device gamma info */
    206 	short devCount = 0;												/* number of devices attached */
    207 	Boolean fail = false;
    208 	GDHandle hGDevice;
    209 
    210 	pSysGammaOut = (precSystemGamma) NewPtr (sizeof (recSystemGamma)); /* allocate for structure */
    211 
    212 	hGDevice = GetDeviceList ();							/* top of device list */
    213 	do																/* iterate */
    214 	{
    215 		devCount++;													/* count devices					 */
    216 		hGDevice = GetNextDevice (hGDevice);						/* next device */
    217 	} while (hGDevice);
    218 
    219 	pSysGammaOut->devGamma = (precDeviceGamma *) NewPtr (sizeof (precDeviceGamma) * devCount); /* allocate for array of pointers to device records */
    220 	if (pSysGammaOut)
    221 	{
    222 		pSysGammaOut->numDevices = devCount;						/* stuff count */
    223 
    224 		devCount = 0;												/* reset iteration */
    225 		hGDevice = GetDeviceList ();
    226 		do
    227 		{
    228 			pSysGammaOut->devGamma [devCount] = (precDeviceGamma) NewPtr (sizeof (recDeviceGamma));	  /* new device record */
    229 			if (pSysGammaOut->devGamma [devCount])					/* if we actually allocated memory */
    230 			{
    231 				pSysGammaOut->devGamma [devCount]->hGD = hGDevice;										  /* stuff handle */
    232 				pSysGammaOut->devGamma [devCount]->pDeviceGamma = (GammaTblPtr)GetDeviceGamma (hGDevice); /* copy gamma table */
    233 			}
    234 			else													/* otherwise dump record on exit */
    235 			 fail = true;
    236 			devCount++;												/* next device */
    237 			hGDevice = GetNextDevice (hGDevice);
    238 		} while (hGDevice);
    239 	}
    240 	if (!fail)														/* if we did not fail */
    241 		return (Ptr) pSysGammaOut;									/* return pointer to structure */
    242 	else
    243 	{
    244 		DisposeSystemGammas ((Ptr *) &pSysGammaOut);					/* otherwise dump the current structures (dispose does error checking) */
    245 		return NULL;												/* could not complete */
    246 	}
    247 }
    248 
    249 static void RestoreDeviceGamma (GDHandle hGD, Ptr pGammaTable)
    250 {
    251 	VDSetEntryRecord setEntriesRec;
    252 	VDGammaRecord	gameRecRestore;
    253 	CTabHandle      hCTabDeviceColors;
    254 	Ptr				csPtr;
    255 	OSErr			err = noErr;
    256 
    257 	if (pGammaTable)												/* if we have a table to restore								 */
    258 	{
    259 		gameRecRestore.csGTable = pGammaTable;						/* setup restore record */
    260 		csPtr = (Ptr) &gameRecRestore;
    261 		err = Control((**hGD).gdRefNum, cscSetGamma, (Ptr) &csPtr);	/* restore gamma */
    262 
    263 		if ((noErr == err) && (8 == (**(**hGD).gdPMap).pixelSize))	/* if successful and on an 8 bit device */
    264 		{
    265 			hCTabDeviceColors = (**(**hGD).gdPMap).pmTable;			/* do SetEntries to force CLUT update */
    266 			setEntriesRec.csTable = (ColorSpec *) &(**hCTabDeviceColors).ctTable;
    267 			setEntriesRec.csStart = 0;
    268 			setEntriesRec.csCount = (**hCTabDeviceColors).ctSize;
    269 			csPtr = (Ptr) &setEntriesRec;
    270 
    271 			err = Control((**hGD).gdRefNum, cscSetEntries, (Ptr) &csPtr); /* SetEntries in CLUT */
    272 		}
    273 	}
    274 }
    275 
    276 static void RestoreSystemGammas (Ptr pSystemGammas)
    277 {
    278 	short i;
    279 	precSystemGamma pSysGammaIn = (precSystemGamma) pSystemGammas;
    280 	if (pSysGammaIn)
    281 		for (i = 0; i < pSysGammaIn->numDevices; i++)			/* for all devices */
    282 			RestoreDeviceGamma (pSysGammaIn->devGamma [i]->hGD, (Ptr) pSysGammaIn->devGamma [i]->pDeviceGamma);	/* restore gamma */
    283 }
    284 
    285 static Ptr CreateEmptyGammaTable (short channels, short entries, short bits)
    286 {
    287 	GammaTblPtr		pTableGammaOut = NULL;
    288 	short			tableSize, dataWidth;
    289 
    290 	dataWidth = (bits + 7) / 8;										/* number of bytes per entry */
    291 	tableSize = sizeof (GammaTbl) + (channels * entries * dataWidth);
    292 	pTableGammaOut = (GammaTblPtr) NewPtrClear (tableSize);			/* allocate new tabel */
    293 
    294 	if (pTableGammaOut)												/* if we successfully allocated */
    295 	{
    296 		pTableGammaOut->gVersion = 0;								/* set parameters based on input */
    297 		pTableGammaOut->gType = 0;
    298 		pTableGammaOut->gFormulaSize = 0;
    299 		pTableGammaOut->gChanCnt = channels;
    300 		pTableGammaOut->gDataCnt = entries;
    301 		pTableGammaOut->gDataWidth = bits;
    302 	}
    303 	return (Ptr)pTableGammaOut;										/* return whatever we allocated */
    304 }
    305 
    306 static Boolean SetDeviceGammaRampGD (GDHandle hGD, Ptr pRamp)
    307 {
    308 	VDSetEntryRecord setEntriesRec;
    309 	VDGammaRecord	gameRecRestore;
    310 	GammaTblPtr		pTableGammaNew;
    311 	GammaTblPtr		pTableGammaCurrent = NULL;
    312 	CTabHandle      hCTabDeviceColors;
    313 	Ptr				csPtr;
    314 	OSErr			err;
    315 	short 			dataBits, entries, channels = 3;						/* force three channels in the gamma table */
    316 
    317 	if (pRamp)																/* ensure pRamp is allocated */
    318 	{
    319 		err= GetGammaTable (hGD, &pTableGammaCurrent);						/* get pointer to current table */
    320 		if ((noErr == err) && pTableGammaCurrent)
    321 		{
    322 			dataBits = pTableGammaCurrent->gDataWidth;						/* table must have same data width */
    323 			entries = pTableGammaCurrent->gDataCnt;							/* table must be same size */
    324 			pTableGammaNew = (GammaTblPtr) CreateEmptyGammaTable (channels, entries, dataBits); /* our new table */
    325 			if (pTableGammaNew)												/* if successful fill table */
    326 			{
    327 				unsigned char * pGammaBase = (unsigned char *) &pTableGammaNew->gFormulaData + pTableGammaNew->gFormulaSize; /* base of table */
    328 				if ((256 == entries) && (8 == dataBits)) 						/* simple case: direct mapping */
    329 					BlockMove ((Ptr)pRamp, (Ptr)pGammaBase, channels * entries); /* move everything */
    330 				else														/* tough case handle entry, channel and data size disparities */
    331 				{
    332 					short indexChan, indexEntry;
    333 					short bytesPerEntry = (dataBits + 7) / 8; 				/* size, in bytes, of the device table entries */
    334 					short shiftRightValue = 8 - dataBits;					/* number of right shifts ramp -> device */
    335 					shiftRightValue += ((bytesPerEntry - 1) * 8);  			/* multibyte entries and the need to map a byte at a time most sig. to least sig. */
    336 					for (indexChan = 0; indexChan < channels; indexChan++) /* for all the channels */
    337 						for (indexEntry = 0; indexEntry < entries; indexEntry++) /* for all the entries */
    338 						{
    339 							short currentShift = shiftRightValue;			/* reset current bit shift */
    340 							long temp = *((unsigned char *)pRamp + (indexChan << 8) + (indexEntry << 8) / entries); /* get data from ramp */
    341 							short indexByte;
    342 							for (indexByte = 0; indexByte < bytesPerEntry; indexByte++) /* for all bytes */
    343 							{
    344 								if (currentShift < 0)						/* shift data correctly for current byte */
    345 									*(pGammaBase++) = temp << -currentShift;
    346 								else
    347 									*(pGammaBase++) = temp >> currentShift;
    348 								currentShift -= 8;							/* increment shift to align to next less sig. byte */
    349 							}
    350 						}
    351 				}
    352 
    353 				/* set gamma */
    354 				gameRecRestore.csGTable = (Ptr) pTableGammaNew;				/* setup restore record */
    355 				csPtr = (Ptr) &gameRecRestore;
    356 				err = Control((**hGD).gdRefNum, cscSetGamma, (Ptr) &csPtr);	/* restore gamma (note, display drivers may delay returning from this until VBL) */
    357 
    358 				if ((8 == (**(**hGD).gdPMap).pixelSize) && (noErr == err))	/* if successful and on an 8 bit device */
    359 				{
    360 					hCTabDeviceColors = (**(**hGD).gdPMap).pmTable;			/* do SetEntries to force CLUT update */
    361 					setEntriesRec.csTable = (ColorSpec *) &(**hCTabDeviceColors).ctTable;
    362 					setEntriesRec.csStart = 0;
    363 					setEntriesRec.csCount = (**hCTabDeviceColors).ctSize;
    364 					csPtr = (Ptr) &setEntriesRec;
    365 					err = Control((**hGD).gdRefNum, cscSetEntries, (Ptr) &csPtr);	/* SetEntries in CLUT */
    366 				}
    367 				DisposeGammaTable ((Ptr) pTableGammaNew);					/* dump table */
    368 				if (noErr == err)
    369 					return true;
    370 			}
    371 		}
    372 	}
    373 	else																	/* set NULL gamma -> results in linear map */
    374 	{
    375 		gameRecRestore.csGTable = (Ptr) NULL;								/* setup restore record */
    376 		csPtr = (Ptr) &gameRecRestore;
    377 		err = Control((**hGD).gdRefNum, cscSetGamma, (Ptr) &csPtr);			/* restore gamma */
    378 
    379 		if ((8 == (**(**hGD).gdPMap).pixelSize) && (noErr == err))			/* if successful and on an 8 bit device */
    380 		{
    381 			hCTabDeviceColors = (**(**hGD).gdPMap).pmTable;					/* do SetEntries to force CLUT update */
    382 			setEntriesRec.csTable = (ColorSpec *) &(**hCTabDeviceColors).ctTable;
    383 			setEntriesRec.csStart = 0;
    384 			setEntriesRec.csCount = (**hCTabDeviceColors).ctSize;
    385 			csPtr = (Ptr) &setEntriesRec;
    386 			err = Control((**hGD).gdRefNum, cscSetEntries, (Ptr) &csPtr);	/* SetEntries in CLUT */
    387 		}
    388 		if (noErr == err)
    389 			return true;
    390 	}
    391 	return false;															/* memory allocation or device control failed if we get here */
    392 }
    393 
    394 /* end of ADC Gamma Ramp support code... */
    395 
    396 static Ptr systemGammaPtr;
    397 
    398 void Mac_QuitGamma(_THIS)
    399 {
    400 	if (systemGammaPtr)
    401 	{
    402 		RestoreSystemGammas(systemGammaPtr);
    403 		DisposeSystemGammas(&systemGammaPtr);
    404 	}
    405 }
    406 
    407 static unsigned char shiftedRamp[3 * 256];
    408 
    409 int Mac_SetGammaRamp(_THIS, Uint16 *ramp)
    410 {
    411 	int i;
    412 	if (!systemGammaPtr)
    413 		systemGammaPtr = GetSystemGammas();
    414 	for (i = 0; i < 3 * 256; i++)
    415 	{
    416 		shiftedRamp[i] = ramp[i] >> 8;
    417 	}
    418 
    419 	if (SetDeviceGammaRampGD(GetMainDevice(), (Ptr) shiftedRamp))
    420 		return 0;
    421 	else
    422 		return -1;
    423 }
    424 
    425 int Mac_GetGammaRamp(_THIS, Uint16 *ramp)
    426 {
    427 	if (GetDeviceGammaRampGD(GetMainDevice(), (Ptr) shiftedRamp))
    428 	{
    429 		int i;
    430 		for (i = 0; i < 3 * 256; i++)
    431 		{
    432 			ramp[i] = shiftedRamp[i] << 8;
    433 		}
    434 		return 0;
    435 	}
    436 	else
    437 		return -1;
    438 }
    439 
    440 #endif  /* SDL_MACCLASSIC_GAMMA_SUPPORT */
    441 
    442 
    443