Partially support _NET_WM_STRUT_PARTIAL.
[wmaker-crm.git] / wrlib / context.c
blob0fe956c5eb97a8dd2a0c8dcc332a602b9a26ce4f
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., 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>
27 #include <X11/Xmu/StdCmap.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <assert.h>
34 #include <math.h>
36 #include "wraster.h"
38 extern void _wraster_change_filter(int type);
40 static Bool bestContext(Display * dpy, int screen_number, RContext * context);
42 static RContextAttributes DEFAULT_CONTEXT_ATTRIBS = {
43 RC_UseSharedMemory | RC_RenderMode | RC_ColorsPerChannel, /* flags */
44 RDitheredRendering, /* render_mode */
45 4, /* colors_per_channel */
50 True, /* use_shared_memory */
51 RMitchellFilter,
52 RUseStdColormap
57 * Colormap allocation for PseudoColor visuals:
60 * switch standardColormap:
61 * none:
62 * allocate colors according to colors_per_channel
64 * best/default:
65 * if there's a std colormap defined then use it
67 * else
68 * create a std colormap and set it
72 *----------------------------------------------------------------------
73 * allocateStandardPseudoColor
74 * Creates the internal colormap for PseudoColor, setting the
75 * color values according to the supplied standard colormap.
77 * Returns: -
79 * Side effects: -
81 * Notes: -
82 *----------------------------------------------------------------------
84 static Bool allocateStandardPseudoColor(RContext * ctx, XStandardColormap * stdcmap)
86 int i;
88 ctx->ncolors = stdcmap->red_max * stdcmap->red_mult
89 + stdcmap->green_max * stdcmap->green_mult + stdcmap->blue_max * stdcmap->blue_mult + 1;
91 if (ctx->ncolors <= 1) {
92 RErrorCode = RERR_INTERNAL;
93 puts("wraster: bad standard colormap");
95 return False;
98 ctx->colors = malloc(sizeof(XColor) * ctx->ncolors);
99 if (!ctx->colors) {
100 RErrorCode = RERR_NOMEMORY;
102 return False;
105 ctx->pixels = malloc(sizeof(unsigned long) * ctx->ncolors);
106 if (!ctx->pixels) {
108 free(ctx->colors);
109 ctx->colors = NULL;
111 RErrorCode = RERR_NOMEMORY;
113 return False;
116 #define calc(max,mult) (((i / stdcmap->mult) % \
117 (stdcmap->max + 1)) * 65535) / stdcmap->max
119 for (i = 0; i < ctx->ncolors; i++) {
120 ctx->colors[i].pixel = i + stdcmap->base_pixel;
121 ctx->colors[i].red = calc(red_max, red_mult);
122 ctx->colors[i].green = calc(green_max, green_mult);
123 ctx->colors[i].blue = calc(blue_max, blue_mult);
125 ctx->pixels[i] = ctx->colors[i].pixel;
128 #undef calc
130 return True;
133 static Bool setupStandardColormap(RContext * ctx, Atom property)
135 if (!XmuLookupStandardColormap(ctx->dpy, ctx->screen_number,
136 ctx->visual->visualid, ctx->depth, property, True, True)) {
137 RErrorCode = RERR_STDCMAPFAIL;
139 return False;
141 return True;
144 static Bool allocatePseudoColor(RContext * ctx)
146 XColor *colors;
147 XColor avcolors[256];
148 int avncolors;
149 int i, ncolors, r, g, b;
150 int retries;
151 int cpc = ctx->attribs->colors_per_channel;
153 ncolors = cpc * cpc * cpc;
155 if (ncolors > (1 << ctx->depth)) {
156 /* reduce colormap size */
157 cpc = ctx->attribs->colors_per_channel = 1 << ((int)ctx->depth / 3);
158 ncolors = cpc * cpc * cpc;
161 assert(cpc >= 2 && ncolors <= (1 << ctx->depth));
163 colors = malloc(sizeof(XColor) * ncolors);
164 if (!colors) {
165 RErrorCode = RERR_NOMEMORY;
166 return False;
169 ctx->pixels = malloc(sizeof(unsigned long) * ncolors);
170 if (!ctx->pixels) {
171 free(colors);
172 RErrorCode = RERR_NOMEMORY;
173 return False;
176 i = 0;
178 if ((ctx->attribs->flags & RC_GammaCorrection) && ctx->attribs->rgamma > 0
179 && ctx->attribs->ggamma > 0 && ctx->attribs->bgamma > 0) {
180 double rg, gg, bg;
181 double tmp;
183 /* do gamma correction */
184 rg = 1.0 / ctx->attribs->rgamma;
185 gg = 1.0 / ctx->attribs->ggamma;
186 bg = 1.0 / ctx->attribs->bgamma;
187 for (r = 0; r < cpc; r++) {
188 for (g = 0; g < cpc; g++) {
189 for (b = 0; b < cpc; b++) {
190 colors[i].red = (r * 0xffff) / (cpc - 1);
191 colors[i].green = (g * 0xffff) / (cpc - 1);
192 colors[i].blue = (b * 0xffff) / (cpc - 1);
193 colors[i].flags = DoRed | DoGreen | DoBlue;
195 tmp = (double)colors[i].red / 65536.0;
196 colors[i].red = (unsigned short)(65536.0 * pow(tmp, rg));
198 tmp = (double)colors[i].green / 65536.0;
199 colors[i].green = (unsigned short)(65536.0 * pow(tmp, gg));
201 tmp = (double)colors[i].blue / 65536.0;
202 colors[i].blue = (unsigned short)(65536.0 * pow(tmp, bg));
204 i++;
209 } else {
210 for (r = 0; r < cpc; r++) {
211 for (g = 0; g < cpc; g++) {
212 for (b = 0; b < cpc; b++) {
213 colors[i].red = (r * 0xffff) / (cpc - 1);
214 colors[i].green = (g * 0xffff) / (cpc - 1);
215 colors[i].blue = (b * 0xffff) / (cpc - 1);
216 colors[i].flags = DoRed | DoGreen | DoBlue;
217 i++;
222 /* try to allocate the colors */
223 for (i = 0; i < ncolors; i++) {
224 if (!XAllocColor(ctx->dpy, ctx->cmap, &(colors[i]))) {
225 colors[i].flags = 0; /* failed */
226 } else {
227 colors[i].flags = DoRed | DoGreen | DoBlue;
230 /* try to allocate close values for the colors that couldn't
231 * be allocated before */
232 avncolors = (1 << ctx->depth > 256 ? 256 : 1 << ctx->depth);
233 for (i = 0; i < avncolors; i++)
234 avcolors[i].pixel = i;
236 XQueryColors(ctx->dpy, ctx->cmap, avcolors, avncolors);
238 for (i = 0; i < ncolors; i++) {
239 if (colors[i].flags == 0) {
240 int j;
241 unsigned long cdiff = 0xffffffff, diff;
242 unsigned long closest = 0;
244 retries = 2;
246 while (retries--) {
247 /* find closest color */
248 for (j = 0; j < avncolors; j++) {
249 r = (colors[i].red - avcolors[i].red) >> 8;
250 g = (colors[i].green - avcolors[i].green) >> 8;
251 b = (colors[i].blue - avcolors[i].blue) >> 8;
252 diff = r * r + g * g + b * b;
253 if (diff < cdiff) {
254 cdiff = diff;
255 closest = j;
258 /* allocate closest color found */
259 colors[i].red = avcolors[closest].red;
260 colors[i].green = avcolors[closest].green;
261 colors[i].blue = avcolors[closest].blue;
262 if (XAllocColor(ctx->dpy, ctx->cmap, &colors[i])) {
263 colors[i].flags = DoRed | DoGreen | DoBlue;
264 break; /* succeeded, don't need to retry */
266 #ifdef WRLIB_DEBUG
267 fputs("close color allocation failed. Retrying...\n", stderr);
268 #endif
273 ctx->colors = colors;
274 ctx->ncolors = ncolors;
276 /* fill the pixels shortcut array */
277 for (i = 0; i < ncolors; i++) {
278 ctx->pixels[i] = ctx->colors[i].pixel;
281 return True;
284 static XColor *allocateGrayScale(RContext * ctx)
286 XColor *colors;
287 XColor avcolors[256];
288 int avncolors;
289 int i, ncolors, r, g, b;
290 int retries;
291 int cpc = ctx->attribs->colors_per_channel;
293 ncolors = cpc * cpc * cpc;
295 if (ctx->vclass == StaticGray) {
296 /* we might as well use all grays */
297 ncolors = 1 << ctx->depth;
298 } else {
299 if (ncolors > (1 << ctx->depth)) {
300 /* reduce colormap size */
301 cpc = ctx->attribs->colors_per_channel = 1 << ((int)ctx->depth / 3);
302 ncolors = cpc * cpc * cpc;
305 assert(cpc >= 2 && ncolors <= (1 << ctx->depth));
308 if (ncolors >= 256 && ctx->vclass == StaticGray) {
309 /* don't need dithering for 256 levels of gray in StaticGray visual */
310 ctx->attribs->render_mode = RBestMatchRendering;
313 colors = malloc(sizeof(XColor) * ncolors);
314 if (!colors) {
315 RErrorCode = RERR_NOMEMORY;
316 return False;
318 for (i = 0; i < ncolors; i++) {
319 colors[i].red = (i * 0xffff) / (ncolors - 1);
320 colors[i].green = (i * 0xffff) / (ncolors - 1);
321 colors[i].blue = (i * 0xffff) / (ncolors - 1);
322 colors[i].flags = DoRed | DoGreen | DoBlue;
324 /* try to allocate the colors */
325 for (i = 0; i < ncolors; i++) {
326 #ifdef WRLIB_DEBUG
327 fprintf(stderr, "trying:%x,%x,%x\n", colors[i].red, colors[i].green, colors[i].blue);
328 #endif
329 if (!XAllocColor(ctx->dpy, ctx->cmap, &(colors[i]))) {
330 colors[i].flags = 0; /* failed */
331 #ifdef WRLIB_DEBUG
332 fprintf(stderr, "failed:%x,%x,%x\n", colors[i].red, colors[i].green, colors[i].blue);
333 #endif
334 } else {
335 colors[i].flags = DoRed | DoGreen | DoBlue;
336 #ifdef WRLIB_DEBUG
337 fprintf(stderr, "success:%x,%x,%x\n", colors[i].red, colors[i].green, colors[i].blue);
338 #endif
341 /* try to allocate close values for the colors that couldn't
342 * be allocated before */
343 avncolors = (1 << ctx->depth > 256 ? 256 : 1 << ctx->depth);
344 for (i = 0; i < avncolors; i++)
345 avcolors[i].pixel = i;
347 XQueryColors(ctx->dpy, ctx->cmap, avcolors, avncolors);
349 for (i = 0; i < ncolors; i++) {
350 if (colors[i].flags == 0) {
351 int j;
352 unsigned long cdiff = 0xffffffff, diff;
353 unsigned long closest = 0;
355 retries = 2;
357 while (retries--) {
358 /* find closest color */
359 for (j = 0; j < avncolors; j++) {
360 r = (colors[i].red - avcolors[i].red) >> 8;
361 g = (colors[i].green - avcolors[i].green) >> 8;
362 b = (colors[i].blue - avcolors[i].blue) >> 8;
363 diff = r * r + g * g + b * b;
364 if (diff < cdiff) {
365 cdiff = diff;
366 closest = j;
369 /* allocate closest color found */
370 #ifdef WRLIB_DEBUG
371 fprintf(stderr, "best match:%x,%x,%x => %x,%x,%x\n",
372 colors[i].red, colors[i].green, colors[i].blue,
373 avcolors[closest].red, avcolors[closest].green, avcolors[closest].blue);
374 #endif
375 colors[i].red = avcolors[closest].red;
376 colors[i].green = avcolors[closest].green;
377 colors[i].blue = avcolors[closest].blue;
378 if (XAllocColor(ctx->dpy, ctx->cmap, &colors[i])) {
379 colors[i].flags = DoRed | DoGreen | DoBlue;
380 break; /* succeeded, don't need to retry */
382 #ifdef WRLIB_DEBUG
383 fputs("close color allocation failed. Retrying...\n", stderr);
384 #endif
388 return colors;
391 static Bool setupPseudoColorColormap(RContext * context)
393 Atom property = 0;
395 if (context->attribs->standard_colormap_mode == RCreateStdColormap) {
396 property = XInternAtom(context->dpy, "RGB_DEFAULT_MAP", False);
398 if (!setupStandardColormap(context, property)) {
399 return False;
403 if (context->attribs->standard_colormap_mode != RIgnoreStdColormap) {
404 XStandardColormap *maps;
405 int count, i;
407 if (!property) {
408 property = XInternAtom(context->dpy, "RGB_BEST_MAP", False);
409 if (!XGetRGBColormaps(context->dpy,
410 DefaultRootWindow(context->dpy), &maps, &count, property)) {
411 maps = NULL;
414 if (!maps) {
415 property = XInternAtom(context->dpy, "RGB_DEFAULT_MAP", False);
416 if (!XGetRGBColormaps(context->dpy,
417 DefaultRootWindow(context->dpy), &maps, &count, property)) {
418 maps = NULL;
421 } else {
422 if (!XGetRGBColormaps(context->dpy,
423 DefaultRootWindow(context->dpy), &maps, &count, property)) {
424 maps = NULL;
428 if (maps) {
429 int theMap = -1;
431 for (i = 0; i < count; i++) {
432 if (maps[i].visualid == context->visual->visualid) {
433 theMap = i;
434 break;
438 if (theMap < 0) {
439 puts("wrlib: no std cmap found");
442 if (theMap >= 0 && allocateStandardPseudoColor(context, &maps[theMap])) {
444 context->std_rgb_map = XAllocStandardColormap();
446 *context->std_rgb_map = maps[theMap];
448 context->cmap = context->std_rgb_map->colormap;
450 XFree(maps);
452 return True;
455 XFree(maps);
459 context->attribs->standard_colormap_mode = RIgnoreStdColormap;
461 /* RIgnoreStdColormap and fallback */
462 return allocatePseudoColor(context);
465 static char *mygetenv(char *var, int scr)
467 char *p;
468 char varname[64];
470 snprintf(varname, sizeof(varname), "%s%i", var, scr);
471 p = getenv(varname);
472 if (!p) {
473 p = getenv(var);
475 return p;
478 static void gatherconfig(RContext * context, int screen_n)
480 char *ptr;
482 ptr = mygetenv("WRASTER_GAMMA", screen_n);
483 if (ptr) {
484 float g1, g2, g3;
485 if (sscanf(ptr, "%f/%f/%f", &g1, &g2, &g3) != 3 || g1 <= 0.0 || g2 <= 0.0 || g3 <= 0.0) {
486 printf("wrlib: invalid value(s) for gamma correction \"%s\"\n", ptr);
487 } else {
488 context->attribs->flags |= RC_GammaCorrection;
489 context->attribs->rgamma = g1;
490 context->attribs->ggamma = g2;
491 context->attribs->bgamma = g3;
494 ptr = mygetenv("WRASTER_COLOR_RESOLUTION", screen_n);
495 if (ptr) {
496 int i;
497 if (sscanf(ptr, "%d", &i) != 1 || i < 2 || i > 6) {
498 printf("wrlib: invalid value for color resolution \"%s\"\n", ptr);
499 } else {
500 context->attribs->flags |= RC_ColorsPerChannel;
501 context->attribs->colors_per_channel = i;
505 ptr = mygetenv("WRASTER_OPTIMIZE_FOR_SPEED", screen_n);
506 if (ptr) {
507 context->flags.optimize_for_speed = 1;
508 } else {
509 context->flags.optimize_for_speed = 0;
514 static void getColormap(RContext * context, int screen_number)
516 Colormap cmap = None;
517 XStandardColormap *cmaps;
518 int ncmaps, i;
520 if (XGetRGBColormaps(context->dpy,
521 RootWindow(context->dpy, screen_number), &cmaps, &ncmaps, XA_RGB_DEFAULT_MAP)) {
522 for (i = 0; i < ncmaps; ++i) {
523 if (cmaps[i].visualid == context->visual->visualid) {
524 cmap = cmaps[i].colormap;
525 break;
528 XFree(cmaps);
530 if (cmap == None) {
531 XColor color;
533 cmap = XCreateColormap(context->dpy,
534 RootWindow(context->dpy, screen_number), context->visual, AllocNone);
536 color.red = color.green = color.blue = 0;
537 XAllocColor(context->dpy, cmap, &color);
538 context->black = color.pixel;
540 color.red = color.green = color.blue = 0xffff;
541 XAllocColor(context->dpy, cmap, &color);
542 context->white = color.pixel;
545 context->cmap = cmap;
548 static int count_offset(unsigned long mask)
550 int i;
552 i = 0;
553 while ((mask & 1) == 0) {
554 i++;
555 mask = mask >> 1;
557 return i;
560 RContext *RCreateContext(Display * dpy, int screen_number, RContextAttributes * attribs)
562 RContext *context;
563 XGCValues gcv;
565 context = malloc(sizeof(RContext));
566 if (!context) {
567 RErrorCode = RERR_NOMEMORY;
568 return NULL;
570 memset(context, 0, sizeof(RContext));
572 context->dpy = dpy;
574 context->screen_number = screen_number;
576 context->attribs = malloc(sizeof(RContextAttributes));
577 if (!context->attribs) {
578 free(context);
579 RErrorCode = RERR_NOMEMORY;
580 return NULL;
582 if (!attribs)
583 *context->attribs = DEFAULT_CONTEXT_ATTRIBS;
584 else
585 *context->attribs = *attribs;
587 if (!(context->attribs->flags & RC_StandardColormap)) {
588 context->attribs->standard_colormap_mode = RUseStdColormap;
591 if (!(context->attribs->flags & RC_ScalingFilter)) {
592 context->attribs->flags |= RC_ScalingFilter;
593 context->attribs->scaling_filter = RMitchellFilter;
596 /* get configuration from environment variables */
597 gatherconfig(context, screen_number);
598 _wraster_change_filter(context->attribs->scaling_filter);
599 if ((context->attribs->flags & RC_VisualID)) {
600 XVisualInfo *vinfo, templ;
601 int nret;
603 templ.screen = screen_number;
604 templ.visualid = context->attribs->visualid;
605 vinfo = XGetVisualInfo(context->dpy, VisualIDMask | VisualScreenMask, &templ, &nret);
606 if (!vinfo || nret == 0) {
607 free(context);
608 RErrorCode = RERR_BADVISUALID;
609 return NULL;
612 if (vinfo[0].visual == DefaultVisual(dpy, screen_number)) {
613 context->attribs->flags |= RC_DefaultVisual;
614 } else {
615 XSetWindowAttributes attr;
616 unsigned long mask;
618 context->visual = vinfo[0].visual;
619 context->depth = vinfo[0].depth;
620 context->vclass = vinfo[0].class;
621 getColormap(context, screen_number);
622 attr.colormap = context->cmap;
623 attr.override_redirect = True;
624 attr.border_pixel = 0;
625 attr.background_pixel = 0;
626 mask = CWBorderPixel | CWColormap | CWOverrideRedirect | CWBackPixel;
627 context->drawable =
628 XCreateWindow(dpy, RootWindow(dpy, screen_number), 1, 1,
629 1, 1, 0, context->depth, CopyFromParent, context->visual, mask, &attr);
631 XFree(vinfo);
634 /* use default */
635 if (!context->visual) {
636 if ((context->attribs->flags & RC_DefaultVisual)
637 || !bestContext(dpy, screen_number, context)) {
638 context->visual = DefaultVisual(dpy, screen_number);
639 context->depth = DefaultDepth(dpy, screen_number);
640 context->cmap = DefaultColormap(dpy, screen_number);
641 context->drawable = RootWindow(dpy, screen_number);
642 context->black = BlackPixel(dpy, screen_number);
643 context->white = WhitePixel(dpy, screen_number);
644 context->vclass = context->visual->class;
648 gcv.function = GXcopy;
649 gcv.graphics_exposures = False;
650 context->copy_gc = XCreateGC(dpy, context->drawable, GCFunction | GCGraphicsExposures, &gcv);
652 if (context->vclass == PseudoColor || context->vclass == StaticColor) {
653 if (!setupPseudoColorColormap(context)) {
654 free(context);
655 return NULL;
657 } else if (context->vclass == GrayScale || context->vclass == StaticGray) {
658 context->colors = allocateGrayScale(context);
659 if (!context->colors) {
660 free(context);
661 return NULL;
663 } else if (context->vclass == TrueColor) {
664 /* calc offsets to create a TrueColor pixel */
665 context->red_offset = count_offset(context->visual->red_mask);
666 context->green_offset = count_offset(context->visual->green_mask);
667 context->blue_offset = count_offset(context->visual->blue_mask);
668 /* disable dithering on 24 bits visuals */
669 if (context->depth >= 24)
670 context->attribs->render_mode = RBestMatchRendering;
673 /* check avaiability of MIT-SHM */
674 #ifdef XSHM
675 if (!(context->attribs->flags & RC_UseSharedMemory)) {
676 context->attribs->flags |= RC_UseSharedMemory;
677 context->attribs->use_shared_memory = True;
680 if (context->attribs->use_shared_memory) {
681 int major, minor;
682 Bool sharedPixmaps;
684 context->flags.use_shared_pixmap = 0;
686 if (!XShmQueryVersion(context->dpy, &major, &minor, &sharedPixmaps)) {
687 context->attribs->use_shared_memory = False;
688 } else {
689 if (XShmPixmapFormat(context->dpy) == ZPixmap)
690 context->flags.use_shared_pixmap = sharedPixmaps;
693 #endif
695 return context;
698 static Bool bestContext(Display * dpy, int screen_number, RContext * context)
700 XVisualInfo *vinfo = NULL, rvinfo;
701 int best = -1, numvis, i;
702 long flags;
703 XSetWindowAttributes attr;
705 rvinfo.class = TrueColor;
706 rvinfo.screen = screen_number;
707 flags = VisualClassMask | VisualScreenMask;
709 vinfo = XGetVisualInfo(dpy, flags, &rvinfo, &numvis);
710 if (vinfo) { /* look for a TrueColor, 24-bit or more (pref 24) */
711 for (i = numvis - 1, best = -1; i >= 0; i--) {
712 if (vinfo[i].depth == 24)
713 best = i;
714 else if (vinfo[i].depth > 24 && best < 0)
715 best = i;
718 if (best > -1) {
719 context->visual = vinfo[best].visual;
720 context->depth = vinfo[best].depth;
721 context->vclass = vinfo[best].class;
722 getColormap(context, screen_number);
723 attr.colormap = context->cmap;
724 attr.override_redirect = True;
725 attr.border_pixel = 0;
726 context->drawable =
727 XCreateWindow(dpy, RootWindow(dpy, screen_number),
728 1, 1, 1, 1, 0, context->depth,
729 CopyFromParent, context->visual,
730 CWBorderPixel | CWColormap | CWOverrideRedirect, &attr);
732 if (vinfo)
733 XFree((char *)vinfo);
735 if (best < 0)
736 return False;
737 else
738 return True;