Update Serbian translation from master branch
[wmaker-crm.git] / wrlib / context.c
blob730445066129249274ee76110e431a57a79cbd9e
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"
42 #include "wr_i18n.h"
45 #ifndef HAVE_FLOAT_MATHFUNC
46 #define powf(x, y) ((float) pow((double)(x), (double)(y)))
47 #endif
49 static Bool bestContext(Display * dpy, int screen_number, RContext * context);
51 static const RContextAttributes DEFAULT_CONTEXT_ATTRIBS = {
52 RC_UseSharedMemory | RC_RenderMode | RC_ColorsPerChannel, /* flags */
53 RDitheredRendering, /* render_mode */
54 4, /* colors_per_channel */
59 True, /* use_shared_memory */
60 RMitchellFilter,
61 RUseStdColormap
66 * Colormap allocation for PseudoColor visuals:
69 * switch standardColormap:
70 * none:
71 * allocate colors according to colors_per_channel
73 * best/default:
74 * if there's a std colormap defined then use it
76 * else
77 * 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 allocateStandardPseudoColor(RContext * ctx, XStandardColormap * stdcmap)
95 int i;
97 ctx->ncolors = stdcmap->red_max * stdcmap->red_mult
98 + stdcmap->green_max * stdcmap->green_mult + stdcmap->blue_max * stdcmap->blue_mult + 1;
100 if (ctx->ncolors <= 1) {
101 RErrorCode = RERR_INTERNAL;
102 puts("wraster: bad standard colormap");
104 return False;
107 ctx->colors = malloc(sizeof(XColor) * ctx->ncolors);
108 if (!ctx->colors) {
109 RErrorCode = RERR_NOMEMORY;
111 return False;
114 ctx->pixels = malloc(sizeof(unsigned long) * ctx->ncolors);
115 if (!ctx->pixels) {
117 free(ctx->colors);
118 ctx->colors = NULL;
120 RErrorCode = RERR_NOMEMORY;
122 return False;
125 #define calc(max,mult) (((i / stdcmap->mult) % \
126 (stdcmap->max + 1)) * 65535) / stdcmap->max
128 for (i = 0; i < ctx->ncolors; i++) {
129 ctx->colors[i].pixel = i + stdcmap->base_pixel;
130 ctx->colors[i].red = calc(red_max, red_mult);
131 ctx->colors[i].green = calc(green_max, green_mult);
132 ctx->colors[i].blue = calc(blue_max, blue_mult);
134 ctx->pixels[i] = ctx->colors[i].pixel;
137 #undef calc
139 return True;
142 static Bool setupStandardColormap(RContext * ctx, Atom property)
144 #ifdef HAVE_LIBXMU
145 if (!XmuLookupStandardColormap(ctx->dpy, ctx->screen_number,
146 ctx->visual->visualid, ctx->depth, property, True, True)) {
147 RErrorCode = RERR_STDCMAPFAIL;
149 return False;
151 return True;
152 #else
153 (void) ctx;
154 (void) property;
155 RErrorCode = RERR_STDCMAPFAIL;
156 return False;
157 #endif
160 static XColor *allocateColor(RContext *ctx, XColor *colors, int ncolors)
162 XColor avcolors[256];
163 int avncolors;
164 int i, r, g, b;
165 int retries;
167 for (i = 0; i < ncolors; i++) {
168 #ifdef WRLIB_DEBUG
169 fprintf(stderr, "trying:%x,%x,%x\n", colors[i].red, colors[i].green, colors[i].blue);
170 #endif
171 if (!XAllocColor(ctx->dpy, ctx->cmap, &(colors[i]))) {
172 colors[i].flags = 0; /* failed */
173 #ifdef WRLIB_DEBUG
174 fprintf(stderr, "failed:%x,%x,%x\n", colors[i].red, colors[i].green, colors[i].blue);
175 #endif
176 } else {
177 colors[i].flags = DoRed | DoGreen | DoBlue;
178 #ifdef WRLIB_DEBUG
179 fprintf(stderr, "success:%x,%x,%x\n", colors[i].red, colors[i].green, colors[i].blue);
180 #endif
183 /* try to allocate close values for the colors that couldn't
184 * be allocated before */
185 avncolors = (1 << ctx->depth > 256 ? 256 : 1 << ctx->depth);
186 for (i = 0; i < avncolors; i++)
187 avcolors[i].pixel = i;
189 XQueryColors(ctx->dpy, ctx->cmap, avcolors, avncolors);
191 for (i = 0; i < ncolors; i++) {
192 if (colors[i].flags == 0) {
193 int j;
194 unsigned long cdiff = 0xffffffff, diff;
195 unsigned long closest = 0;
197 retries = 2;
199 while (retries--) {
200 /* find closest color */
201 for (j = 0; j < avncolors; j++) {
202 r = (colors[i].red - avcolors[i].red) >> 8;
203 g = (colors[i].green - avcolors[i].green) >> 8;
204 b = (colors[i].blue - avcolors[i].blue) >> 8;
205 diff = r * r + g * g + b * b;
206 if (diff < cdiff) {
207 cdiff = diff;
208 closest = j;
211 /* allocate closest color found */
212 #ifdef WRLIB_DEBUG
213 fprintf(stderr, "best match:%x,%x,%x => %x,%x,%x\n",
214 colors[i].red, colors[i].green, colors[i].blue,
215 avcolors[closest].red, avcolors[closest].green, avcolors[closest].blue);
216 #endif
217 colors[i].red = avcolors[closest].red;
218 colors[i].green = avcolors[closest].green;
219 colors[i].blue = avcolors[closest].blue;
220 if (XAllocColor(ctx->dpy, ctx->cmap, &colors[i])) {
221 colors[i].flags = DoRed | DoGreen | DoBlue;
222 break; /* succeeded, don't need to retry */
224 #ifdef WRLIB_DEBUG
225 fputs("close color allocation failed. Retrying...\n", stderr);
226 #endif
230 return colors;
233 static Bool allocatePseudoColor(RContext *ctx)
235 XColor *colors;
236 int i, ncolors, r, g, b;
237 int cpc = ctx->attribs->colors_per_channel;
239 ncolors = cpc * cpc * cpc;
241 if (ncolors > (1 << ctx->depth)) {
242 /* reduce colormap size */
243 cpc = ctx->attribs->colors_per_channel = 1 << ((int)ctx->depth / 3);
244 ncolors = cpc * cpc * cpc;
247 assert(cpc >= 2 && ncolors <= (1 << ctx->depth));
249 colors = malloc(sizeof(XColor) * ncolors);
250 if (!colors) {
251 RErrorCode = RERR_NOMEMORY;
252 return False;
255 ctx->pixels = malloc(sizeof(unsigned long) * ncolors);
256 if (!ctx->pixels) {
257 free(colors);
258 RErrorCode = RERR_NOMEMORY;
259 return False;
262 i = 0;
264 if ((ctx->attribs->flags & RC_GammaCorrection) && ctx->attribs->rgamma > 0
265 && ctx->attribs->ggamma > 0 && ctx->attribs->bgamma > 0) {
266 float rg, gg, bg;
267 float tmp;
269 /* do gamma correction */
270 rg = 1.0F / ctx->attribs->rgamma;
271 gg = 1.0F / ctx->attribs->ggamma;
272 bg = 1.0F / ctx->attribs->bgamma;
273 for (r = 0; r < cpc; r++) {
274 for (g = 0; g < cpc; g++) {
275 for (b = 0; b < cpc; b++) {
276 colors[i].red = (r * 0xffff) / (cpc - 1);
277 colors[i].green = (g * 0xffff) / (cpc - 1);
278 colors[i].blue = (b * 0xffff) / (cpc - 1);
279 colors[i].flags = DoRed | DoGreen | DoBlue;
281 tmp = (float) colors[i].red / 65536.0F;
282 colors[i].red = (unsigned short)(65536.0F * powf(tmp, rg));
284 tmp = (float) colors[i].green / 65536.0F;
285 colors[i].green = (unsigned short)(65536.0F * powf(tmp, gg));
287 tmp = (float) colors[i].blue / 65536.0F;
288 colors[i].blue = (unsigned short)(65536.0F * powf(tmp, bg));
290 i++;
295 } else {
296 for (r = 0; r < cpc; r++) {
297 for (g = 0; g < cpc; g++) {
298 for (b = 0; b < cpc; b++) {
299 colors[i].red = (r * 0xffff) / (cpc - 1);
300 colors[i].green = (g * 0xffff) / (cpc - 1);
301 colors[i].blue = (b * 0xffff) / (cpc - 1);
302 colors[i].flags = DoRed | DoGreen | DoBlue;
303 i++;
308 /* try to allocate the colors */
309 ctx->colors = allocateColor(ctx, colors, ncolors);
310 ctx->ncolors = ncolors;
312 /* fill the pixels shortcut array */
313 for (i = 0; i < ncolors; i++) {
314 ctx->pixels[i] = ctx->colors[i].pixel;
317 return True;
320 static XColor *allocateGrayScale(RContext * ctx)
322 XColor *colors;
323 int i, ncolors;
324 int cpc = ctx->attribs->colors_per_channel;
326 ncolors = cpc * cpc * cpc;
328 if (ctx->vclass == StaticGray) {
329 /* we might as well use all grays */
330 ncolors = 1 << ctx->depth;
331 } else {
332 if (ncolors > (1 << ctx->depth)) {
333 /* reduce colormap size */
334 cpc = ctx->attribs->colors_per_channel = 1 << ((int)ctx->depth / 3);
335 ncolors = cpc * cpc * cpc;
338 assert(cpc >= 2 && ncolors <= (1 << ctx->depth));
341 if (ncolors >= 256 && ctx->vclass == StaticGray) {
342 /* don't need dithering for 256 levels of gray in StaticGray visual */
343 ctx->attribs->render_mode = RBestMatchRendering;
346 colors = malloc(sizeof(XColor) * ncolors);
347 if (!colors) {
348 RErrorCode = RERR_NOMEMORY;
349 return False;
351 for (i = 0; i < ncolors; i++) {
352 colors[i].red = (i * 0xffff) / (ncolors - 1);
353 colors[i].green = (i * 0xffff) / (ncolors - 1);
354 colors[i].blue = (i * 0xffff) / (ncolors - 1);
355 colors[i].flags = DoRed | DoGreen | DoBlue;
358 /* try to allocate the colors */
359 return allocateColor(ctx, colors, ncolors);
362 static Bool setupPseudoColorColormap(RContext * context)
364 Atom property = 0;
366 if (context->attribs->standard_colormap_mode == RCreateStdColormap) {
367 property = XInternAtom(context->dpy, "RGB_DEFAULT_MAP", False);
369 if (!setupStandardColormap(context, property)) {
370 return False;
374 if (context->attribs->standard_colormap_mode != RIgnoreStdColormap) {
375 XStandardColormap *maps;
376 int count, i;
378 if (!property) {
379 property = XInternAtom(context->dpy, "RGB_BEST_MAP", False);
380 if (!XGetRGBColormaps(context->dpy,
381 DefaultRootWindow(context->dpy), &maps, &count, property)) {
382 maps = NULL;
385 if (!maps) {
386 property = XInternAtom(context->dpy, "RGB_DEFAULT_MAP", False);
387 if (!XGetRGBColormaps(context->dpy,
388 DefaultRootWindow(context->dpy), &maps, &count, property)) {
389 maps = NULL;
392 } else {
393 if (!XGetRGBColormaps(context->dpy,
394 DefaultRootWindow(context->dpy), &maps, &count, property)) {
395 maps = NULL;
399 if (maps) {
400 int theMap = -1;
402 for (i = 0; i < count; i++) {
403 if (maps[i].visualid == context->visual->visualid) {
404 theMap = i;
405 break;
409 if (theMap < 0) {
410 fprintf(stderr, _("wrlib: no standard colormap found for visual 0x%lX\n"),
411 context->visual->visualid);
414 if (theMap >= 0 && allocateStandardPseudoColor(context, &maps[theMap])) {
416 context->std_rgb_map = XAllocStandardColormap();
418 *context->std_rgb_map = maps[theMap];
420 context->cmap = context->std_rgb_map->colormap;
422 XFree(maps);
424 return True;
427 XFree(maps);
431 context->attribs->standard_colormap_mode = RIgnoreStdColormap;
433 /* RIgnoreStdColormap and fallback */
434 return allocatePseudoColor(context);
437 static char *mygetenv(const char *var, int scr)
439 char *p;
440 char varname[64];
442 snprintf(varname, sizeof(varname), "%s%i", var, scr);
443 p = getenv(varname);
444 if (!p) {
445 p = getenv(var);
447 return p;
450 static void gatherconfig(RContext * context, int screen_n)
452 char *ptr;
454 ptr = mygetenv("WRASTER_GAMMA", screen_n);
455 if (ptr) {
456 float g1, g2, g3;
457 if (sscanf(ptr, "%f/%f/%f", &g1, &g2, &g3) != 3 || g1 <= 0.0F || g2 <= 0.0F || g3 <= 0.0F) {
458 fprintf(stderr, _("wrlib: invalid value \"%s\" for %s\n"), ptr, "WRASTER_GAMMA");
459 } else {
460 context->attribs->flags |= RC_GammaCorrection;
461 context->attribs->rgamma = g1;
462 context->attribs->ggamma = g2;
463 context->attribs->bgamma = g3;
466 ptr = mygetenv("WRASTER_COLOR_RESOLUTION", screen_n);
467 if (ptr) {
468 int i;
469 if (sscanf(ptr, "%d", &i) != 1 || i < 2 || i > 6) {
470 fprintf(stderr, _("wrlib: invalid value \"%s\" for %s\n"), ptr, "WRASTER_COLOR_RESOLUTION");
471 } else {
472 context->attribs->flags |= RC_ColorsPerChannel;
473 context->attribs->colors_per_channel = i;
478 static void getColormap(RContext * context, int screen_number)
480 Colormap cmap = None;
481 XStandardColormap *cmaps;
482 int ncmaps, i;
484 if (XGetRGBColormaps(context->dpy,
485 RootWindow(context->dpy, screen_number), &cmaps, &ncmaps, XA_RGB_DEFAULT_MAP)) {
486 for (i = 0; i < ncmaps; ++i) {
487 if (cmaps[i].visualid == context->visual->visualid) {
488 cmap = cmaps[i].colormap;
489 break;
492 XFree(cmaps);
494 if (cmap == None) {
495 XColor color;
497 cmap = XCreateColormap(context->dpy,
498 RootWindow(context->dpy, screen_number), context->visual, AllocNone);
500 color.red = color.green = color.blue = 0;
501 XAllocColor(context->dpy, cmap, &color);
502 context->black = color.pixel;
504 color.red = color.green = color.blue = 0xffff;
505 XAllocColor(context->dpy, cmap, &color);
506 context->white = color.pixel;
509 context->cmap = cmap;
512 static int count_offset(unsigned long mask)
514 int i;
516 i = 0;
517 while ((mask & 1) == 0) {
518 i++;
519 mask = mask >> 1;
521 return i;
524 RContext *RCreateContext(Display * dpy, int screen_number, const RContextAttributes * attribs)
526 RContext *context;
527 XGCValues gcv;
529 context = malloc(sizeof(RContext));
530 if (!context) {
531 RErrorCode = RERR_NOMEMORY;
532 return NULL;
534 memset(context, 0, sizeof(RContext));
536 context->dpy = dpy;
538 context->screen_number = screen_number;
540 context->attribs = malloc(sizeof(RContextAttributes));
541 if (!context->attribs) {
542 free(context);
543 RErrorCode = RERR_NOMEMORY;
544 return NULL;
546 if (!attribs)
547 *context->attribs = DEFAULT_CONTEXT_ATTRIBS;
548 else
549 *context->attribs = *attribs;
551 if (!(context->attribs->flags & RC_StandardColormap)) {
552 context->attribs->standard_colormap_mode = RUseStdColormap;
555 if (!(context->attribs->flags & RC_ScalingFilter)) {
556 context->attribs->flags |= RC_ScalingFilter;
557 context->attribs->scaling_filter = RMitchellFilter;
560 /* get configuration from environment variables */
561 gatherconfig(context, screen_number);
562 wraster_change_filter(context->attribs->scaling_filter);
563 if ((context->attribs->flags & RC_VisualID)) {
564 XVisualInfo *vinfo, templ;
565 int nret;
567 templ.screen = screen_number;
568 templ.visualid = context->attribs->visualid;
569 vinfo = XGetVisualInfo(context->dpy, VisualIDMask | VisualScreenMask, &templ, &nret);
570 if (!vinfo || nret == 0) {
571 free(context);
572 RErrorCode = RERR_BADVISUALID;
573 return NULL;
576 if (vinfo[0].visual == DefaultVisual(dpy, screen_number)) {
577 context->attribs->flags |= RC_DefaultVisual;
578 } else {
579 XSetWindowAttributes attr;
580 unsigned long mask;
582 context->visual = vinfo[0].visual;
583 context->depth = vinfo[0].depth;
584 context->vclass = vinfo[0].class;
585 getColormap(context, screen_number);
586 attr.colormap = context->cmap;
587 attr.override_redirect = True;
588 attr.border_pixel = 0;
589 attr.background_pixel = 0;
590 mask = CWBorderPixel | CWColormap | CWOverrideRedirect | CWBackPixel;
591 context->drawable =
592 XCreateWindow(dpy, RootWindow(dpy, screen_number), 1, 1,
593 1, 1, 0, context->depth, CopyFromParent, context->visual, mask, &attr);
595 XFree(vinfo);
598 /* use default */
599 if (!context->visual) {
600 if ((context->attribs->flags & RC_DefaultVisual)
601 || !bestContext(dpy, screen_number, context)) {
602 context->visual = DefaultVisual(dpy, screen_number);
603 context->depth = DefaultDepth(dpy, screen_number);
604 context->cmap = DefaultColormap(dpy, screen_number);
605 context->drawable = RootWindow(dpy, screen_number);
606 context->black = BlackPixel(dpy, screen_number);
607 context->white = WhitePixel(dpy, screen_number);
608 context->vclass = context->visual->class;
612 gcv.function = GXcopy;
613 gcv.graphics_exposures = False;
614 context->copy_gc = XCreateGC(dpy, context->drawable, GCFunction | GCGraphicsExposures, &gcv);
616 if (context->vclass == PseudoColor || context->vclass == StaticColor) {
617 if (!setupPseudoColorColormap(context)) {
618 free(context);
619 return NULL;
621 } else if (context->vclass == GrayScale || context->vclass == StaticGray) {
622 context->colors = allocateGrayScale(context);
623 if (!context->colors) {
624 free(context);
625 return NULL;
627 } else if (context->vclass == TrueColor) {
628 /* calc offsets to create a TrueColor pixel */
629 context->red_offset = count_offset(context->visual->red_mask);
630 context->green_offset = count_offset(context->visual->green_mask);
631 context->blue_offset = count_offset(context->visual->blue_mask);
632 /* disable dithering on 24 bits visuals */
633 if (context->depth >= 24)
634 context->attribs->render_mode = RBestMatchRendering;
637 /* check avaiability of MIT-SHM */
638 #ifdef USE_XSHM
639 if (!(context->attribs->flags & RC_UseSharedMemory)) {
640 context->attribs->flags |= RC_UseSharedMemory;
641 context->attribs->use_shared_memory = True;
644 if (context->attribs->use_shared_memory) {
645 int major, minor;
646 Bool sharedPixmaps;
648 context->flags.use_shared_pixmap = 0;
650 if (!XShmQueryVersion(context->dpy, &major, &minor, &sharedPixmaps)) {
651 context->attribs->use_shared_memory = False;
652 } else {
653 if (XShmPixmapFormat(context->dpy) == ZPixmap)
654 context->flags.use_shared_pixmap = sharedPixmaps;
657 #endif
659 return context;
662 void RDestroyContext(RContext *context)
664 if (context) {
665 if (context->copy_gc)
666 XFreeGC(context->dpy, context->copy_gc);
667 if (context->attribs) {
668 if ((context->attribs->flags & RC_VisualID) &&
669 !(context->attribs->flags & RC_DefaultVisual))
670 XDestroyWindow(context->dpy, context->drawable);
671 free(context->attribs);
673 free(context);
677 static Bool bestContext(Display * dpy, int screen_number, RContext * context)
679 XVisualInfo *vinfo = NULL, rvinfo;
680 int best = -1, numvis, i;
681 long flags;
682 XSetWindowAttributes attr;
684 rvinfo.class = TrueColor;
685 rvinfo.screen = screen_number;
686 flags = VisualClassMask | VisualScreenMask;
688 vinfo = XGetVisualInfo(dpy, flags, &rvinfo, &numvis);
689 if (vinfo) { /* look for a TrueColor, 24-bit or more (pref 24) */
690 for (i = numvis - 1, best = -1; i >= 0; i--) {
691 if (vinfo[i].depth == 24)
692 best = i;
693 else if (vinfo[i].depth > 24 && best < 0)
694 best = i;
697 if (best > -1) {
698 context->visual = vinfo[best].visual;
699 context->depth = vinfo[best].depth;
700 context->vclass = vinfo[best].class;
701 getColormap(context, screen_number);
702 attr.colormap = context->cmap;
703 attr.override_redirect = True;
704 attr.border_pixel = 0;
705 context->drawable =
706 XCreateWindow(dpy, RootWindow(dpy, screen_number),
707 1, 1, 1, 1, 0, context->depth,
708 CopyFromParent, context->visual,
709 CWBorderPixel | CWColormap | CWOverrideRedirect, &attr);
711 if (vinfo)
712 XFree((char *)vinfo);
714 if (best < 0)
715 return False;
716 else
717 return True;