1 /* context.c - X context management
3 * Raster graphics library
5 * Copyright (c) 1997, 1998, 1999 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>
40 extern void _wraster_change_filter(int type
);
43 static Bool
bestContext(Display
*dpy
, int screen_number
, RContext
*context
);
45 static RContextAttributes DEFAULT_CONTEXT_ATTRIBS
= {
46 RC_UseSharedMemory
|RC_RenderMode
|RC_ColorsPerChannel
, /* flags */
47 RDitheredRendering
, /* render_mode */
48 4, /* colors_per_channel */
53 True
, /* use_shared_memory */
62 * Colormap allocation for PseudoColor visuals:
65 * switch standardColormap:
67 * allocate colors according to colors_per_channel
70 * if there's a std colormap defined then use it
73 * create a std colormap and set it
80 *----------------------------------------------------------------------
81 * allocateStandardPseudoColor
82 * Creates the internal colormap for PseudoColor, setting the
83 * color values according to the supplied standard colormap.
90 *----------------------------------------------------------------------
93 allocateStandardPseudoColor(RContext
*ctx
, XStandardColormap
*stdcmap
)
97 ctx
->ncolors
= stdcmap
->red_max
* stdcmap
->red_mult
98 + stdcmap
->green_max
* stdcmap
->green_mult
99 + stdcmap
->blue_max
* stdcmap
->blue_mult
+ 1;
101 if (ctx
->ncolors
<= 1) {
102 RErrorCode
= RERR_INTERNAL
;
103 puts("wraster: bad standard colormap");
108 ctx
->colors
= malloc(sizeof(XColor
)*ctx
->ncolors
);
110 RErrorCode
= RERR_NOMEMORY
;
115 ctx
->pixels
= malloc(sizeof(unsigned long)*ctx
->ncolors
);
121 RErrorCode
= RERR_NOMEMORY
;
127 #define calc(max,mult) (((i / stdcmap->mult) % \
128 (stdcmap->max + 1)) * 65535) / stdcmap->max
130 for (i
= 0; i
< ctx
->ncolors
; i
++) {
131 ctx
->colors
[i
].pixel
= i
+ stdcmap
->base_pixel
;
132 ctx
->colors
[i
].red
= calc(red_max
, red_mult
);
133 ctx
->colors
[i
].green
= calc(green_max
, green_mult
);
134 ctx
->colors
[i
].blue
= calc(blue_max
, blue_mult
);
136 ctx
->pixels
[i
] = ctx
->colors
[i
].pixel
;
146 setupStandardColormap(RContext
*ctx
, Atom property
)
148 if (!XmuLookupStandardColormap(ctx
->dpy
, ctx
->screen_number
,
149 ctx
->visual
->visualid
,
150 ctx
->depth
, property
,
152 RErrorCode
= RERR_STDCMAPFAIL
;
168 allocatePseudoColor(RContext
*ctx
)
171 XColor avcolors
[256];
173 int i
, ncolors
, r
, g
, b
;
175 int cpc
= ctx
->attribs
->colors_per_channel
;
177 ncolors
= cpc
* cpc
* cpc
;
179 if (ncolors
> (1<<ctx
->depth
)) {
180 /* reduce colormap size */
181 cpc
= ctx
->attribs
->colors_per_channel
= 1<<((int)ctx
->depth
/3);
182 ncolors
= cpc
* cpc
* cpc
;
185 assert(cpc
>= 2 && ncolors
<= (1<<ctx
->depth
));
187 colors
= malloc(sizeof(XColor
)*ncolors
);
189 RErrorCode
= RERR_NOMEMORY
;
193 ctx
->pixels
= malloc(sizeof(unsigned long)*ncolors
);
196 RErrorCode
= RERR_NOMEMORY
;
202 if ((ctx
->attribs
->flags
& RC_GammaCorrection
) && ctx
->attribs
->rgamma
> 0
203 && ctx
->attribs
->ggamma
> 0 && ctx
->attribs
->bgamma
> 0) {
207 /* do gamma correction */
208 rg
= 1.0/ctx
->attribs
->rgamma
;
209 gg
= 1.0/ctx
->attribs
->ggamma
;
210 bg
= 1.0/ctx
->attribs
->bgamma
;
211 for (r
=0; r
<cpc
; r
++) {
212 for (g
=0; g
<cpc
; g
++) {
213 for (b
=0; b
<cpc
; b
++) {
214 colors
[i
].red
=(r
*0xffff) / (cpc
-1);
215 colors
[i
].green
=(g
*0xffff) / (cpc
-1);
216 colors
[i
].blue
=(b
*0xffff) / (cpc
-1);
217 colors
[i
].flags
= DoRed
|DoGreen
|DoBlue
;
219 tmp
= (double)colors
[i
].red
/ 65536.0;
220 colors
[i
].red
= (unsigned short)(65536.0*pow(tmp
, rg
));
222 tmp
= (double)colors
[i
].green
/ 65536.0;
223 colors
[i
].green
= (unsigned short)(65536.0*pow(tmp
, gg
));
225 tmp
= (double)colors
[i
].blue
/ 65536.0;
226 colors
[i
].blue
= (unsigned short)(65536.0*pow(tmp
, bg
));
234 for (r
=0; r
<cpc
; r
++) {
235 for (g
=0; g
<cpc
; g
++) {
236 for (b
=0; b
<cpc
; b
++) {
237 colors
[i
].red
=(r
*0xffff) / (cpc
-1);
238 colors
[i
].green
=(g
*0xffff) / (cpc
-1);
239 colors
[i
].blue
=(b
*0xffff) / (cpc
-1);
240 colors
[i
].flags
= DoRed
|DoGreen
|DoBlue
;
246 /* try to allocate the colors */
247 for (i
=0; i
<ncolors
; i
++) {
248 if (!XAllocColor(ctx
->dpy
, ctx
->cmap
, &(colors
[i
]))) {
249 colors
[i
].flags
= 0; /* failed */
251 colors
[i
].flags
= DoRed
|DoGreen
|DoBlue
;
254 /* try to allocate close values for the colors that couldn't
255 * be allocated before */
256 avncolors
= (1<<ctx
->depth
>256 ? 256 : 1<<ctx
->depth
);
257 for (i
=0; i
<avncolors
; i
++) avcolors
[i
].pixel
= i
;
259 XQueryColors(ctx
->dpy
, ctx
->cmap
, avcolors
, avncolors
);
261 for (i
=0; i
<ncolors
; i
++) {
262 if (colors
[i
].flags
==0) {
264 unsigned long cdiff
=0xffffffff, diff
;
265 unsigned long closest
=0;
270 /* find closest color */
271 for (j
=0; j
<avncolors
; j
++) {
272 r
= (colors
[i
].red
- avcolors
[i
].red
)>>8;
273 g
= (colors
[i
].green
- avcolors
[i
].green
)>>8;
274 b
= (colors
[i
].blue
- avcolors
[i
].blue
)>>8;
275 diff
= r
*r
+ g
*g
+ b
*b
;
281 /* allocate closest color found */
282 colors
[i
].red
= avcolors
[closest
].red
;
283 colors
[i
].green
= avcolors
[closest
].green
;
284 colors
[i
].blue
= avcolors
[closest
].blue
;
285 if (XAllocColor(ctx
->dpy
, ctx
->cmap
, &colors
[i
])) {
286 colors
[i
].flags
= DoRed
|DoGreen
|DoBlue
;
287 break; /* succeeded, don't need to retry */
290 printf("close color allocation failed. Retrying...\n");
296 ctx
->colors
= colors
;
297 ctx
->ncolors
= ncolors
;
299 /* fill the pixels shortcut array */
300 for (i
= 0; i
< ncolors
; i
++) {
301 ctx
->pixels
[i
] = ctx
->colors
[i
].pixel
;
309 allocateGrayScale(RContext
*ctx
)
312 XColor avcolors
[256];
314 int i
, ncolors
, r
, g
, b
;
316 int cpc
= ctx
->attribs
->colors_per_channel
;
318 ncolors
= cpc
* cpc
* cpc
;
320 if (ctx
->vclass
== StaticGray
) {
321 /* we might as well use all grays */
322 ncolors
= 1<<ctx
->depth
;
324 if ( ncolors
> (1<<ctx
->depth
) ) {
325 /* reduce colormap size */
326 cpc
= ctx
->attribs
->colors_per_channel
= 1<<((int)ctx
->depth
/3);
327 ncolors
= cpc
* cpc
* cpc
;
330 assert(cpc
>= 2 && ncolors
<= (1<<ctx
->depth
));
333 if (ncolors
>=256 && ctx
->vclass
==StaticGray
) {
334 /* don't need dithering for 256 levels of gray in StaticGray visual */
335 ctx
->attribs
->render_mode
= RBestMatchRendering
;
338 colors
= malloc(sizeof(XColor
)*ncolors
);
340 RErrorCode
= RERR_NOMEMORY
;
343 for (i
=0; i
<ncolors
; i
++) {
344 colors
[i
].red
=(i
*0xffff) / (ncolors
-1);
345 colors
[i
].green
=(i
*0xffff) / (ncolors
-1);
346 colors
[i
].blue
=(i
*0xffff) / (ncolors
-1);
347 colors
[i
].flags
= DoRed
|DoGreen
|DoBlue
;
349 /* try to allocate the colors */
350 for (i
=0; i
<ncolors
; i
++) {
352 printf("trying:%x,%x,%x\n",colors
[i
].red
,colors
[i
].green
,colors
[i
].blue
);
354 if (!XAllocColor(ctx
->dpy
, ctx
->cmap
, &(colors
[i
]))) {
355 colors
[i
].flags
= 0; /* failed */
357 printf("failed:%x,%x,%x\n",colors
[i
].red
,colors
[i
].green
,colors
[i
].blue
);
360 colors
[i
].flags
= DoRed
|DoGreen
|DoBlue
;
362 printf("success:%x,%x,%x\n",colors
[i
].red
,colors
[i
].green
,colors
[i
].blue
);
366 /* try to allocate close values for the colors that couldn't
367 * be allocated before */
368 avncolors
= (1<<ctx
->depth
>256 ? 256 : 1<<ctx
->depth
);
369 for (i
=0; i
<avncolors
; i
++) avcolors
[i
].pixel
= i
;
371 XQueryColors(ctx
->dpy
, ctx
->cmap
, avcolors
, avncolors
);
373 for (i
=0; i
<ncolors
; i
++) {
374 if (colors
[i
].flags
==0) {
376 unsigned long cdiff
=0xffffffff, diff
;
377 unsigned long closest
=0;
382 /* find closest color */
383 for (j
=0; j
<avncolors
; j
++) {
384 r
= (colors
[i
].red
- avcolors
[i
].red
)>>8;
385 g
= (colors
[i
].green
- avcolors
[i
].green
)>>8;
386 b
= (colors
[i
].blue
- avcolors
[i
].blue
)>>8;
387 diff
= r
*r
+ g
*g
+ b
*b
;
393 /* allocate closest color found */
395 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
);
397 colors
[i
].red
= avcolors
[closest
].red
;
398 colors
[i
].green
= avcolors
[closest
].green
;
399 colors
[i
].blue
= avcolors
[closest
].blue
;
400 if (XAllocColor(ctx
->dpy
, ctx
->cmap
, &colors
[i
])) {
401 colors
[i
].flags
= DoRed
|DoGreen
|DoBlue
;
402 break; /* succeeded, don't need to retry */
405 printf("close color allocation failed. Retrying...\n");
415 setupPseudoColorColormap(RContext
*context
)
419 if (context
->attribs
->standard_colormap_mode
== RCreateStdColormap
) {
420 property
= XInternAtom(context
->dpy
, "RGB_DEFAULT_MAP", False
);
422 if (!setupStandardColormap(context
, property
)) {
427 if (context
->attribs
->standard_colormap_mode
!= RIgnoreStdColormap
) {
428 XStandardColormap
*maps
;
432 property
= XInternAtom(context
->dpy
, "RGB_BEST_MAP", False
);
433 if (!XGetRGBColormaps(context
->dpy
,
434 DefaultRootWindow(context
->dpy
),
435 &maps
, &count
, property
)) {
440 property
= XInternAtom(context
->dpy
, "RGB_DEFAULT_MAP", False
);
441 if (!XGetRGBColormaps(context
->dpy
,
442 DefaultRootWindow(context
->dpy
),
443 &maps
, &count
, property
)) {
448 if (!XGetRGBColormaps(context
->dpy
,
449 DefaultRootWindow(context
->dpy
),
450 &maps
, &count
, property
)) {
458 for (i
= 0; i
< count
; i
++) {
459 if (maps
[i
].visualid
== context
->visual
->visualid
) {
466 puts("wrlib: no std cmap found");
470 && allocateStandardPseudoColor(context
, &maps
[theMap
])) {
472 context
->std_rgb_map
= XAllocStandardColormap();
474 *context
->std_rgb_map
= maps
[theMap
];
476 context
->cmap
= context
->std_rgb_map
->colormap
;
487 context
->attribs
->standard_colormap_mode
= RIgnoreStdColormap
;
489 /* RIgnoreStdColormap and fallback */
490 return allocatePseudoColor(context
);
497 mygetenv(char *var
, int scr
)
502 sprintf(varname
, "%s%i", var
, scr
);
512 gatherconfig(RContext
*context
, int screen_n
)
516 ptr
= mygetenv("WRASTER_GAMMA", screen_n
);
519 if (sscanf(ptr
, "%f/%f/%f", &g1
, &g2
, &g3
)!=3
520 || g1
<=0.0 || g2
<=0.0 || g3
<=0.0) {
521 printf("wrlib: invalid value(s) for gamma correction \"%s\"\n",
524 context
->attribs
->flags
|= RC_GammaCorrection
;
525 context
->attribs
->rgamma
= g1
;
526 context
->attribs
->ggamma
= g2
;
527 context
->attribs
->bgamma
= g3
;
530 ptr
= mygetenv("WRASTER_COLOR_RESOLUTION", screen_n
);
533 if (sscanf(ptr
, "%d", &i
)!=1 || i
<2 || i
>6) {
534 printf("wrlib: invalid value for color resolution \"%s\"\n",ptr
);
536 context
->attribs
->flags
|= RC_ColorsPerChannel
;
537 context
->attribs
->colors_per_channel
= i
;
541 ptr
= mygetenv("WRASTER_OPTIMIZE_FOR_SPEED", screen_n
);
543 context
->flags
.optimize_for_speed
= 1;
545 context
->flags
.optimize_for_speed
= 0;
552 getColormap(RContext
*context
, int screen_number
)
554 Colormap cmap
= None
;
555 XStandardColormap
*cmaps
;
558 if (XGetRGBColormaps(context
->dpy
,
559 RootWindow(context
->dpy
, screen_number
),
560 &cmaps
, &ncmaps
, XA_RGB_DEFAULT_MAP
)) {
561 for (i
=0; i
<ncmaps
; ++i
) {
562 if (cmaps
[i
].visualid
== context
->visual
->visualid
) {
564 cmap
= cmaps
[i
].colormap
;
573 cmap
= XCreateColormap(context
->dpy
,
574 RootWindow(context
->dpy
, screen_number
),
575 context
->visual
, AllocNone
);
577 color
.red
= color
.green
= color
.blue
= 0;
578 XAllocColor(context
->dpy
, cmap
, &color
);
579 context
->black
= color
.pixel
;
581 color
.red
= color
.green
= color
.blue
= 0xffff;
582 XAllocColor(context
->dpy
, cmap
, &color
);
583 context
->white
= color
.pixel
;
586 context
->cmap
= cmap
;
591 count_offset(unsigned long mask
)
596 while ((mask
& 1)==0) {
605 RCreateContext(Display
*dpy
, int screen_number
, RContextAttributes
*attribs
)
611 context
= malloc(sizeof(RContext
));
613 RErrorCode
= RERR_NOMEMORY
;
616 memset(context
, 0, sizeof(RContext
));
620 context
->screen_number
= screen_number
;
622 context
->attribs
= malloc(sizeof(RContextAttributes
));
623 if (!context
->attribs
) {
625 RErrorCode
= RERR_NOMEMORY
;
629 *context
->attribs
= DEFAULT_CONTEXT_ATTRIBS
;
631 *context
->attribs
= *attribs
;
633 if (!(context
->attribs
->flags
& RC_StandardColormap
)) {
634 context
->attribs
->standard_colormap_mode
= RUseStdColormap
;
637 if (!(context
->attribs
->flags
& RC_ScalingFilter
)) {
638 context
->attribs
->flags
|= RC_ScalingFilter
;
639 context
->attribs
->scaling_filter
= RMitchellFilter
;
642 /* get configuration from environment variables */
643 gatherconfig(context
, screen_number
);
645 _wraster_change_filter(context
->attribs
->scaling_filter
);
647 if ((context
->attribs
->flags
& RC_VisualID
)) {
648 XVisualInfo
*vinfo
, templ
;
651 templ
.screen
= screen_number
;
652 templ
.visualid
= context
->attribs
->visualid
;
653 vinfo
= XGetVisualInfo(context
->dpy
, VisualIDMask
|VisualScreenMask
,
655 if (!vinfo
|| nret
==0) {
657 RErrorCode
= RERR_BADVISUALID
;
661 if (vinfo
[0].visual
== DefaultVisual(dpy
, screen_number
)) {
662 context
->attribs
->flags
|= RC_DefaultVisual
;
664 XSetWindowAttributes attr
;
667 context
->visual
= vinfo
[0].visual
;
668 context
->depth
= vinfo
[0].depth
;
669 context
->vclass
= vinfo
[0].class;
670 getColormap(context
, screen_number
);
671 attr
.colormap
= context
->cmap
;
672 attr
.override_redirect
= True
;
673 attr
.border_pixel
= 0;
674 attr
.background_pixel
= 0;
675 mask
= CWBorderPixel
|CWColormap
|CWOverrideRedirect
|CWBackPixel
;
677 XCreateWindow(dpy
, RootWindow(dpy
, screen_number
), 1, 1,
678 1, 1, 0, context
->depth
, CopyFromParent
,
679 context
->visual
, mask
, &attr
);
680 /* XSetWindowColormap(dpy, context->drawable, attr.colormap);*/
686 if (!context
->visual
) {
687 if ((context
->attribs
->flags
& RC_DefaultVisual
)
688 || !bestContext(dpy
, screen_number
, context
)) {
689 context
->visual
= DefaultVisual(dpy
, screen_number
);
690 context
->depth
= DefaultDepth(dpy
, screen_number
);
691 context
->cmap
= DefaultColormap(dpy
, screen_number
);
692 context
->drawable
= RootWindow(dpy
, screen_number
);
693 context
->black
= BlackPixel(dpy
, screen_number
);
694 context
->white
= WhitePixel(dpy
, screen_number
);
695 context
->vclass
= context
->visual
->class;
699 gcv
.function
= GXcopy
;
700 gcv
.graphics_exposures
= False
;
701 context
->copy_gc
= XCreateGC(dpy
, context
->drawable
, GCFunction
702 |GCGraphicsExposures
, &gcv
);
704 if (context
->vclass
== PseudoColor
|| context
->vclass
== StaticColor
) {
706 if (!setupPseudoColorColormap(context
)) {
710 } else if (context
->vclass
== GrayScale
|| context
->vclass
== StaticGray
) {
711 context
->colors
= allocateGrayScale(context
);
712 if (!context
->colors
) {
715 } else if (context
->vclass
== TrueColor
) {
716 /* calc offsets to create a TrueColor pixel */
717 context
->red_offset
= count_offset(context
->visual
->red_mask
);
718 context
->green_offset
= count_offset(context
->visual
->green_mask
);
719 context
->blue_offset
= count_offset(context
->visual
->blue_mask
);
720 /* disable dithering on 24 bits visuals */
721 if (context
->depth
>= 24)
722 context
->attribs
->render_mode
= RBestMatchRendering
;
725 /* check avaiability of MIT-SHM */
727 if (!(context
->attribs
->flags
& RC_UseSharedMemory
)) {
728 context
->attribs
->flags
|= RC_UseSharedMemory
;
729 context
->attribs
->use_shared_memory
= True
;
732 if (context
->attribs
->use_shared_memory
) {
736 context
->flags
.use_shared_pixmap
= 0;
738 if (!XShmQueryVersion(context
->dpy
, &major
, &minor
, &sharedPixmaps
)) {
739 context
->attribs
->use_shared_memory
= False
;
741 if (XShmPixmapFormat(context
->dpy
)==ZPixmap
)
742 context
->flags
.use_shared_pixmap
= sharedPixmaps
;
752 bestContext(Display
*dpy
, int screen_number
, RContext
*context
)
754 XVisualInfo
*vinfo
=NULL
, rvinfo
;
755 int best
= -1, numvis
, i
;
757 XSetWindowAttributes attr
;
759 rvinfo
.class = TrueColor
;
760 rvinfo
.screen
= screen_number
;
761 flags
= VisualClassMask
| VisualScreenMask
;
763 vinfo
= XGetVisualInfo(dpy
, flags
, &rvinfo
, &numvis
);
764 if (vinfo
) { /* look for a TrueColor, 24-bit or more (pref 24) */
765 for (i
=numvis
-1, best
= -1; i
>=0; i
--) {
766 if (vinfo
[i
].depth
== 24) best
= i
;
767 else if (vinfo
[i
].depth
>24 && best
<0) best
= i
;
772 if (best
== -1) { /* look for a DirectColor, 24-bit or more (pref 24) */
773 rvinfo
.class = DirectColor
;
774 if (vinfo
) XFree((char *) vinfo
);
775 vinfo
= XGetVisualInfo(dpy
, flags
, &rvinfo
, &numvis
);
777 for (i
=0, best
= -1; i
<numvis
; i
++) {
778 if (vinfo
[i
].depth
== 24) best
= i
;
779 else if (vinfo
[i
].depth
>24 && best
<0) best
= i
;
785 context
->visual
= vinfo
[best
].visual
;
786 context
->depth
= vinfo
[best
].depth
;
787 context
->vclass
= vinfo
[best
].class;
788 getColormap(context
, screen_number
);
789 attr
.colormap
= context
->cmap
;
790 attr
.override_redirect
= True
;
791 attr
.border_pixel
= 0;
793 XCreateWindow(dpy
, RootWindow(dpy
, screen_number
),
794 1, 1, 1, 1, 0, context
->depth
,
795 CopyFromParent
, context
->visual
,
796 CWBorderPixel
|CWColormap
|CWOverrideRedirect
, &attr
);
797 /* XSetWindowColormap(dpy, context->drawable, context->cmap);*/
799 if (vinfo
) XFree((char *) vinfo
);