1 /* context.c - X context management
3 * Raster graphics library
5 * Copyright (c) 1997-2003 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>
27 #include <X11/Xmu/StdCmap.h>
38 extern void _wraster_change_filter(int type
);
40 static Bool
bestContext(Display
* dpy
, int screen_number
, RContext
* context
);
42 static RContextAttributes DEFAULT_CONTEXT_ATTRIBS
= {
43 RC_UseSharedMemory
| RC_RenderMode
| RC_ColorsPerChannel
, /* flags */
44 RDitheredRendering
, /* render_mode */
45 4, /* colors_per_channel */
50 True
, /* use_shared_memory */
57 * Colormap allocation for PseudoColor visuals:
60 * switch standardColormap:
62 * allocate colors according to colors_per_channel
65 * if there's a std colormap defined then use it
68 * create a std colormap and set it
72 *----------------------------------------------------------------------
73 * allocateStandardPseudoColor
74 * Creates the internal colormap for PseudoColor, setting the
75 * color values according to the supplied standard colormap.
82 *----------------------------------------------------------------------
84 static Bool
allocateStandardPseudoColor(RContext
* ctx
, XStandardColormap
* stdcmap
)
88 ctx
->ncolors
= stdcmap
->red_max
* stdcmap
->red_mult
89 + stdcmap
->green_max
* stdcmap
->green_mult
+ stdcmap
->blue_max
* stdcmap
->blue_mult
+ 1;
91 if (ctx
->ncolors
<= 1) {
92 RErrorCode
= RERR_INTERNAL
;
93 puts("wraster: bad standard colormap");
98 ctx
->colors
= malloc(sizeof(XColor
) * ctx
->ncolors
);
100 RErrorCode
= RERR_NOMEMORY
;
105 ctx
->pixels
= malloc(sizeof(unsigned long) * ctx
->ncolors
);
111 RErrorCode
= RERR_NOMEMORY
;
116 #define calc(max,mult) (((i / stdcmap->mult) % \
117 (stdcmap->max + 1)) * 65535) / stdcmap->max
119 for (i
= 0; i
< ctx
->ncolors
; i
++) {
120 ctx
->colors
[i
].pixel
= i
+ stdcmap
->base_pixel
;
121 ctx
->colors
[i
].red
= calc(red_max
, red_mult
);
122 ctx
->colors
[i
].green
= calc(green_max
, green_mult
);
123 ctx
->colors
[i
].blue
= calc(blue_max
, blue_mult
);
125 ctx
->pixels
[i
] = ctx
->colors
[i
].pixel
;
133 static Bool
setupStandardColormap(RContext
* ctx
, Atom property
)
135 if (!XmuLookupStandardColormap(ctx
->dpy
, ctx
->screen_number
,
136 ctx
->visual
->visualid
, ctx
->depth
, property
, True
, True
)) {
137 RErrorCode
= RERR_STDCMAPFAIL
;
144 static Bool
allocatePseudoColor(RContext
* ctx
)
147 XColor avcolors
[256];
149 int i
, ncolors
, r
, g
, b
;
151 int cpc
= ctx
->attribs
->colors_per_channel
;
153 ncolors
= cpc
* cpc
* cpc
;
155 if (ncolors
> (1 << ctx
->depth
)) {
156 /* reduce colormap size */
157 cpc
= ctx
->attribs
->colors_per_channel
= 1 << ((int)ctx
->depth
/ 3);
158 ncolors
= cpc
* cpc
* cpc
;
161 assert(cpc
>= 2 && ncolors
<= (1 << ctx
->depth
));
163 colors
= malloc(sizeof(XColor
) * ncolors
);
165 RErrorCode
= RERR_NOMEMORY
;
169 ctx
->pixels
= malloc(sizeof(unsigned long) * ncolors
);
172 RErrorCode
= RERR_NOMEMORY
;
178 if ((ctx
->attribs
->flags
& RC_GammaCorrection
) && ctx
->attribs
->rgamma
> 0
179 && ctx
->attribs
->ggamma
> 0 && ctx
->attribs
->bgamma
> 0) {
183 /* do gamma correction */
184 rg
= 1.0 / ctx
->attribs
->rgamma
;
185 gg
= 1.0 / ctx
->attribs
->ggamma
;
186 bg
= 1.0 / ctx
->attribs
->bgamma
;
187 for (r
= 0; r
< cpc
; r
++) {
188 for (g
= 0; g
< cpc
; g
++) {
189 for (b
= 0; b
< cpc
; b
++) {
190 colors
[i
].red
= (r
* 0xffff) / (cpc
- 1);
191 colors
[i
].green
= (g
* 0xffff) / (cpc
- 1);
192 colors
[i
].blue
= (b
* 0xffff) / (cpc
- 1);
193 colors
[i
].flags
= DoRed
| DoGreen
| DoBlue
;
195 tmp
= (double)colors
[i
].red
/ 65536.0;
196 colors
[i
].red
= (unsigned short)(65536.0 * pow(tmp
, rg
));
198 tmp
= (double)colors
[i
].green
/ 65536.0;
199 colors
[i
].green
= (unsigned short)(65536.0 * pow(tmp
, gg
));
201 tmp
= (double)colors
[i
].blue
/ 65536.0;
202 colors
[i
].blue
= (unsigned short)(65536.0 * pow(tmp
, bg
));
210 for (r
= 0; r
< cpc
; r
++) {
211 for (g
= 0; g
< cpc
; g
++) {
212 for (b
= 0; b
< cpc
; b
++) {
213 colors
[i
].red
= (r
* 0xffff) / (cpc
- 1);
214 colors
[i
].green
= (g
* 0xffff) / (cpc
- 1);
215 colors
[i
].blue
= (b
* 0xffff) / (cpc
- 1);
216 colors
[i
].flags
= DoRed
| DoGreen
| DoBlue
;
222 /* try to allocate the colors */
223 for (i
= 0; i
< ncolors
; i
++) {
224 if (!XAllocColor(ctx
->dpy
, ctx
->cmap
, &(colors
[i
]))) {
225 colors
[i
].flags
= 0; /* failed */
227 colors
[i
].flags
= DoRed
| DoGreen
| DoBlue
;
230 /* try to allocate close values for the colors that couldn't
231 * be allocated before */
232 avncolors
= (1 << ctx
->depth
> 256 ? 256 : 1 << ctx
->depth
);
233 for (i
= 0; i
< avncolors
; i
++)
234 avcolors
[i
].pixel
= i
;
236 XQueryColors(ctx
->dpy
, ctx
->cmap
, avcolors
, avncolors
);
238 for (i
= 0; i
< ncolors
; i
++) {
239 if (colors
[i
].flags
== 0) {
241 unsigned long cdiff
= 0xffffffff, diff
;
242 unsigned long closest
= 0;
247 /* find closest color */
248 for (j
= 0; j
< avncolors
; j
++) {
249 r
= (colors
[i
].red
- avcolors
[i
].red
) >> 8;
250 g
= (colors
[i
].green
- avcolors
[i
].green
) >> 8;
251 b
= (colors
[i
].blue
- avcolors
[i
].blue
) >> 8;
252 diff
= r
* r
+ g
* g
+ b
* b
;
258 /* allocate closest color found */
259 colors
[i
].red
= avcolors
[closest
].red
;
260 colors
[i
].green
= avcolors
[closest
].green
;
261 colors
[i
].blue
= avcolors
[closest
].blue
;
262 if (XAllocColor(ctx
->dpy
, ctx
->cmap
, &colors
[i
])) {
263 colors
[i
].flags
= DoRed
| DoGreen
| DoBlue
;
264 break; /* succeeded, don't need to retry */
267 printf("close color allocation failed. Retrying...\n");
273 ctx
->colors
= colors
;
274 ctx
->ncolors
= ncolors
;
276 /* fill the pixels shortcut array */
277 for (i
= 0; i
< ncolors
; i
++) {
278 ctx
->pixels
[i
] = ctx
->colors
[i
].pixel
;
284 static XColor
*allocateGrayScale(RContext
* ctx
)
287 XColor avcolors
[256];
289 int i
, ncolors
, r
, g
, b
;
291 int cpc
= ctx
->attribs
->colors_per_channel
;
293 ncolors
= cpc
* cpc
* cpc
;
295 if (ctx
->vclass
== StaticGray
) {
296 /* we might as well use all grays */
297 ncolors
= 1 << ctx
->depth
;
299 if (ncolors
> (1 << ctx
->depth
)) {
300 /* reduce colormap size */
301 cpc
= ctx
->attribs
->colors_per_channel
= 1 << ((int)ctx
->depth
/ 3);
302 ncolors
= cpc
* cpc
* cpc
;
305 assert(cpc
>= 2 && ncolors
<= (1 << ctx
->depth
));
308 if (ncolors
>= 256 && ctx
->vclass
== StaticGray
) {
309 /* don't need dithering for 256 levels of gray in StaticGray visual */
310 ctx
->attribs
->render_mode
= RBestMatchRendering
;
313 colors
= malloc(sizeof(XColor
) * ncolors
);
315 RErrorCode
= RERR_NOMEMORY
;
318 for (i
= 0; i
< ncolors
; i
++) {
319 colors
[i
].red
= (i
* 0xffff) / (ncolors
- 1);
320 colors
[i
].green
= (i
* 0xffff) / (ncolors
- 1);
321 colors
[i
].blue
= (i
* 0xffff) / (ncolors
- 1);
322 colors
[i
].flags
= DoRed
| DoGreen
| DoBlue
;
324 /* try to allocate the colors */
325 for (i
= 0; i
< ncolors
; i
++) {
327 printf("trying:%x,%x,%x\n", colors
[i
].red
, colors
[i
].green
, colors
[i
].blue
);
329 if (!XAllocColor(ctx
->dpy
, ctx
->cmap
, &(colors
[i
]))) {
330 colors
[i
].flags
= 0; /* failed */
332 printf("failed:%x,%x,%x\n", colors
[i
].red
, colors
[i
].green
, colors
[i
].blue
);
335 colors
[i
].flags
= DoRed
| DoGreen
| DoBlue
;
337 printf("success:%x,%x,%x\n", colors
[i
].red
, colors
[i
].green
, colors
[i
].blue
);
341 /* try to allocate close values for the colors that couldn't
342 * be allocated before */
343 avncolors
= (1 << ctx
->depth
> 256 ? 256 : 1 << ctx
->depth
);
344 for (i
= 0; i
< avncolors
; i
++)
345 avcolors
[i
].pixel
= i
;
347 XQueryColors(ctx
->dpy
, ctx
->cmap
, avcolors
, avncolors
);
349 for (i
= 0; i
< ncolors
; i
++) {
350 if (colors
[i
].flags
== 0) {
352 unsigned long cdiff
= 0xffffffff, diff
;
353 unsigned long closest
= 0;
358 /* find closest color */
359 for (j
= 0; j
< avncolors
; j
++) {
360 r
= (colors
[i
].red
- avcolors
[i
].red
) >> 8;
361 g
= (colors
[i
].green
- avcolors
[i
].green
) >> 8;
362 b
= (colors
[i
].blue
- avcolors
[i
].blue
) >> 8;
363 diff
= r
* r
+ g
* g
+ b
* b
;
369 /* allocate closest color found */
371 printf("best match:%x,%x,%x => %x,%x,%x\n", colors
[i
].red
, colors
[i
].green
,
372 colors
[i
].blue
, avcolors
[closest
].red
, avcolors
[closest
].green
,
373 avcolors
[closest
].blue
);
375 colors
[i
].red
= avcolors
[closest
].red
;
376 colors
[i
].green
= avcolors
[closest
].green
;
377 colors
[i
].blue
= avcolors
[closest
].blue
;
378 if (XAllocColor(ctx
->dpy
, ctx
->cmap
, &colors
[i
])) {
379 colors
[i
].flags
= DoRed
| DoGreen
| DoBlue
;
380 break; /* succeeded, don't need to retry */
383 printf("close color allocation failed. Retrying...\n");
391 static Bool
setupPseudoColorColormap(RContext
* context
)
395 if (context
->attribs
->standard_colormap_mode
== RCreateStdColormap
) {
396 property
= XInternAtom(context
->dpy
, "RGB_DEFAULT_MAP", False
);
398 if (!setupStandardColormap(context
, property
)) {
403 if (context
->attribs
->standard_colormap_mode
!= RIgnoreStdColormap
) {
404 XStandardColormap
*maps
;
408 property
= XInternAtom(context
->dpy
, "RGB_BEST_MAP", False
);
409 if (!XGetRGBColormaps(context
->dpy
,
410 DefaultRootWindow(context
->dpy
), &maps
, &count
, property
)) {
415 property
= XInternAtom(context
->dpy
, "RGB_DEFAULT_MAP", False
);
416 if (!XGetRGBColormaps(context
->dpy
,
417 DefaultRootWindow(context
->dpy
), &maps
, &count
, property
)) {
422 if (!XGetRGBColormaps(context
->dpy
,
423 DefaultRootWindow(context
->dpy
), &maps
, &count
, property
)) {
431 for (i
= 0; i
< count
; i
++) {
432 if (maps
[i
].visualid
== context
->visual
->visualid
) {
439 puts("wrlib: no std cmap found");
442 if (theMap
>= 0 && allocateStandardPseudoColor(context
, &maps
[theMap
])) {
444 context
->std_rgb_map
= XAllocStandardColormap();
446 *context
->std_rgb_map
= maps
[theMap
];
448 context
->cmap
= context
->std_rgb_map
->colormap
;
459 context
->attribs
->standard_colormap_mode
= RIgnoreStdColormap
;
461 /* RIgnoreStdColormap and fallback */
462 return allocatePseudoColor(context
);
465 static char *mygetenv(char *var
, int scr
)
470 snprintf(varname
, sizeof(varname
), "%s%i", var
, scr
);
478 static void gatherconfig(RContext
* context
, int screen_n
)
482 ptr
= mygetenv("WRASTER_GAMMA", screen_n
);
485 if (sscanf(ptr
, "%f/%f/%f", &g1
, &g2
, &g3
) != 3 || g1
<= 0.0 || g2
<= 0.0 || g3
<= 0.0) {
486 printf("wrlib: invalid value(s) for gamma correction \"%s\"\n", ptr
);
488 context
->attribs
->flags
|= RC_GammaCorrection
;
489 context
->attribs
->rgamma
= g1
;
490 context
->attribs
->ggamma
= g2
;
491 context
->attribs
->bgamma
= g3
;
494 ptr
= mygetenv("WRASTER_COLOR_RESOLUTION", screen_n
);
497 if (sscanf(ptr
, "%d", &i
) != 1 || i
< 2 || i
> 6) {
498 printf("wrlib: invalid value for color resolution \"%s\"\n", ptr
);
500 context
->attribs
->flags
|= RC_ColorsPerChannel
;
501 context
->attribs
->colors_per_channel
= i
;
505 ptr
= mygetenv("WRASTER_OPTIMIZE_FOR_SPEED", screen_n
);
507 context
->flags
.optimize_for_speed
= 1;
509 context
->flags
.optimize_for_speed
= 0;
514 static void getColormap(RContext
* context
, int screen_number
)
516 Colormap cmap
= None
;
517 XStandardColormap
*cmaps
;
520 if (XGetRGBColormaps(context
->dpy
,
521 RootWindow(context
->dpy
, screen_number
), &cmaps
, &ncmaps
, XA_RGB_DEFAULT_MAP
)) {
522 for (i
= 0; i
< ncmaps
; ++i
) {
523 if (cmaps
[i
].visualid
== context
->visual
->visualid
) {
524 cmap
= cmaps
[i
].colormap
;
533 cmap
= XCreateColormap(context
->dpy
,
534 RootWindow(context
->dpy
, screen_number
), context
->visual
, AllocNone
);
536 color
.red
= color
.green
= color
.blue
= 0;
537 XAllocColor(context
->dpy
, cmap
, &color
);
538 context
->black
= color
.pixel
;
540 color
.red
= color
.green
= color
.blue
= 0xffff;
541 XAllocColor(context
->dpy
, cmap
, &color
);
542 context
->white
= color
.pixel
;
545 context
->cmap
= cmap
;
548 static int count_offset(unsigned long mask
)
553 while ((mask
& 1) == 0) {
560 RContext
*RCreateContext(Display
* dpy
, int screen_number
, RContextAttributes
* attribs
)
565 context
= malloc(sizeof(RContext
));
567 RErrorCode
= RERR_NOMEMORY
;
570 memset(context
, 0, sizeof(RContext
));
574 context
->screen_number
= screen_number
;
576 context
->attribs
= malloc(sizeof(RContextAttributes
));
577 if (!context
->attribs
) {
579 RErrorCode
= RERR_NOMEMORY
;
583 *context
->attribs
= DEFAULT_CONTEXT_ATTRIBS
;
585 *context
->attribs
= *attribs
;
587 if (!(context
->attribs
->flags
& RC_StandardColormap
)) {
588 context
->attribs
->standard_colormap_mode
= RUseStdColormap
;
591 if (!(context
->attribs
->flags
& RC_ScalingFilter
)) {
592 context
->attribs
->flags
|= RC_ScalingFilter
;
593 context
->attribs
->scaling_filter
= RMitchellFilter
;
596 /* get configuration from environment variables */
597 gatherconfig(context
, screen_number
);
598 _wraster_change_filter(context
->attribs
->scaling_filter
);
599 if ((context
->attribs
->flags
& RC_VisualID
)) {
600 XVisualInfo
*vinfo
, templ
;
603 templ
.screen
= screen_number
;
604 templ
.visualid
= context
->attribs
->visualid
;
605 vinfo
= XGetVisualInfo(context
->dpy
, VisualIDMask
| VisualScreenMask
, &templ
, &nret
);
606 if (!vinfo
|| nret
== 0) {
608 RErrorCode
= RERR_BADVISUALID
;
612 if (vinfo
[0].visual
== DefaultVisual(dpy
, screen_number
)) {
613 context
->attribs
->flags
|= RC_DefaultVisual
;
615 XSetWindowAttributes attr
;
618 context
->visual
= vinfo
[0].visual
;
619 context
->depth
= vinfo
[0].depth
;
620 context
->vclass
= vinfo
[0].class;
621 getColormap(context
, screen_number
);
622 attr
.colormap
= context
->cmap
;
623 attr
.override_redirect
= True
;
624 attr
.border_pixel
= 0;
625 attr
.background_pixel
= 0;
626 mask
= CWBorderPixel
| CWColormap
| CWOverrideRedirect
| CWBackPixel
;
628 XCreateWindow(dpy
, RootWindow(dpy
, screen_number
), 1, 1,
629 1, 1, 0, context
->depth
, CopyFromParent
, context
->visual
, mask
, &attr
);
635 if (!context
->visual
) {
636 if ((context
->attribs
->flags
& RC_DefaultVisual
)
637 || !bestContext(dpy
, screen_number
, context
)) {
638 context
->visual
= DefaultVisual(dpy
, screen_number
);
639 context
->depth
= DefaultDepth(dpy
, screen_number
);
640 context
->cmap
= DefaultColormap(dpy
, screen_number
);
641 context
->drawable
= RootWindow(dpy
, screen_number
);
642 context
->black
= BlackPixel(dpy
, screen_number
);
643 context
->white
= WhitePixel(dpy
, screen_number
);
644 context
->vclass
= context
->visual
->class;
648 gcv
.function
= GXcopy
;
649 gcv
.graphics_exposures
= False
;
650 context
->copy_gc
= XCreateGC(dpy
, context
->drawable
, GCFunction
| GCGraphicsExposures
, &gcv
);
652 if (context
->vclass
== PseudoColor
|| context
->vclass
== StaticColor
) {
653 if (!setupPseudoColorColormap(context
)) {
657 } else if (context
->vclass
== GrayScale
|| context
->vclass
== StaticGray
) {
658 context
->colors
= allocateGrayScale(context
);
659 if (!context
->colors
) {
663 } else if (context
->vclass
== TrueColor
) {
664 /* calc offsets to create a TrueColor pixel */
665 context
->red_offset
= count_offset(context
->visual
->red_mask
);
666 context
->green_offset
= count_offset(context
->visual
->green_mask
);
667 context
->blue_offset
= count_offset(context
->visual
->blue_mask
);
668 /* disable dithering on 24 bits visuals */
669 if (context
->depth
>= 24)
670 context
->attribs
->render_mode
= RBestMatchRendering
;
673 /* check avaiability of MIT-SHM */
675 if (!(context
->attribs
->flags
& RC_UseSharedMemory
)) {
676 context
->attribs
->flags
|= RC_UseSharedMemory
;
677 context
->attribs
->use_shared_memory
= True
;
680 if (context
->attribs
->use_shared_memory
) {
684 context
->flags
.use_shared_pixmap
= 0;
686 if (!XShmQueryVersion(context
->dpy
, &major
, &minor
, &sharedPixmaps
)) {
687 context
->attribs
->use_shared_memory
= False
;
689 if (XShmPixmapFormat(context
->dpy
) == ZPixmap
)
690 context
->flags
.use_shared_pixmap
= sharedPixmaps
;
698 static Bool
bestContext(Display
* dpy
, int screen_number
, RContext
* context
)
700 XVisualInfo
*vinfo
= NULL
, rvinfo
;
701 int best
= -1, numvis
, i
;
703 XSetWindowAttributes attr
;
705 rvinfo
.class = TrueColor
;
706 rvinfo
.screen
= screen_number
;
707 flags
= VisualClassMask
| VisualScreenMask
;
709 vinfo
= XGetVisualInfo(dpy
, flags
, &rvinfo
, &numvis
);
710 if (vinfo
) { /* look for a TrueColor, 24-bit or more (pref 24) */
711 for (i
= numvis
- 1, best
= -1; i
>= 0; i
--) {
712 if (vinfo
[i
].depth
== 24)
714 else if (vinfo
[i
].depth
> 24 && best
< 0)
719 context
->visual
= vinfo
[best
].visual
;
720 context
->depth
= vinfo
[best
].depth
;
721 context
->vclass
= vinfo
[best
].class;
722 getColormap(context
, screen_number
);
723 attr
.colormap
= context
->cmap
;
724 attr
.override_redirect
= True
;
725 attr
.border_pixel
= 0;
727 XCreateWindow(dpy
, RootWindow(dpy
, screen_number
),
728 1, 1, 1, 1, 0, context
->depth
,
729 CopyFromParent
, context
->visual
,
730 CWBorderPixel
| CWColormap
| CWOverrideRedirect
, &attr
);
733 XFree((char *)vinfo
);