- Added a test in configure for the version of libPropList that is installed
[wmaker-crm.git] / wrlib / context.c
blob9ddebba4f6f0bdf2636af98b7fa52dcea1d63d13
1 /* context.c - X context management
2 *
3 * Raster graphics library
4 *
5 * Copyright (c) 1997, 1998, 1999 Alfredo K. Kojima
6 *
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.
22 #include <config.h>
24 #include <X11/Xlib.h>
25 #include <X11/Xutil.h>
26 #include <X11/Xatom.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <assert.h>
33 #include <math.h>
35 #include "StdCmap.h"
37 #include "wraster.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 */
49 0,
53 True, /* use_shared_memory */
54 RMitchellFilter,
55 RUseStdColormap
62 * Colormap allocation for PseudoColor visuals:
65 * switch standardColormap:
66 * none:
67 * allocate colors according to colors_per_channel
69 * best/default:
70 * if there's a std colormap defined
71 then use it
73 * else
74 * create a std colormap and set it
81 *----------------------------------------------------------------------
82 * allocateStandardPseudoColor
83 * Creates the internal colormap for PseudoColor, setting the
84 * color values according to the supplied standard colormap.
86 * Returns: -
88 * Side effects: -
90 * Notes: -
91 *----------------------------------------------------------------------
93 static Bool
94 allocateStandardPseudoColor(RContext *ctx, XStandardColormap *stdcmap)
96 int i;
98 ctx->ncolors = stdcmap->red_max * stdcmap->red_mult
99 + stdcmap->green_max * stdcmap->green_mult
100 + stdcmap->blue_max * stdcmap->blue_mult + 1;
102 if (ctx->ncolors <= 1) {
103 RErrorCode = RERR_INTERNAL;
104 puts("wraster: bad standard colormap");
106 return False;
109 ctx->colors = malloc(sizeof(XColor)*ctx->ncolors);
110 if (!ctx->colors) {
111 RErrorCode = RERR_NOMEMORY;
113 return False;
117 #define calc(max,mult) (((i / stdcmap->mult) % \
118 (stdcmap->max + 1)) * 65535) / stdcmap->max
120 for (i = 0; i < ctx->ncolors; i++) {
121 ctx->colors[i].pixel = i + stdcmap->base_pixel;
122 ctx->colors[i].red = calc(red_max, red_mult);
123 ctx->colors[i].green = calc(green_max, green_mult);
124 ctx->colors[i].blue = calc(blue_max, blue_mult);
127 #undef calc
129 return True;
133 static Bool
134 setupStandardColormap(RContext *ctx, Atom property)
136 if (!XmuLookupStandardColormap(ctx->dpy, ctx->screen_number,
137 ctx->visual->visualid,
138 ctx->depth, property,
139 True, True)) {
140 RErrorCode = RERR_STDCMAPFAIL;
142 return False;
144 return True;
155 static Bool
156 allocatePseudoColor(RContext *ctx)
158 XColor *colors;
159 XColor avcolors[256];
160 int avncolors;
161 int i, ncolors, r, g, b;
162 int retries;
163 int cpc = ctx->attribs->colors_per_channel;
165 ncolors = cpc * cpc * cpc;
167 if (ncolors > (1<<ctx->depth)) {
168 /* reduce colormap size */
169 cpc = ctx->attribs->colors_per_channel = 1<<((int)ctx->depth/3);
170 ncolors = cpc * cpc * cpc;
173 assert(cpc >= 2 && ncolors <= (1<<ctx->depth));
175 colors = malloc(sizeof(XColor)*ncolors);
176 if (!colors) {
177 RErrorCode = RERR_NOMEMORY;
178 return False;
180 i=0;
182 if ((ctx->attribs->flags & RC_GammaCorrection) && ctx->attribs->rgamma > 0
183 && ctx->attribs->ggamma > 0 && ctx->attribs->bgamma > 0) {
184 double rg, gg, bg;
185 double tmp;
187 /* do gamma correction */
188 rg = 1.0/ctx->attribs->rgamma;
189 gg = 1.0/ctx->attribs->ggamma;
190 bg = 1.0/ctx->attribs->bgamma;
191 for (r=0; r<cpc; r++) {
192 for (g=0; g<cpc; g++) {
193 for (b=0; b<cpc; b++) {
194 colors[i].red=(r*0xffff) / (cpc-1);
195 colors[i].green=(g*0xffff) / (cpc-1);
196 colors[i].blue=(b*0xffff) / (cpc-1);
197 colors[i].flags = DoRed|DoGreen|DoBlue;
199 tmp = (double)colors[i].red / 65536.0;
200 colors[i].red = (unsigned short)(65536.0*pow(tmp, rg));
202 tmp = (double)colors[i].green / 65536.0;
203 colors[i].green = (unsigned short)(65536.0*pow(tmp, gg));
205 tmp = (double)colors[i].blue / 65536.0;
206 colors[i].blue = (unsigned short)(65536.0*pow(tmp, bg));
208 i++;
213 } else {
214 for (r=0; r<cpc; r++) {
215 for (g=0; g<cpc; g++) {
216 for (b=0; b<cpc; b++) {
217 colors[i].red=(r*0xffff) / (cpc-1);
218 colors[i].green=(g*0xffff) / (cpc-1);
219 colors[i].blue=(b*0xffff) / (cpc-1);
220 colors[i].flags = DoRed|DoGreen|DoBlue;
221 i++;
226 /* try to allocate the colors */
227 for (i=0; i<ncolors; i++) {
228 if (!XAllocColor(ctx->dpy, ctx->cmap, &(colors[i]))) {
229 colors[i].flags = 0; /* failed */
230 } else {
231 colors[i].flags = DoRed|DoGreen|DoBlue;
234 /* try to allocate close values for the colors that couldn't
235 * be allocated before */
236 avncolors = (1<<ctx->depth>256 ? 256 : 1<<ctx->depth);
237 for (i=0; i<avncolors; i++) avcolors[i].pixel = i;
239 XQueryColors(ctx->dpy, ctx->cmap, avcolors, avncolors);
241 for (i=0; i<ncolors; i++) {
242 if (colors[i].flags==0) {
243 int j;
244 unsigned long cdiff=0xffffffff, diff;
245 unsigned long closest=0;
247 retries = 2;
249 while (retries--) {
250 /* find closest color */
251 for (j=0; j<avncolors; j++) {
252 r = (colors[i].red - avcolors[i].red)>>8;
253 g = (colors[i].green - avcolors[i].green)>>8;
254 b = (colors[i].blue - avcolors[i].blue)>>8;
255 diff = r*r + g*g + b*b;
256 if (diff<cdiff) {
257 cdiff = diff;
258 closest = j;
261 /* allocate closest color found */
262 colors[i].red = avcolors[closest].red;
263 colors[i].green = avcolors[closest].green;
264 colors[i].blue = avcolors[closest].blue;
265 if (XAllocColor(ctx->dpy, ctx->cmap, &colors[i])) {
266 colors[i].flags = DoRed|DoGreen|DoBlue;
267 break; /* succeeded, don't need to retry */
269 #ifdef DEBUG
270 printf("close color allocation failed. Retrying...\n");
271 #endif
276 ctx->colors = colors;
277 ctx->ncolors = ncolors;
279 return True;
283 static XColor*
284 allocateGrayScale(RContext *ctx)
286 XColor *colors;
287 XColor avcolors[256];
288 int avncolors;
289 int i, ncolors, r, g, b;
290 int retries;
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;
298 } else {
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);
314 if (!colors) {
315 RErrorCode = RERR_NOMEMORY;
316 return False;
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++) {
326 #ifdef DEBUG
327 printf("trying:%x,%x,%x\n",colors[i].red,colors[i].green,colors[i].blue);
328 #endif
329 if (!XAllocColor(ctx->dpy, ctx->cmap, &(colors[i]))) {
330 colors[i].flags = 0; /* failed */
331 #ifdef DEBUG
332 printf("failed:%x,%x,%x\n",colors[i].red,colors[i].green,colors[i].blue);
333 #endif
334 } else {
335 colors[i].flags = DoRed|DoGreen|DoBlue;
336 #ifdef DEBUG
337 printf("success:%x,%x,%x\n",colors[i].red,colors[i].green,colors[i].blue);
338 #endif
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++) avcolors[i].pixel = i;
346 XQueryColors(ctx->dpy, ctx->cmap, avcolors, avncolors);
348 for (i=0; i<ncolors; i++) {
349 if (colors[i].flags==0) {
350 int j;
351 unsigned long cdiff=0xffffffff, diff;
352 unsigned long closest=0;
354 retries = 2;
356 while (retries--) {
357 /* find closest color */
358 for (j=0; j<avncolors; j++) {
359 r = (colors[i].red - avcolors[i].red)>>8;
360 g = (colors[i].green - avcolors[i].green)>>8;
361 b = (colors[i].blue - avcolors[i].blue)>>8;
362 diff = r*r + g*g + b*b;
363 if (diff<cdiff) {
364 cdiff = diff;
365 closest = j;
368 /* allocate closest color found */
369 #ifdef DEBUG
370 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);
371 #endif
372 colors[i].red = avcolors[closest].red;
373 colors[i].green = avcolors[closest].green;
374 colors[i].blue = avcolors[closest].blue;
375 if (XAllocColor(ctx->dpy, ctx->cmap, &colors[i])) {
376 colors[i].flags = DoRed|DoGreen|DoBlue;
377 break; /* succeeded, don't need to retry */
379 #ifdef DEBUG
380 printf("close color allocation failed. Retrying...\n");
381 #endif
385 return colors;
389 static Bool
390 setupPseudoColorColormap(RContext *context)
392 Atom property = 0;
394 if (context->attribs->standard_colormap_mode == RCreateStdColormap) {
395 property = XInternAtom(context->dpy, "RGB_DEFAULT_MAP", False);
397 if (!setupStandardColormap(context, property)) {
398 return False;
402 if (context->attribs->standard_colormap_mode != RIgnoreStdColormap) {
403 XStandardColormap *maps;
404 int count, i;
406 if (!property) {
407 property = XInternAtom(context->dpy, "RGB_BEST_MAP", False);
408 if (!XGetRGBColormaps(context->dpy,
409 DefaultRootWindow(context->dpy),
410 &maps, &count, property)) {
411 maps = NULL;
414 if (!maps) {
415 property = XInternAtom(context->dpy, "RGB_DEFAULT_MAP", False);
416 if (!XGetRGBColormaps(context->dpy,
417 DefaultRootWindow(context->dpy),
418 &maps, &count, property)) {
419 maps = NULL;
422 } else {
423 if (!XGetRGBColormaps(context->dpy,
424 DefaultRootWindow(context->dpy),
425 &maps, &count, property)) {
426 maps = NULL;
430 if (maps) {
431 int theMap = -1;
433 for (i = 0; i < count; i++) {
434 if (maps[i].visualid == context->visual->visualid) {
435 theMap = i;
436 break;
440 if (theMap < 0) {
441 puts("wrlib: no std cmap found");
444 if (theMap >= 0
445 && allocateStandardPseudoColor(context, &maps[theMap])) {
447 context->std_rgb_map = XAllocStandardColormap();
449 *context->std_rgb_map = maps[theMap];
451 context->cmap = context->std_rgb_map->colormap;
453 XFree(maps);
455 return True;
458 XFree(maps);
462 context->attribs->standard_colormap_mode = RIgnoreStdColormap;
464 /* RIgnoreStdColormap and fallback */
465 return allocatePseudoColor(context);
471 static char*
472 mygetenv(char *var, int scr)
474 char *p;
475 char varname[64];
477 sprintf(varname, "%s%i", var, scr);
478 p = getenv(varname);
479 if (!p) {
480 p = getenv(var);
482 return p;
486 static void
487 gatherconfig(RContext *context, int screen_n)
489 char *ptr;
491 ptr = mygetenv("WRASTER_GAMMA", screen_n);
492 if (ptr) {
493 float g1,g2,g3;
494 if (sscanf(ptr, "%f/%f/%f", &g1, &g2, &g3)!=3
495 || g1<=0.0 || g2<=0.0 || g3<=0.0) {
496 printf("wrlib: invalid value(s) for gamma correction \"%s\"\n",
497 ptr);
498 } else {
499 context->attribs->flags |= RC_GammaCorrection;
500 context->attribs->rgamma = g1;
501 context->attribs->ggamma = g2;
502 context->attribs->bgamma = g3;
505 ptr = mygetenv("WRASTER_COLOR_RESOLUTION", screen_n);
506 if (ptr) {
507 int i;
508 if (sscanf(ptr, "%d", &i)!=1 || i<2 || i>6) {
509 printf("wrlib: invalid value for color resolution \"%s\"\n",ptr);
510 } else {
511 context->attribs->flags |= RC_ColorsPerChannel;
512 context->attribs->colors_per_channel = i;
516 ptr = mygetenv("WRASTER_OPTIMIZE_FOR_SPEED", screen_n);
517 if (ptr) {
518 context->flags.optimize_for_speed = 1;
519 } else {
520 context->flags.optimize_for_speed = 0;
526 static void
527 getColormap(RContext *context, int screen_number)
529 Colormap cmap = None;
530 XStandardColormap *cmaps;
531 int ncmaps, i;
533 if (XGetRGBColormaps(context->dpy,
534 RootWindow(context->dpy, screen_number),
535 &cmaps, &ncmaps, XA_RGB_DEFAULT_MAP)) {
536 for (i=0; i<ncmaps; ++i) {
537 if (cmaps[i].visualid == context->visual->visualid) {
538 puts("ACHOU");
539 cmap = cmaps[i].colormap;
540 break;
543 XFree(cmaps);
545 if (cmap == None) {
546 XColor color;
548 cmap = XCreateColormap(context->dpy,
549 RootWindow(context->dpy, screen_number),
550 context->visual, AllocNone);
552 color.red = color.green = color.blue = 0;
553 XAllocColor(context->dpy, cmap, &color);
554 context->black = color.pixel;
556 color.red = color.green = color.blue = 0xffff;
557 XAllocColor(context->dpy, cmap, &color);
558 context->white = color.pixel;
561 context->cmap = cmap;
565 static int
566 count_offset(unsigned long mask)
568 int i;
570 i=0;
571 while ((mask & 1)==0) {
572 i++;
573 mask = mask >> 1;
575 return i;
579 RContext*
580 RCreateContext(Display *dpy, int screen_number, RContextAttributes *attribs)
582 RContext *context;
583 XGCValues gcv;
586 context = malloc(sizeof(RContext));
587 if (!context) {
588 RErrorCode = RERR_NOMEMORY;
589 return NULL;
591 memset(context, 0, sizeof(RContext));
593 context->dpy = dpy;
595 context->screen_number = screen_number;
597 context->attribs = malloc(sizeof(RContextAttributes));
598 if (!context->attribs) {
599 free(context);
600 RErrorCode = RERR_NOMEMORY;
601 return NULL;
603 if (!attribs)
604 *context->attribs = DEFAULT_CONTEXT_ATTRIBS;
605 else
606 *context->attribs = *attribs;
608 if (!(context->attribs->flags & RC_StandardColormap)) {
609 context->attribs->standard_colormap_mode = RUseStdColormap;
612 /* get configuration from environment variables */
613 gatherconfig(context, screen_number);
614 #ifndef BENCH
615 _wraster_change_filter(context->attribs->scaling_filter);
616 #endif
617 if ((context->attribs->flags & RC_VisualID)) {
618 XVisualInfo *vinfo, templ;
619 int nret;
621 templ.screen = screen_number;
622 templ.visualid = context->attribs->visualid;
623 vinfo = XGetVisualInfo(context->dpy, VisualIDMask|VisualScreenMask,
624 &templ, &nret);
625 if (!vinfo || nret==0) {
626 free(context);
627 RErrorCode = RERR_BADVISUALID;
628 return NULL;
631 if (vinfo[0].visual == DefaultVisual(dpy, screen_number)) {
632 context->attribs->flags |= RC_DefaultVisual;
633 } else {
634 XSetWindowAttributes attr;
635 unsigned long mask;
637 context->visual = vinfo[0].visual;
638 context->depth = vinfo[0].depth;
639 context->vclass = vinfo[0].class;
640 getColormap(context, screen_number);
641 attr.colormap = context->cmap;
642 attr.override_redirect = True;
643 attr.border_pixel = 0;
644 attr.background_pixel = 0;
645 mask = CWBorderPixel|CWColormap|CWOverrideRedirect|CWBackPixel;
646 context->drawable =
647 XCreateWindow(dpy, RootWindow(dpy, screen_number), 1, 1,
648 1, 1, 0, context->depth, CopyFromParent,
649 context->visual, mask, &attr);
650 /* XSetWindowColormap(dpy, context->drawable, attr.colormap);*/
652 XFree(vinfo);
655 /* use default */
656 if (!context->visual) {
657 if ((context->attribs->flags & RC_DefaultVisual)
658 || !bestContext(dpy, screen_number, context)) {
659 context->visual = DefaultVisual(dpy, screen_number);
660 context->depth = DefaultDepth(dpy, screen_number);
661 context->cmap = DefaultColormap(dpy, screen_number);
662 context->drawable = RootWindow(dpy, screen_number);
663 context->black = BlackPixel(dpy, screen_number);
664 context->white = WhitePixel(dpy, screen_number);
665 context->vclass = context->visual->class;
669 gcv.function = GXcopy;
670 gcv.graphics_exposures = False;
671 context->copy_gc = XCreateGC(dpy, context->drawable, GCFunction
672 |GCGraphicsExposures, &gcv);
674 if (context->vclass == PseudoColor || context->vclass == StaticColor) {
676 if (!setupPseudoColorColormap(context)) {
677 return NULL;
680 } else if (context->vclass == GrayScale || context->vclass == StaticGray) {
681 context->colors = allocateGrayScale(context);
682 if (!context->colors) {
683 return NULL;
685 } else if (context->vclass == TrueColor) {
686 /* calc offsets to create a TrueColor pixel */
687 context->red_offset = count_offset(context->visual->red_mask);
688 context->green_offset = count_offset(context->visual->green_mask);
689 context->blue_offset = count_offset(context->visual->blue_mask);
690 /* disable dithering on 24 bits visuals */
691 if (context->depth >= 24)
692 context->attribs->render_mode = RBestMatchRendering;
695 /* check avaiability of MIT-SHM */
696 #ifdef XSHM
697 if (!(context->attribs->flags & RC_UseSharedMemory)) {
698 context->attribs->flags |= RC_UseSharedMemory;
699 context->attribs->use_shared_memory = True;
702 if (context->attribs->use_shared_memory) {
703 int major, minor;
704 Bool sharedPixmaps;
706 context->flags.use_shared_pixmap = 0;
708 if (!XShmQueryVersion(context->dpy, &major, &minor, &sharedPixmaps)) {
709 context->attribs->use_shared_memory = False;
710 } else {
711 if (XShmPixmapFormat(context->dpy)==ZPixmap)
712 context->flags.use_shared_pixmap = sharedPixmaps;
715 #endif
717 return context;
721 static Bool
722 bestContext(Display *dpy, int screen_number, RContext *context)
724 XVisualInfo *vinfo=NULL, rvinfo;
725 int best = -1, numvis, i;
726 long flags;
727 XSetWindowAttributes attr;
729 rvinfo.class = TrueColor;
730 rvinfo.screen = screen_number;
731 flags = VisualClassMask | VisualScreenMask;
733 vinfo = XGetVisualInfo(dpy, flags, &rvinfo, &numvis);
734 if (vinfo) { /* look for a TrueColor, 24-bit or more (pref 24) */
735 for (i=numvis-1, best = -1; i>=0; i--) {
736 if (vinfo[i].depth == 24) best = i;
737 else if (vinfo[i].depth>24 && best<0) best = i;
741 #if 0
742 if (best == -1) { /* look for a DirectColor, 24-bit or more (pref 24) */
743 rvinfo.class = DirectColor;
744 if (vinfo) XFree((char *) vinfo);
745 vinfo = XGetVisualInfo(dpy, flags, &rvinfo, &numvis);
746 if (vinfo) {
747 for (i=0, best = -1; i<numvis; i++) {
748 if (vinfo[i].depth == 24) best = i;
749 else if (vinfo[i].depth>24 && best<0) best = i;
753 #endif
754 if (best > -1) {
755 context->visual = vinfo[best].visual;
756 context->depth = vinfo[best].depth;
757 context->vclass = vinfo[best].class;
758 getColormap(context, screen_number);
759 attr.colormap = context->cmap;
760 attr.override_redirect = True;
761 attr.border_pixel = 0;
762 context->drawable =
763 XCreateWindow(dpy, RootWindow(dpy, screen_number),
764 1, 1, 1, 1, 0, context->depth,
765 CopyFromParent, context->visual,
766 CWBorderPixel|CWColormap|CWOverrideRedirect, &attr);
767 /* XSetWindowColormap(dpy, context->drawable, context->cmap);*/
769 if (vinfo) XFree((char *) vinfo);
771 if (best < 0)
772 return False;
773 else
774 return True;