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.
11  *
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.
16  *
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.
21  */
22 
23 #include <config.h>
24 
25 #include <X11/Xlib.h>
26 #include <X11/Xutil.h>
27 #include <X11/Xatom.h>
28 
29 #ifdef HAVE_LIBXMU
30 #include <X11/Xmu/StdCmap.h>
31 #endif
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <assert.h>
37 
38 #include <math.h>
39 
40 #include "wraster.h"
41 #include "scale.h"
42 
43 
44 #ifndef HAVE_FLOAT_MATHFUNC
45 #define powf(x, y) ((float) pow((double)(x), (double)(y)))
46 #endif
47 
48 static Bool bestContext(Display * dpy, int screen_number, RContext * context);
49 
50 static const RContextAttributes DEFAULT_CONTEXT_ATTRIBS = {
51 	RC_UseSharedMemory | RC_RenderMode | RC_ColorsPerChannel,	/* flags */
52 	RDitheredRendering,	/* render_mode */
53 	4,			/* colors_per_channel */
54 	0,
55 	0,
56 	0,
57 	0,
58 	True,			/* use_shared_memory */
59 	RMitchellFilter,
60 	RUseStdColormap
61 };
62 
63 /*
64  *
65  * Colormap allocation for PseudoColor visuals:
66  *
67  *
68  * switch standardColormap:
69  * 	none:
70  * 		allocate colors according to colors_per_channel
71  *
72  * 	best/default:
73  * 		if there's a std colormap defined then use it
74  *
75  * 		else
76  * 			create a std colormap and set it
77  */
78 
79 /*
80  *----------------------------------------------------------------------
81  * allocateStandardPseudoColor
82  * 	Creates the internal colormap for PseudoColor, setting the
83  * color values according to the supplied standard colormap.
84  *
85  * Returns: -
86  *
87  * Side effects: -
88  *
89  * Notes: -
90  *----------------------------------------------------------------------
91  */
allocateStandardPseudoColor(RContext * ctx,XStandardColormap * stdcmap)92 static Bool allocateStandardPseudoColor(RContext * ctx, XStandardColormap * stdcmap)
93 {
94 	int i;
95 
96 	ctx->ncolors = stdcmap->red_max * stdcmap->red_mult
97 	    + stdcmap->green_max * stdcmap->green_mult + stdcmap->blue_max * stdcmap->blue_mult + 1;
98 
99 	if (ctx->ncolors <= 1) {
100 		RErrorCode = RERR_INTERNAL;
101 		puts("wraster: bad standard colormap");
102 
103 		return False;
104 	}
105 
106 	ctx->colors = malloc(sizeof(XColor) * ctx->ncolors);
107 	if (!ctx->colors) {
108 		RErrorCode = RERR_NOMEMORY;
109 
110 		return False;
111 	}
112 
113 	ctx->pixels = malloc(sizeof(unsigned long) * ctx->ncolors);
114 	if (!ctx->pixels) {
115 
116 		free(ctx->colors);
117 		ctx->colors = NULL;
118 
119 		RErrorCode = RERR_NOMEMORY;
120 
121 		return False;
122 	}
123 
124 #define calc(max,mult) (((i / stdcmap->mult) % \
125     (stdcmap->max + 1)) * 65535) / stdcmap->max
126 
127 	for (i = 0; i < ctx->ncolors; i++) {
128 		ctx->colors[i].pixel = i + stdcmap->base_pixel;
129 		ctx->colors[i].red = calc(red_max, red_mult);
130 		ctx->colors[i].green = calc(green_max, green_mult);
131 		ctx->colors[i].blue = calc(blue_max, blue_mult);
132 
133 		ctx->pixels[i] = ctx->colors[i].pixel;
134 	}
135 
136 #undef calc
137 
138 	return True;
139 }
140 
setupStandardColormap(RContext * ctx,Atom property)141 static Bool setupStandardColormap(RContext * ctx, Atom property)
142 {
143 #ifdef HAVE_LIBXMU
144 	if (!XmuLookupStandardColormap(ctx->dpy, ctx->screen_number,
145 				       ctx->visual->visualid, ctx->depth, property, True, True)) {
146 		RErrorCode = RERR_STDCMAPFAIL;
147 
148 		return False;
149 	}
150 	return True;
151 #else
152 	(void) ctx;
153 	(void) property;
154 	RErrorCode = RERR_STDCMAPFAIL;
155 	return False;
156 #endif
157 }
158 
allocateColor(RContext * ctx,XColor * colors,int ncolors)159 static XColor *allocateColor(RContext *ctx, XColor *colors, int ncolors)
160 {
161 	XColor avcolors[256];
162 	int avncolors;
163 	int i, r, g, b;
164 	int retries;
165 
166 	for (i = 0; i < ncolors; i++) {
167 #ifdef WRLIB_DEBUG
168 		fprintf(stderr, "trying:%x,%x,%x\n", colors[i].red, colors[i].green, colors[i].blue);
169 #endif
170 		if (!XAllocColor(ctx->dpy, ctx->cmap, &(colors[i]))) {
171 			colors[i].flags = 0;	/* failed */
172 #ifdef WRLIB_DEBUG
173 			fprintf(stderr, "failed:%x,%x,%x\n", colors[i].red, colors[i].green, colors[i].blue);
174 #endif
175 		} else {
176 			colors[i].flags = DoRed | DoGreen | DoBlue;
177 #ifdef WRLIB_DEBUG
178 			fprintf(stderr, "success:%x,%x,%x\n", colors[i].red, colors[i].green, colors[i].blue);
179 #endif
180 		}
181 	}
182 	/* try to allocate close values for the colors that couldn't
183 	 * be allocated before */
184 	avncolors = (1 << ctx->depth > 256 ? 256 : 1 << ctx->depth);
185 	for (i = 0; i < avncolors; i++)
186 		avcolors[i].pixel = i;
187 
188 	XQueryColors(ctx->dpy, ctx->cmap, avcolors, avncolors);
189 
190 	for (i = 0; i < ncolors; i++) {
191 		if (colors[i].flags == 0) {
192 			int j;
193 			unsigned long cdiff = 0xffffffff, diff;
194 			unsigned long closest = 0;
195 
196 			retries = 2;
197 
198 			while (retries--) {
199 				/* find closest color */
200 				for (j = 0; j < avncolors; j++) {
201 					r = (colors[i].red - avcolors[i].red) >> 8;
202 					g = (colors[i].green - avcolors[i].green) >> 8;
203 					b = (colors[i].blue - avcolors[i].blue) >> 8;
204 					diff = r * r + g * g + b * b;
205 					if (diff < cdiff) {
206 						cdiff = diff;
207 						closest = j;
208 					}
209 				}
210 				/* allocate closest color found */
211 #ifdef WRLIB_DEBUG
212 				fprintf(stderr, "best match:%x,%x,%x => %x,%x,%x\n",
213 					colors[i].red, colors[i].green, colors[i].blue,
214 					avcolors[closest].red, avcolors[closest].green, avcolors[closest].blue);
215 #endif
216 				colors[i].red = avcolors[closest].red;
217 				colors[i].green = avcolors[closest].green;
218 				colors[i].blue = avcolors[closest].blue;
219 				if (XAllocColor(ctx->dpy, ctx->cmap, &colors[i])) {
220 					colors[i].flags = DoRed | DoGreen | DoBlue;
221 					break;	/* succeeded, don't need to retry */
222 				}
223 #ifdef WRLIB_DEBUG
224 				fputs("close color allocation failed. Retrying...\n", stderr);
225 #endif
226 			}
227 		}
228 	}
229 	return colors;
230 }
231 
allocatePseudoColor(RContext * ctx)232 static Bool allocatePseudoColor(RContext *ctx)
233 {
234 	XColor *colors;
235 	int i, ncolors, r, g, b;
236 	int cpc = ctx->attribs->colors_per_channel;
237 
238 	ncolors = cpc * cpc * cpc;
239 
240 	if (ncolors > (1 << ctx->depth)) {
241 		/* reduce colormap size */
242 		cpc = ctx->attribs->colors_per_channel = 1 << ((int)ctx->depth / 3);
243 		ncolors = cpc * cpc * cpc;
244 	}
245 
246 	assert(cpc >= 2 && ncolors <= (1 << ctx->depth));
247 
248 	colors = malloc(sizeof(XColor) * ncolors);
249 	if (!colors) {
250 		RErrorCode = RERR_NOMEMORY;
251 		return False;
252 	}
253 
254 	ctx->pixels = malloc(sizeof(unsigned long) * ncolors);
255 	if (!ctx->pixels) {
256 		free(colors);
257 		RErrorCode = RERR_NOMEMORY;
258 		return False;
259 	}
260 
261 	i = 0;
262 
263 	if ((ctx->attribs->flags & RC_GammaCorrection) && ctx->attribs->rgamma > 0
264 	    && ctx->attribs->ggamma > 0 && ctx->attribs->bgamma > 0) {
265 		float rg, gg, bg;
266 		float tmp;
267 
268 		/* do gamma correction */
269 		rg = 1.0F / ctx->attribs->rgamma;
270 		gg = 1.0F / ctx->attribs->ggamma;
271 		bg = 1.0F / ctx->attribs->bgamma;
272 		for (r = 0; r < cpc; r++) {
273 			for (g = 0; g < cpc; g++) {
274 				for (b = 0; b < cpc; b++) {
275 					colors[i].red = (r * 0xffff) / (cpc - 1);
276 					colors[i].green = (g * 0xffff) / (cpc - 1);
277 					colors[i].blue = (b * 0xffff) / (cpc - 1);
278 					colors[i].flags = DoRed | DoGreen | DoBlue;
279 
280 					tmp = (float) colors[i].red / 65536.0F;
281 					colors[i].red = (unsigned short)(65536.0F * powf(tmp, rg));
282 
283 					tmp = (float) colors[i].green / 65536.0F;
284 					colors[i].green = (unsigned short)(65536.0F * powf(tmp, gg));
285 
286 					tmp = (float) colors[i].blue / 65536.0F;
287 					colors[i].blue = (unsigned short)(65536.0F * powf(tmp, bg));
288 
289 					i++;
290 				}
291 			}
292 		}
293 
294 	} else {
295 		for (r = 0; r < cpc; r++) {
296 			for (g = 0; g < cpc; g++) {
297 				for (b = 0; b < cpc; b++) {
298 					colors[i].red = (r * 0xffff) / (cpc - 1);
299 					colors[i].green = (g * 0xffff) / (cpc - 1);
300 					colors[i].blue = (b * 0xffff) / (cpc - 1);
301 					colors[i].flags = DoRed | DoGreen | DoBlue;
302 					i++;
303 				}
304 			}
305 		}
306 	}
307 	/* try to allocate the colors */
308 	ctx->colors = allocateColor(ctx, colors, ncolors);
309 	ctx->ncolors = ncolors;
310 
311 	/* fill the pixels shortcut array */
312 	for (i = 0; i < ncolors; i++) {
313 		ctx->pixels[i] = ctx->colors[i].pixel;
314 	}
315 
316 	return True;
317 }
318 
allocateGrayScale(RContext * ctx)319 static XColor *allocateGrayScale(RContext * ctx)
320 {
321 	XColor *colors;
322 	int i, ncolors;
323 	int cpc = ctx->attribs->colors_per_channel;
324 
325 	ncolors = cpc * cpc * cpc;
326 
327 	if (ctx->vclass == StaticGray) {
328 		/* we might as well use all grays */
329 		ncolors = 1 << ctx->depth;
330 	} else {
331 		if (ncolors > (1 << ctx->depth)) {
332 			/* reduce colormap size */
333 			cpc = ctx->attribs->colors_per_channel = 1 << ((int)ctx->depth / 3);
334 			ncolors = cpc * cpc * cpc;
335 		}
336 
337 		assert(cpc >= 2 && ncolors <= (1 << ctx->depth));
338 	}
339 
340 	if (ncolors >= 256 && ctx->vclass == StaticGray) {
341 		/* don't need dithering for 256 levels of gray in StaticGray visual */
342 		ctx->attribs->render_mode = RBestMatchRendering;
343 	}
344 
345 	colors = malloc(sizeof(XColor) * ncolors);
346 	if (!colors) {
347 		RErrorCode = RERR_NOMEMORY;
348 		return False;
349 	}
350 	for (i = 0; i < ncolors; i++) {
351 		colors[i].red = (i * 0xffff) / (ncolors - 1);
352 		colors[i].green = (i * 0xffff) / (ncolors - 1);
353 		colors[i].blue = (i * 0xffff) / (ncolors - 1);
354 		colors[i].flags = DoRed | DoGreen | DoBlue;
355 	}
356 
357 	/* try to allocate the colors */
358 	return allocateColor(ctx, colors, ncolors);
359 }
360 
setupPseudoColorColormap(RContext * context)361 static Bool setupPseudoColorColormap(RContext * context)
362 {
363 	Atom property = 0;
364 
365 	if (context->attribs->standard_colormap_mode == RCreateStdColormap) {
366 		property = XInternAtom(context->dpy, "RGB_DEFAULT_MAP", False);
367 
368 		if (!setupStandardColormap(context, property)) {
369 			return False;
370 		}
371 	}
372 
373 	if (context->attribs->standard_colormap_mode != RIgnoreStdColormap) {
374 		XStandardColormap *maps;
375 		int count, i;
376 
377 		if (!property) {
378 			property = XInternAtom(context->dpy, "RGB_BEST_MAP", False);
379 			if (!XGetRGBColormaps(context->dpy,
380 					      DefaultRootWindow(context->dpy), &maps, &count, property)) {
381 				maps = NULL;
382 			}
383 
384 			if (!maps) {
385 				property = XInternAtom(context->dpy, "RGB_DEFAULT_MAP", False);
386 				if (!XGetRGBColormaps(context->dpy,
387 						      DefaultRootWindow(context->dpy), &maps, &count, property)) {
388 					maps = NULL;
389 				}
390 			}
391 		} else {
392 			if (!XGetRGBColormaps(context->dpy,
393 					      DefaultRootWindow(context->dpy), &maps, &count, property)) {
394 				maps = NULL;
395 			}
396 		}
397 
398 		if (maps) {
399 			int theMap = -1;
400 
401 			for (i = 0; i < count; i++) {
402 				if (maps[i].visualid == context->visual->visualid) {
403 					theMap = i;
404 					break;
405 				}
406 			}
407 
408 			if (theMap < 0) {
409 				puts("wrlib: no std cmap found");
410 			}
411 
412 			if (theMap >= 0 && allocateStandardPseudoColor(context, &maps[theMap])) {
413 
414 				context->std_rgb_map = XAllocStandardColormap();
415 
416 				*context->std_rgb_map = maps[theMap];
417 
418 				context->cmap = context->std_rgb_map->colormap;
419 
420 				XFree(maps);
421 
422 				return True;
423 			}
424 
425 			XFree(maps);
426 		}
427 	}
428 
429 	context->attribs->standard_colormap_mode = RIgnoreStdColormap;
430 
431 	/* RIgnoreStdColormap and fallback */
432 	return allocatePseudoColor(context);
433 }
434 
mygetenv(const char * var,int scr)435 static char *mygetenv(const char *var, int scr)
436 {
437 	char *p;
438 	char varname[64];
439 
440 	snprintf(varname, sizeof(varname), "%s%i", var, scr);
441 	p = getenv(varname);
442 	if (!p) {
443 		p = getenv(var);
444 	}
445 	return p;
446 }
447 
gatherconfig(RContext * context,int screen_n)448 static void gatherconfig(RContext * context, int screen_n)
449 {
450 	char *ptr;
451 
452 	ptr = mygetenv("WRASTER_GAMMA", screen_n);
453 	if (ptr) {
454 		float g1, g2, g3;
455 		if (sscanf(ptr, "%f/%f/%f", &g1, &g2, &g3) != 3 || g1 <= 0.0F || g2 <= 0.0F || g3 <= 0.0F) {
456 			printf("wrlib: invalid value(s) for gamma correction \"%s\"\n", ptr);
457 		} else {
458 			context->attribs->flags |= RC_GammaCorrection;
459 			context->attribs->rgamma = g1;
460 			context->attribs->ggamma = g2;
461 			context->attribs->bgamma = g3;
462 		}
463 	}
464 	ptr = mygetenv("WRASTER_COLOR_RESOLUTION", screen_n);
465 	if (ptr) {
466 		int i;
467 		if (sscanf(ptr, "%d", &i) != 1 || i < 2 || i > 6) {
468 			printf("wrlib: invalid value for color resolution \"%s\"\n", ptr);
469 		} else {
470 			context->attribs->flags |= RC_ColorsPerChannel;
471 			context->attribs->colors_per_channel = i;
472 		}
473 	}
474 }
475 
getColormap(RContext * context,int screen_number)476 static void getColormap(RContext * context, int screen_number)
477 {
478 	Colormap cmap = None;
479 	XStandardColormap *cmaps;
480 	int ncmaps, i;
481 
482 	if (XGetRGBColormaps(context->dpy,
483 			     RootWindow(context->dpy, screen_number), &cmaps, &ncmaps, XA_RGB_DEFAULT_MAP)) {
484 		for (i = 0; i < ncmaps; ++i) {
485 			if (cmaps[i].visualid == context->visual->visualid) {
486 				cmap = cmaps[i].colormap;
487 				break;
488 			}
489 		}
490 		XFree(cmaps);
491 	}
492 	if (cmap == None) {
493 		XColor color;
494 
495 		cmap = XCreateColormap(context->dpy,
496 				       RootWindow(context->dpy, screen_number), context->visual, AllocNone);
497 
498 		color.red = color.green = color.blue = 0;
499 		XAllocColor(context->dpy, cmap, &color);
500 		context->black = color.pixel;
501 
502 		color.red = color.green = color.blue = 0xffff;
503 		XAllocColor(context->dpy, cmap, &color);
504 		context->white = color.pixel;
505 
506 	}
507 	context->cmap = cmap;
508 }
509 
count_offset(unsigned long mask)510 static int count_offset(unsigned long mask)
511 {
512 	int i;
513 
514 	i = 0;
515 	while ((mask & 1) == 0) {
516 		i++;
517 		mask = mask >> 1;
518 	}
519 	return i;
520 }
521 
RCreateContext(Display * dpy,int screen_number,const RContextAttributes * attribs)522 RContext *RCreateContext(Display * dpy, int screen_number, const RContextAttributes * attribs)
523 {
524 	RContext *context;
525 	XGCValues gcv;
526 
527 	context = malloc(sizeof(RContext));
528 	if (!context) {
529 		RErrorCode = RERR_NOMEMORY;
530 		return NULL;
531 	}
532 	memset(context, 0, sizeof(RContext));
533 
534 	context->dpy = dpy;
535 
536 	context->screen_number = screen_number;
537 
538 	context->attribs = malloc(sizeof(RContextAttributes));
539 	if (!context->attribs) {
540 		free(context);
541 		RErrorCode = RERR_NOMEMORY;
542 		return NULL;
543 	}
544 	if (!attribs)
545 		*context->attribs = DEFAULT_CONTEXT_ATTRIBS;
546 	else
547 		*context->attribs = *attribs;
548 
549 	if (!(context->attribs->flags & RC_StandardColormap)) {
550 		context->attribs->standard_colormap_mode = RUseStdColormap;
551 	}
552 
553 	if (!(context->attribs->flags & RC_ScalingFilter)) {
554 		context->attribs->flags |= RC_ScalingFilter;
555 		context->attribs->scaling_filter = RMitchellFilter;
556 	}
557 
558 	/* get configuration from environment variables */
559 	gatherconfig(context, screen_number);
560 	wraster_change_filter(context->attribs->scaling_filter);
561 	if ((context->attribs->flags & RC_VisualID)) {
562 		XVisualInfo *vinfo, templ;
563 		int nret;
564 
565 		templ.screen = screen_number;
566 		templ.visualid = context->attribs->visualid;
567 		vinfo = XGetVisualInfo(context->dpy, VisualIDMask | VisualScreenMask, &templ, &nret);
568 		if (!vinfo || nret == 0) {
569 			free(context);
570 			RErrorCode = RERR_BADVISUALID;
571 			return NULL;
572 		}
573 
574 		if (vinfo[0].visual == DefaultVisual(dpy, screen_number)) {
575 			context->attribs->flags |= RC_DefaultVisual;
576 		} else {
577 			XSetWindowAttributes attr;
578 			unsigned long mask;
579 
580 			context->visual = vinfo[0].visual;
581 			context->depth = vinfo[0].depth;
582 			context->vclass = vinfo[0].class;
583 			getColormap(context, screen_number);
584 			attr.colormap = context->cmap;
585 			attr.override_redirect = True;
586 			attr.border_pixel = 0;
587 			attr.background_pixel = 0;
588 			mask = CWBorderPixel | CWColormap | CWOverrideRedirect | CWBackPixel;
589 			context->drawable =
590 			    XCreateWindow(dpy, RootWindow(dpy, screen_number), 1, 1,
591 					  1, 1, 0, context->depth, CopyFromParent, context->visual, mask, &attr);
592 		}
593 		XFree(vinfo);
594 	}
595 
596 	/* use default */
597 	if (!context->visual) {
598 		if ((context->attribs->flags & RC_DefaultVisual)
599 		    || !bestContext(dpy, screen_number, context)) {
600 			context->visual = DefaultVisual(dpy, screen_number);
601 			context->depth = DefaultDepth(dpy, screen_number);
602 			context->cmap = DefaultColormap(dpy, screen_number);
603 			context->drawable = RootWindow(dpy, screen_number);
604 			context->black = BlackPixel(dpy, screen_number);
605 			context->white = WhitePixel(dpy, screen_number);
606 			context->vclass = context->visual->class;
607 		}
608 	}
609 
610 	gcv.function = GXcopy;
611 	gcv.graphics_exposures = False;
612 	context->copy_gc = XCreateGC(dpy, context->drawable, GCFunction | GCGraphicsExposures, &gcv);
613 
614 	if (context->vclass == PseudoColor || context->vclass == StaticColor) {
615 		if (!setupPseudoColorColormap(context)) {
616 			free(context);
617 			return NULL;
618 		}
619 	} else if (context->vclass == GrayScale || context->vclass == StaticGray) {
620 		context->colors = allocateGrayScale(context);
621 		if (!context->colors) {
622 			free(context);
623 			return NULL;
624 		}
625 	} else if (context->vclass == TrueColor) {
626 		/* calc offsets to create a TrueColor pixel */
627 		context->red_offset = count_offset(context->visual->red_mask);
628 		context->green_offset = count_offset(context->visual->green_mask);
629 		context->blue_offset = count_offset(context->visual->blue_mask);
630 		/* disable dithering on 24 bits visuals */
631 		if (context->depth >= 24)
632 			context->attribs->render_mode = RBestMatchRendering;
633 	}
634 
635 	/* check avaiability of MIT-SHM */
636 #ifdef USE_XSHM
637 	if (!(context->attribs->flags & RC_UseSharedMemory)) {
638 		context->attribs->flags |= RC_UseSharedMemory;
639 		context->attribs->use_shared_memory = True;
640 	}
641 
642 	if (context->attribs->use_shared_memory) {
643 		int major, minor;
644 		Bool sharedPixmaps;
645 
646 		context->flags.use_shared_pixmap = 0;
647 
648 		if (!XShmQueryVersion(context->dpy, &major, &minor, &sharedPixmaps)) {
649 			context->attribs->use_shared_memory = False;
650 		} else {
651 			if (XShmPixmapFormat(context->dpy) == ZPixmap)
652 				context->flags.use_shared_pixmap = sharedPixmaps;
653 		}
654 	}
655 #endif
656 
657 	return context;
658 }
659 
RDestroyContext(RContext * context)660 void RDestroyContext(RContext *context)
661 {
662 	if (context) {
663 		if (context->copy_gc)
664 			XFreeGC(context->dpy, context->copy_gc);
665 		if (context->attribs) {
666 			if ((context->attribs->flags & RC_VisualID) &&
667 			    !(context->attribs->flags & RC_DefaultVisual))
668 				XDestroyWindow(context->dpy, context->drawable);
669 			free(context->attribs);
670 		}
671 		free(context);
672 	}
673 }
674 
bestContext(Display * dpy,int screen_number,RContext * context)675 static Bool bestContext(Display * dpy, int screen_number, RContext * context)
676 {
677 	XVisualInfo *vinfo = NULL, rvinfo;
678 	int best = -1, numvis, i;
679 	long flags;
680 	XSetWindowAttributes attr;
681 
682 	rvinfo.class = TrueColor;
683 	rvinfo.screen = screen_number;
684 	flags = VisualClassMask | VisualScreenMask;
685 
686 	vinfo = XGetVisualInfo(dpy, flags, &rvinfo, &numvis);
687 	if (vinfo) {		/* look for a TrueColor, 24-bit or more (pref 24) */
688 		for (i = numvis - 1, best = -1; i >= 0; i--) {
689 			if (vinfo[i].depth == 24)
690 				best = i;
691 			else if (vinfo[i].depth > 24 && best < 0)
692 				best = i;
693 		}
694 	}
695 	if (best > -1) {
696 		context->visual = vinfo[best].visual;
697 		context->depth = vinfo[best].depth;
698 		context->vclass = vinfo[best].class;
699 		getColormap(context, screen_number);
700 		attr.colormap = context->cmap;
701 		attr.override_redirect = True;
702 		attr.border_pixel = 0;
703 		context->drawable =
704 		    XCreateWindow(dpy, RootWindow(dpy, screen_number),
705 				  1, 1, 1, 1, 0, context->depth,
706 				  CopyFromParent, context->visual,
707 				  CWBorderPixel | CWColormap | CWOverrideRedirect, &attr);
708 	}
709 	if (vinfo)
710 		XFree((char *)vinfo);
711 
712 	if (best < 0)
713 		return False;
714 	else
715 		return True;
716 }
717