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