1/*
2    SDL - Simple DirectMedia Layer
3    Copyright (C) 1997-2012 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@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
45void 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
74typedef 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;
79typedef recDeviceGamma * precDeviceGamma;
80
81typedef 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;
86typedef recSystemGamma * precSystemGamma;
87
88static 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
105static 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
125static 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
138static void DisposeGammaTable (Ptr pGamma)
139{
140	if (pGamma)
141		DisposePtr((Ptr) pGamma);									/* get rid of it */
142}
143
144static 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
166static 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
203static 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
249static 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
276static 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
285static 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
306static 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
396static Ptr systemGammaPtr;
397
398void Mac_QuitGamma(_THIS)
399{
400	if (systemGammaPtr)
401	{
402		RestoreSystemGammas(systemGammaPtr);
403		DisposeSystemGammas(&systemGammaPtr);
404	}
405}
406
407static unsigned char shiftedRamp[3 * 256];
408
409int 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
425int 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