1 /* context.c - X context management
3 * Raster graphics library
5 * Copyright (c) 1997-2002 Alfredo K. Kojima
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the Free
19 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include <X11/Xutil.h>
26 #include <X11/Xatom.h>
38 #include <Hermes/Hermes.h>
46 extern void _wraster_change_filter(int type
);
49 static Bool
bestContext(Display
*dpy
, int screen_number
, RContext
*context
);
51 static RContextAttributes DEFAULT_CONTEXT_ATTRIBS
= {
52 RC_UseSharedMemory
|RC_RenderMode
|RC_ColorsPerChannel
, /* flags */
53 RDitheredRendering
, /* render_mode */
54 4, /* colors_per_channel */
59 True
, /* use_shared_memory */
68 * Colormap allocation for PseudoColor visuals:
71 * switch standardColormap:
73 * allocate colors according to colors_per_channel
76 * if there's a std colormap defined then use it
79 * create a std colormap and set it
86 *----------------------------------------------------------------------
87 * allocateStandardPseudoColor
88 * Creates the internal colormap for PseudoColor, setting the
89 * color values according to the supplied standard colormap.
96 *----------------------------------------------------------------------
99 allocateStandardPseudoColor(RContext
*ctx
, XStandardColormap
*stdcmap
)
103 ctx
->ncolors
= stdcmap
->red_max
* stdcmap
->red_mult
104 + stdcmap
->green_max
* stdcmap
->green_mult
105 + stdcmap
->blue_max
* stdcmap
->blue_mult
+ 1;
107 if (ctx
->ncolors
<= 1) {
108 RErrorCode
= RERR_INTERNAL
;
109 puts("wraster: bad standard colormap");
114 ctx
->colors
= malloc(sizeof(XColor
)*ctx
->ncolors
);
116 RErrorCode
= RERR_NOMEMORY
;
121 ctx
->pixels
= malloc(sizeof(unsigned long)*ctx
->ncolors
);
127 RErrorCode
= RERR_NOMEMORY
;
133 #define calc(max,mult) (((i / stdcmap->mult) % \
134 (stdcmap->max + 1)) * 65535) / stdcmap->max
136 for (i
= 0; i
< ctx
->ncolors
; i
++) {
137 ctx
->colors
[i
].pixel
= i
+ stdcmap
->base_pixel
;
138 ctx
->colors
[i
].red
= calc(red_max
, red_mult
);
139 ctx
->colors
[i
].green
= calc(green_max
, green_mult
);
140 ctx
->colors
[i
].blue
= calc(blue_max
, blue_mult
);
142 ctx
->pixels
[i
] = ctx
->colors
[i
].pixel
;
152 setupStandardColormap(RContext
*ctx
, Atom property
)
154 if (!XmuLookupStandardColormap(ctx
->dpy
, ctx
->screen_number
,
155 ctx
->visual
->visualid
,
156 ctx
->depth
, property
,
158 RErrorCode
= RERR_STDCMAPFAIL
;
174 allocatePseudoColor(RContext
*ctx
)
177 XColor avcolors
[256];
179 int i
, ncolors
, r
, g
, b
;
181 int cpc
= ctx
->attribs
->colors_per_channel
;
183 ncolors
= cpc
* cpc
* cpc
;
185 if (ncolors
> (1<<ctx
->depth
)) {
186 /* reduce colormap size */
187 cpc
= ctx
->attribs
->colors_per_channel
= 1<<((int)ctx
->depth
/3);
188 ncolors
= cpc
* cpc
* cpc
;
191 assert(cpc
>= 2 && ncolors
<= (1<<ctx
->depth
));
193 colors
= malloc(sizeof(XColor
)*ncolors
);
195 RErrorCode
= RERR_NOMEMORY
;
199 ctx
->pixels
= malloc(sizeof(unsigned long)*ncolors
);
202 RErrorCode
= RERR_NOMEMORY
;
208 if ((ctx
->attribs
->flags
& RC_GammaCorrection
) && ctx
->attribs
->rgamma
> 0
209 && ctx
->attribs
->ggamma
> 0 && ctx
->attribs
->bgamma
> 0) {
213 /* do gamma correction */
214 rg
= 1.0/ctx
->attribs
->rgamma
;
215 gg
= 1.0/ctx
->attribs
->ggamma
;
216 bg
= 1.0/ctx
->attribs
->bgamma
;
217 for (r
=0; r
<cpc
; r
++) {
218 for (g
=0; g
<cpc
; g
++) {
219 for (b
=0; b
<cpc
; b
++) {
220 colors
[i
].red
=(r
*0xffff) / (cpc
-1);
221 colors
[i
].green
=(g
*0xffff) / (cpc
-1);
222 colors
[i
].blue
=(b
*0xffff) / (cpc
-1);
223 colors
[i
].flags
= DoRed
|DoGreen
|DoBlue
;
225 tmp
= (double)colors
[i
].red
/ 65536.0;
226 colors
[i
].red
= (unsigned short)(65536.0*pow(tmp
, rg
));
228 tmp
= (double)colors
[i
].green
/ 65536.0;
229 colors
[i
].green
= (unsigned short)(65536.0*pow(tmp
, gg
));
231 tmp
= (double)colors
[i
].blue
/ 65536.0;
232 colors
[i
].blue
= (unsigned short)(65536.0*pow(tmp
, bg
));
240 for (r
=0; r
<cpc
; r
++) {
241 for (g
=0; g
<cpc
; g
++) {
242 for (b
=0; b
<cpc
; b
++) {
243 colors
[i
].red
=(r
*0xffff) / (cpc
-1);
244 colors
[i
].green
=(g
*0xffff) / (cpc
-1);
245 colors
[i
].blue
=(b
*0xffff) / (cpc
-1);
246 colors
[i
].flags
= DoRed
|DoGreen
|DoBlue
;
252 /* try to allocate the colors */
253 for (i
=0; i
<ncolors
; i
++) {
254 if (!XAllocColor(ctx
->dpy
, ctx
->cmap
, &(colors
[i
]))) {
255 colors
[i
].flags
= 0; /* failed */
257 colors
[i
].flags
= DoRed
|DoGreen
|DoBlue
;
260 /* try to allocate close values for the colors that couldn't
261 * be allocated before */
262 avncolors
= (1<<ctx
->depth
>256 ? 256 : 1<<ctx
->depth
);
263 for (i
=0; i
<avncolors
; i
++) avcolors
[i
].pixel
= i
;
265 XQueryColors(ctx
->dpy
, ctx
->cmap
, avcolors
, avncolors
);
267 for (i
=0; i
<ncolors
; i
++) {
268 if (colors
[i
].flags
==0) {
270 unsigned long cdiff
=0xffffffff, diff
;
271 unsigned long closest
=0;
276 /* find closest color */
277 for (j
=0; j
<avncolors
; j
++) {
278 r
= (colors
[i
].red
- avcolors
[i
].red
)>>8;
279 g
= (colors
[i
].green
- avcolors
[i
].green
)>>8;
280 b
= (colors
[i
].blue
- avcolors
[i
].blue
)>>8;
281 diff
= r
*r
+ g
*g
+ b
*b
;
287 /* allocate closest color found */
288 colors
[i
].red
= avcolors
[closest
].red
;
289 colors
[i
].green
= avcolors
[closest
].green
;
290 colors
[i
].blue
= avcolors
[closest
].blue
;
291 if (XAllocColor(ctx
->dpy
, ctx
->cmap
, &colors
[i
])) {
292 colors
[i
].flags
= DoRed
|DoGreen
|DoBlue
;
293 break; /* succeeded, don't need to retry */
296 printf("close color allocation failed. Retrying...\n");
302 ctx
->colors
= colors
;
303 ctx
->ncolors
= ncolors
;
305 /* fill the pixels shortcut array */
306 for (i
= 0; i
< ncolors
; i
++) {
307 ctx
->pixels
[i
] = ctx
->colors
[i
].pixel
;
315 allocateGrayScale(RContext
*ctx
)
318 XColor avcolors
[256];
320 int i
, ncolors
, r
, g
, b
;
322 int cpc
= ctx
->attribs
->colors_per_channel
;
324 ncolors
= cpc
* cpc
* cpc
;
326 if (ctx
->vclass
== StaticGray
) {
327 /* we might as well use all grays */
328 ncolors
= 1<<ctx
->depth
;
330 if ( ncolors
> (1<<ctx
->depth
) ) {
331 /* reduce colormap size */
332 cpc
= ctx
->attribs
->colors_per_channel
= 1<<((int)ctx
->depth
/3);
333 ncolors
= cpc
* cpc
* cpc
;
336 assert(cpc
>= 2 && ncolors
<= (1<<ctx
->depth
));
339 if (ncolors
>=256 && ctx
->vclass
==StaticGray
) {
340 /* don't need dithering for 256 levels of gray in StaticGray visual */
341 ctx
->attribs
->render_mode
= RBestMatchRendering
;
344 colors
= malloc(sizeof(XColor
)*ncolors
);
346 RErrorCode
= RERR_NOMEMORY
;
349 for (i
=0; i
<ncolors
; i
++) {
350 colors
[i
].red
=(i
*0xffff) / (ncolors
-1);
351 colors
[i
].green
=(i
*0xffff) / (ncolors
-1);
352 colors
[i
].blue
=(i
*0xffff) / (ncolors
-1);
353 colors
[i
].flags
= DoRed
|DoGreen
|DoBlue
;
355 /* try to allocate the colors */
356 for (i
=0; i
<ncolors
; i
++) {
358 printf("trying:%x,%x,%x\n",colors
[i
].red
,colors
[i
].green
,colors
[i
].blue
);
360 if (!XAllocColor(ctx
->dpy
, ctx
->cmap
, &(colors
[i
]))) {
361 colors
[i
].flags
= 0; /* failed */
363 printf("failed:%x,%x,%x\n",colors
[i
].red
,colors
[i
].green
,colors
[i
].blue
);
366 colors
[i
].flags
= DoRed
|DoGreen
|DoBlue
;
368 printf("success:%x,%x,%x\n",colors
[i
].red
,colors
[i
].green
,colors
[i
].blue
);
372 /* try to allocate close values for the colors that couldn't
373 * be allocated before */
374 avncolors
= (1<<ctx
->depth
>256 ? 256 : 1<<ctx
->depth
);
375 for (i
=0; i
<avncolors
; i
++) avcolors
[i
].pixel
= i
;
377 XQueryColors(ctx
->dpy
, ctx
->cmap
, avcolors
, avncolors
);
379 for (i
=0; i
<ncolors
; i
++) {
380 if (colors
[i
].flags
==0) {
382 unsigned long cdiff
=0xffffffff, diff
;
383 unsigned long closest
=0;
388 /* find closest color */
389 for (j
=0; j
<avncolors
; j
++) {
390 r
= (colors
[i
].red
- avcolors
[i
].red
)>>8;
391 g
= (colors
[i
].green
- avcolors
[i
].green
)>>8;
392 b
= (colors
[i
].blue
- avcolors
[i
].blue
)>>8;
393 diff
= r
*r
+ g
*g
+ b
*b
;
399 /* allocate closest color found */
401 printf("best match:%x,%x,%x => %x,%x,%x\n",colors
[i
].red
,colors
[i
].green
,colors
[i
].blue
,avcolors
[closest
].red
,avcolors
[closest
].green
,avcolors
[closest
].blue
);
403 colors
[i
].red
= avcolors
[closest
].red
;
404 colors
[i
].green
= avcolors
[closest
].green
;
405 colors
[i
].blue
= avcolors
[closest
].blue
;
406 if (XAllocColor(ctx
->dpy
, ctx
->cmap
, &colors
[i
])) {
407 colors
[i
].flags
= DoRed
|DoGreen
|DoBlue
;
408 break; /* succeeded, don't need to retry */
411 printf("close color allocation failed. Retrying...\n");
421 setupPseudoColorColormap(RContext
*context
)
425 if (context
->attribs
->standard_colormap_mode
== RCreateStdColormap
) {
426 property
= XInternAtom(context
->dpy
, "RGB_DEFAULT_MAP", False
);
428 if (!setupStandardColormap(context
, property
)) {
433 if (context
->attribs
->standard_colormap_mode
!= RIgnoreStdColormap
) {
434 XStandardColormap
*maps
;
438 property
= XInternAtom(context
->dpy
, "RGB_BEST_MAP", False
);
439 if (!XGetRGBColormaps(context
->dpy
,
440 DefaultRootWindow(context
->dpy
),
441 &maps
, &count
, property
)) {
446 property
= XInternAtom(context
->dpy
, "RGB_DEFAULT_MAP", False
);
447 if (!XGetRGBColormaps(context
->dpy
,
448 DefaultRootWindow(context
->dpy
),
449 &maps
, &count
, property
)) {
454 if (!XGetRGBColormaps(context
->dpy
,
455 DefaultRootWindow(context
->dpy
),
456 &maps
, &count
, property
)) {
464 for (i
= 0; i
< count
; i
++) {
465 if (maps
[i
].visualid
== context
->visual
->visualid
) {
472 puts("wrlib: no std cmap found");
476 && allocateStandardPseudoColor(context
, &maps
[theMap
])) {
478 context
->std_rgb_map
= XAllocStandardColormap();
480 *context
->std_rgb_map
= maps
[theMap
];
482 context
->cmap
= context
->std_rgb_map
->colormap
;
493 context
->attribs
->standard_colormap_mode
= RIgnoreStdColormap
;
495 /* RIgnoreStdColormap and fallback */
496 return allocatePseudoColor(context
);
503 mygetenv(char *var
, int scr
)
508 sprintf(varname
, "%s%i", var
, scr
);
518 gatherconfig(RContext
*context
, int screen_n
)
522 ptr
= mygetenv("WRASTER_GAMMA", screen_n
);
525 if (sscanf(ptr
, "%f/%f/%f", &g1
, &g2
, &g3
)!=3
526 || g1
<=0.0 || g2
<=0.0 || g3
<=0.0) {
527 printf("wrlib: invalid value(s) for gamma correction \"%s\"\n",
530 context
->attribs
->flags
|= RC_GammaCorrection
;
531 context
->attribs
->rgamma
= g1
;
532 context
->attribs
->ggamma
= g2
;
533 context
->attribs
->bgamma
= g3
;
536 ptr
= mygetenv("WRASTER_COLOR_RESOLUTION", screen_n
);
539 if (sscanf(ptr
, "%d", &i
)!=1 || i
<2 || i
>6) {
540 printf("wrlib: invalid value for color resolution \"%s\"\n",ptr
);
542 context
->attribs
->flags
|= RC_ColorsPerChannel
;
543 context
->attribs
->colors_per_channel
= i
;
547 ptr
= mygetenv("WRASTER_OPTIMIZE_FOR_SPEED", screen_n
);
549 context
->flags
.optimize_for_speed
= 1;
551 context
->flags
.optimize_for_speed
= 0;
558 getColormap(RContext
*context
, int screen_number
)
560 Colormap cmap
= None
;
561 XStandardColormap
*cmaps
;
564 if (XGetRGBColormaps(context
->dpy
,
565 RootWindow(context
->dpy
, screen_number
),
566 &cmaps
, &ncmaps
, XA_RGB_DEFAULT_MAP
)) {
567 for (i
=0; i
<ncmaps
; ++i
) {
568 if (cmaps
[i
].visualid
== context
->visual
->visualid
) {
569 cmap
= cmaps
[i
].colormap
;
578 cmap
= XCreateColormap(context
->dpy
,
579 RootWindow(context
->dpy
, screen_number
),
580 context
->visual
, AllocNone
);
582 color
.red
= color
.green
= color
.blue
= 0;
583 XAllocColor(context
->dpy
, cmap
, &color
);
584 context
->black
= color
.pixel
;
586 color
.red
= color
.green
= color
.blue
= 0xffff;
587 XAllocColor(context
->dpy
, cmap
, &color
);
588 context
->white
= color
.pixel
;
591 context
->cmap
= cmap
;
596 count_offset(unsigned long mask
)
601 while ((mask
& 1)==0) {
610 RCreateContext(Display
*dpy
, int screen_number
, RContextAttributes
*attribs
)
616 context
= malloc(sizeof(RContext
));
618 RErrorCode
= RERR_NOMEMORY
;
621 memset(context
, 0, sizeof(RContext
));
625 context
->screen_number
= screen_number
;
627 context
->attribs
= malloc(sizeof(RContextAttributes
));
628 if (!context
->attribs
) {
630 RErrorCode
= RERR_NOMEMORY
;
634 *context
->attribs
= DEFAULT_CONTEXT_ATTRIBS
;
636 *context
->attribs
= *attribs
;
638 if (!(context
->attribs
->flags
& RC_StandardColormap
)) {
639 context
->attribs
->standard_colormap_mode
= RUseStdColormap
;
642 if (!(context
->attribs
->flags
& RC_ScalingFilter
)) {
643 context
->attribs
->flags
|= RC_ScalingFilter
;
644 context
->attribs
->scaling_filter
= RMitchellFilter
;
647 /* get configuration from environment variables */
648 gatherconfig(context
, screen_number
);
650 _wraster_change_filter(context
->attribs
->scaling_filter
);
652 if ((context
->attribs
->flags
& RC_VisualID
)) {
653 XVisualInfo
*vinfo
, templ
;
656 templ
.screen
= screen_number
;
657 templ
.visualid
= context
->attribs
->visualid
;
658 vinfo
= XGetVisualInfo(context
->dpy
, VisualIDMask
|VisualScreenMask
,
660 if (!vinfo
|| nret
==0) {
662 RErrorCode
= RERR_BADVISUALID
;
666 if (vinfo
[0].visual
== DefaultVisual(dpy
, screen_number
)) {
667 context
->attribs
->flags
|= RC_DefaultVisual
;
669 XSetWindowAttributes attr
;
672 context
->visual
= vinfo
[0].visual
;
673 context
->depth
= vinfo
[0].depth
;
674 context
->vclass
= vinfo
[0].class;
675 getColormap(context
, screen_number
);
676 attr
.colormap
= context
->cmap
;
677 attr
.override_redirect
= True
;
678 attr
.border_pixel
= 0;
679 attr
.background_pixel
= 0;
680 mask
= CWBorderPixel
|CWColormap
|CWOverrideRedirect
|CWBackPixel
;
682 XCreateWindow(dpy
, RootWindow(dpy
, screen_number
), 1, 1,
683 1, 1, 0, context
->depth
, CopyFromParent
,
684 context
->visual
, mask
, &attr
);
685 /* XSetWindowColormap(dpy, context->drawable, attr.colormap);*/
691 if (!context
->visual
) {
692 if ((context
->attribs
->flags
& RC_DefaultVisual
)
693 || !bestContext(dpy
, screen_number
, context
)) {
694 context
->visual
= DefaultVisual(dpy
, screen_number
);
695 context
->depth
= DefaultDepth(dpy
, screen_number
);
696 context
->cmap
= DefaultColormap(dpy
, screen_number
);
697 context
->drawable
= RootWindow(dpy
, screen_number
);
698 context
->black
= BlackPixel(dpy
, screen_number
);
699 context
->white
= WhitePixel(dpy
, screen_number
);
700 context
->vclass
= context
->visual
->class;
704 gcv
.function
= GXcopy
;
705 gcv
.graphics_exposures
= False
;
706 context
->copy_gc
= XCreateGC(dpy
, context
->drawable
, GCFunction
707 |GCGraphicsExposures
, &gcv
);
710 context
->hermes_data
= malloc(sizeof(RHermesData
));
711 if (!context
->hermes_data
) {
712 RErrorCode
= RERR_NOMEMORY
;
719 context
->hermes_data
->palette
= Hermes_PaletteInstance();
721 unsigned long flags
= 0;
723 if (context
->attribs
->render_mode
== RDitheredRendering
) {
724 flags
|= HERMES_CONVERT_DITHER
;
726 context
->hermes_data
->converter
= Hermes_ConverterInstance(flags
);
730 if (context
->vclass
== PseudoColor
|| context
->vclass
== StaticColor
) {
731 if (!setupPseudoColorColormap(context
)) {
740 for (i
= 0; i
< context
->ncolors
; i
++) {
741 palette
[i
] = ((context
->colors
[i
].red
>> 8) << 16) ||
742 ((context
->colors
[i
].green
>> 8) << 8) ||
743 ((context
->colors
[i
].blue
>> 8));
746 Hermes_PaletteSet(context
->hermes_data
->palette
, palette
);
749 } else if (context
->vclass
== GrayScale
|| context
->vclass
== StaticGray
) {
750 context
->colors
= allocateGrayScale(context
);
751 if (!context
->colors
) {
760 for (i
= 0; i
< context
->ncolors
; i
++) {
761 palette
[i
] = ((context
->colors
[i
].red
>> 8) << 16) ||
762 ((context
->colors
[i
].green
>> 8) << 8) ||
763 ((context
->colors
[i
].blue
>> 8));
766 Hermes_PaletteSet(context
->hermes_data
->palette
, palette
);
769 } else if (context
->vclass
== TrueColor
) {
770 /* calc offsets to create a TrueColor pixel */
771 context
->red_offset
= count_offset(context
->visual
->red_mask
);
772 context
->green_offset
= count_offset(context
->visual
->green_mask
);
773 context
->blue_offset
= count_offset(context
->visual
->blue_mask
);
774 /* disable dithering on 24 bits visuals */
775 if (context
->depth
>= 24)
776 context
->attribs
->render_mode
= RBestMatchRendering
;
783 /* check avaiability of MIT-SHM */
785 if (!(context
->attribs
->flags
& RC_UseSharedMemory
)) {
786 context
->attribs
->flags
|= RC_UseSharedMemory
;
787 context
->attribs
->use_shared_memory
= True
;
790 if (context
->attribs
->use_shared_memory
) {
794 context
->flags
.use_shared_pixmap
= 0;
796 if (!XShmQueryVersion(context
->dpy
, &major
, &minor
, &sharedPixmaps
)) {
797 context
->attribs
->use_shared_memory
= False
;
799 if (XShmPixmapFormat(context
->dpy
)==ZPixmap
)
800 context
->flags
.use_shared_pixmap
= sharedPixmaps
;
810 bestContext(Display
*dpy
, int screen_number
, RContext
*context
)
812 XVisualInfo
*vinfo
=NULL
, rvinfo
;
813 int best
= -1, numvis
, i
;
815 XSetWindowAttributes attr
;
817 rvinfo
.class = TrueColor
;
818 rvinfo
.screen
= screen_number
;
819 flags
= VisualClassMask
| VisualScreenMask
;
821 vinfo
= XGetVisualInfo(dpy
, flags
, &rvinfo
, &numvis
);
822 if (vinfo
) { /* look for a TrueColor, 24-bit or more (pref 24) */
823 for (i
=numvis
-1, best
= -1; i
>=0; i
--) {
824 if (vinfo
[i
].depth
== 24) best
= i
;
825 else if (vinfo
[i
].depth
>24 && best
<0) best
= i
;
830 if (best
== -1) { /* look for a DirectColor, 24-bit or more (pref 24) */
831 rvinfo
.class = DirectColor
;
832 if (vinfo
) XFree((char *) vinfo
);
833 vinfo
= XGetVisualInfo(dpy
, flags
, &rvinfo
, &numvis
);
835 for (i
=0, best
= -1; i
<numvis
; i
++) {
836 if (vinfo
[i
].depth
== 24) best
= i
;
837 else if (vinfo
[i
].depth
>24 && best
<0) best
= i
;
843 context
->visual
= vinfo
[best
].visual
;
844 context
->depth
= vinfo
[best
].depth
;
845 context
->vclass
= vinfo
[best
].class;
846 getColormap(context
, screen_number
);
847 attr
.colormap
= context
->cmap
;
848 attr
.override_redirect
= True
;
849 attr
.border_pixel
= 0;
851 XCreateWindow(dpy
, RootWindow(dpy
, screen_number
),
852 1, 1, 1, 1, 0, context
->depth
,
853 CopyFromParent
, context
->visual
,
854 CWBorderPixel
|CWColormap
|CWOverrideRedirect
, &attr
);
855 /* XSetWindowColormap(dpy, context->drawable, context->cmap);*/
857 if (vinfo
) XFree((char *) vinfo
);