Initial revision
[wmaker-crm.git] / wrlib / context.c
blob21efc0e4780eaa6a4b0576fb58ebf14ca0a98ce4
1 /* context.c - X context management
2 *
3 * Raster graphics library
4 *
5 * Copyright (c) 1997 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>
32 #include <math.h>
34 #include "wraster.h"
36 static Bool bestContext(Display *dpy, int screen_number, RContext *context);
38 static RContextAttributes DEFAULT_CONTEXT_ATTRIBS = {
39 RC_UseSharedMemory|RC_RenderMode|RC_ColorsPerChannel, /* flags */
40 RM_DITHER, /* render_mode */
41 4, /* colors_per_channel */
42 0,
46 True /* use_shared_memory */
50 static XColor*
51 allocatePseudoColor(RContext *ctx)
53 XColor *colors;
54 XColor avcolors[256];
55 int avncolors;
56 int i, ncolors, r, g, b;
57 int retries;
58 int cpc = ctx->attribs->colors_per_channel;
60 ncolors = cpc * cpc * cpc;
62 if ( ncolors > (1<<ctx->depth) ) {
63 /* reduce colormap size */
64 cpc = ctx->attribs->colors_per_channel = 1<<((int)ctx->depth/3);
65 ncolors = cpc * cpc * cpc;
68 if (cpc < 2 || ncolors > (1<<ctx->depth)) {
69 sprintf(RErrorString, "invalid colormap size %i", cpc);
70 return NULL;
73 colors = malloc(sizeof(XColor)*ncolors);
74 if (!colors) {
75 sprintf(RErrorString, "out of memory");
76 return NULL;
78 i=0;
80 if ((ctx->attribs->flags & RC_GammaCorrection) && ctx->attribs->rgamma > 0
81 && ctx->attribs->ggamma > 0 && ctx->attribs->bgamma > 0) {
82 double rg, gg, bg;
83 double tmp;
85 /* do gamma correction */
86 rg = 1.0/ctx->attribs->rgamma;
87 gg = 1.0/ctx->attribs->ggamma;
88 bg = 1.0/ctx->attribs->bgamma;
89 for (r=0; r<cpc; r++) {
90 for (g=0; g<cpc; g++) {
91 for (b=0; b<cpc; b++) {
93 colors[i].red=(r*0xffff) / (cpc-1);
94 colors[i].green=(g*0xffff) / (cpc-1);
95 colors[i].blue=(b*0xffff) / (cpc-1);
96 colors[i].flags = DoRed|DoGreen|DoBlue;
98 tmp = (double)colors[i].red / 65536.0;
99 colors[i].red = (unsigned short)(65536.0*pow(tmp, rg));
101 tmp = (double)colors[i].green / 65536.0;
102 colors[i].green = (unsigned short)(65536.0*pow(tmp, gg));
104 tmp = (double)colors[i].blue / 65536.0;
105 colors[i].blue = (unsigned short)(65536.0*pow(tmp, bg));
107 i++;
112 } else {
113 for (r=0; r<cpc; r++) {
114 for (g=0; g<cpc; g++) {
115 for (b=0; b<cpc; b++) {
116 colors[i].red=(r*0xffff) / (cpc-1);
117 colors[i].green=(g*0xffff) / (cpc-1);
118 colors[i].blue=(b*0xffff) / (cpc-1);
119 colors[i].flags = DoRed|DoGreen|DoBlue;
120 i++;
125 /* try to allocate the colors */
126 for (i=0; i<ncolors; i++) {
127 if (!XAllocColor(ctx->dpy, ctx->cmap, &(colors[i]))) {
128 colors[i].flags = 0; /* failed */
129 } else {
130 colors[i].flags = DoRed|DoGreen|DoBlue;
133 /* try to allocate close values for the colors that couldn't
134 * be allocated before */
135 avncolors = (1<<ctx->depth>256 ? 256 : 1<<ctx->depth);
136 for (i=0; i<avncolors; i++) avcolors[i].pixel = i;
138 XQueryColors(ctx->dpy, ctx->cmap, avcolors, avncolors);
140 for (i=0; i<ncolors; i++) {
141 if (colors[i].flags==0) {
142 int j;
143 unsigned long cdiff=0xffffffff, diff;
144 unsigned long closest=0;
146 retries = 2;
148 while (retries--) {
149 /* find closest color */
150 for (j=0; j<avncolors; j++) {
151 r = (colors[i].red - avcolors[i].red)>>8;
152 g = (colors[i].green - avcolors[i].green)>>8;
153 b = (colors[i].blue - avcolors[i].blue)>>8;
154 diff = r*r + g*g + b*b;
155 if (diff<cdiff) {
156 cdiff = diff;
157 closest = j;
160 /* allocate closest color found */
161 colors[i].red = avcolors[closest].red;
162 colors[i].green = avcolors[closest].green;
163 colors[i].blue = avcolors[closest].blue;
164 if (XAllocColor(ctx->dpy, ctx->cmap, &colors[i])) {
165 colors[i].flags = DoRed|DoGreen|DoBlue;
166 break; /* succeeded, don't need to retry */
168 #ifdef DEBUG
169 printf("close color allocation failed. Retrying...\n");
170 #endif
174 return colors;
178 static XColor*
179 allocateGrayScale(RContext *ctx)
181 XColor *colors;
182 XColor avcolors[256];
183 int avncolors;
184 int i, ncolors, r, g, b;
185 int retries;
186 int cpc = ctx->attribs->colors_per_channel;
188 ncolors = cpc * cpc * cpc;
190 if (ctx->vclass == StaticGray) {
191 /* we might as well use all grays */
192 ncolors = 1<<ctx->depth;
193 } else {
194 if ( ncolors > (1<<ctx->depth) ) {
195 /* reduce colormap size */
196 cpc = ctx->attribs->colors_per_channel = 1<<((int)ctx->depth/3);
197 ncolors = cpc * cpc * cpc;
200 if (cpc < 2 || ncolors > (1<<ctx->depth)) {
201 sprintf(RErrorString, "invalid colormap size %i", cpc);
202 return NULL;
206 if (ncolors>=256 && ctx->vclass==StaticGray) {
207 /* don't need dithering for 256 levels of gray in StaticGray visual */
208 ctx->attribs->render_mode = RM_MATCH;
211 colors = malloc(sizeof(XColor)*ncolors);
212 if (!colors) {
213 sprintf(RErrorString, "out of memory");
214 return False;
216 for (i=0; i<ncolors; i++) {
217 colors[i].red=(i*0xffff) / (ncolors-1);
218 colors[i].green=(i*0xffff) / (ncolors-1);
219 colors[i].blue=(i*0xffff) / (ncolors-1);
220 colors[i].flags = DoRed|DoGreen|DoBlue;
222 /* try to allocate the colors */
223 for (i=0; i<ncolors; i++) {
224 #ifdef DEBUG
225 printf("trying:%x,%x,%x\n",colors[i].red,colors[i].green,colors[i].blue);
226 #endif
227 if (!XAllocColor(ctx->dpy, ctx->cmap, &(colors[i]))) {
228 colors[i].flags = 0; /* failed */
229 #ifdef DEBUG
230 printf("failed:%x,%x,%x\n",colors[i].red,colors[i].green,colors[i].blue);
231 #endif
232 } else {
233 colors[i].flags = DoRed|DoGreen|DoBlue;
234 #ifdef DEBUG
235 printf("success:%x,%x,%x\n",colors[i].red,colors[i].green,colors[i].blue);
236 #endif
239 /* try to allocate close values for the colors that couldn't
240 * be allocated before */
241 avncolors = (1<<ctx->depth>256 ? 256 : 1<<ctx->depth);
242 for (i=0; i<avncolors; i++) avcolors[i].pixel = i;
244 XQueryColors(ctx->dpy, ctx->cmap, avcolors, avncolors);
246 for (i=0; i<ncolors; i++) {
247 if (colors[i].flags==0) {
248 int j;
249 unsigned long cdiff=0xffffffff, diff;
250 unsigned long closest=0;
252 retries = 2;
254 while (retries--) {
255 /* find closest color */
256 for (j=0; j<avncolors; j++) {
257 r = (colors[i].red - avcolors[i].red)>>8;
258 g = (colors[i].green - avcolors[i].green)>>8;
259 b = (colors[i].blue - avcolors[i].blue)>>8;
260 diff = r*r + g*g + b*b;
261 if (diff<cdiff) {
262 cdiff = diff;
263 closest = j;
266 /* allocate closest color found */
267 #ifdef DEBUG
268 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);
269 #endif
270 colors[i].red = avcolors[closest].red;
271 colors[i].green = avcolors[closest].green;
272 colors[i].blue = avcolors[closest].blue;
273 if (XAllocColor(ctx->dpy, ctx->cmap, &colors[i])) {
274 colors[i].flags = DoRed|DoGreen|DoBlue;
275 break; /* succeeded, don't need to retry */
277 #ifdef DEBUG
278 printf("close color allocation failed. Retrying...\n");
279 #endif
283 return colors;
287 static char*
288 mygetenv(char *var, int scr)
290 char *p;
291 char varname[64];
293 if (scr==0) {
294 p = getenv(var);
296 if (scr!=0 || !p) {
297 sprintf(varname, "%s%i", var, scr);
298 p = getenv(var);
300 return p;
304 static void
305 gatherconfig(RContext *context, int screen_n)
307 char *ptr;
309 ptr = mygetenv("WRASTER_GAMMA", screen_n);
310 if (ptr) {
311 float g1,g2,g3;
312 if (sscanf(ptr, "%f/%f/%f", &g1, &g2, &g3)!=3
313 || g1<=0.0 || g2<=0.0 || g3<=0.0) {
314 printf("wrlib: invalid value(s) for gamma correction \"%s\"\n",
315 ptr);
316 } else {
317 context->attribs->flags |= RC_GammaCorrection;
318 context->attribs->rgamma = g1;
319 context->attribs->ggamma = g2;
320 context->attribs->bgamma = g3;
326 static void
327 getColormap(RContext *context, int screen_number)
329 Colormap cmap = None;
330 XStandardColormap *cmaps;
331 int ncmaps, i;
333 if (XGetRGBColormaps(context->dpy,
334 RootWindow(context->dpy, screen_number),
335 &cmaps, &ncmaps, XA_RGB_DEFAULT_MAP)) {
336 for (i=0; i<ncmaps; ++i) {
337 if (cmaps[i].visualid == context->visual->visualid) {
338 cmap = cmaps[i].colormap;
339 break;
342 XFree(cmaps);
344 if (cmap == None) {
345 XColor color;
347 cmap = XCreateColormap(context->dpy,
348 RootWindow(context->dpy, screen_number),
349 context->visual, AllocNone);
351 color.red = color.green = color.blue = 0;
352 XAllocColor(context->dpy, cmap, &color);
353 context->black = color.pixel;
355 color.red = color.green = color.blue = 0xffff;
356 XAllocColor(context->dpy, cmap, &color);
357 context->white = color.pixel;
359 context->cmap = cmap;
363 static int
364 count_offset(unsigned long mask)
366 int i;
368 i=0;
369 while ((mask & 1)==0) {
370 i++;
371 mask = mask >> 1;
373 return i;
377 RContext*
378 RCreateContext(Display *dpy, int screen_number, RContextAttributes *attribs)
380 RContext *context;
381 XGCValues gcv;
384 RErrorString[0]=0;
385 context = malloc(sizeof(RContext));
386 if (!context) {
387 sprintf(RErrorString, "out of memory");
388 return NULL;
390 memset(context, 0, sizeof(RContext));
392 context->dpy = dpy;
394 context->screen_number = screen_number;
396 context->attribs = malloc(sizeof(RContextAttributes));
397 if (!context->attribs) {
398 free(context);
399 sprintf(RErrorString, "out of memory");
400 return NULL;
402 if (!attribs)
403 *context->attribs = DEFAULT_CONTEXT_ATTRIBS;
404 else
405 *context->attribs = *attribs;
407 /* get configuration from environment variables */
408 gatherconfig(context, screen_number);
410 if ((context->attribs->flags & RC_VisualID)) {
411 XVisualInfo *vinfo, templ;
412 int nret;
414 templ.screen = screen_number;
415 templ.visualid = context->attribs->visualid;
416 vinfo = XGetVisualInfo(context->dpy, VisualIDMask|VisualScreenMask,
417 &templ, &nret);
419 if (!vinfo || nret==0) {
420 sprintf(RErrorString, "invalid visual id %x\n",
421 (unsigned int)context->attribs->visualid);
422 } else {
423 if (vinfo[0].visual == DefaultVisual(dpy, screen_number)) {
424 context->attribs->flags |= RC_DefaultVisual;
425 } else {
426 XSetWindowAttributes attr;
427 unsigned long mask;
429 context->visual = vinfo[0].visual;
430 context->depth = vinfo[0].depth;
431 context->vclass = vinfo[0].class;
432 getColormap(context, screen_number);
433 attr.colormap = context->cmap;
434 attr.override_redirect = True;
435 attr.border_pixel = 0;
436 attr.background_pixel = 0;
437 mask = CWBorderPixel|CWColormap|CWOverrideRedirect|CWBackPixel;
438 context->drawable =
439 XCreateWindow(dpy, RootWindow(dpy, screen_number), 1, 1,
440 1, 1, 0, context->depth, CopyFromParent,
441 context->visual, mask, &attr);
442 /* XSetWindowColormap(dpy, context->drawable, attr.colormap);*/
444 XFree(vinfo);
448 /* use default */
449 if (!context->visual) {
450 if ((context->attribs->flags & RC_DefaultVisual)
451 || !bestContext(dpy, screen_number, context)) {
452 context->visual = DefaultVisual(dpy, screen_number);
453 context->depth = DefaultDepth(dpy, screen_number);
454 context->cmap = DefaultColormap(dpy, screen_number);
455 context->drawable = RootWindow(dpy, screen_number);
456 context->black = BlackPixel(dpy, screen_number);
457 context->white = WhitePixel(dpy, screen_number);
458 context->vclass = context->visual->class;
462 gcv.function = GXcopy;
463 gcv.graphics_exposures = False;
464 context->copy_gc = XCreateGC(dpy, context->drawable, GCFunction
465 |GCGraphicsExposures, &gcv);
467 if (context->vclass == PseudoColor || context->vclass == StaticColor) {
468 context->colors = allocatePseudoColor(context);
469 if (!context->colors) {
470 return NULL;
472 } else if (context->vclass == GrayScale || context->vclass == StaticGray) {
473 context->colors = allocateGrayScale(context);
474 if (!context->colors) {
475 return NULL;
477 } else if (context->vclass == TrueColor) {
478 /* calc offsets to create a TrueColor pixel */
479 context->red_offset = count_offset(context->visual->red_mask);
480 context->green_offset = count_offset(context->visual->green_mask);
481 context->blue_offset = count_offset(context->visual->blue_mask);
482 /* disable dithering on 24 bits visuals */
483 if (context->depth >= 24)
484 context->attribs->render_mode = RM_MATCH;
487 /* check avaiability of MIT-SHM */
488 #ifdef XSHM
489 if (!(context->attribs->flags & RC_UseSharedMemory)) {
490 context->attribs->flags |= RC_UseSharedMemory;
491 context->attribs->use_shared_memory = True;
494 if (context->attribs->use_shared_memory) {
495 if (!XShmQueryExtension(context->dpy)) {
496 context->attribs->use_shared_memory = False;
499 #endif
501 return context;
505 static Bool
506 bestContext(Display *dpy, int screen_number, RContext *context)
508 XVisualInfo *vinfo=NULL, rvinfo;
509 int best = -1, numvis, i;
510 long flags;
511 XSetWindowAttributes attr;
513 rvinfo.class = TrueColor;
514 rvinfo.screen = screen_number;
515 flags = VisualClassMask | VisualScreenMask;
517 vinfo = XGetVisualInfo(dpy, flags, &rvinfo, &numvis);
518 if (vinfo) { /* look for a TrueColor, 24-bit or more (pref 24) */
519 for (i=numvis-1, best = -1; i>=0; i--) {
520 if (vinfo[i].depth == 24) best = i;
521 else if (vinfo[i].depth>24 && best<0) best = i;
525 if (best == -1) { /* look for a DirectColor, 24-bit or more (pref 24) */
526 rvinfo.class = DirectColor;
527 if (vinfo) XFree((char *) vinfo);
528 vinfo = XGetVisualInfo(dpy, flags, &rvinfo, &numvis);
529 if (vinfo) {
530 for (i=0, best = -1; i<numvis; i++) {
531 if (vinfo[i].depth == 24) best = i;
532 else if (vinfo[i].depth>24 && best<0) best = i;
536 if (best > -1) {
537 context->visual = vinfo[best].visual;
538 context->depth = vinfo[best].depth;
539 context->vclass = vinfo[best].class;
540 getColormap(context, screen_number);
541 attr.colormap = context->cmap;
542 attr.override_redirect = True;
543 attr.border_pixel = 0;
544 context->drawable =
545 XCreateWindow(dpy, RootWindow(dpy, screen_number),
546 1, 1, 1, 1, 0, context->depth,
547 CopyFromParent, context->visual,
548 CWBorderPixel|CWColormap|CWOverrideRedirect, &attr);
549 /* XSetWindowColormap(dpy, context->drawable, context->cmap);*/
551 if (vinfo) XFree((char *) vinfo);
553 if (best < 0)
554 return False;
555 else
556 return True;