wrlib: Fix typo in macro containing ImageMagick version
[wmaker-crm.git] / wrlib / context.c
blob8e30a63f4f5fd2ca838b89a9f7e8d954195e5094
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., 51 Franklin St, Fifth Floor, Boston,
20 * MA 02110-1301, USA.
23 #include <config.h>
25 #include <X11/Xlib.h>
26 #include <X11/Xutil.h>
27 #include <X11/Xatom.h>
29 #ifdef HAVE_LIBXMU
30 #include <X11/Xmu/StdCmap.h>
31 #endif
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <assert.h>
38 #include <math.h>
40 #include "wraster.h"
41 #include "scale.h"
44 #ifndef HAVE_FLOAT_MATHFUNC
45 #define powf(x, y) ((float) pow((double)(x), (double)(y)))
46 #endif
48 static Bool bestContext(Display * dpy, int screen_number, RContext * context);
50 static const RContextAttributes DEFAULT_CONTEXT_ATTRIBS = {
51 RC_UseSharedMemory | RC_RenderMode | RC_ColorsPerChannel, /* flags */
52 RDitheredRendering, /* render_mode */
53 4, /* colors_per_channel */
58 True, /* use_shared_memory */
59 RMitchellFilter,
60 RUseStdColormap
65 * Colormap allocation for PseudoColor visuals:
68 * switch standardColormap:
69 * none:
70 * allocate colors according to colors_per_channel
72 * best/default:
73 * if there's a std colormap defined then use it
75 * else
76 * 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.
85 * Returns: -
87 * Side effects: -
89 * Notes: -
90 *----------------------------------------------------------------------
92 static Bool allocateStandardPseudoColor(RContext * ctx, XStandardColormap * stdcmap)
94 int i;
96 ctx->ncolors = stdcmap->red_max * stdcmap->red_mult
97 + stdcmap->green_max * stdcmap->green_mult + stdcmap->blue_max * stdcmap->blue_mult + 1;
99 if (ctx->ncolors <= 1) {
100 RErrorCode = RERR_INTERNAL;
101 puts("wraster: bad standard colormap");
103 return False;
106 ctx->colors = malloc(sizeof(XColor) * ctx->ncolors);
107 if (!ctx->colors) {
108 RErrorCode = RERR_NOMEMORY;
110 return False;
113 ctx->pixels = malloc(sizeof(unsigned long) * ctx->ncolors);
114 if (!ctx->pixels) {
116 free(ctx->colors);
117 ctx->colors = NULL;
119 RErrorCode = RERR_NOMEMORY;
121 return False;
124 #define calc(max,mult) (((i / stdcmap->mult) % \
125 (stdcmap->max + 1)) * 65535) / stdcmap->max
127 for (i = 0; i < ctx->ncolors; i++) {
128 ctx->colors[i].pixel = i + stdcmap->base_pixel;
129 ctx->colors[i].red = calc(red_max, red_mult);
130 ctx->colors[i].green = calc(green_max, green_mult);
131 ctx->colors[i].blue = calc(blue_max, blue_mult);
133 ctx->pixels[i] = ctx->colors[i].pixel;
136 #undef calc
138 return True;
141 static Bool setupStandardColormap(RContext * ctx, Atom property)
143 #ifdef HAVE_LIBXMU
144 if (!XmuLookupStandardColormap(ctx->dpy, ctx->screen_number,
145 ctx->visual->visualid, ctx->depth, property, True, True)) {
146 RErrorCode = RERR_STDCMAPFAIL;
148 return False;
150 return True;
151 #else
152 (void) ctx;
153 (void) property;
154 RErrorCode = RERR_STDCMAPFAIL;
155 return False;
156 #endif
159 static XColor *allocateColor(RContext *ctx, XColor *colors, int ncolors)
161 XColor avcolors[256];
162 int avncolors;
163 int i, r, g, b;
164 int retries;
166 for (i = 0; i < ncolors; i++) {
167 #ifdef WRLIB_DEBUG
168 fprintf(stderr, "trying:%x,%x,%x\n", colors[i].red, colors[i].green, colors[i].blue);
169 #endif
170 if (!XAllocColor(ctx->dpy, ctx->cmap, &(colors[i]))) {
171 colors[i].flags = 0; /* failed */
172 #ifdef WRLIB_DEBUG
173 fprintf(stderr, "failed:%x,%x,%x\n", colors[i].red, colors[i].green, colors[i].blue);
174 #endif
175 } else {
176 colors[i].flags = DoRed | DoGreen | DoBlue;
177 #ifdef WRLIB_DEBUG
178 fprintf(stderr, "success:%x,%x,%x\n", colors[i].red, colors[i].green, colors[i].blue);
179 #endif
182 /* try to allocate close values for the colors that couldn't
183 * be allocated before */
184 avncolors = (1 << ctx->depth > 256 ? 256 : 1 << ctx->depth);
185 for (i = 0; i < avncolors; i++)
186 avcolors[i].pixel = i;
188 XQueryColors(ctx->dpy, ctx->cmap, avcolors, avncolors);
190 for (i = 0; i < ncolors; i++) {
191 if (colors[i].flags == 0) {
192 int j;
193 unsigned long cdiff = 0xffffffff, diff;
194 unsigned long closest = 0;
196 retries = 2;
198 while (retries--) {
199 /* find closest color */
200 for (j = 0; j < avncolors; j++) {
201 r = (colors[i].red - avcolors[i].red) >> 8;
202 g = (colors[i].green - avcolors[i].green) >> 8;
203 b = (colors[i].blue - avcolors[i].blue) >> 8;
204 diff = r * r + g * g + b * b;
205 if (diff < cdiff) {
206 cdiff = diff;
207 closest = j;
210 /* allocate closest color found */
211 #ifdef WRLIB_DEBUG
212 fprintf(stderr, "best match:%x,%x,%x => %x,%x,%x\n",
213 colors[i].red, colors[i].green, colors[i].blue,
214 avcolors[closest].red, avcolors[closest].green, avcolors[closest].blue);
215 #endif
216 colors[i].red = avcolors[closest].red;
217 colors[i].green = avcolors[closest].green;
218 colors[i].blue = avcolors[closest].blue;
219 if (XAllocColor(ctx->dpy, ctx->cmap, &colors[i])) {
220 colors[i].flags = DoRed | DoGreen | DoBlue;
221 break; /* succeeded, don't need to retry */
223 #ifdef WRLIB_DEBUG
224 fputs("close color allocation failed. Retrying...\n", stderr);
225 #endif
229 return colors;
232 static Bool allocatePseudoColor(RContext *ctx)
234 XColor *colors;
235 int i, ncolors, r, g, b;
236 int cpc = ctx->attribs->colors_per_channel;
238 ncolors = cpc * cpc * cpc;
240 if (ncolors > (1 << ctx->depth)) {
241 /* reduce colormap size */
242 cpc = ctx->attribs->colors_per_channel = 1 << ((int)ctx->depth / 3);
243 ncolors = cpc * cpc * cpc;
246 assert(cpc >= 2 && ncolors <= (1 << ctx->depth));
248 colors = malloc(sizeof(XColor) * ncolors);
249 if (!colors) {
250 RErrorCode = RERR_NOMEMORY;
251 return False;
254 ctx->pixels = malloc(sizeof(unsigned long) * ncolors);
255 if (!ctx->pixels) {
256 free(colors);
257 RErrorCode = RERR_NOMEMORY;
258 return False;
261 i = 0;
263 if ((ctx->attribs->flags & RC_GammaCorrection) && ctx->attribs->rgamma > 0
264 && ctx->attribs->ggamma > 0 && ctx->attribs->bgamma > 0) {
265 float rg, gg, bg;
266 float tmp;
268 /* do gamma correction */
269 rg = 1.0F / ctx->attribs->rgamma;
270 gg = 1.0F / ctx->attribs->ggamma;
271 bg = 1.0F / ctx->attribs->bgamma;
272 for (r = 0; r < cpc; r++) {
273 for (g = 0; g < cpc; g++) {
274 for (b = 0; b < cpc; b++) {
275 colors[i].red = (r * 0xffff) / (cpc - 1);
276 colors[i].green = (g * 0xffff) / (cpc - 1);
277 colors[i].blue = (b * 0xffff) / (cpc - 1);
278 colors[i].flags = DoRed | DoGreen | DoBlue;
280 tmp = (float) colors[i].red / 65536.0F;
281 colors[i].red = (unsigned short)(65536.0F * powf(tmp, rg));
283 tmp = (float) colors[i].green / 65536.0F;
284 colors[i].green = (unsigned short)(65536.0F * powf(tmp, gg));
286 tmp = (float) colors[i].blue / 65536.0F;
287 colors[i].blue = (unsigned short)(65536.0F * powf(tmp, bg));
289 i++;
294 } else {
295 for (r = 0; r < cpc; r++) {
296 for (g = 0; g < cpc; g++) {
297 for (b = 0; b < cpc; b++) {
298 colors[i].red = (r * 0xffff) / (cpc - 1);
299 colors[i].green = (g * 0xffff) / (cpc - 1);
300 colors[i].blue = (b * 0xffff) / (cpc - 1);
301 colors[i].flags = DoRed | DoGreen | DoBlue;
302 i++;
307 /* try to allocate the colors */
308 ctx->colors = allocateColor(ctx, colors, ncolors);
309 ctx->ncolors = ncolors;
311 /* fill the pixels shortcut array */
312 for (i = 0; i < ncolors; i++) {
313 ctx->pixels[i] = ctx->colors[i].pixel;
316 return True;
319 static XColor *allocateGrayScale(RContext * ctx)
321 XColor *colors;
322 int i, ncolors;
323 int cpc = ctx->attribs->colors_per_channel;
325 ncolors = cpc * cpc * cpc;
327 if (ctx->vclass == StaticGray) {
328 /* we might as well use all grays */
329 ncolors = 1 << ctx->depth;
330 } else {
331 if (ncolors > (1 << ctx->depth)) {
332 /* reduce colormap size */
333 cpc = ctx->attribs->colors_per_channel = 1 << ((int)ctx->depth / 3);
334 ncolors = cpc * cpc * cpc;
337 assert(cpc >= 2 && ncolors <= (1 << ctx->depth));
340 if (ncolors >= 256 && ctx->vclass == StaticGray) {
341 /* don't need dithering for 256 levels of gray in StaticGray visual */
342 ctx->attribs->render_mode = RBestMatchRendering;
345 colors = malloc(sizeof(XColor) * ncolors);
346 if (!colors) {
347 RErrorCode = RERR_NOMEMORY;
348 return False;
350 for (i = 0; i < ncolors; i++) {
351 colors[i].red = (i * 0xffff) / (ncolors - 1);
352 colors[i].green = (i * 0xffff) / (ncolors - 1);
353 colors[i].blue = (i * 0xffff) / (ncolors - 1);
354 colors[i].flags = DoRed | DoGreen | DoBlue;
357 /* try to allocate the colors */
358 return allocateColor(ctx, colors, ncolors);
361 static Bool setupPseudoColorColormap(RContext * context)
363 Atom property = 0;
365 if (context->attribs->standard_colormap_mode == RCreateStdColormap) {
366 property = XInternAtom(context->dpy, "RGB_DEFAULT_MAP", False);
368 if (!setupStandardColormap(context, property)) {
369 return False;
373 if (context->attribs->standard_colormap_mode != RIgnoreStdColormap) {
374 XStandardColormap *maps;
375 int count, i;
377 if (!property) {
378 property = XInternAtom(context->dpy, "RGB_BEST_MAP", False);
379 if (!XGetRGBColormaps(context->dpy,
380 DefaultRootWindow(context->dpy), &maps, &count, property)) {
381 maps = NULL;
384 if (!maps) {
385 property = XInternAtom(context->dpy, "RGB_DEFAULT_MAP", False);
386 if (!XGetRGBColormaps(context->dpy,
387 DefaultRootWindow(context->dpy), &maps, &count, property)) {
388 maps = NULL;
391 } else {
392 if (!XGetRGBColormaps(context->dpy,
393 DefaultRootWindow(context->dpy), &maps, &count, property)) {
394 maps = NULL;
398 if (maps) {
399 int theMap = -1;
401 for (i = 0; i < count; i++) {
402 if (maps[i].visualid == context->visual->visualid) {
403 theMap = i;
404 break;
408 if (theMap < 0) {
409 puts("wrlib: no std cmap found");
412 if (theMap >= 0 && allocateStandardPseudoColor(context, &maps[theMap])) {
414 context->std_rgb_map = XAllocStandardColormap();
416 *context->std_rgb_map = maps[theMap];
418 context->cmap = context->std_rgb_map->colormap;
420 XFree(maps);
422 return True;
425 XFree(maps);
429 context->attribs->standard_colormap_mode = RIgnoreStdColormap;
431 /* RIgnoreStdColormap and fallback */
432 return allocatePseudoColor(context);
435 static char *mygetenv(const char *var, int scr)
437 char *p;
438 char varname[64];
440 snprintf(varname, sizeof(varname), "%s%i", var, scr);
441 p = getenv(varname);
442 if (!p) {
443 p = getenv(var);
445 return p;
448 static void gatherconfig(RContext * context, int screen_n)
450 char *ptr;
452 ptr = mygetenv("WRASTER_GAMMA", screen_n);
453 if (ptr) {
454 float g1, g2, g3;
455 if (sscanf(ptr, "%f/%f/%f", &g1, &g2, &g3) != 3 || g1 <= 0.0F || g2 <= 0.0F || g3 <= 0.0F) {
456 printf("wrlib: invalid value(s) for gamma correction \"%s\"\n", ptr);
457 } else {
458 context->attribs->flags |= RC_GammaCorrection;
459 context->attribs->rgamma = g1;
460 context->attribs->ggamma = g2;
461 context->attribs->bgamma = g3;
464 ptr = mygetenv("WRASTER_COLOR_RESOLUTION", screen_n);
465 if (ptr) {
466 int i;
467 if (sscanf(ptr, "%d", &i) != 1 || i < 2 || i > 6) {
468 printf("wrlib: invalid value for color resolution \"%s\"\n", ptr);
469 } else {
470 context->attribs->flags |= RC_ColorsPerChannel;
471 context->attribs->colors_per_channel = i;
476 static void getColormap(RContext * context, int screen_number)
478 Colormap cmap = None;
479 XStandardColormap *cmaps;
480 int ncmaps, i;
482 if (XGetRGBColormaps(context->dpy,
483 RootWindow(context->dpy, screen_number), &cmaps, &ncmaps, XA_RGB_DEFAULT_MAP)) {
484 for (i = 0; i < ncmaps; ++i) {
485 if (cmaps[i].visualid == context->visual->visualid) {
486 cmap = cmaps[i].colormap;
487 break;
490 XFree(cmaps);
492 if (cmap == None) {
493 XColor color;
495 cmap = XCreateColormap(context->dpy,
496 RootWindow(context->dpy, screen_number), context->visual, AllocNone);
498 color.red = color.green = color.blue = 0;
499 XAllocColor(context->dpy, cmap, &color);
500 context->black = color.pixel;
502 color.red = color.green = color.blue = 0xffff;
503 XAllocColor(context->dpy, cmap, &color);
504 context->white = color.pixel;
507 context->cmap = cmap;
510 static int count_offset(unsigned long mask)
512 int i;
514 i = 0;
515 while ((mask & 1) == 0) {
516 i++;
517 mask = mask >> 1;
519 return i;
522 RContext *RCreateContext(Display * dpy, int screen_number, const RContextAttributes * attribs)
524 RContext *context;
525 XGCValues gcv;
527 context = malloc(sizeof(RContext));
528 if (!context) {
529 RErrorCode = RERR_NOMEMORY;
530 return NULL;
532 memset(context, 0, sizeof(RContext));
534 context->dpy = dpy;
536 context->screen_number = screen_number;
538 context->attribs = malloc(sizeof(RContextAttributes));
539 if (!context->attribs) {
540 free(context);
541 RErrorCode = RERR_NOMEMORY;
542 return NULL;
544 if (!attribs)
545 *context->attribs = DEFAULT_CONTEXT_ATTRIBS;
546 else
547 *context->attribs = *attribs;
549 if (!(context->attribs->flags & RC_StandardColormap)) {
550 context->attribs->standard_colormap_mode = RUseStdColormap;
553 if (!(context->attribs->flags & RC_ScalingFilter)) {
554 context->attribs->flags |= RC_ScalingFilter;
555 context->attribs->scaling_filter = RMitchellFilter;
558 /* get configuration from environment variables */
559 gatherconfig(context, screen_number);
560 wraster_change_filter(context->attribs->scaling_filter);
561 if ((context->attribs->flags & RC_VisualID)) {
562 XVisualInfo *vinfo, templ;
563 int nret;
565 templ.screen = screen_number;
566 templ.visualid = context->attribs->visualid;
567 vinfo = XGetVisualInfo(context->dpy, VisualIDMask | VisualScreenMask, &templ, &nret);
568 if (!vinfo || nret == 0) {
569 free(context);
570 RErrorCode = RERR_BADVISUALID;
571 return NULL;
574 if (vinfo[0].visual == DefaultVisual(dpy, screen_number)) {
575 context->attribs->flags |= RC_DefaultVisual;
576 } else {
577 XSetWindowAttributes attr;
578 unsigned long mask;
580 context->visual = vinfo[0].visual;
581 context->depth = vinfo[0].depth;
582 context->vclass = vinfo[0].class;
583 getColormap(context, screen_number);
584 attr.colormap = context->cmap;
585 attr.override_redirect = True;
586 attr.border_pixel = 0;
587 attr.background_pixel = 0;
588 mask = CWBorderPixel | CWColormap | CWOverrideRedirect | CWBackPixel;
589 context->drawable =
590 XCreateWindow(dpy, RootWindow(dpy, screen_number), 1, 1,
591 1, 1, 0, context->depth, CopyFromParent, context->visual, mask, &attr);
593 XFree(vinfo);
596 /* use default */
597 if (!context->visual) {
598 if ((context->attribs->flags & RC_DefaultVisual)
599 || !bestContext(dpy, screen_number, context)) {
600 context->visual = DefaultVisual(dpy, screen_number);
601 context->depth = DefaultDepth(dpy, screen_number);
602 context->cmap = DefaultColormap(dpy, screen_number);
603 context->drawable = RootWindow(dpy, screen_number);
604 context->black = BlackPixel(dpy, screen_number);
605 context->white = WhitePixel(dpy, screen_number);
606 context->vclass = context->visual->class;
610 gcv.function = GXcopy;
611 gcv.graphics_exposures = False;
612 context->copy_gc = XCreateGC(dpy, context->drawable, GCFunction | GCGraphicsExposures, &gcv);
614 if (context->vclass == PseudoColor || context->vclass == StaticColor) {
615 if (!setupPseudoColorColormap(context)) {
616 free(context);
617 return NULL;
619 } else if (context->vclass == GrayScale || context->vclass == StaticGray) {
620 context->colors = allocateGrayScale(context);
621 if (!context->colors) {
622 free(context);
623 return NULL;
625 } else if (context->vclass == TrueColor) {
626 /* calc offsets to create a TrueColor pixel */
627 context->red_offset = count_offset(context->visual->red_mask);
628 context->green_offset = count_offset(context->visual->green_mask);
629 context->blue_offset = count_offset(context->visual->blue_mask);
630 /* disable dithering on 24 bits visuals */
631 if (context->depth >= 24)
632 context->attribs->render_mode = RBestMatchRendering;
635 /* check avaiability of MIT-SHM */
636 #ifdef USE_XSHM
637 if (!(context->attribs->flags & RC_UseSharedMemory)) {
638 context->attribs->flags |= RC_UseSharedMemory;
639 context->attribs->use_shared_memory = True;
642 if (context->attribs->use_shared_memory) {
643 int major, minor;
644 Bool sharedPixmaps;
646 context->flags.use_shared_pixmap = 0;
648 if (!XShmQueryVersion(context->dpy, &major, &minor, &sharedPixmaps)) {
649 context->attribs->use_shared_memory = False;
650 } else {
651 if (XShmPixmapFormat(context->dpy) == ZPixmap)
652 context->flags.use_shared_pixmap = sharedPixmaps;
655 #endif
657 return context;
660 void RDestroyContext(RContext *context)
662 if (context) {
663 if (context->copy_gc)
664 XFreeGC(context->dpy, context->copy_gc);
665 if (context->attribs) {
666 if ((context->attribs->flags & RC_VisualID) &&
667 !(context->attribs->flags & RC_DefaultVisual))
668 XDestroyWindow(context->dpy, context->drawable);
669 free(context->attribs);
671 free(context);
675 static Bool bestContext(Display * dpy, int screen_number, RContext * context)
677 XVisualInfo *vinfo = NULL, rvinfo;
678 int best = -1, numvis, i;
679 long flags;
680 XSetWindowAttributes attr;
682 rvinfo.class = TrueColor;
683 rvinfo.screen = screen_number;
684 flags = VisualClassMask | VisualScreenMask;
686 vinfo = XGetVisualInfo(dpy, flags, &rvinfo, &numvis);
687 if (vinfo) { /* look for a TrueColor, 24-bit or more (pref 24) */
688 for (i = numvis - 1, best = -1; i >= 0; i--) {
689 if (vinfo[i].depth == 24)
690 best = i;
691 else if (vinfo[i].depth > 24 && best < 0)
692 best = i;
695 if (best > -1) {
696 context->visual = vinfo[best].visual;
697 context->depth = vinfo[best].depth;
698 context->vclass = vinfo[best].class;
699 getColormap(context, screen_number);
700 attr.colormap = context->cmap;
701 attr.override_redirect = True;
702 attr.border_pixel = 0;
703 context->drawable =
704 XCreateWindow(dpy, RootWindow(dpy, screen_number),
705 1, 1, 1, 1, 0, context->depth,
706 CopyFromParent, context->visual,
707 CWBorderPixel | CWColormap | CWOverrideRedirect, &attr);
709 if (vinfo)
710 XFree((char *)vinfo);
712 if (best < 0)
713 return False;
714 else
715 return True;