Updating to version 0.20.2
[wmaker-crm.git] / wrlib / context.c
blob3c3c983fb7f1a38a5affa761c913a8d4182f3650
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>
31 #include <assert.h>
33 #include <math.h>
35 #include "wraster.h"
37 static Bool bestContext(Display *dpy, int screen_number, RContext *context);
39 static RContextAttributes DEFAULT_CONTEXT_ATTRIBS = {
40 RC_UseSharedMemory|RC_RenderMode|RC_ColorsPerChannel, /* flags */
41 RM_DITHER, /* render_mode */
42 4, /* colors_per_channel */
43 0,
47 True /* use_shared_memory */
51 static XColor*
52 allocatePseudoColor(RContext *ctx)
54 XColor *colors;
55 XColor avcolors[256];
56 int avncolors;
57 int i, ncolors, r, g, b;
58 int retries;
59 int cpc = ctx->attribs->colors_per_channel;
61 ncolors = cpc * cpc * cpc;
63 if ( ncolors > (1<<ctx->depth) ) {
64 /* reduce colormap size */
65 cpc = ctx->attribs->colors_per_channel = 1<<((int)ctx->depth/3);
66 ncolors = cpc * cpc * cpc;
69 assert(cpc >= 2 && ncolors <= (1<<ctx->depth));
71 colors = malloc(sizeof(XColor)*ncolors);
72 if (!colors) {
73 RErrorCode = RERR_NOMEMORY;
74 return NULL;
76 i=0;
78 if ((ctx->attribs->flags & RC_GammaCorrection) && ctx->attribs->rgamma > 0
79 && ctx->attribs->ggamma > 0 && ctx->attribs->bgamma > 0) {
80 double rg, gg, bg;
81 double tmp;
83 /* do gamma correction */
84 rg = 1.0/ctx->attribs->rgamma;
85 gg = 1.0/ctx->attribs->ggamma;
86 bg = 1.0/ctx->attribs->bgamma;
87 for (r=0; r<cpc; r++) {
88 for (g=0; g<cpc; g++) {
89 for (b=0; b<cpc; b++) {
90 colors[i].red=(r*0xffff) / (cpc-1);
91 colors[i].green=(g*0xffff) / (cpc-1);
92 colors[i].blue=(b*0xffff) / (cpc-1);
93 colors[i].flags = DoRed|DoGreen|DoBlue;
95 tmp = (double)colors[i].red / 65536.0;
96 colors[i].red = (unsigned short)(65536.0*pow(tmp, rg));
98 tmp = (double)colors[i].green / 65536.0;
99 colors[i].green = (unsigned short)(65536.0*pow(tmp, gg));
101 tmp = (double)colors[i].blue / 65536.0;
102 colors[i].blue = (unsigned short)(65536.0*pow(tmp, bg));
104 i++;
109 } else {
110 for (r=0; r<cpc; r++) {
111 for (g=0; g<cpc; g++) {
112 for (b=0; b<cpc; b++) {
113 colors[i].red=(r*0xffff) / (cpc-1);
114 colors[i].green=(g*0xffff) / (cpc-1);
115 colors[i].blue=(b*0xffff) / (cpc-1);
116 colors[i].flags = DoRed|DoGreen|DoBlue;
117 i++;
122 /* try to allocate the colors */
123 for (i=0; i<ncolors; i++) {
124 if (!XAllocColor(ctx->dpy, ctx->cmap, &(colors[i]))) {
125 colors[i].flags = 0; /* failed */
126 } else {
127 colors[i].flags = DoRed|DoGreen|DoBlue;
130 /* try to allocate close values for the colors that couldn't
131 * be allocated before */
132 avncolors = (1<<ctx->depth>256 ? 256 : 1<<ctx->depth);
133 for (i=0; i<avncolors; i++) avcolors[i].pixel = i;
135 XQueryColors(ctx->dpy, ctx->cmap, avcolors, avncolors);
137 for (i=0; i<ncolors; i++) {
138 if (colors[i].flags==0) {
139 int j;
140 unsigned long cdiff=0xffffffff, diff;
141 unsigned long closest=0;
143 retries = 2;
145 while (retries--) {
146 /* find closest color */
147 for (j=0; j<avncolors; j++) {
148 r = (colors[i].red - avcolors[i].red)>>8;
149 g = (colors[i].green - avcolors[i].green)>>8;
150 b = (colors[i].blue - avcolors[i].blue)>>8;
151 diff = r*r + g*g + b*b;
152 if (diff<cdiff) {
153 cdiff = diff;
154 closest = j;
157 /* allocate closest color found */
158 colors[i].red = avcolors[closest].red;
159 colors[i].green = avcolors[closest].green;
160 colors[i].blue = avcolors[closest].blue;
161 if (XAllocColor(ctx->dpy, ctx->cmap, &colors[i])) {
162 colors[i].flags = DoRed|DoGreen|DoBlue;
163 break; /* succeeded, don't need to retry */
165 #ifdef DEBUG
166 printf("close color allocation failed. Retrying...\n");
167 #endif
171 return colors;
175 static XColor*
176 allocateGrayScale(RContext *ctx)
178 XColor *colors;
179 XColor avcolors[256];
180 int avncolors;
181 int i, ncolors, r, g, b;
182 int retries;
183 int cpc = ctx->attribs->colors_per_channel;
185 ncolors = cpc * cpc * cpc;
187 if (ctx->vclass == StaticGray) {
188 /* we might as well use all grays */
189 ncolors = 1<<ctx->depth;
190 } else {
191 if ( ncolors > (1<<ctx->depth) ) {
192 /* reduce colormap size */
193 cpc = ctx->attribs->colors_per_channel = 1<<((int)ctx->depth/3);
194 ncolors = cpc * cpc * cpc;
197 assert(cpc >= 2 && ncolors <= (1<<ctx->depth));
200 if (ncolors>=256 && ctx->vclass==StaticGray) {
201 /* don't need dithering for 256 levels of gray in StaticGray visual */
202 ctx->attribs->render_mode = RM_MATCH;
205 colors = malloc(sizeof(XColor)*ncolors);
206 if (!colors) {
207 RErrorCode = RERR_NOMEMORY;
208 return False;
210 for (i=0; i<ncolors; i++) {
211 colors[i].red=(i*0xffff) / (ncolors-1);
212 colors[i].green=(i*0xffff) / (ncolors-1);
213 colors[i].blue=(i*0xffff) / (ncolors-1);
214 colors[i].flags = DoRed|DoGreen|DoBlue;
216 /* try to allocate the colors */
217 for (i=0; i<ncolors; i++) {
218 #ifdef DEBUG
219 printf("trying:%x,%x,%x\n",colors[i].red,colors[i].green,colors[i].blue);
220 #endif
221 if (!XAllocColor(ctx->dpy, ctx->cmap, &(colors[i]))) {
222 colors[i].flags = 0; /* failed */
223 #ifdef DEBUG
224 printf("failed:%x,%x,%x\n",colors[i].red,colors[i].green,colors[i].blue);
225 #endif
226 } else {
227 colors[i].flags = DoRed|DoGreen|DoBlue;
228 #ifdef DEBUG
229 printf("success:%x,%x,%x\n",colors[i].red,colors[i].green,colors[i].blue);
230 #endif
233 /* try to allocate close values for the colors that couldn't
234 * be allocated before */
235 avncolors = (1<<ctx->depth>256 ? 256 : 1<<ctx->depth);
236 for (i=0; i<avncolors; i++) avcolors[i].pixel = i;
238 XQueryColors(ctx->dpy, ctx->cmap, avcolors, avncolors);
240 for (i=0; i<ncolors; i++) {
241 if (colors[i].flags==0) {
242 int j;
243 unsigned long cdiff=0xffffffff, diff;
244 unsigned long closest=0;
246 retries = 2;
248 while (retries--) {
249 /* find closest color */
250 for (j=0; j<avncolors; j++) {
251 r = (colors[i].red - avcolors[i].red)>>8;
252 g = (colors[i].green - avcolors[i].green)>>8;
253 b = (colors[i].blue - avcolors[i].blue)>>8;
254 diff = r*r + g*g + b*b;
255 if (diff<cdiff) {
256 cdiff = diff;
257 closest = j;
260 /* allocate closest color found */
261 #ifdef DEBUG
262 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);
263 #endif
264 colors[i].red = avcolors[closest].red;
265 colors[i].green = avcolors[closest].green;
266 colors[i].blue = avcolors[closest].blue;
267 if (XAllocColor(ctx->dpy, ctx->cmap, &colors[i])) {
268 colors[i].flags = DoRed|DoGreen|DoBlue;
269 break; /* succeeded, don't need to retry */
271 #ifdef DEBUG
272 printf("close color allocation failed. Retrying...\n");
273 #endif
277 return colors;
281 static char*
282 mygetenv(char *var, int scr)
284 char *p;
285 char varname[64];
287 sprintf(varname, "%s%i", var, scr);
288 p = getenv(varname);
289 if (!p) {
290 p = getenv(var);
292 return p;
296 static void
297 gatherconfig(RContext *context, int screen_n)
299 char *ptr;
301 ptr = mygetenv("WRASTER_GAMMA", screen_n);
302 if (ptr) {
303 float g1,g2,g3;
304 if (sscanf(ptr, "%f/%f/%f", &g1, &g2, &g3)!=3
305 || g1<=0.0 || g2<=0.0 || g3<=0.0) {
306 printf("wrlib: invalid value(s) for gamma correction \"%s\"\n",
307 ptr);
308 } else {
309 context->attribs->flags |= RC_GammaCorrection;
310 context->attribs->rgamma = g1;
311 context->attribs->ggamma = g2;
312 context->attribs->bgamma = g3;
315 ptr = mygetenv("WRASTER_COLOR_RESOLUTION", screen_n);
316 if (ptr) {
317 int i;
318 if (sscanf(ptr, "%d", &i)!=1 || i<2 || i>6) {
319 printf("wrlib: invalid value for color resolution \"%s\"\n",ptr);
320 } else {
321 context->attribs->flags |= RC_ColorsPerChannel;
322 context->attribs->colors_per_channel = i;
328 static void
329 getColormap(RContext *context, int screen_number)
331 Colormap cmap = None;
332 XStandardColormap *cmaps;
333 int ncmaps, i;
335 if (XGetRGBColormaps(context->dpy,
336 RootWindow(context->dpy, screen_number),
337 &cmaps, &ncmaps, XA_RGB_DEFAULT_MAP)) {
338 for (i=0; i<ncmaps; ++i) {
339 if (cmaps[i].visualid == context->visual->visualid) {
340 cmap = cmaps[i].colormap;
341 break;
344 XFree(cmaps);
346 if (cmap == None) {
347 XColor color;
349 cmap = XCreateColormap(context->dpy,
350 RootWindow(context->dpy, screen_number),
351 context->visual, AllocNone);
353 color.red = color.green = color.blue = 0;
354 XAllocColor(context->dpy, cmap, &color);
355 context->black = color.pixel;
357 color.red = color.green = color.blue = 0xffff;
358 XAllocColor(context->dpy, cmap, &color);
359 context->white = color.pixel;
362 context->cmap = cmap;
366 static int
367 count_offset(unsigned long mask)
369 int i;
371 i=0;
372 while ((mask & 1)==0) {
373 i++;
374 mask = mask >> 1;
376 return i;
380 RContext*
381 RCreateContext(Display *dpy, int screen_number, RContextAttributes *attribs)
383 RContext *context;
384 XGCValues gcv;
387 context = malloc(sizeof(RContext));
388 if (!context) {
389 RErrorCode = RERR_NOMEMORY;
390 return NULL;
392 memset(context, 0, sizeof(RContext));
394 context->dpy = dpy;
396 context->screen_number = screen_number;
398 context->attribs = malloc(sizeof(RContextAttributes));
399 if (!context->attribs) {
400 free(context);
401 RErrorCode = RERR_NOMEMORY;
402 return NULL;
404 if (!attribs)
405 *context->attribs = DEFAULT_CONTEXT_ATTRIBS;
406 else
407 *context->attribs = *attribs;
409 /* get configuration from environment variables */
410 gatherconfig(context, screen_number);
412 if ((context->attribs->flags & RC_VisualID)) {
413 XVisualInfo *vinfo, templ;
414 int nret;
416 templ.screen = screen_number;
417 templ.visualid = context->attribs->visualid;
418 vinfo = XGetVisualInfo(context->dpy, VisualIDMask|VisualScreenMask,
419 &templ, &nret);
420 if (!vinfo || nret==0) {
421 free(context);
422 RErrorCode = RERR_BADVISUALID;
423 return NULL;
426 if (vinfo[0].visual == DefaultVisual(dpy, screen_number)) {
427 context->attribs->flags |= RC_DefaultVisual;
428 } else {
429 XSetWindowAttributes attr;
430 unsigned long mask;
432 context->visual = vinfo[0].visual;
433 context->depth = vinfo[0].depth;
434 context->vclass = vinfo[0].class;
435 getColormap(context, screen_number);
436 attr.colormap = context->cmap;
437 attr.override_redirect = True;
438 attr.border_pixel = 0;
439 attr.background_pixel = 0;
440 mask = CWBorderPixel|CWColormap|CWOverrideRedirect|CWBackPixel;
441 context->drawable =
442 XCreateWindow(dpy, RootWindow(dpy, screen_number), 1, 1,
443 1, 1, 0, context->depth, CopyFromParent,
444 context->visual, mask, &attr);
445 /* XSetWindowColormap(dpy, context->drawable, attr.colormap);*/
447 XFree(vinfo);
450 /* use default */
451 if (!context->visual) {
452 if ((context->attribs->flags & RC_DefaultVisual)
453 || !bestContext(dpy, screen_number, context)) {
454 context->visual = DefaultVisual(dpy, screen_number);
455 context->depth = DefaultDepth(dpy, screen_number);
456 context->cmap = DefaultColormap(dpy, screen_number);
457 context->drawable = RootWindow(dpy, screen_number);
458 context->black = BlackPixel(dpy, screen_number);
459 context->white = WhitePixel(dpy, screen_number);
460 context->vclass = context->visual->class;
464 gcv.function = GXcopy;
465 gcv.graphics_exposures = False;
466 context->copy_gc = XCreateGC(dpy, context->drawable, GCFunction
467 |GCGraphicsExposures, &gcv);
469 if (context->vclass == PseudoColor || context->vclass == StaticColor) {
470 context->colors = allocatePseudoColor(context);
471 if (!context->colors) {
472 return NULL;
474 } else if (context->vclass == GrayScale || context->vclass == StaticGray) {
475 context->colors = allocateGrayScale(context);
476 if (!context->colors) {
477 return NULL;
479 } else if (context->vclass == TrueColor) {
480 /* calc offsets to create a TrueColor pixel */
481 context->red_offset = count_offset(context->visual->red_mask);
482 context->green_offset = count_offset(context->visual->green_mask);
483 context->blue_offset = count_offset(context->visual->blue_mask);
484 /* disable dithering on 24 bits visuals */
485 if (context->depth >= 24)
486 context->attribs->render_mode = RM_MATCH;
489 /* check avaiability of MIT-SHM */
490 #ifdef XSHM
491 if (!(context->attribs->flags & RC_UseSharedMemory)) {
492 context->attribs->flags |= RC_UseSharedMemory;
493 context->attribs->use_shared_memory = True;
496 if (context->attribs->use_shared_memory) {
497 if (!XShmQueryExtension(context->dpy)) {
498 context->attribs->use_shared_memory = False;
501 #endif
503 return context;
507 static Bool
508 bestContext(Display *dpy, int screen_number, RContext *context)
510 XVisualInfo *vinfo=NULL, rvinfo;
511 int best = -1, numvis, i;
512 long flags;
513 XSetWindowAttributes attr;
515 rvinfo.class = TrueColor;
516 rvinfo.screen = screen_number;
517 flags = VisualClassMask | VisualScreenMask;
519 vinfo = XGetVisualInfo(dpy, flags, &rvinfo, &numvis);
520 if (vinfo) { /* look for a TrueColor, 24-bit or more (pref 24) */
521 for (i=numvis-1, best = -1; i>=0; i--) {
522 if (vinfo[i].depth == 24) best = i;
523 else if (vinfo[i].depth>24 && best<0) best = i;
527 if (best == -1) { /* look for a DirectColor, 24-bit or more (pref 24) */
528 rvinfo.class = DirectColor;
529 if (vinfo) XFree((char *) vinfo);
530 vinfo = XGetVisualInfo(dpy, flags, &rvinfo, &numvis);
531 if (vinfo) {
532 for (i=0, best = -1; i<numvis; i++) {
533 if (vinfo[i].depth == 24) best = i;
534 else if (vinfo[i].depth>24 && best<0) best = i;
538 if (best > -1) {
539 context->visual = vinfo[best].visual;
540 context->depth = vinfo[best].depth;
541 context->vclass = vinfo[best].class;
542 getColormap(context, screen_number);
543 attr.colormap = context->cmap;
544 attr.override_redirect = True;
545 attr.border_pixel = 0;
546 context->drawable =
547 XCreateWindow(dpy, RootWindow(dpy, screen_number),
548 1, 1, 1, 1, 0, context->depth,
549 CopyFromParent, context->visual,
550 CWBorderPixel|CWColormap|CWOverrideRedirect, &attr);
551 /* XSetWindowColormap(dpy, context->drawable, context->cmap);*/
553 if (vinfo) XFree((char *) vinfo);
555 if (best < 0)
556 return False;
557 else
558 return True;