1/*
2 * Copyright (c) 1998-2001 by The XFree86 Project, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * Except as contained in this notice, the name of the copyright holder(s)
23 * and author(s) shall not be used in advertising or otherwise to promote
24 * the sale, use or other dealings in this Software without prior written
25 * authorization from the copyright holder(s) and author(s).
26 */
27
28#ifdef HAVE_XORG_CONFIG_H
29#include <xorg-config.h>
30#endif
31
32#if defined(_XOPEN_SOURCE) || defined(sun) && defined(__SVR4)
33#include <math.h>
34#else
35#define _XOPEN_SOURCE   /* to get prototype for pow on some systems */
36#include <math.h>
37#undef _XOPEN_SOURCE
38#endif
39
40#include <X11/X.h>
41#include "misc.h"
42#include <X11/Xproto.h>
43#include "colormapst.h"
44#include "scrnintstr.h"
45
46#include "resource.h"
47
48#include "xf86.h"
49#include "xf86_OSproc.h"
50#include "xf86str.h"
51#include "micmap.h"
52#include "xf86Crtc.h"
53
54#ifdef XFreeXDGA
55#include <X11/extensions/xf86dgaproto.h>
56#include "dgaproc.h"
57#endif
58
59#include "xf86cmap.h"
60
61#define SCREEN_PROLOGUE(pScreen, field) ((pScreen)->field = \
62    ((CMapScreenPtr)dixLookupPrivate(&(pScreen)->devPrivates, CMapScreenKey))->field)
63#define SCREEN_EPILOGUE(pScreen, field, wrapper)\
64    ((pScreen)->field = wrapper)
65
66#define LOAD_PALETTE(pmap) \
67    ((pmap == GetInstalledmiColormap(pmap->pScreen)) && \
68     ((pScreenPriv->flags & CMAP_LOAD_EVEN_IF_OFFSCREEN) || \
69      xf86Screens[pmap->pScreen->myNum]->vtSema || pScreenPriv->isDGAmode))
70
71
72typedef struct _CMapLink {
73  ColormapPtr		cmap;
74  struct _CMapLink	*next;
75} CMapLink, *CMapLinkPtr;
76
77typedef struct {
78  ScrnInfoPtr			pScrn;
79  CloseScreenProcPtr		CloseScreen;
80  CreateColormapProcPtr 	CreateColormap;
81  DestroyColormapProcPtr	DestroyColormap;
82  InstallColormapProcPtr	InstallColormap;
83  StoreColorsProcPtr		StoreColors;
84  Bool				(*EnterVT)(int, int);
85  Bool				(*SwitchMode)(int, DisplayModePtr, int);
86  int				(*SetDGAMode)(int, int, DGADevicePtr);
87  xf86ChangeGammaProc		*ChangeGamma;
88  int				maxColors;
89  int				sigRGBbits;
90  int				gammaElements;
91  LOCO				*gamma;
92  int				*PreAllocIndices;
93  CMapLinkPtr			maps;
94  unsigned int			flags;
95  Bool				isDGAmode;
96} CMapScreenRec, *CMapScreenPtr;
97
98typedef struct {
99  int		numColors;
100  LOCO		*colors;
101  Bool		recalculate;
102  int		overscan;
103} CMapColormapRec, *CMapColormapPtr;
104
105static DevPrivateKeyRec CMapScreenKeyRec;
106#define CMapScreenKeyRegistered dixPrivateKeyRegistered(&CMapScreenKeyRec)
107#define CMapScreenKey (&CMapScreenKeyRec)
108static DevPrivateKeyRec CMapColormapKeyRec;
109#define CMapColormapKey (&CMapColormapKeyRec)
110
111static void CMapInstallColormap(ColormapPtr);
112static void CMapStoreColors(ColormapPtr, int, xColorItem *);
113static Bool CMapCloseScreen (int, ScreenPtr);
114static Bool CMapCreateColormap (ColormapPtr);
115static void CMapDestroyColormap (ColormapPtr);
116
117static Bool CMapEnterVT(int, int);
118static Bool CMapSwitchMode(int, DisplayModePtr, int);
119#ifdef XFreeXDGA
120static int  CMapSetDGAMode(int, int, DGADevicePtr);
121#endif
122static int  CMapChangeGamma(int, Gamma);
123
124static void ComputeGamma(CMapScreenPtr);
125static Bool CMapAllocateColormapPrivate(ColormapPtr);
126static void CMapRefreshColors(ColormapPtr, int, int*);
127static void CMapSetOverscan(ColormapPtr, int, int *);
128static void CMapReinstallMap(ColormapPtr);
129static void CMapUnwrapScreen(ScreenPtr pScreen);
130
131
132Bool xf86ColormapAllocatePrivates(ScrnInfoPtr pScrn)
133{
134    /* If we support a better colormap system, then pretend we succeeded. */
135    if (xf86_crtc_supports_gamma(pScrn))
136	return TRUE;
137    if (!dixRegisterPrivateKey(&CMapScreenKeyRec, PRIVATE_SCREEN, 0))
138	return FALSE;
139
140    if (!dixRegisterPrivateKey(&CMapColormapKeyRec, PRIVATE_COLORMAP, 0))
141	return FALSE;
142    return TRUE;
143}
144
145Bool xf86HandleColormaps(
146    ScreenPtr pScreen,
147    int maxColors,
148    int sigRGBbits,
149    xf86LoadPaletteProc *loadPalette,
150    xf86SetOverscanProc *setOverscan,
151    unsigned int flags
152){
153    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
154    ColormapPtr pDefMap = NULL;
155    CMapScreenPtr pScreenPriv;
156    LOCO *gamma;
157    int *indices;
158    int elements;
159
160    /* If we support a better colormap system, then pretend we succeeded. */
161    if (xf86_crtc_supports_gamma(pScrn))
162	return TRUE;
163
164    if(!maxColors || !sigRGBbits || !loadPalette)
165	return FALSE;
166
167    elements = 1 << sigRGBbits;
168
169    if(!(gamma = malloc(elements * sizeof(LOCO))))
170    	return FALSE;
171
172    if(!(indices = malloc(maxColors * sizeof(int)))) {
173	free(gamma);
174	return FALSE;
175    }
176
177    if(!(pScreenPriv = malloc(sizeof(CMapScreenRec)))) {
178	free(gamma);
179	free(indices);
180	return FALSE;
181    }
182
183    dixSetPrivate(&pScreen->devPrivates, &CMapScreenKeyRec, pScreenPriv);
184
185    pScreenPriv->CloseScreen = pScreen->CloseScreen;
186    pScreenPriv->CreateColormap = pScreen->CreateColormap;
187    pScreenPriv->DestroyColormap = pScreen->DestroyColormap;
188    pScreenPriv->InstallColormap = pScreen->InstallColormap;
189    pScreenPriv->StoreColors = pScreen->StoreColors;
190    pScreen->CloseScreen = CMapCloseScreen;
191    pScreen->CreateColormap = CMapCreateColormap;
192    pScreen->DestroyColormap = CMapDestroyColormap;
193    pScreen->InstallColormap = CMapInstallColormap;
194    pScreen->StoreColors = CMapStoreColors;
195
196    pScreenPriv->pScrn = pScrn;
197    pScrn->LoadPalette = loadPalette;
198    pScrn->SetOverscan = setOverscan;
199    pScreenPriv->maxColors = maxColors;
200    pScreenPriv->sigRGBbits = sigRGBbits;
201    pScreenPriv->gammaElements = elements;
202    pScreenPriv->gamma = gamma;
203    pScreenPriv->PreAllocIndices = indices;
204    pScreenPriv->maps = NULL;
205    pScreenPriv->flags = flags;
206    pScreenPriv->isDGAmode = FALSE;
207
208    pScreenPriv->EnterVT = pScrn->EnterVT;
209    pScreenPriv->SwitchMode = pScrn->SwitchMode;
210    pScreenPriv->SetDGAMode = pScrn->SetDGAMode;
211    pScreenPriv->ChangeGamma = pScrn->ChangeGamma;
212
213    if (!(flags & CMAP_LOAD_EVEN_IF_OFFSCREEN)) {
214	pScrn->EnterVT = CMapEnterVT;
215	if ((flags & CMAP_RELOAD_ON_MODE_SWITCH) && pScrn->SwitchMode)
216	    pScrn->SwitchMode = CMapSwitchMode;
217    }
218#ifdef XFreeXDGA
219    pScrn->SetDGAMode = CMapSetDGAMode;
220#endif
221    pScrn->ChangeGamma = CMapChangeGamma;
222
223    ComputeGamma(pScreenPriv);
224
225    /* get the default map */
226    dixLookupResourceByType((pointer *)&pDefMap, pScreen->defColormap,
227			    RT_COLORMAP, serverClient, DixInstallAccess);
228
229    if(!CMapAllocateColormapPrivate(pDefMap)) {
230        CMapUnwrapScreen(pScreen);
231	return FALSE;
232    }
233
234    /* Force the initial map to be loaded */
235    SetInstalledmiColormap(pScreen, NULL);
236    CMapInstallColormap(pDefMap);
237    return TRUE;
238}
239
240
241/**** Screen functions ****/
242
243
244static Bool
245CMapCloseScreen (int i, ScreenPtr pScreen)
246{
247    CMapUnwrapScreen(pScreen);
248
249    return (*pScreen->CloseScreen) (i, pScreen);
250}
251
252static Bool
253CMapColormapUseMax(VisualPtr pVisual, CMapScreenPtr pScreenPriv)
254{
255    if (pVisual->nplanes > 16)
256        return TRUE;
257    return ((1 << pVisual->nplanes) > pScreenPriv->maxColors);
258}
259
260static Bool
261CMapAllocateColormapPrivate(ColormapPtr pmap)
262{
263    CMapScreenPtr pScreenPriv = (CMapScreenPtr)dixLookupPrivate(
264	&pmap->pScreen->devPrivates, CMapScreenKey);
265    CMapColormapPtr pColPriv;
266    CMapLinkPtr pLink;
267    int numColors;
268    LOCO *colors;
269
270    if (CMapColormapUseMax(pmap->pVisual, pScreenPriv))
271	numColors = pmap->pVisual->ColormapEntries;
272    else
273	numColors = 1 << pmap->pVisual->nplanes;
274
275    if(!(colors = malloc(numColors * sizeof(LOCO))))
276	return FALSE;
277
278    if(!(pColPriv = malloc(sizeof(CMapColormapRec)))) {
279	free(colors);
280	return FALSE;
281    }
282
283    dixSetPrivate(&pmap->devPrivates, CMapColormapKey, pColPriv);
284
285    pColPriv->numColors = numColors;
286    pColPriv->colors = colors;
287    pColPriv->recalculate = TRUE;
288    pColPriv->overscan = -1;
289
290    /* add map to list */
291    pLink = malloc(sizeof(CMapLink));
292    if(pLink) {
293	pLink->cmap = pmap;
294	pLink->next = pScreenPriv->maps;
295	pScreenPriv->maps = pLink;
296    }
297
298    return TRUE;
299}
300
301static Bool
302CMapCreateColormap (ColormapPtr pmap)
303{
304    ScreenPtr pScreen = pmap->pScreen;
305    CMapScreenPtr pScreenPriv = (CMapScreenPtr)dixLookupPrivate(
306	&pScreen->devPrivates, CMapScreenKey);
307    Bool ret = FALSE;
308
309    pScreen->CreateColormap = pScreenPriv->CreateColormap;
310    if((*pScreen->CreateColormap)(pmap)) {
311	if(CMapAllocateColormapPrivate(pmap))
312	   ret = TRUE;
313    }
314    pScreen->CreateColormap = CMapCreateColormap;
315
316    return ret;
317}
318
319static void
320CMapDestroyColormap (ColormapPtr cmap)
321{
322    ScreenPtr pScreen = cmap->pScreen;
323    CMapScreenPtr pScreenPriv = (CMapScreenPtr)dixLookupPrivate(
324	&pScreen->devPrivates, CMapScreenKey);
325    CMapColormapPtr pColPriv = (CMapColormapPtr)dixLookupPrivate(
326	&cmap->devPrivates, CMapColormapKey);
327    CMapLinkPtr prevLink = NULL, pLink = pScreenPriv->maps;
328
329    if(pColPriv) {
330	free(pColPriv->colors);
331	free(pColPriv);
332    }
333
334    /* remove map from list */
335    while(pLink) {
336	if(pLink->cmap == cmap) {
337	   if(prevLink)
338		prevLink->next = pLink->next;
339	   else
340		pScreenPriv->maps = pLink->next;
341	   free(pLink);
342	   break;
343	}
344	prevLink = pLink;
345	pLink = pLink->next;
346    }
347
348    if(pScreenPriv->DestroyColormap) {
349    	pScreen->DestroyColormap = pScreenPriv->DestroyColormap;
350	(*pScreen->DestroyColormap)(cmap);
351    	pScreen->DestroyColormap = CMapDestroyColormap;
352    }
353}
354
355
356
357static void
358CMapStoreColors(
359     ColormapPtr	pmap,
360     int		ndef,
361     xColorItem	        *pdefs
362){
363    ScreenPtr 	pScreen = pmap->pScreen;
364    VisualPtr	pVisual = pmap->pVisual;
365    CMapScreenPtr pScreenPriv = (CMapScreenPtr)dixLookupPrivate(
366	&pScreen->devPrivates, CMapScreenKey);
367    int 	*indices = pScreenPriv->PreAllocIndices;
368    int		num = ndef;
369
370    /* At the moment this isn't necessary since there's nobody below us */
371    pScreen->StoreColors = pScreenPriv->StoreColors;
372    (*pScreen->StoreColors)(pmap, ndef, pdefs);
373    pScreen->StoreColors = CMapStoreColors;
374
375    /* should never get here for these */
376    if(	(pVisual->class == TrueColor) ||
377	(pVisual->class == StaticColor) ||
378	(pVisual->class == StaticGray))
379	return;
380
381    if(pVisual->class == DirectColor) {
382	CMapColormapPtr pColPriv = (CMapColormapPtr)dixLookupPrivate(
383	    &pmap->devPrivates, CMapColormapKey);
384	int i;
385
386	if (CMapColormapUseMax(pVisual, pScreenPriv)) {
387	    int index;
388
389	    num = 0;
390	    while(ndef--) {
391		if(pdefs[ndef].flags & DoRed) {
392		    index = (pdefs[ndef].pixel & pVisual->redMask) >>
393					pVisual->offsetRed;
394		    i = num;
395		    while(i--)
396			if(indices[i] == index) break;
397		    if(i == -1)
398			indices[num++] = index;
399		}
400		if(pdefs[ndef].flags & DoGreen) {
401		    index = (pdefs[ndef].pixel & pVisual->greenMask) >>
402					pVisual->offsetGreen;
403		    i = num;
404		    while(i--)
405			if(indices[i] == index) break;
406		    if(i == -1)
407			indices[num++] = index;
408		}
409		if(pdefs[ndef].flags & DoBlue) {
410		    index = (pdefs[ndef].pixel & pVisual->blueMask) >>
411					pVisual->offsetBlue;
412		    i = num;
413		    while(i--)
414			if(indices[i] == index) break;
415		    if(i == -1)
416			indices[num++] = index;
417		}
418	    }
419
420	} else {
421	    /* not really as overkill as it seems */
422	    num = pColPriv->numColors;
423	    for(i = 0; i < pColPriv->numColors; i++)
424		indices[i] = i;
425	}
426    } else {
427	while(ndef--)
428	   indices[ndef] = pdefs[ndef].pixel;
429    }
430
431    CMapRefreshColors(pmap, num, indices);
432}
433
434
435static void
436CMapInstallColormap(ColormapPtr pmap)
437{
438    ScreenPtr 	  pScreen = pmap->pScreen;
439    CMapScreenPtr pScreenPriv = (CMapScreenPtr)dixLookupPrivate(
440	&pScreen->devPrivates, CMapScreenKey);
441
442    if (pmap == GetInstalledmiColormap(pmap->pScreen))
443	return;
444
445    pScreen->InstallColormap = pScreenPriv->InstallColormap;
446    (*pScreen->InstallColormap)(pmap);
447    pScreen->InstallColormap = CMapInstallColormap;
448
449    /* Important. We let the lower layers, namely DGA,
450       overwrite the choice of Colormap to install */
451    if (GetInstalledmiColormap(pmap->pScreen))
452	pmap = GetInstalledmiColormap(pmap->pScreen);
453
454    if (!(pScreenPriv->flags & CMAP_PALETTED_TRUECOLOR) &&
455	 (pmap->pVisual->class == TrueColor) &&
456	 CMapColormapUseMax(pmap->pVisual, pScreenPriv))
457	return;
458
459    if(LOAD_PALETTE(pmap))
460	CMapReinstallMap(pmap);
461}
462
463
464/**** ScrnInfoRec functions ****/
465
466static Bool
467CMapEnterVT(int index, int flags)
468{
469    ScrnInfoPtr pScrn = xf86Screens[index];
470    ScreenPtr pScreen = screenInfo.screens[index];
471    Bool ret;
472    CMapScreenPtr pScreenPriv = (CMapScreenPtr)dixLookupPrivate(
473	&pScreen->devPrivates, CMapScreenKey);
474
475    pScrn->EnterVT = pScreenPriv->EnterVT;
476    ret = (*pScreenPriv->EnterVT)(index, flags);
477    pScreenPriv->EnterVT = pScrn->EnterVT;
478    pScrn->EnterVT = CMapEnterVT;
479    if(ret) {
480	if(GetInstalledmiColormap(pScreen))
481	    CMapReinstallMap(GetInstalledmiColormap(pScreen));
482	return TRUE;
483    }
484    return FALSE;
485}
486
487
488static Bool
489CMapSwitchMode(int index, DisplayModePtr mode, int flags)
490{
491    ScreenPtr pScreen = screenInfo.screens[index];
492    CMapScreenPtr pScreenPriv = (CMapScreenPtr)dixLookupPrivate(
493	&pScreen->devPrivates, CMapScreenKey);
494
495    if((*pScreenPriv->SwitchMode)(index, mode, flags)) {
496	if(GetInstalledmiColormap(pScreen))
497	    CMapReinstallMap(GetInstalledmiColormap(pScreen));
498	return TRUE;
499    }
500    return FALSE;
501}
502
503#ifdef XFreeXDGA
504static int
505CMapSetDGAMode(int index, int num, DGADevicePtr dev)
506{
507    ScreenPtr pScreen = screenInfo.screens[index];
508    CMapScreenPtr pScreenPriv = (CMapScreenPtr)dixLookupPrivate(
509	&pScreen->devPrivates, CMapScreenKey);
510    int ret;
511
512    ret = (*pScreenPriv->SetDGAMode)(index, num, dev);
513
514    pScreenPriv->isDGAmode = DGAActive(index);
515
516    if(!pScreenPriv->isDGAmode && GetInstalledmiColormap(pScreen)
517         && xf86Screens[pScreen->myNum]->vtSema)
518	CMapReinstallMap(GetInstalledmiColormap(pScreen));
519
520    return ret;
521}
522#endif
523
524
525/**** Utilities ****/
526
527static void
528CMapReinstallMap(ColormapPtr pmap)
529{
530    CMapScreenPtr pScreenPriv = (CMapScreenPtr)dixLookupPrivate(
531	&pmap->pScreen->devPrivates, CMapScreenKey);
532    CMapColormapPtr cmapPriv = (CMapColormapPtr)dixLookupPrivate(
533	&pmap->devPrivates, CMapColormapKey);
534    ScrnInfoPtr pScrn = xf86Screens[pmap->pScreen->myNum];
535    int i = cmapPriv->numColors;
536    int *indices = pScreenPriv->PreAllocIndices;
537
538    while(i--)
539	indices[i] = i;
540
541    if(cmapPriv->recalculate)
542	CMapRefreshColors(pmap, cmapPriv->numColors, indices);
543    else {
544	(*pScrn->LoadPalette)(pScrn, cmapPriv->numColors,
545 			indices, cmapPriv->colors, pmap->pVisual);
546	if (pScrn->SetOverscan) {
547#ifdef DEBUGOVERSCAN
548	    ErrorF("SetOverscan() called from CMapReinstallMap\n");
549#endif
550	    pScrn->SetOverscan(pScrn, cmapPriv->overscan);
551	}
552    }
553
554    cmapPriv->recalculate = FALSE;
555}
556
557
558static void
559CMapRefreshColors(ColormapPtr pmap, int defs, int* indices)
560{
561    CMapScreenPtr pScreenPriv = (CMapScreenPtr)dixLookupPrivate(
562	&pmap->pScreen->devPrivates, CMapScreenKey);
563    CMapColormapPtr pColPriv = (CMapColormapPtr)dixLookupPrivate(
564	&pmap->devPrivates, CMapColormapKey);
565    VisualPtr pVisual = pmap->pVisual;
566    ScrnInfoPtr pScrn = xf86Screens[pmap->pScreen->myNum];
567    int numColors, i;
568    LOCO *gamma, *colors;
569    EntryPtr entry;
570    int reds, greens, blues, maxValue, index, shift;
571
572    numColors = pColPriv->numColors;
573    shift = 16 - pScreenPriv->sigRGBbits;
574    maxValue = (1 << pScreenPriv->sigRGBbits) - 1;
575    gamma = pScreenPriv->gamma;
576    colors = pColPriv->colors;
577
578    reds = pVisual->redMask >> pVisual->offsetRed;
579    greens = pVisual->greenMask >> pVisual->offsetGreen;
580    blues = pVisual->blueMask >> pVisual->offsetBlue;
581
582    switch(pVisual->class) {
583    case StaticGray:
584	for(i = 0; i < numColors; i++) {
585	    index = (i+1) * maxValue / numColors;
586	    colors[i].red   = gamma[index].red;
587	    colors[i].green = gamma[index].green;
588	    colors[i].blue  = gamma[index].blue;
589	}
590	break;
591    case TrueColor:
592        if (CMapColormapUseMax(pVisual, pScreenPriv)) {
593	    for(i = 0; i <= reds; i++)
594		colors[i].red   = gamma[i * maxValue / reds].red;
595	    for(i = 0; i <= greens; i++)
596		colors[i].green = gamma[i * maxValue / greens].green;
597	    for(i = 0; i <= blues; i++)
598		colors[i].blue  = gamma[i * maxValue / blues].blue;
599	    break;
600	}
601	for(i = 0; i < numColors; i++) {
602	    colors[i].red   = gamma[((i >> pVisual->offsetRed) & reds) *
603					maxValue / reds].red;
604	    colors[i].green = gamma[((i >> pVisual->offsetGreen) & greens) *
605					maxValue / greens].green;
606	    colors[i].blue  = gamma[((i >> pVisual->offsetBlue) & blues) *
607					maxValue / blues].blue;
608	}
609	break;
610    case StaticColor:
611    case PseudoColor:
612    case GrayScale:
613	for(i = 0; i < defs; i++) {
614	    index = indices[i];
615	    entry = (EntryPtr)&pmap->red[index];
616
617	    if(entry->fShared) {
618		colors[index].red =
619			gamma[entry->co.shco.red->color >> shift].red;
620		colors[index].green =
621			gamma[entry->co.shco.green->color >> shift].green;
622		colors[index].blue =
623			gamma[entry->co.shco.blue->color >> shift].blue;
624	    } else {
625		colors[index].red   =
626				gamma[entry->co.local.red >> shift].red;
627		colors[index].green =
628				gamma[entry->co.local.green >> shift].green;
629		colors[index].blue  =
630				gamma[entry->co.local.blue >> shift].blue;
631	    }
632	}
633	break;
634    case DirectColor:
635        if (CMapColormapUseMax(pVisual, pScreenPriv)) {
636	    for(i = 0; i < defs; i++) {
637		index = indices[i];
638		if(index <= reds)
639		    colors[index].red   =
640			gamma[pmap->red[index].co.local.red >> shift].red;
641		if(index <= greens)
642		    colors[index].green =
643			gamma[pmap->green[index].co.local.green >> shift].green;
644		if(index <= blues)
645		    colors[index].blue   =
646			gamma[pmap->blue[index].co.local.blue >> shift].blue;
647
648	    }
649	    break;
650	}
651	for(i = 0; i < defs; i++) {
652	    index = indices[i];
653
654	    colors[index].red   = gamma[pmap->red[
655				(index >> pVisual->offsetRed) & reds
656				].co.local.red >> shift].red;
657	    colors[index].green = gamma[pmap->green[
658				(index >> pVisual->offsetGreen) & greens
659				].co.local.green >> shift].green;
660	    colors[index].blue  = gamma[pmap->blue[
661				(index >> pVisual->offsetBlue) & blues
662				].co.local.blue >> shift].blue;
663	}
664	break;
665    }
666
667
668    if(LOAD_PALETTE(pmap))
669	(*pScrn->LoadPalette)(pScreenPriv->pScrn, defs, indices,
670 					colors, pmap->pVisual);
671
672    if (pScrn->SetOverscan)
673	CMapSetOverscan(pmap, defs, indices);
674
675}
676
677static Bool
678CMapCompareColors(LOCO *color1, LOCO *color2)
679{
680    /* return TRUE if the color1 is "closer" to black than color2 */
681#ifdef DEBUGOVERSCAN
682    ErrorF("#%02x%02x%02x vs #%02x%02x%02x (%d vs %d)\n",
683	color1->red, color1->green, color1->blue,
684	color2->red, color2->green, color2->blue,
685	color1->red + color1->green + color1->blue,
686	color2->red + color2->green + color2->blue);
687#endif
688    return (color1->red + color1->green + color1->blue <
689	    color2->red + color2->green + color2->blue);
690}
691
692static void
693CMapSetOverscan(ColormapPtr pmap, int defs, int *indices)
694{
695    CMapScreenPtr pScreenPriv = (CMapScreenPtr)dixLookupPrivate(
696	&pmap->pScreen->devPrivates, CMapScreenKey);
697    CMapColormapPtr pColPriv = (CMapColormapPtr)dixLookupPrivate(
698	&pmap->devPrivates, CMapColormapKey);
699    ScrnInfoPtr pScrn = xf86Screens[pmap->pScreen->myNum];
700    VisualPtr pVisual = pmap->pVisual;
701    int i;
702    LOCO *colors;
703    int index;
704    Bool newOverscan = FALSE;
705    int overscan, tmpOverscan;
706
707    colors = pColPriv->colors;
708    overscan = pColPriv->overscan;
709
710    /*
711     * Search for a new overscan index in the following cases:
712     *
713     *   - The index hasn't yet been initialised.  In this case search
714     *     for an index that is black or a close match to black.
715     *
716     *   - The colour of the old index is changed.  In this case search
717     *     all indices for a black or close match to black.
718     *
719     *   - The colour of the old index wasn't black.  In this case only
720     *     search the indices that were changed for a better match to black.
721     */
722
723    switch (pVisual->class) {
724    case StaticGray:
725    case TrueColor:
726	/* Should only come here once.  Initialise the overscan index to 0 */
727	overscan = 0;
728	newOverscan = TRUE;
729	break;
730    case StaticColor:
731	/*
732         * Only come here once, but search for the overscan in the same way
733         * as for the other cases.
734	 */
735    case DirectColor:
736    case PseudoColor:
737    case GrayScale:
738	if (overscan < 0 || overscan > pScreenPriv->maxColors - 1) {
739	    /* Uninitialised */
740	    newOverscan = TRUE;
741	} else {
742	    /* Check if the overscan was changed */
743	    for (i = 0; i < defs; i++) {
744		index = indices[i];
745		if (index == overscan) {
746		    newOverscan = TRUE;
747		    break;
748		}
749	    }
750	}
751	if (newOverscan) {
752	    /* The overscan is either uninitialised or it has been changed */
753
754	    if (overscan < 0 || overscan > pScreenPriv->maxColors - 1)
755		tmpOverscan = pScreenPriv->maxColors - 1;
756	    else
757		tmpOverscan = overscan;
758
759	    /* search all entries for a close match to black */
760	    for (i = pScreenPriv->maxColors - 1; i >= 0; i--) {
761		if (colors[i].red == 0 && colors[i].green == 0 &&
762		    colors[i].blue == 0) {
763		    overscan = i;
764#ifdef DEBUGOVERSCAN
765		    ErrorF("Black found at index 0x%02x\n", i);
766#endif
767		    break;
768		} else {
769#ifdef DEBUGOVERSCAN
770		    ErrorF("0x%02x: ", i);
771#endif
772		    if (CMapCompareColors(&colors[i], &colors[tmpOverscan])) {
773			tmpOverscan = i;
774#ifdef DEBUGOVERSCAN
775			ErrorF("possible \"Black\" at index 0x%02x\n", i);
776#endif
777		    }
778		}
779	    }
780	    if (i < 0)
781		overscan = tmpOverscan;
782	} else {
783	    /* Check of the old overscan wasn't black */
784	    if (colors[overscan].red != 0 || colors[overscan].green != 0 ||
785		colors[overscan].blue != 0) {
786		int oldOverscan = tmpOverscan = overscan;
787		/* See of there is now a better match */
788		for (i = 0; i < defs; i++) {
789		    index = indices[i];
790		    if (colors[index].red == 0 && colors[index].green == 0 &&
791			colors[index].blue == 0) {
792			overscan = index;
793#ifdef DEBUGOVERSCAN
794			ErrorF("Black found at index 0x%02x\n", index);
795#endif
796			break;
797		    } else {
798#ifdef DEBUGOVERSCAN
799			ErrorF("0x%02x: ", index);
800#endif
801			if (CMapCompareColors(&colors[index],
802					      &colors[tmpOverscan])) {
803			    tmpOverscan = index;
804#ifdef DEBUGOVERSCAN
805			    ErrorF("possible \"Black\" at index 0x%02x\n",
806				   index);
807#endif
808			}
809		    }
810		}
811		if (i == defs)
812		    overscan = tmpOverscan;
813		if (overscan != oldOverscan)
814		    newOverscan = TRUE;
815	    }
816	}
817	break;
818    }
819    if (newOverscan) {
820	pColPriv->overscan = overscan;
821	if (LOAD_PALETTE(pmap)) {
822#ifdef DEBUGOVERSCAN
823	    ErrorF("SetOverscan() called from CmapSetOverscan\n");
824#endif
825	    pScrn->SetOverscan(pScreenPriv->pScrn, overscan);
826	}
827    }
828}
829
830static void
831CMapUnwrapScreen(ScreenPtr pScreen)
832{
833    CMapScreenPtr pScreenPriv = (CMapScreenPtr)dixLookupPrivate(
834	&pScreen->devPrivates, CMapScreenKey);
835    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
836
837    pScreen->CloseScreen = pScreenPriv->CloseScreen;
838    pScreen->CreateColormap = pScreenPriv->CreateColormap;
839    pScreen->DestroyColormap = pScreenPriv->DestroyColormap;
840    pScreen->InstallColormap = pScreenPriv->InstallColormap;
841    pScreen->StoreColors = pScreenPriv->StoreColors;
842
843    pScrn->EnterVT = pScreenPriv->EnterVT;
844    pScrn->SwitchMode = pScreenPriv->SwitchMode;
845    pScrn->SetDGAMode = pScreenPriv->SetDGAMode;
846    pScrn->ChangeGamma = pScreenPriv->ChangeGamma;
847
848    free(pScreenPriv->gamma);
849    free(pScreenPriv->PreAllocIndices);
850    free(pScreenPriv);
851}
852
853
854static void
855ComputeGamma(CMapScreenPtr priv)
856{
857    int elements = priv->gammaElements - 1;
858    double RedGamma, GreenGamma, BlueGamma;
859    int i;
860
861#ifndef DONT_CHECK_GAMMA
862    /* This check is to catch drivers that are not initialising pScrn->gamma */
863    if (priv->pScrn->gamma.red < GAMMA_MIN ||
864	priv->pScrn->gamma.red > GAMMA_MAX ||
865	priv->pScrn->gamma.green < GAMMA_MIN ||
866	priv->pScrn->gamma.green > GAMMA_MAX ||
867	priv->pScrn->gamma.blue < GAMMA_MIN ||
868	priv->pScrn->gamma.blue > GAMMA_MAX) {
869
870	xf86DrvMsgVerb(priv->pScrn->scrnIndex, X_WARNING, 0,
871	    "The %s driver didn't call xf86SetGamma() to initialise\n"
872	    "\tthe gamma values.\n", priv->pScrn->driverName);
873	xf86DrvMsgVerb(priv->pScrn->scrnIndex, X_WARNING, 0,
874	    "PLEASE FIX THE `%s' DRIVER!\n", priv->pScrn->driverName);
875	priv->pScrn->gamma.red = 1.0;
876	priv->pScrn->gamma.green = 1.0;
877	priv->pScrn->gamma.blue = 1.0;
878    }
879#endif
880
881    RedGamma = 1.0 / (double)priv->pScrn->gamma.red;
882    GreenGamma = 1.0 / (double)priv->pScrn->gamma.green;
883    BlueGamma = 1.0 / (double)priv->pScrn->gamma.blue;
884
885    for(i = 0; i <= elements; i++) {
886	if(RedGamma == 1.0)
887	    priv->gamma[i].red = i;
888	else
889	    priv->gamma[i].red = (CARD16)(pow((double)i/(double)elements,
890			RedGamma) * (double)elements + 0.5);
891
892	if(GreenGamma == 1.0)
893	    priv->gamma[i].green = i;
894	else
895	    priv->gamma[i].green = (CARD16)(pow((double)i/(double)elements,
896			GreenGamma) * (double)elements + 0.5);
897
898	if(BlueGamma == 1.0)
899	    priv->gamma[i].blue = i;
900	else
901	    priv->gamma[i].blue = (CARD16)(pow((double)i/(double)elements,
902			BlueGamma) * (double)elements + 0.5);
903    }
904}
905
906
907int
908CMapChangeGamma(
909   int index,
910   Gamma gamma
911){
912    int ret = Success;
913    ScrnInfoPtr pScrn = xf86Screens[index];
914    ScreenPtr pScreen = pScrn->pScreen;
915    CMapColormapPtr pColPriv;
916    CMapScreenPtr pScreenPriv;
917    CMapLinkPtr pLink;
918
919    /* Is this sufficient checking ? */
920    if(!CMapScreenKeyRegistered)
921	return BadImplementation;
922
923    pScreenPriv = (CMapScreenPtr)dixLookupPrivate(&pScreen->devPrivates,
924						  CMapScreenKey);
925    if(!pScreenPriv)
926	return BadImplementation;
927
928    if (gamma.red < GAMMA_MIN || gamma.red > GAMMA_MAX ||
929	gamma.green < GAMMA_MIN || gamma.green > GAMMA_MAX ||
930	gamma.blue < GAMMA_MIN || gamma.blue > GAMMA_MAX)
931	return BadValue;
932
933    pScrn->gamma.red = gamma.red;
934    pScrn->gamma.green = gamma.green;
935    pScrn->gamma.blue = gamma.blue;
936
937    ComputeGamma(pScreenPriv);
938
939    /* mark all colormaps on this screen */
940    pLink = pScreenPriv->maps;
941    while(pLink) {
942    	pColPriv = (CMapColormapPtr)dixLookupPrivate(&pLink->cmap->devPrivates,
943						     CMapColormapKey);
944	pColPriv->recalculate = TRUE;
945	pLink = pLink->next;
946    }
947
948    if(GetInstalledmiColormap(pScreen) &&
949       ((pScreenPriv->flags & CMAP_LOAD_EVEN_IF_OFFSCREEN) ||
950	pScrn->vtSema || pScreenPriv->isDGAmode)) {
951	ColormapPtr pMap = GetInstalledmiColormap(pScreen);
952
953	if (!(pScreenPriv->flags & CMAP_PALETTED_TRUECOLOR) &&
954	    (pMap->pVisual->class == TrueColor) &&
955	    CMapColormapUseMax(pMap->pVisual, pScreenPriv)) {
956
957	    /* if the current map doesn't have a palette look
958		for another map to change the gamma on. */
959
960	    pLink = pScreenPriv->maps;
961	    while(pLink) {
962		if(pLink->cmap->pVisual->class == PseudoColor)
963		    break;
964		pLink = pLink->next;
965	    }
966
967	    if(pLink) {
968		/* need to trick CMapRefreshColors() into thinking
969		   this is the currently installed map */
970		SetInstalledmiColormap(pScreen, pLink->cmap);
971		CMapReinstallMap(pLink->cmap);
972		SetInstalledmiColormap(pScreen, pMap);
973	    }
974	} else
975	    CMapReinstallMap(pMap);
976    }
977
978    pScrn->ChangeGamma = pScreenPriv->ChangeGamma;
979    if (pScrn->ChangeGamma)
980	ret = pScrn->ChangeGamma(index, gamma);
981    pScrn->ChangeGamma = CMapChangeGamma;
982
983    return ret;
984}
985
986
987static void
988ComputeGammaRamp (
989    CMapScreenPtr priv,
990    unsigned short *red,
991    unsigned short *green,
992    unsigned short *blue
993){
994    int elements = priv->gammaElements;
995    LOCO *entry = priv->gamma;
996    int shift = 16 - priv->sigRGBbits;
997
998    while(elements--) {
999	entry->red = *(red++) >> shift;
1000	entry->green = *(green++) >> shift;
1001	entry->blue = *(blue++) >> shift;
1002	entry++;
1003    }
1004}
1005
1006int
1007xf86ChangeGammaRamp(
1008   ScreenPtr pScreen,
1009   int size,
1010   unsigned short *red,
1011   unsigned short *green,
1012   unsigned short *blue
1013){
1014    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
1015    CMapColormapPtr pColPriv;
1016    CMapScreenPtr pScreenPriv;
1017    CMapLinkPtr pLink;
1018
1019    if (xf86_crtc_supports_gamma(pScrn)) {
1020	RRCrtcPtr crtc = xf86CompatRRCrtc(pScrn);
1021
1022	if (crtc) {
1023	    if (crtc->gammaSize != size)
1024		return BadValue;
1025
1026	    RRCrtcGammaSet(crtc, red, green, blue);
1027
1028	    return Success;
1029	}
1030    }
1031
1032    if(!CMapScreenKeyRegistered)
1033        return BadImplementation;
1034
1035    pScreenPriv = (CMapScreenPtr)dixLookupPrivate(&pScreen->devPrivates,
1036						  CMapScreenKey);
1037    if(!pScreenPriv)
1038        return BadImplementation;
1039
1040    if(pScreenPriv->gammaElements != size)
1041	return BadValue;
1042
1043    ComputeGammaRamp(pScreenPriv, red, green, blue);
1044
1045    /* mark all colormaps on this screen */
1046    pLink = pScreenPriv->maps;
1047    while(pLink) {
1048    	pColPriv = (CMapColormapPtr)dixLookupPrivate(&pLink->cmap->devPrivates,
1049						     CMapColormapKey);
1050        pColPriv->recalculate = TRUE;
1051        pLink = pLink->next;
1052    }
1053
1054    if(GetInstalledmiColormap(pScreen) &&
1055       ((pScreenPriv->flags & CMAP_LOAD_EVEN_IF_OFFSCREEN) ||
1056        pScrn->vtSema || pScreenPriv->isDGAmode)) {
1057        ColormapPtr pMap = GetInstalledmiColormap(pScreen);
1058
1059        if (!(pScreenPriv->flags & CMAP_PALETTED_TRUECOLOR) &&
1060            (pMap->pVisual->class == TrueColor) &&
1061	    CMapColormapUseMax(pMap->pVisual, pScreenPriv)) {
1062
1063            /* if the current map doesn't have a palette look
1064                for another map to change the gamma on. */
1065
1066            pLink = pScreenPriv->maps;
1067            while(pLink) {
1068                if(pLink->cmap->pVisual->class == PseudoColor)
1069                    break;
1070                pLink = pLink->next;
1071            }
1072
1073            if(pLink) {
1074                /* need to trick CMapRefreshColors() into thinking
1075                   this is the currently installed map */
1076                SetInstalledmiColormap(pScreen, pLink->cmap);
1077                CMapReinstallMap(pLink->cmap);
1078                SetInstalledmiColormap(pScreen, pMap);
1079            }
1080        } else
1081            CMapReinstallMap(pMap);
1082    }
1083
1084    return Success;
1085}
1086
1087int
1088xf86GetGammaRampSize(ScreenPtr pScreen)
1089{
1090    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
1091    CMapScreenPtr pScreenPriv;
1092
1093    if (xf86_crtc_supports_gamma(pScrn)) {
1094	RRCrtcPtr crtc = xf86CompatRRCrtc(pScrn);
1095
1096	if (crtc)
1097	    return crtc->gammaSize;
1098    }
1099
1100    if(!CMapScreenKeyRegistered) return 0;
1101
1102    pScreenPriv = (CMapScreenPtr)dixLookupPrivate(&pScreen->devPrivates,
1103						  CMapScreenKey);
1104    if(!pScreenPriv) return 0;
1105
1106    return pScreenPriv->gammaElements;
1107}
1108
1109int
1110xf86GetGammaRamp(
1111   ScreenPtr pScreen,
1112   int size,
1113   unsigned short *red,
1114   unsigned short *green,
1115   unsigned short *blue
1116){
1117    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
1118    CMapScreenPtr pScreenPriv;
1119    LOCO *entry;
1120    int shift, sigbits;
1121
1122    if (xf86_crtc_supports_gamma(pScrn)) {
1123	RRCrtcPtr crtc = xf86CompatRRCrtc(pScrn);
1124
1125	if (crtc) {
1126	    if (crtc->gammaSize < size)
1127		return BadValue;
1128
1129	    if (!RRCrtcGammaGet(crtc))
1130		return BadImplementation;
1131
1132	    memcpy(red, crtc->gammaRed, size * sizeof(*red));
1133	    memcpy(green, crtc->gammaGreen, size * sizeof(*green));
1134	    memcpy(blue, crtc->gammaBlue, size * sizeof(*blue));
1135
1136	    return Success;
1137	}
1138    }
1139
1140    if(!CMapScreenKeyRegistered)
1141	return BadImplementation;
1142
1143    pScreenPriv = (CMapScreenPtr)dixLookupPrivate(&pScreen->devPrivates,
1144						  CMapScreenKey);
1145    if(!pScreenPriv)
1146	return BadImplementation;
1147
1148    if(size > pScreenPriv->gammaElements)
1149	return BadValue;
1150
1151    entry = pScreenPriv->gamma;
1152    sigbits = pScreenPriv->sigRGBbits;
1153
1154    while(size--) {
1155	*red = entry->red << (16 - sigbits);
1156	*green = entry->green << (16 - sigbits);
1157	*blue = entry->blue << (16 - sigbits);
1158	shift = sigbits;
1159	while(shift < 16) {
1160	    *red |= *red >> shift;
1161	    *green |= *green >> shift;
1162	    *blue |= *blue >> shift;
1163	    shift += sigbits;
1164	}
1165	red++; green++; blue++;
1166        entry++;
1167    }
1168
1169    return Success;
1170}
1171
1172int
1173xf86ChangeGamma(
1174   ScreenPtr pScreen,
1175   Gamma gamma
1176){
1177    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
1178
1179    if(pScrn->ChangeGamma)
1180	return (*pScrn->ChangeGamma)(pScreen->myNum, gamma);
1181
1182    return BadImplementation;
1183}
1184