debian: Update with version 0.95.7-2 packaging.
[wmaker-crm.git] / wrlib / context.c
blobee0b9931b59c9362036867bc0236f83eff5d29d5
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>
28 #include <X11/Xmu/StdCmap.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <assert.h>
35 #include <math.h>
37 #include "wraster.h"
38 #include "scale.h"
41 #ifndef HAVE_FLOAT_MATHFUNC
42 #define powf(x, y) ((float) pow((double)(x), (double)(y)))
43 #endif
45 static Bool bestContext(Display * dpy, int screen_number, RContext * context);
47 static const RContextAttributes DEFAULT_CONTEXT_ATTRIBS = {
48 RC_UseSharedMemory | RC_RenderMode | RC_ColorsPerChannel, /* flags */
49 RDitheredRendering, /* render_mode */
50 4, /* colors_per_channel */
55 True, /* use_shared_memory */
56 RMitchellFilter,
57 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 then use it
72 * else
73 * create a std colormap and set it
77 *----------------------------------------------------------------------
78 * allocateStandardPseudoColor
79 * Creates the internal colormap for PseudoColor, setting the
80 * color values according to the supplied standard colormap.
82 * Returns: -
84 * Side effects: -
86 * Notes: -
87 *----------------------------------------------------------------------
89 static Bool allocateStandardPseudoColor(RContext * ctx, XStandardColormap * stdcmap)
91 int i;
93 ctx->ncolors = stdcmap->red_max * stdcmap->red_mult
94 + stdcmap->green_max * stdcmap->green_mult + stdcmap->blue_max * stdcmap->blue_mult + 1;
96 if (ctx->ncolors <= 1) {
97 RErrorCode = RERR_INTERNAL;
98 puts("wraster: bad standard colormap");
100 return False;
103 ctx->colors = malloc(sizeof(XColor) * ctx->ncolors);
104 if (!ctx->colors) {
105 RErrorCode = RERR_NOMEMORY;
107 return False;
110 ctx->pixels = malloc(sizeof(unsigned long) * ctx->ncolors);
111 if (!ctx->pixels) {
113 free(ctx->colors);
114 ctx->colors = NULL;
116 RErrorCode = RERR_NOMEMORY;
118 return False;
121 #define calc(max,mult) (((i / stdcmap->mult) % \
122 (stdcmap->max + 1)) * 65535) / stdcmap->max
124 for (i = 0; i < ctx->ncolors; i++) {
125 ctx->colors[i].pixel = i + stdcmap->base_pixel;
126 ctx->colors[i].red = calc(red_max, red_mult);
127 ctx->colors[i].green = calc(green_max, green_mult);
128 ctx->colors[i].blue = calc(blue_max, blue_mult);
130 ctx->pixels[i] = ctx->colors[i].pixel;
133 #undef calc
135 return True;
138 static Bool setupStandardColormap(RContext * ctx, Atom property)
140 if (!XmuLookupStandardColormap(ctx->dpy, ctx->screen_number,
141 ctx->visual->visualid, ctx->depth, property, True, True)) {
142 RErrorCode = RERR_STDCMAPFAIL;
144 return False;
146 return True;
149 static XColor *allocateColor(RContext *ctx, XColor *colors, int ncolors)
151 XColor avcolors[256];
152 int avncolors;
153 int i, r, g, b;
154 int retries;
156 for (i = 0; i < ncolors; i++) {
157 #ifdef WRLIB_DEBUG
158 fprintf(stderr, "trying:%x,%x,%x\n", colors[i].red, colors[i].green, colors[i].blue);
159 #endif
160 if (!XAllocColor(ctx->dpy, ctx->cmap, &(colors[i]))) {
161 colors[i].flags = 0; /* failed */
162 #ifdef WRLIB_DEBUG
163 fprintf(stderr, "failed:%x,%x,%x\n", colors[i].red, colors[i].green, colors[i].blue);
164 #endif
165 } else {
166 colors[i].flags = DoRed | DoGreen | DoBlue;
167 #ifdef WRLIB_DEBUG
168 fprintf(stderr, "success:%x,%x,%x\n", colors[i].red, colors[i].green, colors[i].blue);
169 #endif
172 /* try to allocate close values for the colors that couldn't
173 * be allocated before */
174 avncolors = (1 << ctx->depth > 256 ? 256 : 1 << ctx->depth);
175 for (i = 0; i < avncolors; i++)
176 avcolors[i].pixel = i;
178 XQueryColors(ctx->dpy, ctx->cmap, avcolors, avncolors);
180 for (i = 0; i < ncolors; i++) {
181 if (colors[i].flags == 0) {
182 int j;
183 unsigned long cdiff = 0xffffffff, diff;
184 unsigned long closest = 0;
186 retries = 2;
188 while (retries--) {
189 /* find closest color */
190 for (j = 0; j < avncolors; j++) {
191 r = (colors[i].red - avcolors[i].red) >> 8;
192 g = (colors[i].green - avcolors[i].green) >> 8;
193 b = (colors[i].blue - avcolors[i].blue) >> 8;
194 diff = r * r + g * g + b * b;
195 if (diff < cdiff) {
196 cdiff = diff;
197 closest = j;
200 /* allocate closest color found */
201 #ifdef WRLIB_DEBUG
202 fprintf(stderr, "best match:%x,%x,%x => %x,%x,%x\n",
203 colors[i].red, colors[i].green, colors[i].blue,
204 avcolors[closest].red, avcolors[closest].green, avcolors[closest].blue);
205 #endif
206 colors[i].red = avcolors[closest].red;
207 colors[i].green = avcolors[closest].green;
208 colors[i].blue = avcolors[closest].blue;
209 if (XAllocColor(ctx->dpy, ctx->cmap, &colors[i])) {
210 colors[i].flags = DoRed | DoGreen | DoBlue;
211 break; /* succeeded, don't need to retry */
213 #ifdef WRLIB_DEBUG
214 fputs("close color allocation failed. Retrying...\n", stderr);
215 #endif
219 return colors;
222 static Bool allocatePseudoColor(RContext *ctx)
224 XColor *colors;
225 int i, ncolors, r, g, b;
226 int cpc = ctx->attribs->colors_per_channel;
228 ncolors = cpc * cpc * cpc;
230 if (ncolors > (1 << ctx->depth)) {
231 /* reduce colormap size */
232 cpc = ctx->attribs->colors_per_channel = 1 << ((int)ctx->depth / 3);
233 ncolors = cpc * cpc * cpc;
236 assert(cpc >= 2 && ncolors <= (1 << ctx->depth));
238 colors = malloc(sizeof(XColor) * ncolors);
239 if (!colors) {
240 RErrorCode = RERR_NOMEMORY;
241 return False;
244 ctx->pixels = malloc(sizeof(unsigned long) * ncolors);
245 if (!ctx->pixels) {
246 free(colors);
247 RErrorCode = RERR_NOMEMORY;
248 return False;
251 i = 0;
253 if ((ctx->attribs->flags & RC_GammaCorrection) && ctx->attribs->rgamma > 0
254 && ctx->attribs->ggamma > 0 && ctx->attribs->bgamma > 0) {
255 float rg, gg, bg;
256 float tmp;
258 /* do gamma correction */
259 rg = 1.0F / ctx->attribs->rgamma;
260 gg = 1.0F / ctx->attribs->ggamma;
261 bg = 1.0F / ctx->attribs->bgamma;
262 for (r = 0; r < cpc; r++) {
263 for (g = 0; g < cpc; g++) {
264 for (b = 0; b < cpc; b++) {
265 colors[i].red = (r * 0xffff) / (cpc - 1);
266 colors[i].green = (g * 0xffff) / (cpc - 1);
267 colors[i].blue = (b * 0xffff) / (cpc - 1);
268 colors[i].flags = DoRed | DoGreen | DoBlue;
270 tmp = (float) colors[i].red / 65536.0F;
271 colors[i].red = (unsigned short)(65536.0F * powf(tmp, rg));
273 tmp = (float) colors[i].green / 65536.0F;
274 colors[i].green = (unsigned short)(65536.0F * powf(tmp, gg));
276 tmp = (float) colors[i].blue / 65536.0F;
277 colors[i].blue = (unsigned short)(65536.0F * powf(tmp, bg));
279 i++;
284 } else {
285 for (r = 0; r < cpc; r++) {
286 for (g = 0; g < cpc; g++) {
287 for (b = 0; b < cpc; b++) {
288 colors[i].red = (r * 0xffff) / (cpc - 1);
289 colors[i].green = (g * 0xffff) / (cpc - 1);
290 colors[i].blue = (b * 0xffff) / (cpc - 1);
291 colors[i].flags = DoRed | DoGreen | DoBlue;
292 i++;
297 /* try to allocate the colors */
298 ctx->colors = allocateColor(ctx, colors, ncolors);
299 ctx->ncolors = ncolors;
301 /* fill the pixels shortcut array */
302 for (i = 0; i < ncolors; i++) {
303 ctx->pixels[i] = ctx->colors[i].pixel;
306 return True;
309 static XColor *allocateGrayScale(RContext * ctx)
311 XColor *colors;
312 int i, ncolors;
313 int cpc = ctx->attribs->colors_per_channel;
315 ncolors = cpc * cpc * cpc;
317 if (ctx->vclass == StaticGray) {
318 /* we might as well use all grays */
319 ncolors = 1 << ctx->depth;
320 } else {
321 if (ncolors > (1 << ctx->depth)) {
322 /* reduce colormap size */
323 cpc = ctx->attribs->colors_per_channel = 1 << ((int)ctx->depth / 3);
324 ncolors = cpc * cpc * cpc;
327 assert(cpc >= 2 && ncolors <= (1 << ctx->depth));
330 if (ncolors >= 256 && ctx->vclass == StaticGray) {
331 /* don't need dithering for 256 levels of gray in StaticGray visual */
332 ctx->attribs->render_mode = RBestMatchRendering;
335 colors = malloc(sizeof(XColor) * ncolors);
336 if (!colors) {
337 RErrorCode = RERR_NOMEMORY;
338 return False;
340 for (i = 0; i < ncolors; i++) {
341 colors[i].red = (i * 0xffff) / (ncolors - 1);
342 colors[i].green = (i * 0xffff) / (ncolors - 1);
343 colors[i].blue = (i * 0xffff) / (ncolors - 1);
344 colors[i].flags = DoRed | DoGreen | DoBlue;
347 /* try to allocate the colors */
348 return allocateColor(ctx, colors, ncolors);
351 static Bool setupPseudoColorColormap(RContext * context)
353 Atom property = 0;
355 if (context->attribs->standard_colormap_mode == RCreateStdColormap) {
356 property = XInternAtom(context->dpy, "RGB_DEFAULT_MAP", False);
358 if (!setupStandardColormap(context, property)) {
359 return False;
363 if (context->attribs->standard_colormap_mode != RIgnoreStdColormap) {
364 XStandardColormap *maps;
365 int count, i;
367 if (!property) {
368 property = XInternAtom(context->dpy, "RGB_BEST_MAP", False);
369 if (!XGetRGBColormaps(context->dpy,
370 DefaultRootWindow(context->dpy), &maps, &count, property)) {
371 maps = NULL;
374 if (!maps) {
375 property = XInternAtom(context->dpy, "RGB_DEFAULT_MAP", False);
376 if (!XGetRGBColormaps(context->dpy,
377 DefaultRootWindow(context->dpy), &maps, &count, property)) {
378 maps = NULL;
381 } else {
382 if (!XGetRGBColormaps(context->dpy,
383 DefaultRootWindow(context->dpy), &maps, &count, property)) {
384 maps = NULL;
388 if (maps) {
389 int theMap = -1;
391 for (i = 0; i < count; i++) {
392 if (maps[i].visualid == context->visual->visualid) {
393 theMap = i;
394 break;
398 if (theMap < 0) {
399 puts("wrlib: no std cmap found");
402 if (theMap >= 0 && allocateStandardPseudoColor(context, &maps[theMap])) {
404 context->std_rgb_map = XAllocStandardColormap();
406 *context->std_rgb_map = maps[theMap];
408 context->cmap = context->std_rgb_map->colormap;
410 XFree(maps);
412 return True;
415 XFree(maps);
419 context->attribs->standard_colormap_mode = RIgnoreStdColormap;
421 /* RIgnoreStdColormap and fallback */
422 return allocatePseudoColor(context);
425 static char *mygetenv(const char *var, int scr)
427 char *p;
428 char varname[64];
430 snprintf(varname, sizeof(varname), "%s%i", var, scr);
431 p = getenv(varname);
432 if (!p) {
433 p = getenv(var);
435 return p;
438 static void gatherconfig(RContext * context, int screen_n)
440 char *ptr;
442 ptr = mygetenv("WRASTER_GAMMA", screen_n);
443 if (ptr) {
444 float g1, g2, g3;
445 if (sscanf(ptr, "%f/%f/%f", &g1, &g2, &g3) != 3 || g1 <= 0.0F || g2 <= 0.0F || g3 <= 0.0F) {
446 printf("wrlib: invalid value(s) for gamma correction \"%s\"\n", ptr);
447 } else {
448 context->attribs->flags |= RC_GammaCorrection;
449 context->attribs->rgamma = g1;
450 context->attribs->ggamma = g2;
451 context->attribs->bgamma = g3;
454 ptr = mygetenv("WRASTER_COLOR_RESOLUTION", screen_n);
455 if (ptr) {
456 int i;
457 if (sscanf(ptr, "%d", &i) != 1 || i < 2 || i > 6) {
458 printf("wrlib: invalid value for color resolution \"%s\"\n", ptr);
459 } else {
460 context->attribs->flags |= RC_ColorsPerChannel;
461 context->attribs->colors_per_channel = i;
466 static void getColormap(RContext * context, int screen_number)
468 Colormap cmap = None;
469 XStandardColormap *cmaps;
470 int ncmaps, i;
472 if (XGetRGBColormaps(context->dpy,
473 RootWindow(context->dpy, screen_number), &cmaps, &ncmaps, XA_RGB_DEFAULT_MAP)) {
474 for (i = 0; i < ncmaps; ++i) {
475 if (cmaps[i].visualid == context->visual->visualid) {
476 cmap = cmaps[i].colormap;
477 break;
480 XFree(cmaps);
482 if (cmap == None) {
483 XColor color;
485 cmap = XCreateColormap(context->dpy,
486 RootWindow(context->dpy, screen_number), context->visual, AllocNone);
488 color.red = color.green = color.blue = 0;
489 XAllocColor(context->dpy, cmap, &color);
490 context->black = color.pixel;
492 color.red = color.green = color.blue = 0xffff;
493 XAllocColor(context->dpy, cmap, &color);
494 context->white = color.pixel;
497 context->cmap = cmap;
500 static int count_offset(unsigned long mask)
502 int i;
504 i = 0;
505 while ((mask & 1) == 0) {
506 i++;
507 mask = mask >> 1;
509 return i;
512 RContext *RCreateContext(Display * dpy, int screen_number, const RContextAttributes * attribs)
514 RContext *context;
515 XGCValues gcv;
517 context = malloc(sizeof(RContext));
518 if (!context) {
519 RErrorCode = RERR_NOMEMORY;
520 return NULL;
522 memset(context, 0, sizeof(RContext));
524 context->dpy = dpy;
526 context->screen_number = screen_number;
528 context->attribs = malloc(sizeof(RContextAttributes));
529 if (!context->attribs) {
530 free(context);
531 RErrorCode = RERR_NOMEMORY;
532 return NULL;
534 if (!attribs)
535 *context->attribs = DEFAULT_CONTEXT_ATTRIBS;
536 else
537 *context->attribs = *attribs;
539 if (!(context->attribs->flags & RC_StandardColormap)) {
540 context->attribs->standard_colormap_mode = RUseStdColormap;
543 if (!(context->attribs->flags & RC_ScalingFilter)) {
544 context->attribs->flags |= RC_ScalingFilter;
545 context->attribs->scaling_filter = RMitchellFilter;
548 /* get configuration from environment variables */
549 gatherconfig(context, screen_number);
550 wraster_change_filter(context->attribs->scaling_filter);
551 if ((context->attribs->flags & RC_VisualID)) {
552 XVisualInfo *vinfo, templ;
553 int nret;
555 templ.screen = screen_number;
556 templ.visualid = context->attribs->visualid;
557 vinfo = XGetVisualInfo(context->dpy, VisualIDMask | VisualScreenMask, &templ, &nret);
558 if (!vinfo || nret == 0) {
559 free(context);
560 RErrorCode = RERR_BADVISUALID;
561 return NULL;
564 if (vinfo[0].visual == DefaultVisual(dpy, screen_number)) {
565 context->attribs->flags |= RC_DefaultVisual;
566 } else {
567 XSetWindowAttributes attr;
568 unsigned long mask;
570 context->visual = vinfo[0].visual;
571 context->depth = vinfo[0].depth;
572 context->vclass = vinfo[0].class;
573 getColormap(context, screen_number);
574 attr.colormap = context->cmap;
575 attr.override_redirect = True;
576 attr.border_pixel = 0;
577 attr.background_pixel = 0;
578 mask = CWBorderPixel | CWColormap | CWOverrideRedirect | CWBackPixel;
579 context->drawable =
580 XCreateWindow(dpy, RootWindow(dpy, screen_number), 1, 1,
581 1, 1, 0, context->depth, CopyFromParent, context->visual, mask, &attr);
583 XFree(vinfo);
586 /* use default */
587 if (!context->visual) {
588 if ((context->attribs->flags & RC_DefaultVisual)
589 || !bestContext(dpy, screen_number, context)) {
590 context->visual = DefaultVisual(dpy, screen_number);
591 context->depth = DefaultDepth(dpy, screen_number);
592 context->cmap = DefaultColormap(dpy, screen_number);
593 context->drawable = RootWindow(dpy, screen_number);
594 context->black = BlackPixel(dpy, screen_number);
595 context->white = WhitePixel(dpy, screen_number);
596 context->vclass = context->visual->class;
600 gcv.function = GXcopy;
601 gcv.graphics_exposures = False;
602 context->copy_gc = XCreateGC(dpy, context->drawable, GCFunction | GCGraphicsExposures, &gcv);
604 if (context->vclass == PseudoColor || context->vclass == StaticColor) {
605 if (!setupPseudoColorColormap(context)) {
606 free(context);
607 return NULL;
609 } else if (context->vclass == GrayScale || context->vclass == StaticGray) {
610 context->colors = allocateGrayScale(context);
611 if (!context->colors) {
612 free(context);
613 return NULL;
615 } else if (context->vclass == TrueColor) {
616 /* calc offsets to create a TrueColor pixel */
617 context->red_offset = count_offset(context->visual->red_mask);
618 context->green_offset = count_offset(context->visual->green_mask);
619 context->blue_offset = count_offset(context->visual->blue_mask);
620 /* disable dithering on 24 bits visuals */
621 if (context->depth >= 24)
622 context->attribs->render_mode = RBestMatchRendering;
625 /* check avaiability of MIT-SHM */
626 #ifdef USE_XSHM
627 if (!(context->attribs->flags & RC_UseSharedMemory)) {
628 context->attribs->flags |= RC_UseSharedMemory;
629 context->attribs->use_shared_memory = True;
632 if (context->attribs->use_shared_memory) {
633 int major, minor;
634 Bool sharedPixmaps;
636 context->flags.use_shared_pixmap = 0;
638 if (!XShmQueryVersion(context->dpy, &major, &minor, &sharedPixmaps)) {
639 context->attribs->use_shared_memory = False;
640 } else {
641 if (XShmPixmapFormat(context->dpy) == ZPixmap)
642 context->flags.use_shared_pixmap = sharedPixmaps;
645 #endif
647 return context;
650 void RDestroyContext(RContext *context)
652 if (context) {
653 if (context->copy_gc)
654 XFreeGC(context->dpy, context->copy_gc);
655 if (context->attribs) {
656 if ((context->attribs->flags & RC_VisualID) &&
657 !(context->attribs->flags & RC_DefaultVisual))
658 XDestroyWindow(context->dpy, context->drawable);
659 free(context->attribs);
661 free(context);
665 static Bool bestContext(Display * dpy, int screen_number, RContext * context)
667 XVisualInfo *vinfo = NULL, rvinfo;
668 int best = -1, numvis, i;
669 long flags;
670 XSetWindowAttributes attr;
672 rvinfo.class = TrueColor;
673 rvinfo.screen = screen_number;
674 flags = VisualClassMask | VisualScreenMask;
676 vinfo = XGetVisualInfo(dpy, flags, &rvinfo, &numvis);
677 if (vinfo) { /* look for a TrueColor, 24-bit or more (pref 24) */
678 for (i = numvis - 1, best = -1; i >= 0; i--) {
679 if (vinfo[i].depth == 24)
680 best = i;
681 else if (vinfo[i].depth > 24 && best < 0)
682 best = i;
685 if (best > -1) {
686 context->visual = vinfo[best].visual;
687 context->depth = vinfo[best].depth;
688 context->vclass = vinfo[best].class;
689 getColormap(context, screen_number);
690 attr.colormap = context->cmap;
691 attr.override_redirect = True;
692 attr.border_pixel = 0;
693 context->drawable =
694 XCreateWindow(dpy, RootWindow(dpy, screen_number),
695 1, 1, 1, 1, 0, context->depth,
696 CopyFromParent, context->visual,
697 CWBorderPixel | CWColormap | CWOverrideRedirect, &attr);
699 if (vinfo)
700 XFree((char *)vinfo);
702 if (best < 0)
703 return False;
704 else
705 return True;