- Fixed crashing bug in menu.c
[wmaker-crm.git] / wrlib / context.c
blob678d00fd8f93d590c77842eaf0e5ef9fb825f72e
1 /* context.c - X context management
2 *
3 * Raster graphics library
4 *
5 * Copyright (c) 1997-2003 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 then use it
72 * else
73 * 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
93 allocateStandardPseudoColor(RContext *ctx, XStandardColormap *stdcmap)
95 int i;
97 ctx->ncolors = stdcmap->red_max * stdcmap->red_mult
98 + stdcmap->green_max * stdcmap->green_mult
99 + stdcmap->blue_max * stdcmap->blue_mult + 1;
101 if (ctx->ncolors <= 1) {
102 RErrorCode = RERR_INTERNAL;
103 puts("wraster: bad standard colormap");
105 return False;
108 ctx->colors = malloc(sizeof(XColor)*ctx->ncolors);
109 if (!ctx->colors) {
110 RErrorCode = RERR_NOMEMORY;
112 return False;
115 ctx->pixels = malloc(sizeof(unsigned long)*ctx->ncolors);
116 if (!ctx->pixels) {
118 free(ctx->colors);
119 ctx->colors = NULL;
121 RErrorCode = RERR_NOMEMORY;
123 return False;
127 #define calc(max,mult) (((i / stdcmap->mult) % \
128 (stdcmap->max + 1)) * 65535) / stdcmap->max
130 for (i = 0; i < ctx->ncolors; i++) {
131 ctx->colors[i].pixel = i + stdcmap->base_pixel;
132 ctx->colors[i].red = calc(red_max, red_mult);
133 ctx->colors[i].green = calc(green_max, green_mult);
134 ctx->colors[i].blue = calc(blue_max, blue_mult);
136 ctx->pixels[i] = ctx->colors[i].pixel;
139 #undef calc
141 return True;
145 static Bool
146 setupStandardColormap(RContext *ctx, Atom property)
148 if (!XmuLookupStandardColormap(ctx->dpy, ctx->screen_number,
149 ctx->visual->visualid,
150 ctx->depth, property,
151 True, True)) {
152 RErrorCode = RERR_STDCMAPFAIL;
154 return False;
156 return True;
167 static Bool
168 allocatePseudoColor(RContext *ctx)
170 XColor *colors;
171 XColor avcolors[256];
172 int avncolors;
173 int i, ncolors, r, g, b;
174 int retries;
175 int cpc = ctx->attribs->colors_per_channel;
177 ncolors = cpc * cpc * cpc;
179 if (ncolors > (1<<ctx->depth)) {
180 /* reduce colormap size */
181 cpc = ctx->attribs->colors_per_channel = 1<<((int)ctx->depth/3);
182 ncolors = cpc * cpc * cpc;
185 assert(cpc >= 2 && ncolors <= (1<<ctx->depth));
187 colors = malloc(sizeof(XColor)*ncolors);
188 if (!colors) {
189 RErrorCode = RERR_NOMEMORY;
190 return False;
193 ctx->pixels = malloc(sizeof(unsigned long)*ncolors);
194 if (!ctx->pixels) {
195 free(colors);
196 RErrorCode = RERR_NOMEMORY;
197 return False;
200 i=0;
202 if ((ctx->attribs->flags & RC_GammaCorrection) && ctx->attribs->rgamma > 0
203 && ctx->attribs->ggamma > 0 && ctx->attribs->bgamma > 0) {
204 double rg, gg, bg;
205 double tmp;
207 /* do gamma correction */
208 rg = 1.0/ctx->attribs->rgamma;
209 gg = 1.0/ctx->attribs->ggamma;
210 bg = 1.0/ctx->attribs->bgamma;
211 for (r=0; r<cpc; r++) {
212 for (g=0; g<cpc; g++) {
213 for (b=0; b<cpc; b++) {
214 colors[i].red=(r*0xffff) / (cpc-1);
215 colors[i].green=(g*0xffff) / (cpc-1);
216 colors[i].blue=(b*0xffff) / (cpc-1);
217 colors[i].flags = DoRed|DoGreen|DoBlue;
219 tmp = (double)colors[i].red / 65536.0;
220 colors[i].red = (unsigned short)(65536.0*pow(tmp, rg));
222 tmp = (double)colors[i].green / 65536.0;
223 colors[i].green = (unsigned short)(65536.0*pow(tmp, gg));
225 tmp = (double)colors[i].blue / 65536.0;
226 colors[i].blue = (unsigned short)(65536.0*pow(tmp, bg));
228 i++;
233 } else {
234 for (r=0; r<cpc; r++) {
235 for (g=0; g<cpc; g++) {
236 for (b=0; b<cpc; b++) {
237 colors[i].red=(r*0xffff) / (cpc-1);
238 colors[i].green=(g*0xffff) / (cpc-1);
239 colors[i].blue=(b*0xffff) / (cpc-1);
240 colors[i].flags = DoRed|DoGreen|DoBlue;
241 i++;
246 /* try to allocate the colors */
247 for (i=0; i<ncolors; i++) {
248 if (!XAllocColor(ctx->dpy, ctx->cmap, &(colors[i]))) {
249 colors[i].flags = 0; /* failed */
250 } else {
251 colors[i].flags = DoRed|DoGreen|DoBlue;
254 /* try to allocate close values for the colors that couldn't
255 * be allocated before */
256 avncolors = (1<<ctx->depth>256 ? 256 : 1<<ctx->depth);
257 for (i=0; i<avncolors; i++) avcolors[i].pixel = i;
259 XQueryColors(ctx->dpy, ctx->cmap, avcolors, avncolors);
261 for (i=0; i<ncolors; i++) {
262 if (colors[i].flags==0) {
263 int j;
264 unsigned long cdiff=0xffffffff, diff;
265 unsigned long closest=0;
267 retries = 2;
269 while (retries--) {
270 /* find closest color */
271 for (j=0; j<avncolors; j++) {
272 r = (colors[i].red - avcolors[i].red)>>8;
273 g = (colors[i].green - avcolors[i].green)>>8;
274 b = (colors[i].blue - avcolors[i].blue)>>8;
275 diff = r*r + g*g + b*b;
276 if (diff<cdiff) {
277 cdiff = diff;
278 closest = j;
281 /* allocate closest color found */
282 colors[i].red = avcolors[closest].red;
283 colors[i].green = avcolors[closest].green;
284 colors[i].blue = avcolors[closest].blue;
285 if (XAllocColor(ctx->dpy, ctx->cmap, &colors[i])) {
286 colors[i].flags = DoRed|DoGreen|DoBlue;
287 break; /* succeeded, don't need to retry */
289 #ifdef DEBUG
290 printf("close color allocation failed. Retrying...\n");
291 #endif
296 ctx->colors = colors;
297 ctx->ncolors = ncolors;
299 /* fill the pixels shortcut array */
300 for (i = 0; i < ncolors; i++) {
301 ctx->pixels[i] = ctx->colors[i].pixel;
304 return True;
308 static XColor*
309 allocateGrayScale(RContext *ctx)
311 XColor *colors;
312 XColor avcolors[256];
313 int avncolors;
314 int i, ncolors, r, g, b;
315 int retries;
316 int cpc = ctx->attribs->colors_per_channel;
318 ncolors = cpc * cpc * cpc;
320 if (ctx->vclass == StaticGray) {
321 /* we might as well use all grays */
322 ncolors = 1<<ctx->depth;
323 } else {
324 if ( ncolors > (1<<ctx->depth) ) {
325 /* reduce colormap size */
326 cpc = ctx->attribs->colors_per_channel = 1<<((int)ctx->depth/3);
327 ncolors = cpc * cpc * cpc;
330 assert(cpc >= 2 && ncolors <= (1<<ctx->depth));
333 if (ncolors>=256 && ctx->vclass==StaticGray) {
334 /* don't need dithering for 256 levels of gray in StaticGray visual */
335 ctx->attribs->render_mode = RBestMatchRendering;
338 colors = malloc(sizeof(XColor)*ncolors);
339 if (!colors) {
340 RErrorCode = RERR_NOMEMORY;
341 return False;
343 for (i=0; i<ncolors; i++) {
344 colors[i].red=(i*0xffff) / (ncolors-1);
345 colors[i].green=(i*0xffff) / (ncolors-1);
346 colors[i].blue=(i*0xffff) / (ncolors-1);
347 colors[i].flags = DoRed|DoGreen|DoBlue;
349 /* try to allocate the colors */
350 for (i=0; i<ncolors; i++) {
351 #ifdef DEBUG
352 printf("trying:%x,%x,%x\n",colors[i].red,colors[i].green,colors[i].blue);
353 #endif
354 if (!XAllocColor(ctx->dpy, ctx->cmap, &(colors[i]))) {
355 colors[i].flags = 0; /* failed */
356 #ifdef DEBUG
357 printf("failed:%x,%x,%x\n",colors[i].red,colors[i].green,colors[i].blue);
358 #endif
359 } else {
360 colors[i].flags = DoRed|DoGreen|DoBlue;
361 #ifdef DEBUG
362 printf("success:%x,%x,%x\n",colors[i].red,colors[i].green,colors[i].blue);
363 #endif
366 /* try to allocate close values for the colors that couldn't
367 * be allocated before */
368 avncolors = (1<<ctx->depth>256 ? 256 : 1<<ctx->depth);
369 for (i=0; i<avncolors; i++) avcolors[i].pixel = i;
371 XQueryColors(ctx->dpy, ctx->cmap, avcolors, avncolors);
373 for (i=0; i<ncolors; i++) {
374 if (colors[i].flags==0) {
375 int j;
376 unsigned long cdiff=0xffffffff, diff;
377 unsigned long closest=0;
379 retries = 2;
381 while (retries--) {
382 /* find closest color */
383 for (j=0; j<avncolors; j++) {
384 r = (colors[i].red - avcolors[i].red)>>8;
385 g = (colors[i].green - avcolors[i].green)>>8;
386 b = (colors[i].blue - avcolors[i].blue)>>8;
387 diff = r*r + g*g + b*b;
388 if (diff<cdiff) {
389 cdiff = diff;
390 closest = j;
393 /* allocate closest color found */
394 #ifdef DEBUG
395 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);
396 #endif
397 colors[i].red = avcolors[closest].red;
398 colors[i].green = avcolors[closest].green;
399 colors[i].blue = avcolors[closest].blue;
400 if (XAllocColor(ctx->dpy, ctx->cmap, &colors[i])) {
401 colors[i].flags = DoRed|DoGreen|DoBlue;
402 break; /* succeeded, don't need to retry */
404 #ifdef DEBUG
405 printf("close color allocation failed. Retrying...\n");
406 #endif
410 return colors;
414 static Bool
415 setupPseudoColorColormap(RContext *context)
417 Atom property = 0;
419 if (context->attribs->standard_colormap_mode == RCreateStdColormap) {
420 property = XInternAtom(context->dpy, "RGB_DEFAULT_MAP", False);
422 if (!setupStandardColormap(context, property)) {
423 return False;
427 if (context->attribs->standard_colormap_mode != RIgnoreStdColormap) {
428 XStandardColormap *maps;
429 int count, i;
431 if (!property) {
432 property = XInternAtom(context->dpy, "RGB_BEST_MAP", False);
433 if (!XGetRGBColormaps(context->dpy,
434 DefaultRootWindow(context->dpy),
435 &maps, &count, property)) {
436 maps = NULL;
439 if (!maps) {
440 property = XInternAtom(context->dpy, "RGB_DEFAULT_MAP", False);
441 if (!XGetRGBColormaps(context->dpy,
442 DefaultRootWindow(context->dpy),
443 &maps, &count, property)) {
444 maps = NULL;
447 } else {
448 if (!XGetRGBColormaps(context->dpy,
449 DefaultRootWindow(context->dpy),
450 &maps, &count, property)) {
451 maps = NULL;
455 if (maps) {
456 int theMap = -1;
458 for (i = 0; i < count; i++) {
459 if (maps[i].visualid == context->visual->visualid) {
460 theMap = i;
461 break;
465 if (theMap < 0) {
466 puts("wrlib: no std cmap found");
469 if (theMap >= 0
470 && allocateStandardPseudoColor(context, &maps[theMap])) {
472 context->std_rgb_map = XAllocStandardColormap();
474 *context->std_rgb_map = maps[theMap];
476 context->cmap = context->std_rgb_map->colormap;
478 XFree(maps);
480 return True;
483 XFree(maps);
487 context->attribs->standard_colormap_mode = RIgnoreStdColormap;
489 /* RIgnoreStdColormap and fallback */
490 return allocatePseudoColor(context);
496 static char*
497 mygetenv(char *var, int scr)
499 char *p;
500 char varname[64];
502 sprintf(varname, "%s%i", var, scr);
503 p = getenv(varname);
504 if (!p) {
505 p = getenv(var);
507 return p;
511 static void
512 gatherconfig(RContext *context, int screen_n)
514 char *ptr;
516 ptr = mygetenv("WRASTER_GAMMA", screen_n);
517 if (ptr) {
518 float g1,g2,g3;
519 if (sscanf(ptr, "%f/%f/%f", &g1, &g2, &g3)!=3
520 || g1<=0.0 || g2<=0.0 || g3<=0.0) {
521 printf("wrlib: invalid value(s) for gamma correction \"%s\"\n",
522 ptr);
523 } else {
524 context->attribs->flags |= RC_GammaCorrection;
525 context->attribs->rgamma = g1;
526 context->attribs->ggamma = g2;
527 context->attribs->bgamma = g3;
530 ptr = mygetenv("WRASTER_COLOR_RESOLUTION", screen_n);
531 if (ptr) {
532 int i;
533 if (sscanf(ptr, "%d", &i)!=1 || i<2 || i>6) {
534 printf("wrlib: invalid value for color resolution \"%s\"\n",ptr);
535 } else {
536 context->attribs->flags |= RC_ColorsPerChannel;
537 context->attribs->colors_per_channel = i;
541 ptr = mygetenv("WRASTER_OPTIMIZE_FOR_SPEED", screen_n);
542 if (ptr) {
543 context->flags.optimize_for_speed = 1;
544 } else {
545 context->flags.optimize_for_speed = 0;
551 static void
552 getColormap(RContext *context, int screen_number)
554 Colormap cmap = None;
555 XStandardColormap *cmaps;
556 int ncmaps, i;
558 if (XGetRGBColormaps(context->dpy,
559 RootWindow(context->dpy, screen_number),
560 &cmaps, &ncmaps, XA_RGB_DEFAULT_MAP)) {
561 for (i=0; i<ncmaps; ++i) {
562 if (cmaps[i].visualid == context->visual->visualid) {
563 cmap = cmaps[i].colormap;
564 break;
567 XFree(cmaps);
569 if (cmap == None) {
570 XColor color;
572 cmap = XCreateColormap(context->dpy,
573 RootWindow(context->dpy, screen_number),
574 context->visual, AllocNone);
576 color.red = color.green = color.blue = 0;
577 XAllocColor(context->dpy, cmap, &color);
578 context->black = color.pixel;
580 color.red = color.green = color.blue = 0xffff;
581 XAllocColor(context->dpy, cmap, &color);
582 context->white = color.pixel;
585 context->cmap = cmap;
589 static int
590 count_offset(unsigned long mask)
592 int i;
594 i=0;
595 while ((mask & 1)==0) {
596 i++;
597 mask = mask >> 1;
599 return i;
603 RContext*
604 RCreateContext(Display *dpy, int screen_number, RContextAttributes *attribs)
606 RContext *context;
607 XGCValues gcv;
610 context = malloc(sizeof(RContext));
611 if (!context) {
612 RErrorCode = RERR_NOMEMORY;
613 return NULL;
615 memset(context, 0, sizeof(RContext));
617 context->dpy = dpy;
619 context->screen_number = screen_number;
621 context->attribs = malloc(sizeof(RContextAttributes));
622 if (!context->attribs) {
623 free(context);
624 RErrorCode = RERR_NOMEMORY;
625 return NULL;
627 if (!attribs)
628 *context->attribs = DEFAULT_CONTEXT_ATTRIBS;
629 else
630 *context->attribs = *attribs;
632 if (!(context->attribs->flags & RC_StandardColormap)) {
633 context->attribs->standard_colormap_mode = RUseStdColormap;
636 if (!(context->attribs->flags & RC_ScalingFilter)) {
637 context->attribs->flags |= RC_ScalingFilter;
638 context->attribs->scaling_filter = RMitchellFilter;
641 /* get configuration from environment variables */
642 gatherconfig(context, screen_number);
643 #ifndef BENCH
644 _wraster_change_filter(context->attribs->scaling_filter);
645 #endif
646 if ((context->attribs->flags & RC_VisualID)) {
647 XVisualInfo *vinfo, templ;
648 int nret;
650 templ.screen = screen_number;
651 templ.visualid = context->attribs->visualid;
652 vinfo = XGetVisualInfo(context->dpy, VisualIDMask|VisualScreenMask,
653 &templ, &nret);
654 if (!vinfo || nret==0) {
655 free(context);
656 RErrorCode = RERR_BADVISUALID;
657 return NULL;
660 if (vinfo[0].visual == DefaultVisual(dpy, screen_number)) {
661 context->attribs->flags |= RC_DefaultVisual;
662 } else {
663 XSetWindowAttributes attr;
664 unsigned long mask;
666 context->visual = vinfo[0].visual;
667 context->depth = vinfo[0].depth;
668 context->vclass = vinfo[0].class;
669 getColormap(context, screen_number);
670 attr.colormap = context->cmap;
671 attr.override_redirect = True;
672 attr.border_pixel = 0;
673 attr.background_pixel = 0;
674 mask = CWBorderPixel|CWColormap|CWOverrideRedirect|CWBackPixel;
675 context->drawable =
676 XCreateWindow(dpy, RootWindow(dpy, screen_number), 1, 1,
677 1, 1, 0, context->depth, CopyFromParent,
678 context->visual, mask, &attr);
679 /* XSetWindowColormap(dpy, context->drawable, attr.colormap);*/
681 XFree(vinfo);
684 /* use default */
685 if (!context->visual) {
686 if ((context->attribs->flags & RC_DefaultVisual)
687 || !bestContext(dpy, screen_number, context)) {
688 context->visual = DefaultVisual(dpy, screen_number);
689 context->depth = DefaultDepth(dpy, screen_number);
690 context->cmap = DefaultColormap(dpy, screen_number);
691 context->drawable = RootWindow(dpy, screen_number);
692 context->black = BlackPixel(dpy, screen_number);
693 context->white = WhitePixel(dpy, screen_number);
694 context->vclass = context->visual->class;
698 gcv.function = GXcopy;
699 gcv.graphics_exposures = False;
700 context->copy_gc = XCreateGC(dpy, context->drawable, GCFunction
701 |GCGraphicsExposures, &gcv);
703 if (context->vclass == PseudoColor || context->vclass == StaticColor) {
704 if (!setupPseudoColorColormap(context)) {
705 free(context);
706 return NULL;
708 } else if (context->vclass == GrayScale || context->vclass == StaticGray) {
709 context->colors = allocateGrayScale(context);
710 if (!context->colors) {
711 free(context);
712 return NULL;
714 } else if (context->vclass == TrueColor) {
715 /* calc offsets to create a TrueColor pixel */
716 context->red_offset = count_offset(context->visual->red_mask);
717 context->green_offset = count_offset(context->visual->green_mask);
718 context->blue_offset = count_offset(context->visual->blue_mask);
719 /* disable dithering on 24 bits visuals */
720 if (context->depth >= 24)
721 context->attribs->render_mode = RBestMatchRendering;
725 /* check avaiability of MIT-SHM */
726 #ifdef XSHM
727 if (!(context->attribs->flags & RC_UseSharedMemory)) {
728 context->attribs->flags |= RC_UseSharedMemory;
729 context->attribs->use_shared_memory = True;
732 if (context->attribs->use_shared_memory) {
733 int major, minor;
734 Bool sharedPixmaps;
736 context->flags.use_shared_pixmap = 0;
738 if (!XShmQueryVersion(context->dpy, &major, &minor, &sharedPixmaps)) {
739 context->attribs->use_shared_memory = False;
740 } else {
741 if (XShmPixmapFormat(context->dpy)==ZPixmap)
742 context->flags.use_shared_pixmap = sharedPixmaps;
745 #endif
747 return context;
751 static Bool
752 bestContext(Display *dpy, int screen_number, RContext *context)
754 XVisualInfo *vinfo=NULL, rvinfo;
755 int best = -1, numvis, i;
756 long flags;
757 XSetWindowAttributes attr;
759 rvinfo.class = TrueColor;
760 rvinfo.screen = screen_number;
761 flags = VisualClassMask | VisualScreenMask;
763 vinfo = XGetVisualInfo(dpy, flags, &rvinfo, &numvis);
764 if (vinfo) { /* look for a TrueColor, 24-bit or more (pref 24) */
765 for (i=numvis-1, best = -1; i>=0; i--) {
766 if (vinfo[i].depth == 24) best = i;
767 else if (vinfo[i].depth>24 && best<0) best = i;
771 #if 0
772 if (best == -1) { /* look for a DirectColor, 24-bit or more (pref 24) */
773 rvinfo.class = DirectColor;
774 if (vinfo) XFree((char *) vinfo);
775 vinfo = XGetVisualInfo(dpy, flags, &rvinfo, &numvis);
776 if (vinfo) {
777 for (i=0, best = -1; i<numvis; i++) {
778 if (vinfo[i].depth == 24) best = i;
779 else if (vinfo[i].depth>24 && best<0) best = i;
783 #endif
784 if (best > -1) {
785 context->visual = vinfo[best].visual;
786 context->depth = vinfo[best].depth;
787 context->vclass = vinfo[best].class;
788 getColormap(context, screen_number);
789 attr.colormap = context->cmap;
790 attr.override_redirect = True;
791 attr.border_pixel = 0;
792 context->drawable =
793 XCreateWindow(dpy, RootWindow(dpy, screen_number),
794 1, 1, 1, 1, 0, context->depth,
795 CopyFromParent, context->visual,
796 CWBorderPixel|CWColormap|CWOverrideRedirect, &attr);
797 /* XSetWindowColormap(dpy, context->drawable, context->cmap);*/
799 if (vinfo) XFree((char *) vinfo);
801 if (best < 0)
802 return False;
803 else
804 return True;