wmaker: replace and be replaced (ICCCM protocol)
[wmaker-crm.git] / wrlib / context.c
blob4c40c88e6f287576590cc34f653cdbb05aa389a4
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 static Bool bestContext(Display * dpy, int screen_number, RContext * context);
43 static const RContextAttributes DEFAULT_CONTEXT_ATTRIBS = {
44 RC_UseSharedMemory | RC_RenderMode | RC_ColorsPerChannel, /* flags */
45 RDitheredRendering, /* render_mode */
46 4, /* colors_per_channel */
51 True, /* use_shared_memory */
52 RMitchellFilter,
53 RUseStdColormap
58 * Colormap allocation for PseudoColor visuals:
61 * switch standardColormap:
62 * none:
63 * allocate colors according to colors_per_channel
65 * best/default:
66 * if there's a std colormap defined then use it
68 * else
69 * create a std colormap and set it
73 *----------------------------------------------------------------------
74 * allocateStandardPseudoColor
75 * Creates the internal colormap for PseudoColor, setting the
76 * color values according to the supplied standard colormap.
78 * Returns: -
80 * Side effects: -
82 * Notes: -
83 *----------------------------------------------------------------------
85 static Bool allocateStandardPseudoColor(RContext * ctx, XStandardColormap * stdcmap)
87 int i;
89 ctx->ncolors = stdcmap->red_max * stdcmap->red_mult
90 + stdcmap->green_max * stdcmap->green_mult + stdcmap->blue_max * stdcmap->blue_mult + 1;
92 if (ctx->ncolors <= 1) {
93 RErrorCode = RERR_INTERNAL;
94 puts("wraster: bad standard colormap");
96 return False;
99 ctx->colors = malloc(sizeof(XColor) * ctx->ncolors);
100 if (!ctx->colors) {
101 RErrorCode = RERR_NOMEMORY;
103 return False;
106 ctx->pixels = malloc(sizeof(unsigned long) * ctx->ncolors);
107 if (!ctx->pixels) {
109 free(ctx->colors);
110 ctx->colors = NULL;
112 RErrorCode = RERR_NOMEMORY;
114 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);
126 ctx->pixels[i] = ctx->colors[i].pixel;
129 #undef calc
131 return True;
134 static Bool setupStandardColormap(RContext * ctx, Atom property)
136 if (!XmuLookupStandardColormap(ctx->dpy, ctx->screen_number,
137 ctx->visual->visualid, ctx->depth, property, True, True)) {
138 RErrorCode = RERR_STDCMAPFAIL;
140 return False;
142 return True;
145 static XColor *allocateColor(RContext *ctx, XColor *colors, int ncolors)
147 XColor avcolors[256];
148 int avncolors;
149 int i, r, g, b;
150 int retries;
152 for (i = 0; i < ncolors; i++) {
153 #ifdef WRLIB_DEBUG
154 fprintf(stderr, "trying:%x,%x,%x\n", colors[i].red, colors[i].green, colors[i].blue);
155 #endif
156 if (!XAllocColor(ctx->dpy, ctx->cmap, &(colors[i]))) {
157 colors[i].flags = 0; /* failed */
158 #ifdef WRLIB_DEBUG
159 fprintf(stderr, "failed:%x,%x,%x\n", colors[i].red, colors[i].green, colors[i].blue);
160 #endif
161 } else {
162 colors[i].flags = DoRed | DoGreen | DoBlue;
163 #ifdef WRLIB_DEBUG
164 fprintf(stderr, "success:%x,%x,%x\n", colors[i].red, colors[i].green, colors[i].blue);
165 #endif
168 /* try to allocate close values for the colors that couldn't
169 * be allocated before */
170 avncolors = (1 << ctx->depth > 256 ? 256 : 1 << ctx->depth);
171 for (i = 0; i < avncolors; i++)
172 avcolors[i].pixel = i;
174 XQueryColors(ctx->dpy, ctx->cmap, avcolors, avncolors);
176 for (i = 0; i < ncolors; i++) {
177 if (colors[i].flags == 0) {
178 int j;
179 unsigned long cdiff = 0xffffffff, diff;
180 unsigned long closest = 0;
182 retries = 2;
184 while (retries--) {
185 /* find closest color */
186 for (j = 0; j < avncolors; j++) {
187 r = (colors[i].red - avcolors[i].red) >> 8;
188 g = (colors[i].green - avcolors[i].green) >> 8;
189 b = (colors[i].blue - avcolors[i].blue) >> 8;
190 diff = r * r + g * g + b * b;
191 if (diff < cdiff) {
192 cdiff = diff;
193 closest = j;
196 /* allocate closest color found */
197 #ifdef WRLIB_DEBUG
198 fprintf(stderr, "best match:%x,%x,%x => %x,%x,%x\n",
199 colors[i].red, colors[i].green, colors[i].blue,
200 avcolors[closest].red, avcolors[closest].green, avcolors[closest].blue);
201 #endif
202 colors[i].red = avcolors[closest].red;
203 colors[i].green = avcolors[closest].green;
204 colors[i].blue = avcolors[closest].blue;
205 if (XAllocColor(ctx->dpy, ctx->cmap, &colors[i])) {
206 colors[i].flags = DoRed | DoGreen | DoBlue;
207 break; /* succeeded, don't need to retry */
209 #ifdef WRLIB_DEBUG
210 fputs("close color allocation failed. Retrying...\n", stderr);
211 #endif
215 return colors;
218 static Bool allocatePseudoColor(RContext *ctx)
220 XColor *colors;
221 int i, ncolors, r, g, b;
222 int cpc = ctx->attribs->colors_per_channel;
224 ncolors = cpc * cpc * cpc;
226 if (ncolors > (1 << ctx->depth)) {
227 /* reduce colormap size */
228 cpc = ctx->attribs->colors_per_channel = 1 << ((int)ctx->depth / 3);
229 ncolors = cpc * cpc * cpc;
232 assert(cpc >= 2 && ncolors <= (1 << ctx->depth));
234 colors = malloc(sizeof(XColor) * ncolors);
235 if (!colors) {
236 RErrorCode = RERR_NOMEMORY;
237 return False;
240 ctx->pixels = malloc(sizeof(unsigned long) * ncolors);
241 if (!ctx->pixels) {
242 free(colors);
243 RErrorCode = RERR_NOMEMORY;
244 return False;
247 i = 0;
249 if ((ctx->attribs->flags & RC_GammaCorrection) && ctx->attribs->rgamma > 0
250 && ctx->attribs->ggamma > 0 && ctx->attribs->bgamma > 0) {
251 double rg, gg, bg;
252 double tmp;
254 /* do gamma correction */
255 rg = 1.0 / ctx->attribs->rgamma;
256 gg = 1.0 / ctx->attribs->ggamma;
257 bg = 1.0 / ctx->attribs->bgamma;
258 for (r = 0; r < cpc; r++) {
259 for (g = 0; g < cpc; g++) {
260 for (b = 0; b < cpc; b++) {
261 colors[i].red = (r * 0xffff) / (cpc - 1);
262 colors[i].green = (g * 0xffff) / (cpc - 1);
263 colors[i].blue = (b * 0xffff) / (cpc - 1);
264 colors[i].flags = DoRed | DoGreen | DoBlue;
266 tmp = (double)colors[i].red / 65536.0;
267 colors[i].red = (unsigned short)(65536.0 * pow(tmp, rg));
269 tmp = (double)colors[i].green / 65536.0;
270 colors[i].green = (unsigned short)(65536.0 * pow(tmp, gg));
272 tmp = (double)colors[i].blue / 65536.0;
273 colors[i].blue = (unsigned short)(65536.0 * pow(tmp, bg));
275 i++;
280 } else {
281 for (r = 0; r < cpc; r++) {
282 for (g = 0; g < cpc; g++) {
283 for (b = 0; b < cpc; b++) {
284 colors[i].red = (r * 0xffff) / (cpc - 1);
285 colors[i].green = (g * 0xffff) / (cpc - 1);
286 colors[i].blue = (b * 0xffff) / (cpc - 1);
287 colors[i].flags = DoRed | DoGreen | DoBlue;
288 i++;
293 /* try to allocate the colors */
294 ctx->colors = allocateColor(ctx, colors, ncolors);
295 ctx->ncolors = ncolors;
297 /* fill the pixels shortcut array */
298 for (i = 0; i < ncolors; i++) {
299 ctx->pixels[i] = ctx->colors[i].pixel;
302 return True;
305 static XColor *allocateGrayScale(RContext * ctx)
307 XColor *colors;
308 int i, ncolors;
309 int cpc = ctx->attribs->colors_per_channel;
311 ncolors = cpc * cpc * cpc;
313 if (ctx->vclass == StaticGray) {
314 /* we might as well use all grays */
315 ncolors = 1 << ctx->depth;
316 } else {
317 if (ncolors > (1 << ctx->depth)) {
318 /* reduce colormap size */
319 cpc = ctx->attribs->colors_per_channel = 1 << ((int)ctx->depth / 3);
320 ncolors = cpc * cpc * cpc;
323 assert(cpc >= 2 && ncolors <= (1 << ctx->depth));
326 if (ncolors >= 256 && ctx->vclass == StaticGray) {
327 /* don't need dithering for 256 levels of gray in StaticGray visual */
328 ctx->attribs->render_mode = RBestMatchRendering;
331 colors = malloc(sizeof(XColor) * ncolors);
332 if (!colors) {
333 RErrorCode = RERR_NOMEMORY;
334 return False;
336 for (i = 0; i < ncolors; i++) {
337 colors[i].red = (i * 0xffff) / (ncolors - 1);
338 colors[i].green = (i * 0xffff) / (ncolors - 1);
339 colors[i].blue = (i * 0xffff) / (ncolors - 1);
340 colors[i].flags = DoRed | DoGreen | DoBlue;
343 /* try to allocate the colors */
344 return allocateColor(ctx, colors, ncolors);
347 static Bool setupPseudoColorColormap(RContext * context)
349 Atom property = 0;
351 if (context->attribs->standard_colormap_mode == RCreateStdColormap) {
352 property = XInternAtom(context->dpy, "RGB_DEFAULT_MAP", False);
354 if (!setupStandardColormap(context, property)) {
355 return False;
359 if (context->attribs->standard_colormap_mode != RIgnoreStdColormap) {
360 XStandardColormap *maps;
361 int count, i;
363 if (!property) {
364 property = XInternAtom(context->dpy, "RGB_BEST_MAP", False);
365 if (!XGetRGBColormaps(context->dpy,
366 DefaultRootWindow(context->dpy), &maps, &count, property)) {
367 maps = NULL;
370 if (!maps) {
371 property = XInternAtom(context->dpy, "RGB_DEFAULT_MAP", False);
372 if (!XGetRGBColormaps(context->dpy,
373 DefaultRootWindow(context->dpy), &maps, &count, property)) {
374 maps = NULL;
377 } else {
378 if (!XGetRGBColormaps(context->dpy,
379 DefaultRootWindow(context->dpy), &maps, &count, property)) {
380 maps = NULL;
384 if (maps) {
385 int theMap = -1;
387 for (i = 0; i < count; i++) {
388 if (maps[i].visualid == context->visual->visualid) {
389 theMap = i;
390 break;
394 if (theMap < 0) {
395 puts("wrlib: no std cmap found");
398 if (theMap >= 0 && allocateStandardPseudoColor(context, &maps[theMap])) {
400 context->std_rgb_map = XAllocStandardColormap();
402 *context->std_rgb_map = maps[theMap];
404 context->cmap = context->std_rgb_map->colormap;
406 XFree(maps);
408 return True;
411 XFree(maps);
415 context->attribs->standard_colormap_mode = RIgnoreStdColormap;
417 /* RIgnoreStdColormap and fallback */
418 return allocatePseudoColor(context);
421 static char *mygetenv(const char *var, int scr)
423 char *p;
424 char varname[64];
426 snprintf(varname, sizeof(varname), "%s%i", var, scr);
427 p = getenv(varname);
428 if (!p) {
429 p = getenv(var);
431 return p;
434 static void gatherconfig(RContext * context, int screen_n)
436 char *ptr;
438 ptr = mygetenv("WRASTER_GAMMA", screen_n);
439 if (ptr) {
440 float g1, g2, g3;
441 if (sscanf(ptr, "%f/%f/%f", &g1, &g2, &g3) != 3 || g1 <= 0.0 || g2 <= 0.0 || g3 <= 0.0) {
442 printf("wrlib: invalid value(s) for gamma correction \"%s\"\n", ptr);
443 } else {
444 context->attribs->flags |= RC_GammaCorrection;
445 context->attribs->rgamma = g1;
446 context->attribs->ggamma = g2;
447 context->attribs->bgamma = g3;
450 ptr = mygetenv("WRASTER_COLOR_RESOLUTION", screen_n);
451 if (ptr) {
452 int i;
453 if (sscanf(ptr, "%d", &i) != 1 || i < 2 || i > 6) {
454 printf("wrlib: invalid value for color resolution \"%s\"\n", ptr);
455 } else {
456 context->attribs->flags |= RC_ColorsPerChannel;
457 context->attribs->colors_per_channel = i;
462 static void getColormap(RContext * context, int screen_number)
464 Colormap cmap = None;
465 XStandardColormap *cmaps;
466 int ncmaps, i;
468 if (XGetRGBColormaps(context->dpy,
469 RootWindow(context->dpy, screen_number), &cmaps, &ncmaps, XA_RGB_DEFAULT_MAP)) {
470 for (i = 0; i < ncmaps; ++i) {
471 if (cmaps[i].visualid == context->visual->visualid) {
472 cmap = cmaps[i].colormap;
473 break;
476 XFree(cmaps);
478 if (cmap == None) {
479 XColor color;
481 cmap = XCreateColormap(context->dpy,
482 RootWindow(context->dpy, screen_number), context->visual, AllocNone);
484 color.red = color.green = color.blue = 0;
485 XAllocColor(context->dpy, cmap, &color);
486 context->black = color.pixel;
488 color.red = color.green = color.blue = 0xffff;
489 XAllocColor(context->dpy, cmap, &color);
490 context->white = color.pixel;
493 context->cmap = cmap;
496 static int count_offset(unsigned long mask)
498 int i;
500 i = 0;
501 while ((mask & 1) == 0) {
502 i++;
503 mask = mask >> 1;
505 return i;
508 RContext *RCreateContext(Display * dpy, int screen_number, const RContextAttributes * attribs)
510 RContext *context;
511 XGCValues gcv;
513 context = malloc(sizeof(RContext));
514 if (!context) {
515 RErrorCode = RERR_NOMEMORY;
516 return NULL;
518 memset(context, 0, sizeof(RContext));
520 context->dpy = dpy;
522 context->screen_number = screen_number;
524 context->attribs = malloc(sizeof(RContextAttributes));
525 if (!context->attribs) {
526 free(context);
527 RErrorCode = RERR_NOMEMORY;
528 return NULL;
530 if (!attribs)
531 *context->attribs = DEFAULT_CONTEXT_ATTRIBS;
532 else
533 *context->attribs = *attribs;
535 if (!(context->attribs->flags & RC_StandardColormap)) {
536 context->attribs->standard_colormap_mode = RUseStdColormap;
539 if (!(context->attribs->flags & RC_ScalingFilter)) {
540 context->attribs->flags |= RC_ScalingFilter;
541 context->attribs->scaling_filter = RMitchellFilter;
544 /* get configuration from environment variables */
545 gatherconfig(context, screen_number);
546 wraster_change_filter(context->attribs->scaling_filter);
547 if ((context->attribs->flags & RC_VisualID)) {
548 XVisualInfo *vinfo, templ;
549 int nret;
551 templ.screen = screen_number;
552 templ.visualid = context->attribs->visualid;
553 vinfo = XGetVisualInfo(context->dpy, VisualIDMask | VisualScreenMask, &templ, &nret);
554 if (!vinfo || nret == 0) {
555 free(context);
556 RErrorCode = RERR_BADVISUALID;
557 return NULL;
560 if (vinfo[0].visual == DefaultVisual(dpy, screen_number)) {
561 context->attribs->flags |= RC_DefaultVisual;
562 } else {
563 XSetWindowAttributes attr;
564 unsigned long mask;
566 context->visual = vinfo[0].visual;
567 context->depth = vinfo[0].depth;
568 context->vclass = vinfo[0].class;
569 getColormap(context, screen_number);
570 attr.colormap = context->cmap;
571 attr.override_redirect = True;
572 attr.border_pixel = 0;
573 attr.background_pixel = 0;
574 mask = CWBorderPixel | CWColormap | CWOverrideRedirect | CWBackPixel;
575 context->drawable =
576 XCreateWindow(dpy, RootWindow(dpy, screen_number), 1, 1,
577 1, 1, 0, context->depth, CopyFromParent, context->visual, mask, &attr);
579 XFree(vinfo);
582 /* use default */
583 if (!context->visual) {
584 if ((context->attribs->flags & RC_DefaultVisual)
585 || !bestContext(dpy, screen_number, context)) {
586 context->visual = DefaultVisual(dpy, screen_number);
587 context->depth = DefaultDepth(dpy, screen_number);
588 context->cmap = DefaultColormap(dpy, screen_number);
589 context->drawable = RootWindow(dpy, screen_number);
590 context->black = BlackPixel(dpy, screen_number);
591 context->white = WhitePixel(dpy, screen_number);
592 context->vclass = context->visual->class;
596 gcv.function = GXcopy;
597 gcv.graphics_exposures = False;
598 context->copy_gc = XCreateGC(dpy, context->drawable, GCFunction | GCGraphicsExposures, &gcv);
600 if (context->vclass == PseudoColor || context->vclass == StaticColor) {
601 if (!setupPseudoColorColormap(context)) {
602 free(context);
603 return NULL;
605 } else if (context->vclass == GrayScale || context->vclass == StaticGray) {
606 context->colors = allocateGrayScale(context);
607 if (!context->colors) {
608 free(context);
609 return NULL;
611 } else if (context->vclass == TrueColor) {
612 /* calc offsets to create a TrueColor pixel */
613 context->red_offset = count_offset(context->visual->red_mask);
614 context->green_offset = count_offset(context->visual->green_mask);
615 context->blue_offset = count_offset(context->visual->blue_mask);
616 /* disable dithering on 24 bits visuals */
617 if (context->depth >= 24)
618 context->attribs->render_mode = RBestMatchRendering;
621 /* check avaiability of MIT-SHM */
622 #ifdef USE_XSHM
623 if (!(context->attribs->flags & RC_UseSharedMemory)) {
624 context->attribs->flags |= RC_UseSharedMemory;
625 context->attribs->use_shared_memory = True;
628 if (context->attribs->use_shared_memory) {
629 int major, minor;
630 Bool sharedPixmaps;
632 context->flags.use_shared_pixmap = 0;
634 if (!XShmQueryVersion(context->dpy, &major, &minor, &sharedPixmaps)) {
635 context->attribs->use_shared_memory = False;
636 } else {
637 if (XShmPixmapFormat(context->dpy) == ZPixmap)
638 context->flags.use_shared_pixmap = sharedPixmaps;
641 #endif
643 return context;
646 void RDestroyContext(RContext *context)
648 if (context) {
649 if (context->copy_gc)
650 XFreeGC(context->dpy, context->copy_gc);
651 if (context->attribs) {
652 if ((context->attribs->flags & RC_VisualID) &&
653 !(context->attribs->flags & RC_DefaultVisual))
654 XDestroyWindow(context->dpy, context->drawable);
655 free(context->attribs);
657 free(context);
661 static Bool bestContext(Display * dpy, int screen_number, RContext * context)
663 XVisualInfo *vinfo = NULL, rvinfo;
664 int best = -1, numvis, i;
665 long flags;
666 XSetWindowAttributes attr;
668 rvinfo.class = TrueColor;
669 rvinfo.screen = screen_number;
670 flags = VisualClassMask | VisualScreenMask;
672 vinfo = XGetVisualInfo(dpy, flags, &rvinfo, &numvis);
673 if (vinfo) { /* look for a TrueColor, 24-bit or more (pref 24) */
674 for (i = numvis - 1, best = -1; i >= 0; i--) {
675 if (vinfo[i].depth == 24)
676 best = i;
677 else if (vinfo[i].depth > 24 && best < 0)
678 best = i;
681 if (best > -1) {
682 context->visual = vinfo[best].visual;
683 context->depth = vinfo[best].depth;
684 context->vclass = vinfo[best].class;
685 getColormap(context, screen_number);
686 attr.colormap = context->cmap;
687 attr.override_redirect = True;
688 attr.border_pixel = 0;
689 context->drawable =
690 XCreateWindow(dpy, RootWindow(dpy, screen_number),
691 1, 1, 1, 1, 0, context->depth,
692 CopyFromParent, context->visual,
693 CWBorderPixel | CWColormap | CWOverrideRedirect, &attr);
695 if (vinfo)
696 XFree((char *)vinfo);
698 if (best < 0)
699 return False;
700 else
701 return True;