1 
2 /*
3  * Argyll Color Correction System
4  * Display target patch window
5  *
6  * Author: Graeme W. Gill
7  * Date:   4/10/96
8  *
9  * Copyright 1998 - 2013, Graeme W. Gill
10  * All rights reserved.
11  *
12  * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
13  * see the License.txt file for licencing details.
14  */
15 
16 /* This program displays test patches on a WinNT, MAC OSX or X11 windowing system. */
17 
18 /* TTBD
19  *
20  * Should probably check the display attributes (like visual depth)
21  * and complain if we aren't using 24 bit color or better.
22  *
23  * Ideally should distinguish clearly between not having access to RAMDAC/VideoLuts
24  * (fail) vs. the display not having them at all.
25  *
26  * Is there a >8 bit way of setting frame buffer value on MSWin (see "Quantize")
27  * when using higher bit depth frame buffers ?
28  *
29  * Is there a >8 bit way of getting/setting RAMDAC indexes ?
30  *
31  * Should add dithering support to overcome 8 bit limitations of
32  * non-RAMDAC access or limited RAMDAC depth. (How do we easily
33  * determine the latter ??)
34  *
35  * For X11/XRANDR, should we check for and save/restore CscMatrix property ??
36  * - or should we assumed that the use intends to use this to manually calibrate the display ??
37  * See <http://us.download.nvidia.com/XFree86/Linux-x86/364.12/README/xrandrextension.html#CscMatrix>
38  */
39 
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <stdarg.h>
43 #include <string.h>
44 #include <math.h>
45 #include <time.h>
46 #include <signal.h>
47 #ifndef NT
48 #include <unistd.h>
49 #endif
50 #include <sys/types.h>
51 #include <sys/stat.h>
52 #include <time.h>
53 #include "copyright.h"
54 #include "aconfig.h"
55 #include "icc.h"
56 #include "numlib.h"
57 #include "cgats.h"
58 #include "conv.h"
59 #include "xicc.h"
60 #include "disptechs.h"
61 #include "dispwin.h"
62 #include "ui.h"
63 #include "webwin.h"
64 #include "ccast.h"
65 #include "ccwin.h"
66 #ifdef NT
67 # include "madvrwin.h"
68 #endif
69 #if defined(UNIX_X11)
70 # include <dlfcn.h>
71 # if defined(USE_UCMM)
72 #  include "ucmm.h"
73 # endif
74 #endif
75 
76 #ifdef UNIX_APPLE
77 
78 /*
79 	Note that the new ColorSync API is defined in
80 	/System/Library/Frameworks/ApplicationServices.framework/Frameworks/ColorSync.framework/Headers
81 
82 	in
83 		ColorSync.h
84 		ColorSyncBase.h
85 		ColorSyncCMM.h
86 		ColorSyncDeprecated.h
87 		ColorSyncDevice.h
88 		ColorSyncProfile.h
89 		ColorSyncTransform.h
90 
91 	and isn't documented by Apple anywhere else.
92  */
93 
94 #include <Foundation/Foundation.h>
95 
96 #include <AppKit/AppKit.h>
97 
98 # if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
99 #  include <copyfile.h>
100 # endif
101 
102 #ifndef CGFLOAT_DEFINED
103 #ifdef __LP64__ || NS_BUILD_32_LIKE_64
104 typedef double CGFloat;
105 #else
106 typedef float CGFloat;
107 #endif  /* defined(__LP64__) */
108 #endif	/* !CGFLOAT_DEFINED */
109 
110 #ifndef NSINTEGER_DEFINED
111 #if __LP64__ || NS_BUILD_32_LIKE_64
112   typedef long NSInteger;
113   typedef unsigned long NSUInteger;
114 #else
115   typedef int NSInteger;
116   typedef unsigned int NSUInteger;
117 #endif
118 #endif	/* !NSINTEGER_DEFINED */
119 
120 #include <IOKit/graphics/IOGraphicsLib.h>
121 
122 #if __MAC_OS_X_VERSION_MAX_ALLOWED <= 1060
123 /* This wasn't declared in 10.6, although it is needed */
124 CFUUIDRef CGDisplayCreateUUIDFromDisplayID (uint32_t displayID);
125 #endif	/* < 10.6 */
126 
127 #endif /* UNIX_APPLE */
128 
129 #define VERIFY_TOL (1.0/255.0)
130 #undef DISABLE_RANDR				/* Disable XRandR code */
131 
132 #undef DEBUG
133 //#define STANDALONE_TEST
134 
135 #ifdef DEBUG
136 #define errout stderr
137 # define debug(xx)	fprintf(errout, xx )
138 # define debug2(xx)	fprintf xx
139 # define debugr(xx)	fprintf(errout, xx )
140 # define debugr2(xx)	fprintf xx
141 # define debugrr(xx)	fprintf(errout, xx )
142 # define debugrr2(xx)	fprintf xx
143 # define debugrr2l(lev, xx)	fprintf xx
144 #else
145 #define errout stderr
146 # define debug(xx)
147 # define debug2(xx)
148 # define debugr(xx) if (p->ddebug) fprintf(errout, xx )
149 # define debugr2(xx) if (p->ddebug) fprintf xx
150 # define debugrr(xx) if (callback_ddebug) fprintf(errout, xx )
151 # define debugrr2(xx) if (callback_ddebug) fprintf xx
152 # define debugrr2l(lev, xx) if (callback_ddebug >= lev) fprintf xx
153 #endif
154 
155 
156 /* ----------------------------------------------- */
157 /* Dealing with locating displays */
158 
159 int callback_ddebug = 0;	/* Diagnostic global for get_displays() and get_a_display() */
160 							/* and events */
161 
162 #ifdef NT
163 
164 #define sleep(secs) Sleep((secs) * 1000)
165 
MonitorEnumProc(HMONITOR hMonitor,HDC hdcMonitor,LPRECT lprcMonitor,LPARAM dwData)166 static BOOL CALLBACK MonitorEnumProc(
167   HMONITOR hMonitor,  /* handle to display monitor */
168   HDC hdcMonitor,     /* NULL, because EnumDisplayMonitors hdc is NULL */
169   LPRECT lprcMonitor, /* Virtual screen coordinates of this monitor */
170   LPARAM dwData       /* Context data */
171 ) {
172 	disppath ***pdisps = (disppath ***)dwData;
173 	disppath **disps = *pdisps;
174 	MONITORINFOEX pmi;
175 	int ndisps = 0;
176 
177 	debugrr2((errout, "MonitorEnumProc() called with hMonitor = 0x%x\n",hMonitor));
178 
179 	/* Get some more information */
180 	pmi.cbSize = sizeof(MONITORINFOEX);
181 	if (GetMonitorInfo(hMonitor, (MONITORINFO *)&pmi) == 0) {
182 		debugrr("get_displays failed GetMonitorInfo - ignoring display\n");
183 		return TRUE;
184 	}
185 
186 	/* See if it seems to be a pseudo-display */
187 	if (strncmp(pmi.szDevice, "\\\\.\\DISPLAYV", 12) == 0) {
188 		debugrr("Seems to be invisible pseudo-display - ignoring it\n");
189 		return TRUE;
190 	}
191 
192 	/* Add the display to the list */
193 	if (disps == NULL) {
194 		if ((disps = (disppath **)calloc(sizeof(disppath *), 1 + 1)) == NULL) {
195 			debugrr("get_displays failed on malloc\n");
196 			return FALSE;
197 		}
198 	} else {
199 		/* Count current number on list */
200 		for (ndisps = 0; disps[ndisps] != NULL; ndisps++)
201 			;
202 		if ((disps = (disppath **)realloc(disps,
203 		                     sizeof(disppath *) * (ndisps + 2))) == NULL) {
204 			debugrr("get_displays failed on malloc\n");
205 			return FALSE;
206 		}
207 		disps[ndisps+1] = NULL;	/* End marker */
208 	}
209 
210 	if ((disps[ndisps] = calloc(sizeof(disppath),1)) == NULL) {
211 		debugrr("get_displays failed on malloc\n");
212 		return FALSE;
213 	}
214 
215 	if ((disps[ndisps]->name = strdup(pmi.szDevice)) == NULL) {
216 		debugrr("malloc failed\n");
217 		return FALSE;
218 	}
219 	disps[ndisps]->prim = (pmi.dwFlags & MONITORINFOF_PRIMARY) ? 1 : 0;
220 
221 	disps[ndisps]->sx = lprcMonitor->left;
222 	disps[ndisps]->sy = lprcMonitor->top;
223 	disps[ndisps]->sw = lprcMonitor->right - lprcMonitor->left;
224 	disps[ndisps]->sh = lprcMonitor->bottom - lprcMonitor->top;
225 
226 	disps[ndisps]->description = NULL;
227 
228 	debugrr2((errout, "MonitorEnumProc() set initial monitor info: %d,%d %d,%d name '%s'\n",disps[ndisps]->sx,disps[ndisps]->sy,disps[ndisps]->sw,disps[ndisps]->sh, disps[ndisps]->name));
229 
230 	*pdisps = disps;
231 	return TRUE;
232 }
233 
234 /* Dynamically linked function support */
235 
236 BOOL (WINAPI* pEnumDisplayDevices)(PVOID,DWORD,PVOID,DWORD) = NULL;
237 
238 #if !defined(NTDDI_LONGHORN) || NTDDI_VERSION < NTDDI_LONGHORN
239 
240 typedef enum {
241 	WCS_PROFILE_MANAGEMENT_SCOPE_SYSTEM_WIDE,
242 	WCS_PROFILE_MANAGEMENT_SCOPE_CURRENT_USER
243 } WCS_PROFILE_MANAGEMENT_SCOPE;
244 
245 BOOL (WINAPI* pWcsAssociateColorProfileWithDevice)(WCS_PROFILE_MANAGEMENT_SCOPE,PCWSTR,PCWSTR) = NULL;
246 BOOL (WINAPI* pWcsDisassociateColorProfileFromDevice)(WCS_PROFILE_MANAGEMENT_SCOPE,PCWSTR,PCWSTR) = NULL;
247 
248 #endif  /* NTDDI_VERSION < NTDDI_LONGHORN */
249 
250 /* See if we can get the wanted function calls */
251 /* return nz if OK */
setup_dyn_calls()252 static int setup_dyn_calls() {
253 	static int dyn_inited = 0;
254 
255 	if (dyn_inited == 0) {
256 		dyn_inited = 1;
257 
258 		/* EnumDisplayDevicesA was left out of lib32.lib on earlier SDK's ... */
259 		pEnumDisplayDevices = (BOOL (WINAPI*)(PVOID,DWORD,PVOID,DWORD)) GetProcAddress(LoadLibrary("USER32"), "EnumDisplayDevicesA");
260 		if (pEnumDisplayDevices == NULL)
261 			dyn_inited = 0;
262 
263 		/* Vista calls */
264 #if !defined(NTDDI_LONGHORN) || NTDDI_VERSION < NTDDI_LONGHORN
265 		pWcsAssociateColorProfileWithDevice = (BOOL (WINAPI*)(WCS_PROFILE_MANAGEMENT_SCOPE,PCWSTR,PCWSTR)) GetProcAddress(LoadLibrary("mscms"), "WcsAssociateColorProfileWithDevice");
266 		pWcsDisassociateColorProfileFromDevice = (BOOL (WINAPI*)(WCS_PROFILE_MANAGEMENT_SCOPE,PCWSTR,PCWSTR)) GetProcAddress(LoadLibrary("mscms"), "WcsDisassociateColorProfileFromDevice");
267 		/* These are checked individually */
268 #endif  /* NTDDI_VERSION < NTDDI_LONGHORN */
269 	}
270 
271 	return dyn_inited;
272 }
273 
274 /* Simple up conversion from char string to wchar string */
275 /* Return NULL if malloc fails */
276 /* ~~~ Note, should probably replace this with mbstowcs() ???? */
char2wchar(char * s)277 static unsigned short *char2wchar(char *s) {
278 	unsigned char *cp;
279 	unsigned short *w, *wp;
280 
281 	if ((w = malloc(sizeof(unsigned short) * (strlen(s) + 1))) == NULL)
282 		return w;
283 
284 	for (cp = (unsigned char *)s, wp = w; ; cp++, wp++) {
285 		*wp = *cp;		/* Zero extend */
286 		if (*cp == 0)
287 			break;
288 	}
289 
290 	return w;
291 }
292 
293 #endif /* NT */
294 
295 #if defined(UNIX_X11)
296 /* Hack to notice if the error handler has been triggered */
297 /* when a function doesn't return a value. */
298 
299 int g_error_handler_triggered = 0;
300 
301 /* A noop X11 error handler */
null_error_handler(Display * disp,XErrorEvent * ev)302 int null_error_handler(Display *disp, XErrorEvent *ev) {
303 	 g_error_handler_triggered = 1;
304 	return 0;
305 }
306 #endif	/* X11 */
307 
308 /* Return pointer to list of disppath. Last will be NULL. */
309 /* Return NULL on failure. Call free_disppaths() to free up allocation */
get_displays()310 disppath **get_displays() {
311 	disppath **disps = NULL;
312 
313 #ifdef NT
314 	DISPLAY_DEVICE dd;
315 	char buf[200];
316 	int i, j;
317 
318 	if (setup_dyn_calls() == 0) {
319 		debugrr("Dynamic linking to EnumDisplayDevices or Vista AssociateColorProfile failed\n");
320 		free_disppaths(disps);
321 		return NULL;
322 	}
323 
324 	/* Create an initial list of monitors */
325 	/* (It might be better to call pEnumDisplayDevices(NULL, i ..) instead ??, */
326 	/* then we can use the StateFlags to distingish monitors not attached to the desktop etc.) */
327 	if (EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&disps) == 0) {
328 		debugrr("EnumDisplayMonitors failed\n");
329 		free_disppaths(disps);
330 		return NULL;
331 	}
332 
333 	/* Now locate detailed information about displays */
334 	for (i = 0; ; i++) {
335 		if (disps == NULL || disps[i] == NULL)
336 			break;
337 
338 		dd.cb = sizeof(dd);
339 
340 		debugrr2((errout, "get_displays about to get monitor information for %d\n",i));
341 		/* Get monitor information */
342 		for (j = 0; ;j++) {
343 			if ((*pEnumDisplayDevices)(disps[i]->name, j, &dd, 0) == 0) {
344 				debugrr2((errout,"EnumDisplayDevices failed on '%s' Mon = %d\n",disps[i]->name,j));
345 				if (j == 0) {
346 					strcpy(disps[i]->monid, "");		/* We won't be able to set a profile */
347 				}
348 				break;
349 			}
350 			if (callback_ddebug) {
351 				fprintf(errout,"Mon %d, name '%s'\n",j,dd.DeviceName);
352 				fprintf(errout,"Mon %d, string '%s'\n",j,dd.DeviceString);
353 				fprintf(errout,"Mon %d, flags 0x%x\n",j,dd.StateFlags);
354 				fprintf(errout,"Mon %d, id '%s'\n",j,dd.DeviceID);
355 				fprintf(errout,"Mon %d, key '%s'\n",j,dd.DeviceKey);
356 			}
357 			if (j == 0) {
358 				strcpy(disps[i]->monid, dd.DeviceID);
359 			}
360 		}
361 
362 		sprintf(buf,"%s, at %d, %d, width %d, height %d%s",disps[i]->name+4,
363 	        disps[i]->sx, disps[i]->sy, disps[i]->sw, disps[i]->sh,
364 	        disps[i]->prim ? " (Primary Display)" : "");
365 
366 		if ((disps[i]->description = strdup(buf)) == NULL) {
367 			debugrr("get_displays failed on malloc\n");
368 			free_disppaths(disps);
369 			return NULL;
370 		}
371 
372 		debugrr2((errout, "get_displays added description '%s' to display %d\n",disps[i]->description,i));
373 
374 		/* Note that calling EnumDisplayDevices(NULL, j, ..) for the adapter can return other */
375 		/* information, such as the graphics card name, and additional state flags. */
376 		/* EnumDisplaySettings() can also be called to get information such as display depth etc. */
377 	}
378 
379 #ifdef NEVER
380 	/* Explore adapter information */
381 	for (j = 0; ; j++) {
382 		/* Get adapater information */
383 		if ((*pEnumDisplayDevices)(NULL, j, &dd, 0) == 0)
384 			break;
385 		printf("Adapt %d, name '%s'\n",j,dd.DeviceName);
386 		printf("Adapt %d, string '%s'\n",j,dd.DeviceString);
387 		printf("Adapt %d, flags 0x%x\n",j,dd.StateFlags);
388 		printf("Adapt %d, id '%s'\n",j,dd.DeviceID);
389 		printf("Adapt %d, key '%s'\n",j,dd.DeviceKey);
390 	}
391 #endif /* NEVER */
392 
393 #endif /* NT */
394 
395 #ifdef UNIX_APPLE
396 	/* Note :- some recent releases of OS X have a feature which */
397 	/* automatically adjusts the screen brigtness with ambient level. */
398 	/* We may have to find a way of disabling this during calibration and profiling. */
399 	/* See the "pset -g" command. */
400 
401 	/*
402 		We could possibly use NSScreen instead of CG here,
403 		If we're using libui, then we have an NSApp, so
404 		this would be possible.
405 	 */
406 
407 	int i;
408 	CGDisplayErr dstat;
409 	CGDisplayCount dcount;		/* Number of display IDs */
410 	CGDirectDisplayID *dids;	/* Array of display IDs */
411 
412 	if ((dstat = CGGetActiveDisplayList(0, NULL, &dcount)) != kCGErrorSuccess || dcount < 1) {
413 		debugrr("CGGetActiveDisplayList #1 returned error\n");
414 		return NULL;
415 	}
416 	if ((dids = (CGDirectDisplayID *)malloc(dcount * sizeof(CGDirectDisplayID))) == NULL) {
417 		debugrr("malloc of CGDirectDisplayID's failed\n");
418 		return NULL;
419 	}
420 	if ((dstat = CGGetActiveDisplayList(dcount, dids, &dcount)) != kCGErrorSuccess) {
421 		debugrr("CGGetActiveDisplayList #2 returned error\n");
422 		free(dids);
423 		return NULL;
424 	}
425 
426 	/* Found dcount displays */
427 	debugrr2((errout,"Found %d screens\n",dcount));
428 
429 	/* Allocate our list */
430 	if ((disps = (disppath **)calloc(sizeof(disppath *), dcount + 1)) == NULL) {
431 		debugrr("get_displays failed on malloc\n");
432 		free(dids);
433 		return NULL;
434 	}
435 	for (i = 0; i < dcount; i++) {
436 		if ((disps[i] = calloc(sizeof(disppath), 1)) == NULL) {
437 			debugrr("get_displays failed on malloc\n");
438 			free_disppaths(disps);
439 			free(dids);
440 			return NULL;
441 		}
442 		disps[i]->ddid = dids[i];
443 	}
444 
445 	/* Got displays, now figure out a description for each one */
446 	for (i = 0; i < dcount; i++) {
447 		CGRect dbound;				/* Bounding rectangle of chosen display */
448 		io_service_t dport;
449 		CFDictionaryRef ddr, pndr;
450 		CFIndex dcount;
451 		char *dp = NULL, desc[50];
452 		char buf[200];
453 
454 		dbound = CGDisplayBounds(dids[i]);
455 		disps[i]->sx = dbound.origin.x;
456 		disps[i]->sy = dbound.origin.y;
457 		disps[i]->sw = dbound.size.width;
458 		disps[i]->sh = dbound.size.height;
459 
460 		/* Try and get some information about the display */
461 		if ((dport = CGDisplayIOServicePort(dids[i])) == MACH_PORT_NULL) {
462 			debugrr("CGDisplayIOServicePort returned error\n");
463 			free_disppaths(disps);
464 			free(dids);
465 			return NULL;
466 		}
467 
468 #ifdef NEVER
469 		{
470 			io_name_t name;
471 			if (IORegistryEntryGetName(dport, name) != KERN_SUCCESS) {
472 				debugrr("IORegistryEntryGetName returned error\n");
473 				free_disppaths(disps);
474 				free(dids);
475 				return NULL;
476 			}
477 			printf("Driver %d name = '%s'\n",i,name);
478 		}
479 #endif
480 		if ((ddr = IODisplayCreateInfoDictionary(dport, 0)) == NULL) {
481 			debugrr("IODisplayCreateInfoDictionary returned NULL\n");
482 			free_disppaths(disps);
483 			free(dids);
484 			return NULL;
485 		}
486 		if ((pndr = CFDictionaryGetValue(ddr, CFSTR(kDisplayProductName))) == NULL) {
487 			debugrr("CFDictionaryGetValue returned NULL\n");
488 			CFRelease(ddr);
489 			free_disppaths(disps);
490 			free(dids);
491 			return NULL;
492 		}
493 		if ((dcount = CFDictionaryGetCount(pndr)) > 0) {
494 			const void **keys;
495 			const void **values;
496 			int j;
497 
498 			keys = (const void **)calloc(sizeof(void *), dcount);
499 			values = (const void **)calloc(sizeof(void *), dcount);
500 			if (keys == NULL || values == NULL) {
501 				if (keys != NULL)
502 					free(keys);
503 				if (values != NULL)
504 					free(values);
505 				debugrr("malloc failed\n");
506 				CFRelease(ddr);
507 				free_disppaths(disps);
508 				free(dids);
509 				return NULL;
510 			}
511 			CFDictionaryGetKeysAndValues(pndr, keys, values);
512 			for (j = 0; j < dcount; j++) {
513 				const char *k, *v;
514 				char kbuf[50], vbuf[50];
515 				k = CFStringGetCStringPtr(keys[j], kCFStringEncodingMacRoman);
516 				if (k == NULL) {
517 					if (CFStringGetCString(keys[j], kbuf, 50, kCFStringEncodingMacRoman))
518 						k = kbuf;
519 				}
520 				v = CFStringGetCStringPtr(values[j], kCFStringEncodingMacRoman);
521 				if (v == NULL) {
522 					if (CFStringGetCString(values[j], vbuf, 50, kCFStringEncodingMacRoman))
523 						v = vbuf;
524 				}
525 				/* We're only grabing the english description... */
526 				if (k != NULL && v != NULL && strcmp(k, "en_US") == 0) {
527 					strncpy(desc, v, 49);
528 					desc[49] = '\000';
529 					dp = desc;
530 				}
531 			}
532 			free(keys);
533 			free(values);
534 		}
535 		CFRelease(ddr);
536 
537 		if (dp == NULL) {
538 			strcpy(desc, "(unknown)");
539 			dp = desc;
540 		}
541 		sprintf(buf,"%s, at %d, %d, width %d, height %d%s",dp,
542 	        disps[i]->sx, disps[i]->sy, disps[i]->sw, disps[i]->sh,
543 	        CGDisplayIsMain(dids[i]) ? " (Primary Display)" : "");
544 
545 		if ((disps[i]->name = strdup(dp)) == NULL
546 		 || (disps[i]->description = strdup(buf)) == NULL) {
547 			debugrr("get_displays failed on malloc\n");
548 			free_disppaths(disps);
549 			free(dids);
550 			return NULL;
551 		}
552 	}
553 
554 	free(dids);
555 #endif /* UNIX_APPLE */
556 
557 #if defined(UNIX_X11)
558 	int i, j, k;
559 	int defsix = 0;		/* default screen index */
560 	int dcount;			/* Number of screens */
561 	char *dname;
562 	char dnbuf[100];
563 	int evb = 0, erb = 0;
564 	int majv, minv;			/* Version */
565 	Display *mydisplay;
566 	int ndisps = 0;
567 	XineramaScreenInfo *xai = NULL;
568 	char desc1[100], desc2[200];
569 
570 	/* There seems to be no way of getting the available displays */
571 	/* on an X11 system. Attempting to open them in sequence */
572 	/* takes too long. We just rely on the user supplying the */
573 	/* right display. We can enumerate screens though. */
574 
575 	/* Open the base display, and then enumerate all the screens */
576 	if ((dname = getenv("DISPLAY")) != NULL) {
577 		char *pp;
578 		strncpy(dnbuf,dname,99); dnbuf[99] = '\000';
579 		if ((pp = strrchr(dnbuf, ':')) != NULL) {
580 			if ((pp = strchr(pp, '.')) == NULL)
581 				strcat(dnbuf,".0");
582 			else  {
583 				if (pp[1] == '\000')
584 					strcat(dnbuf,"0");
585 				else {
586 					pp[1] = '0';
587 					pp[2] = '\000';
588 				}
589 			}
590 		}
591 	} else
592 		strcpy(dnbuf,":0.0");
593 
594 	if ((mydisplay = XOpenDisplay(dnbuf)) == NULL) {
595 		debugrr2((errout, "failed to open display '%s'\n",dnbuf));
596 		return NULL;
597 	}
598 
599 #if RANDR_MAJOR == 1 && RANDR_MINOR >= 2 && !defined(DISABLE_RANDR)
600 	/* Use Xrandr 1.2 if it's available, and if it's not disabled */
601 	if (getenv("ARGYLL_IGNORE_XRANDR1_2") == NULL
602 	 && XRRQueryExtension(mydisplay, &evb, &erb) != 0
603 	 && XRRQueryVersion(mydisplay, &majv, &minv)
604 	 && majv == 1 && minv >= 2) {
605 
606 		if (XSetErrorHandler(null_error_handler) == 0) {
607 			debugrr("get_displays failed on XSetErrorHandler\n");
608 			XCloseDisplay(mydisplay);
609 			free_disppaths(disps);
610 			return NULL;
611 		}
612 
613 		dcount = ScreenCount(mydisplay);
614 
615 		/* Go through all the screens */
616 		for (i = 0; i < dcount; i++) {
617 			static void *xrr_found = NULL;	/* .so handle */
618 			static XRRScreenResources *(*_XRRGetScreenResourcesCurrent)
619 					                  (Display *dpy, Window window) = NULL;
620 			XRRScreenResources *scrnres;
621 			int jj;		/* Screen index */
622 
623 			if (minv >= 3 && xrr_found == NULL) {
624 				if ((xrr_found = dlopen("libXrandr.so", RTLD_LAZY)) != NULL)
625 					_XRRGetScreenResourcesCurrent = dlsym(xrr_found, "XRRGetScreenResourcesCurrent");
626 			}
627 
628 			if (minv >= 3 && _XRRGetScreenResourcesCurrent != NULL) {
629 				scrnres = _XRRGetScreenResourcesCurrent(mydisplay, RootWindow(mydisplay,i));
630 
631 			} else {
632 				scrnres = XRRGetScreenResources(mydisplay, RootWindow(mydisplay,i));
633 			}
634 			if (scrnres == NULL) {
635 				debugrr("XRRGetScreenResources failed\n");
636 				XCloseDisplay(mydisplay);
637 				free_disppaths(disps);
638 				return NULL;
639 			}
640 
641 			/* Look at all the screens outputs */
642 			for (jj = j = 0; j < scrnres->noutput; j++) {
643 				XRROutputInfo *outi = NULL;
644 				XRRCrtcInfo *crtci = NULL;
645 
646 				if ((outi = XRRGetOutputInfo(mydisplay, scrnres, scrnres->outputs[j])) == NULL) {
647 					debugrr("XRRGetOutputInfo failed\n");
648 					XRRFreeScreenResources(scrnres);
649 					XCloseDisplay(mydisplay);
650 					free_disppaths(disps);
651 					return NULL;
652 				}
653 
654 				if (outi->connection == RR_Disconnected ||
655 					outi->crtc == None) {
656 					XRRFreeOutputInfo(outi);
657 					continue;
658 				}
659 
660 				/* Check that the VideoLUT's are accessible */
661 				{
662 					XRRCrtcGamma *crtcgam = NULL;
663 
664 					debugrr("Checking XRandR 1.2 VideoLUT access\n");
665 					if ((crtcgam = XRRGetCrtcGamma(mydisplay, outi->crtc)) == NULL
666 					 || crtcgam->size == 0) {
667 						fprintf(stderr,"XRandR 1.2 is faulty - falling back to older extensions\n");
668 						if (crtcgam != NULL)
669 							XRRFreeGamma(crtcgam);
670 						free_disppaths(disps);
671 						disps = NULL;
672 						j = scrnres->noutput;
673 						i = dcount;
674 						XRRFreeOutputInfo(outi);
675 						continue;				/* Abort XRandR 1.2 */
676 					}
677 					if (crtcgam != NULL)
678 						XRRFreeGamma(crtcgam);
679 				}
680 #ifdef NEVER
681 				{
682 					Atom *oprops;
683 					int noprop;
684 
685 					/* Get a list of the properties of the output */
686 					oprops = XRRListOutputProperties(mydisplay, scrnres->outputs[j], &noprop);
687 
688 					printf("num props = %d\n", noprop);
689 					for (k = 0; k < noprop; k++) {
690 						printf("%d: atom 0x%x, name = '%s'\n", k, oprops[k], XGetAtomName(mydisplay, oprops[k]));
691 					}
692 				}
693 #endif /* NEVER */
694 
695 				if ((crtci = XRRGetCrtcInfo(mydisplay, scrnres, outi->crtc)) != NULL) {
696 					char *pp;
697 
698 					/* Add the output to the list */
699 					if (disps == NULL) {
700 						if ((disps = (disppath **)calloc(sizeof(disppath *), 1 + 1)) == NULL) {
701 							debugrr("get_displays failed on malloc\n");
702 							XRRFreeCrtcInfo(crtci);
703 							XRRFreeOutputInfo(outi);
704 							XRRFreeScreenResources(scrnres);
705 							XCloseDisplay(mydisplay);
706 							return NULL;
707 						}
708 					} else {
709 						if ((disps = (disppath **)realloc(disps,
710 						                     sizeof(disppath *) * (ndisps + 2))) == NULL) {
711 							debugrr("get_displays failed on malloc\n");
712 							XRRFreeCrtcInfo(crtci);
713 							XRRFreeOutputInfo(outi);
714 							XRRFreeScreenResources(scrnres);
715 							XCloseDisplay(mydisplay);
716 							return NULL;
717 						}
718 						disps[ndisps+1] = NULL;	/* End marker */
719 					}
720 					/* ndisps is current display we're filling in */
721 					if ((disps[ndisps] = calloc(sizeof(disppath),1)) == NULL) {
722 						debugrr("get_displays failed on malloc\n");
723 						XRRFreeCrtcInfo(crtci);
724 						XRRFreeOutputInfo(outi);
725 						XRRFreeScreenResources(scrnres);
726 						XCloseDisplay(mydisplay);
727 						free_disppaths(disps);
728 						return NULL;
729 					}
730 
731 					disps[ndisps]->screen = i;
732 					disps[ndisps]->uscreen = i;
733 					disps[ndisps]->rscreen = i;
734 					disps[ndisps]->sx = crtci->x;
735 					disps[ndisps]->sy = crtci->y;
736 					disps[ndisps]->sw = crtci->width;
737 					disps[ndisps]->sh = crtci->height;
738 					disps[ndisps]->crtc = outi->crtc;				/* XID of crtc */
739 					disps[ndisps]->output = scrnres->outputs[j];	/* XID of output */
740 
741 					sprintf(desc1,"Screen %d, Output %s",ndisps+1,outi->name);
742 					sprintf(desc2,"%s at %d, %d, width %d, height %d",desc1,
743 				        disps[ndisps]->sx, disps[ndisps]->sy, disps[ndisps]->sw, disps[ndisps]->sh);
744 
745 					/* See if it is a clone */
746 					for (k = 0; k < ndisps; k++) {
747 						if (disps[k]->crtc == disps[ndisps]->crtc) {
748 							sprintf(desc1, "[ Clone of %d ]",k+1);
749 							strcat(desc2, desc1);
750 						}
751 					}
752 					if ((disps[ndisps]->description = strdup(desc2)) == NULL) {
753 						debugrr("get_displays failed on malloc\n");
754 						XRRFreeCrtcInfo(crtci);
755 						XRRFreeOutputInfo(outi);
756 						XRRFreeScreenResources(scrnres);
757 						XCloseDisplay(mydisplay);
758 						free_disppaths(disps);
759 						return NULL;
760 					}
761 
762 					/* Form the display name */
763 					if ((pp = strrchr(dnbuf, ':')) != NULL) {
764 						if ((pp = strchr(pp, '.')) != NULL) {
765 							sprintf(pp,".%d",i);
766 						}
767 					}
768 					if ((disps[ndisps]->name = strdup(dnbuf)) == NULL) {
769 						debugrr("get_displays failed on malloc\n");
770 						XRRFreeCrtcInfo(crtci);
771 						XRRFreeOutputInfo(outi);
772 						XRRFreeScreenResources(scrnres);
773 						XCloseDisplay(mydisplay);
774 						free_disppaths(disps);
775 						return NULL;
776 					}
777 					debugrr2((errout, "Display %d name = '%s'\n",ndisps,disps[ndisps]->name));
778 
779 					/* Create the X11 root atom of the default screen */
780 					/* that may contain the associated ICC profile */
781 					/* (The _%d variant will probably break with non-Xrandr */
782 					/* aware software if Xrandr is configured to have more than */
783 					/* a single virtual screen.) */
784 					if (jj == 0)
785 						strcpy(desc1, "_ICC_PROFILE");
786 					else
787 						sprintf(desc1, "_ICC_PROFILE_%d",jj);
788 
789 					if ((disps[ndisps]->icc_atom = XInternAtom(mydisplay, desc1, False)) == None)
790 						error("Unable to intern atom '%s'",desc1);
791 
792 					/* Create the atom of the output that may contain the associated ICC profile */
793 					if ((disps[ndisps]->icc_out_atom = XInternAtom(mydisplay, "_ICC_PROFILE", False)) == None)
794 						error("Unable to intern atom '%s'","_ICC_PROFILE");
795 
796 					/* Grab the EDID from the output */
797 					{
798 						Atom edid_atom, ret_type;
799 						int ret_format;
800 						long ret_len = 0, ret_togo;
801 						unsigned char *atomv = NULL;
802 						int ii;
803 						char *keys[] = {		/* Possible keys that may be used */
804 							"EDID_DATA",
805 							"EDID",
806 							""
807 						};
808 
809 						/* Try each key in turn */
810 						for (ii = 0; keys[ii][0] != '\000'; ii++) {
811 							/* Get the atom for the EDID data */
812 							if ((edid_atom = XInternAtom(mydisplay, keys[ii], True)) == None) {
813 								debugrr2((errout, "Unable to intern atom '%s'\n",keys[ii]));
814 								/* Try the next key */
815 							} else {
816 
817 								/* Get the EDID_DATA */
818 								if (XRRGetOutputProperty(mydisplay, scrnres->outputs[j], edid_atom,
819 								            0, 0x7ffffff, False, False, XA_INTEGER,
820    		                            &ret_type, &ret_format, &ret_len, &ret_togo, &atomv) == Success
821 							            && (ret_len == 128 || ret_len == 256)) {
822 									if ((disps[ndisps]->edid = malloc(sizeof(unsigned char) * ret_len)) == NULL) {
823 										debugrr("get_displays failed on malloc\n");
824 										XRRFreeCrtcInfo(crtci);
825 										XRRFreeOutputInfo(outi);
826 										XRRFreeScreenResources(scrnres);
827 										XCloseDisplay(mydisplay);
828 										free_disppaths(disps);
829 										return NULL;
830 									}
831 									memmove(disps[ndisps]->edid, atomv, ret_len);
832 									disps[ndisps]->edid_len = ret_len;
833 									XFree(atomv);
834 									debugrr2((errout, "Got EDID for display\n"));
835 									break;
836 								}
837 								/* Try the next key */
838 							}
839 						}
840 						if (keys[ii][0] == '\000')
841 							debugrr2((errout, "Failed to get EDID for display\n"));
842 					}
843 
844 					jj++;			/* Next enabled index */
845 					ndisps++;		/* Now it's number of displays */
846 					XRRFreeCrtcInfo(crtci);
847 				}
848 				XRRFreeOutputInfo(outi);
849 			}
850 			XRRFreeScreenResources(scrnres);
851 		}
852 		XSetErrorHandler(NULL);
853 		defsix = DefaultScreen(mydisplay);
854 	}
855 #endif /* randr >= V 1.2 */
856 
857 	if (disps == NULL) {	/* Use Older style identification */
858 		debugrr("get_displays checking for Xinerama\n");
859 
860 		if (XSetErrorHandler(null_error_handler) == 0) {
861 			debugrr("get_displays failed on XSetErrorHandler\n");
862 			XCloseDisplay(mydisplay);
863 			return NULL;
864 		}
865 
866 		if (XineramaQueryExtension(mydisplay, &evb, &erb) != 0
867 		 && XineramaIsActive(mydisplay)) {
868 
869 			xai = XineramaQueryScreens(mydisplay, &dcount);
870 
871 			if (xai == NULL || dcount == 0) {
872 				debugrr("XineramaQueryScreens failed\n");
873 				XCloseDisplay(mydisplay);
874 				return NULL;
875 			}
876 			defsix = 0;
877 		} else {
878 			dcount = ScreenCount(mydisplay);
879 			defsix = DefaultScreen(mydisplay);
880 		}
881 
882 		/* Allocate our list */
883 		if ((disps = (disppath **)calloc(sizeof(disppath *), dcount + 1)) == NULL) {
884 			debugrr("get_displays failed on malloc\n");
885 			XCloseDisplay(mydisplay);
886 			return NULL;
887 		}
888 		for (i = 0; i < dcount; i++) {
889 			if ((disps[i] = calloc(sizeof(disppath), 1)) == NULL) {
890 				debugrr("get_displays failed on malloc\n");
891 				free_disppaths(disps);
892 				XCloseDisplay(mydisplay);
893 				return NULL;
894 			}
895 		}
896 
897 		/* Create a description for each screen */
898 		for (i = 0; i < dcount; i++) {
899 		    XF86VidModeMonitor monitor;
900 			int evb = 0, erb = 0;
901 			char *pp;
902 
903 			/* Form the display name */
904 			if ((pp = strrchr(dnbuf, ':')) != NULL) {
905 				if ((pp = strchr(pp, '.')) != NULL) {
906 					sprintf(pp,".%d",i);
907 				}
908 			}
909 			if ((disps[i]->name = strdup(dnbuf)) == NULL) {
910 				debugrr("get_displays failed on malloc\n");
911 				free_disppaths(disps);
912 				XCloseDisplay(mydisplay);
913 				return NULL;
914 			}
915 
916 			debugrr2((errout, "Display %d name = '%s'\n",i,disps[i]->name));
917 			if (xai != NULL) {					/* Xinerama */
918 				disps[i]->screen = 0;			/* We are asuming Xinerame creates a single virtual screen */
919 				disps[i]->uscreen = i;			/* We are assuming xinerama lists screens in the same order */
920 				disps[i]->rscreen = i;
921 				disps[i]->sx = xai[i].x_org;
922 				disps[i]->sy = xai[i].y_org;
923 				disps[i]->sw = xai[i].width;
924 				disps[i]->sh = xai[i].height;
925 			} else {
926 				disps[i]->screen = i;
927 				disps[i]->uscreen = i;
928 				disps[i]->rscreen = i;
929 				disps[i]->sx = 0;			/* Must be 0 */
930 				disps[i]->sy = 0;
931 				disps[i]->sw = DisplayWidth(mydisplay, disps[i]->screen);
932 				disps[i]->sh = DisplayHeight(mydisplay, disps[i]->screen);
933 			}
934 
935 			/* Create the X11 root atom of the default screen */
936 			/* that may contain the associated ICC profile */
937 			if (disps[i]->uscreen == 0)
938 				strcpy(desc1, "_ICC_PROFILE");
939 			else
940 				sprintf(desc1, "_ICC_PROFILE_%d",disps[i]->uscreen);
941 
942 			if ((disps[i]->icc_atom = XInternAtom(mydisplay, desc1, False)) == None)
943 				error("Unable to intern atom '%s'",desc1);
944 
945 			/* See if we can locate the EDID of the monitor for this screen */
946 			for (j = 0; j < 2; j++) {
947 				char edid_name[50];
948 				Atom edid_atom, ret_type;
949 				int ret_format = 8;
950 				long ret_len, ret_togo;
951 				unsigned char *atomv = NULL;
952 
953 				if (disps[i]->uscreen == 0) {
954 					if (j == 0)
955 						strcpy(edid_name,"XFree86_DDC_EDID1_RAWDATA");
956 					else
957 						strcpy(edid_name,"XFree86_DDC_EDID2_RAWDATA");
958 				} else {
959 					if (j == 0)
960 						sprintf(edid_name,"XFree86_DDC_EDID1_RAWDATA_%d",disps[i]->uscreen);
961 					else
962 						sprintf(edid_name,"XFree86_DDC_EDID2_RAWDATA_%d",disps[i]->uscreen);
963 				}
964 
965 				if ((edid_atom = XInternAtom(mydisplay, edid_name, True)) == None)
966 					continue;
967 				if (XGetWindowProperty(mydisplay, RootWindow(mydisplay, disps[i]->uscreen), edid_atom,
968 				            0, 0x7ffffff, False, XA_INTEGER,
969 				            &ret_type, &ret_format, &ret_len, &ret_togo, &atomv) == Success
970 				            && (ret_len == 128 || ret_len == 256)) {
971 					if ((disps[i]->edid = malloc(sizeof(unsigned char) * ret_len)) == NULL) {
972 						debugrr("get_displays failed on malloc\n");
973 						free_disppaths(disps);
974 						XCloseDisplay(mydisplay);
975 						return NULL;
976 					}
977 					memmove(disps[i]->edid, atomv, ret_len);
978 					disps[i]->edid_len = ret_len;
979 					XFree(atomv);
980 					debugrr2((errout, "Got EDID for display\n"));
981 					break;
982 				} else {
983 					debugrr2((errout, "Failed to get EDID for display\n"));
984 				}
985 			}
986 
987 			if (XF86VidModeQueryExtension(mydisplay, &evb, &erb) != 0) {
988 				/* Some propietary multi-screen drivers (ie. TwinView & MergeFB) */
989 				/* don't implement the XVidMode extension properly. */
990 				monitor.model = NULL;
991 				if (XF86VidModeGetMonitor(mydisplay, disps[i]->uscreen, &monitor) != 0
992 				 && monitor.model != NULL && monitor.model[0] != '\000')
993 					sprintf(desc1, "%s",monitor.model);
994 				else
995 					sprintf(desc1,"Screen %d",i+1);
996 			} else
997 				sprintf(desc1,"Screen %d",i+1);
998 
999 			sprintf(desc2,"%s at %d, %d, width %d, height %d",desc1,
1000 		        disps[i]->sx, disps[i]->sy, disps[i]->sw, disps[i]->sh);
1001 			if ((disps[i]->description = strdup(desc2)) == NULL) {
1002 				debugrr("get_displays failed on malloc\n");
1003 				free_disppaths(disps);
1004 				XCloseDisplay(mydisplay);
1005 				return NULL;
1006 			}
1007 		}
1008 		XSetErrorHandler(NULL);
1009 	}
1010 
1011 	/* Put the screen given by the display name at the top */
1012 	{
1013 		disppath *tdispp;
1014 		tdispp = disps[defsix];
1015 		disps[defsix] = disps[0];
1016 		disps[0] = tdispp;
1017 	}
1018 
1019 	if (xai != NULL)
1020 		XFree(xai);
1021 
1022 	XCloseDisplay(mydisplay);
1023 
1024 #endif /* UNIX X11 */
1025 
1026 	return disps;
1027 }
1028 
1029 /* Free a whole list of display paths */
free_disppaths(disppath ** disps)1030 void free_disppaths(disppath **disps) {
1031 	if (disps != NULL) {
1032 		int i;
1033 		for (i = 0; ; i++) {
1034 			if (disps[i] == NULL)
1035 				break;
1036 
1037 			if (disps[i]->name != NULL)
1038 				free(disps[i]->name);
1039 			if (disps[i]->description != NULL)
1040 				free(disps[i]->description);
1041 #if defined(UNIX_X11)
1042 			if (disps[i]->edid != NULL)
1043 				free(disps[i]->edid);
1044 #endif
1045 			free(disps[i]);
1046 		}
1047 		free(disps);
1048 	}
1049 }
1050 
1051 /* Delete a single display from the list of display paths */
del_disppath(disppath ** disps,int ix)1052 void del_disppath(disppath **disps, int ix) {
1053 	if (disps != NULL) {
1054 		int i, j, k;
1055 		for (i = 0; ; i++) {
1056 			if (disps[i] == NULL)
1057 				break;
1058 
1059 			if (i == ix) {	/* One to delete */
1060 				if (disps[i]->name != NULL)
1061 					free(disps[i]->name);
1062 				if (disps[i]->description != NULL)
1063 					free(disps[i]->description);
1064 #if defined(UNIX_X11)
1065 				if (disps[i]->edid != NULL)
1066 					free(disps[i]->edid);
1067 #endif
1068 				free(disps[i]);
1069 
1070 				/* Shuffle the rest down */
1071 				for (j = i, k = i + 1; ;j++, k++) {
1072 					disps[j] = disps[k];
1073 					if (disps[k] == NULL)
1074 						break;
1075 				}
1076 				return;
1077 			}
1078 		}
1079 	}
1080 }
1081 
1082 /* ----------------------------------------------- */
1083 /* Deal with selecting a display */
1084 
1085 /* Return the given display given its index 0..n-1 */
get_a_display(int ix)1086 disppath *get_a_display(int ix) {
1087 	disppath **paths, *rv = NULL;
1088 	int i;
1089 
1090 	if ((paths = get_displays()) == NULL)
1091 		return NULL;
1092 
1093 	for (i = 0; ;i++) {
1094 		if (paths[i] == NULL) {
1095 			free_disppaths(paths);
1096 			return NULL;
1097 		}
1098 		if (i == ix)
1099 			break;
1100 	}
1101 	if ((rv = malloc(sizeof(disppath))) == NULL) {
1102 		debugrr("get_a_display failed malloc\n");
1103 		free_disppaths(paths);
1104 		return NULL;
1105 	}
1106 	*rv = *paths[i];		/* Structure copy */
1107 	if ((rv->name = strdup(paths[i]->name)) == NULL) {
1108 		debugrr("get_displays failed on malloc\n");
1109 		free(rv->description);
1110 		free(rv);
1111 		free_disppaths(paths);
1112 		return NULL;
1113 	}
1114 	if ((rv->description = strdup(paths[i]->description)) == NULL) {
1115 		debugrr("get_displays failed on malloc\n");
1116 		free(rv);
1117 		free_disppaths(paths);
1118 		return NULL;
1119 	}
1120 #if defined(UNIX_X11)
1121 	if (paths[i]->edid != NULL) {
1122 		if ((rv->edid = malloc(sizeof(unsigned char) * paths[i]->edid_len)) == NULL) {
1123 			debugrr("get_displays failed on malloc\n");
1124 			free(rv);
1125 			free_disppaths(paths);
1126 			return NULL;
1127 		}
1128 		rv->edid_len = paths[i]->edid_len;
1129 		memmove(rv->edid, paths[i]->edid, rv->edid_len );
1130 	}
1131 #endif
1132 	free_disppaths(paths);
1133 	return rv;
1134 }
1135 
free_a_disppath(disppath * path)1136 void free_a_disppath(disppath *path) {
1137 	if (path != NULL) {
1138 		if (path->name != NULL)
1139 			free(path->name);
1140 		if (path->description != NULL)
1141 			free(path->description);
1142 #if defined(UNIX_X11)
1143 		if (path->edid != NULL)
1144 			free(path->edid);
1145 #endif
1146 		free(path);
1147 	}
1148 }
1149 
1150 /* ----------------------------------------------- */
1151 
1152 /* For VideoLUT/RAMDAC use, we assume that the frame buffer */
1153 /* may map through some intermediate hardware or lookup */
1154 /* into a RAMDAC index. */
1155 
1156 /* !!! Would be nice to add an error message return to dispwin and */
1157 /* !!! pass errors back to it so that the detail can be reported */
1158 /* !!! to the user. */
1159 
1160 static void dispwin_dump_ramdac(FILE *fp, ramdac *r);
1161 
1162 /* Get RAMDAC values. ->del() when finished. */
1163 /* Return NULL if not possible */
dispwin_get_ramdac(dispwin * p)1164 static ramdac *dispwin_get_ramdac(dispwin *p) {
1165 	ramdac *r = NULL;
1166 	int i, j;
1167 
1168 #ifdef NT
1169 	WORD vals[3][256];		/* 256 x 16 bit elements (Quantize) */
1170 
1171 	debugr("dispwin_get_ramdac called\n");
1172 
1173 #ifdef NEVER	/* Doesn't seem to return correct information on win2K systems */
1174 	if ((GetDeviceCaps(p->hdc, COLORMGMTCAPS) & CM_GAMMA_RAMP) == 0) {
1175 		debugr("dispwin_get_ramdac failed on GetDeviceCaps(CM_GAMMA_RAMP)\n");
1176 		return NULL;
1177 	}
1178 #endif
1179 
1180 	/* Allocate a ramdac */
1181 	if ((r = (ramdac *)calloc(sizeof(ramdac), 1)) == NULL) {
1182 		debugr("dispwin_get_ramdac failed on malloc()\n");
1183 		return NULL;
1184 	}
1185 	r->fdepth = p->fdepth;
1186 	r->rdepth = p->rdepth;
1187 	r->ndepth = p->ndepth;
1188 	r->nent   = p->nent;
1189 	r->clone  = dispwin_clone_ramdac;
1190 	r->setlin = dispwin_setlin_ramdac;
1191 	r->del =    dispwin_del_ramdac;
1192 
1193 	for (j = 0; j < 3; j++) {
1194 
1195 		if ((r->v[j] = (double *)calloc(sizeof(double), r->nent)) == NULL) {
1196 			for (j--; j >= 0; j--)
1197 				free(r->v[j]);
1198 			free(r);
1199 			debugr("dispwin_get_ramdac failed on malloc()\n");
1200 			return NULL;
1201 		}
1202 	}
1203 
1204 	/* GetDeviceGammaRamp() is hard coded for 3 x 256 entries (Quantize) */
1205 	if (256 != r->nent) {
1206 		free(r);
1207 		debugr2((errout,"GetDeviceGammaRamp number of entries %d inconsistent with expected value %d\n",256,p->nent));
1208 		return NULL;
1209 	}
1210 
1211 	if (GetDeviceGammaRamp(p->hdc, vals) == 0) {
1212 		free(r);
1213 		debugr("dispwin_get_ramdac failed on GetDeviceGammaRamp()\n");
1214 		return NULL;
1215 	}
1216 	for (j = 0; j < 3; j++) {
1217 		for (i = 0; i < r->nent; i++) {
1218 			r->v[j][i] = vals[j][i]/65535.0;
1219 		}
1220 	}
1221 #endif	/* NT */
1222 
1223 #ifdef UNIX_APPLE
1224 	unsigned int nent;
1225 	CGGammaValue vals[3][16385];
1226 
1227 	debugr("dispwin_get_ramdac called\n");
1228 
1229 	if (CGGetDisplayTransferByTable(p->ddid, 163845, vals[0], vals[1], vals[2], &nent) != 0) {
1230 		debugr("CGGetDisplayTransferByTable failed\n");
1231 		return NULL;
1232 	}
1233 
1234 	if (nent == 16385) {	/* oops - we didn't provide enought space! */
1235 		debugr("CGGetDisplayTransferByTable has more entries than we can handle\n");
1236 		return NULL;
1237 	}
1238 
1239 	if (nent != p->nent) {
1240 		debugr2((errout,"CGGetDisplayTransferByTable number of entries %u inconsistent with previous value %d\n",nent,p->nent));
1241 		return NULL;
1242 	}
1243 
1244 	/* Allocate a ramdac */
1245 	if ((r = (ramdac *)calloc(sizeof(ramdac), 1)) == NULL) {
1246 		debugr("dispwin_get_ramdac failed on malloc()\n");
1247 		return NULL;
1248 	}
1249 
1250 	r->fdepth = p->fdepth;
1251 	r->rdepth = p->rdepth;
1252 	r->ndepth = p->ndepth;
1253 	r->nent   = p->nent;
1254 	r->clone =  dispwin_clone_ramdac;
1255 	r->setlin = dispwin_setlin_ramdac;
1256 	r->del =    dispwin_del_ramdac;
1257 	for (j = 0; j < 3; j++) {
1258 
1259 		if ((r->v[j] = (double *)calloc(sizeof(double), r->nent)) == NULL) {
1260 			for (j--; j >= 0; j--)
1261 				free(r->v[j]);
1262 			free(r);
1263 			debugr("dispwin_get_ramdac failed on malloc()\n");
1264 			return NULL;
1265 		}
1266 	}
1267 
1268 	for (j = 0; j < 3; j++) {
1269 		for (i = 0; i < r->nent; i++) {
1270 			r->v[j][i] = vals[j][i];
1271 		}
1272 	}
1273 #endif /* UNIX_APPLE */
1274 
1275 #if defined(UNIX_X11)
1276 	unsigned short vals[3][16384];
1277 	int nent = 0;
1278 	int evb = 0, erb = 0;
1279 
1280 	debugr("dispwin_get_ramdac called\n");
1281 
1282 #if RANDR_MAJOR == 1 && RANDR_MINOR >= 2 && !defined(DISABLE_RANDR)
1283 	if (p->crtc != 0) {		/* Using Xrandr 1.2 */
1284 		XRRCrtcGamma *crtcgam;
1285 		int nz = 0;
1286 
1287 		debugr("Getting gamma using Randr 1.2\n");
1288 
1289 		if ((crtcgam = XRRGetCrtcGamma(p->mydisplay, p->crtc)) == NULL) {
1290 			debugr("XRRGetCrtcGamma failed\n");
1291 			return NULL;
1292 		}
1293 
1294 		nent = crtcgam->size;
1295 
1296 		if (nent != p->nent) {
1297 			debugr2((errout,"XRRGetCrtcGammaSize number of entries %d inconsistent with previous value\n",nent,p->nent));
1298 			return NULL;
1299 		}
1300 
1301 		/* Check for XRandR 1.2 startup bug */
1302 		for (i = 0; i < nent; i++) {
1303 			vals[0][i] = crtcgam->red[i];
1304 			vals[1][i] = crtcgam->green[i];
1305 			vals[2][i] = crtcgam->blue[i];
1306 			nz = vals[0][i] | vals[1][i] | vals[2][i];
1307 		}
1308 
1309 		/* Compensate for XRandR 1.2 startup bug */
1310 		if (nz == 0) {
1311 			debugr("Detected XRandR 1.2 bug ? Assuming linear ramp!\n");
1312 			for (i = 0; i < nent; i++) {
1313 				for (j = 0; j < 3; j++)
1314 					vals[j][i] = (int)(65535.0 * i/(nent-1.0) + 0.5);
1315 			}
1316 		}
1317 
1318 		XRRFreeGamma(crtcgam);
1319 
1320 	} else
1321 #endif /* randr >= V 1.2 */
1322 	{
1323 
1324 		if (XF86VidModeQueryExtension(p->mydisplay, &evb, &erb) == 0) {
1325 			debugr("XF86VidModeQueryExtension failed\n");
1326 			return NULL;
1327 		}
1328 		/* Some propietary multi-screen drivers (ie. TwinView & MergedFB) */
1329 		/* don't implement the XVidMode extenstion properly. */
1330 		if (XSetErrorHandler(null_error_handler) == 0) {
1331 			debugr("get_displays failed on XSetErrorHandler\n");
1332 			return NULL;
1333 		}
1334 		nent = -1;
1335 		if (XF86VidModeGetGammaRampSize(p->mydisplay, p->myrscreen, &nent) == 0
1336 		 || nent == -1) {
1337 			XSetErrorHandler(NULL);
1338 			debugr("XF86VidModeGetGammaRampSize failed\n");
1339 			return NULL;
1340 		}
1341 		XSetErrorHandler(NULL);		/* Restore handler */
1342 		if (nent == 0) {
1343 			debugr("XF86VidModeGetGammaRampSize returned 0 size\n");
1344 			return NULL;
1345 		}
1346 
1347 		if (nent != p->nent) {
1348 			debugr2((errout,"XF86VidModeGetGammaRampSize number of entries %d inconsistent with previous value\n",nent,p->nent));
1349 			return NULL;
1350 		}
1351 
1352 		if (XF86VidModeGetGammaRamp(p->mydisplay, p->myrscreen, nent,  vals[0], vals[1], vals[2]) == 0) {
1353 			debugr("XF86VidModeGetGammaRamp failed\n");
1354 			return NULL;
1355 		}
1356 	}
1357 
1358 	/* Allocate a ramdac */
1359 	if ((r = (ramdac *)calloc(sizeof(ramdac), 1)) == NULL) {
1360 		debugr("dispwin_get_ramdac failed on malloc()\n");
1361 		return NULL;
1362 	}
1363 
1364 	r->fdepth = p->fdepth;
1365 	r->rdepth = p->rdepth;
1366 	r->ndepth = p->ndepth;
1367 	r->nent   = p->nent;
1368 	r->clone  = dispwin_clone_ramdac;
1369 	r->setlin = dispwin_setlin_ramdac;
1370 	r->del = dispwin_del_ramdac;
1371 	for (j = 0; j < 3; j++) {
1372 
1373 		if ((r->v[j] = (double *)calloc(sizeof(double), r->nent)) == NULL) {
1374 			for (j--; j >= 0; j--)
1375 				free(r->v[j]);
1376 			free(r);
1377 			debugr("dispwin_get_ramdac failed on malloc()\n");
1378 			return NULL;
1379 		}
1380 	}
1381 
1382 	for (i = 0; i < r->nent; i++) {
1383 		for (j = 0; j < 3; j++) {
1384 			r->v[j][i] = vals[j][i]/65535.0;
1385 		}
1386 	}
1387 #endif	/* UNXI X11 */
1388 	debugr("dispwin_get_ramdac returning OK\n");
1389 	return r;
1390 }
1391 
1392 #ifdef UNIX_APPLE
1393 	/* Various support functions */
1394 
1395 #if __MAC_OS_X_VERSION_MAX_ALLOWED < 1060
1396 
1397 /* Given a location, return a string for it's path */
plocpath(CMProfileLocation * ploc)1398 static char *plocpath(CMProfileLocation *ploc) {
1399 
1400 	if (ploc->locType == cmFileBasedProfile) {
1401 		FSRef newRef;
1402   		UInt8 path[256] = "";
1403 
1404 		/* Note that there is no non-deprecated equivalent to this. */
1405 		/* Apple need to remove the cmFileBasedProfile type from the */
1406 		/* CMProfileLocation to do away with it. */
1407 		if (FSpMakeFSRef(&ploc->u.fileLoc.spec, &newRef) == noErr) {
1408 			OSStatus stus;
1409 			if ((stus = FSRefMakePath(&newRef, path, 256)) == 0 || stus == fnfErr)
1410 				return strdup((char *)path);
1411 			return NULL;
1412 		}
1413 	} else if (ploc->locType == cmPathBasedProfile) {
1414 		return strdup(ploc->u.pathLoc.path);
1415 	}
1416 	return NULL;
1417 }
1418 
1419 /* Ugh! ColorSync doesn't take care of endian issues !! */
cs_w32(unsigned long * p,unsigned long val)1420 static void cs_w32(unsigned long *p, unsigned long val) {
1421 	((char *)p)[0] = (char)(val >> 24);
1422 	((char *)p)[1] = (char)(val >> 16);
1423 	((char *)p)[2] = (char)(val >> 8);
1424 	((char *)p)[3] = (char)(val);
1425 }
1426 
cs_w16(unsigned short * p,unsigned short val)1427 static void cs_w16(unsigned short *p, unsigned short val) {
1428 	((char *)p)[0] = (char)(val >> 8);
1429 	((char *)p)[1] = (char)(val);
1430 }
1431 
1432 #endif /* < 10.6 */
1433 
1434 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
1435 
1436 /* There doesn't seem to be any means of determining the locations */
1437 /* of profiles using current OS X API's, so we simply hard code them. */
1438 /* This makes the older code easier too. */
1439 
1440 #define COLORSYNC_DIR_NETWORK	"/Network/Library/ColorSync/Profiles/"
1441 #define COLORSYNC_DIR_SYSTEM	"/System/Library/ColorSync/Profiles/"
1442 #define COLORSYNC_DIR_LOCAL	    "/Library/ColorSync/Profiles/"
1443 #define COLORSYNC_DIR_USER	    "/Library/ColorSync/Profiles/"
1444 
1445 /* Given a profile name and a scope, return the path to the */
1446 /* installed profile. free the returned string when done. */
1447 /* Returns NULL on error */
iprof_path(p_scope scope,char * fname)1448 static char *iprof_path(p_scope scope, char *fname) {
1449 	char *home = "", *dirname, *basename, *rv = NULL;
1450 	int tlen = 0;
1451 
1452 	/* Locate the base filename in the fname */
1453 	for (basename = fname + strlen(fname);  ; basename--) {
1454 		if (basename <= fname || basename[-1] == '/')
1455 			break;
1456 	}
1457 
1458 	/* NSFileManager's URLForDirectory: etc. doesn't have ColorSync, */
1459 	/* so we have no choice but to hard code the paths */
1460 	if (scope == p_scope_network)
1461 		dirname = COLORSYNC_DIR_NETWORK;
1462 	else if (scope == p_scope_system)
1463 		dirname = COLORSYNC_DIR_SYSTEM;
1464 	else if (scope == p_scope_local)
1465 		dirname = COLORSYNC_DIR_LOCAL;
1466 	else {
1467 		dirname = COLORSYNC_DIR_USER;
1468 		if ((home = getenv("HOME")) == NULL){
1469 			return NULL;
1470 		}
1471 	}
1472 
1473 	tlen = strlen(home) + strlen(dirname) + strlen(basename) + 1;
1474 	if ((rv = malloc(tlen)) == NULL) {
1475 		return NULL;
1476 	}
1477 
1478 	strcpy(rv, home);
1479 	strcat(rv, dirname);
1480 	strcat(rv, basename);
1481 
1482 	return rv;
1483 }
1484 
1485 /* Callback */
1486 typedef struct {
1487 	CFUUIDRef dispuuid;		/* UUID to match */
1488 	CFStringRef id;			/* ProfileId */
1489 	CFURLRef url;			/* URL to return */
1490 } diter_cntx_t;
1491 
diter_callback(CFDictionaryRef dict,void * cntx)1492 static bool diter_callback(CFDictionaryRef dict, void *cntx) {
1493 	diter_cntx_t *cx = (diter_cntx_t *)cntx;
1494 	CFStringRef str;
1495 	CFUUIDRef uuid;
1496 	CFBooleanRef iscur;
1497 
1498 	if ((str = CFDictionaryGetValue(dict, kColorSyncDeviceClass)) == NULL) {
1499 		debugrr("Failed to get kColorSyncDeviceClass\n");
1500 		return true;
1501 	}
1502 	if (!CFEqual(str, kColorSyncDisplayDeviceClass)) {
1503 		return true;
1504 	}
1505 	if ((uuid = CFDictionaryGetValue(dict, kColorSyncDeviceID)) == NULL) {
1506 		debugrr("Failed to get kColorSyncDeviceID\n");
1507 		return true;
1508 	}
1509 	if (!CFEqual(uuid, cx->dispuuid)) {
1510 		return true;
1511 	}
1512 	if ((iscur = CFDictionaryGetValue(dict, kColorSyncDeviceProfileIsCurrent)) == NULL) {
1513 		debugrr("Failed to get kColorSyncDeviceProfileIsCurrent\n");
1514 		return true;
1515 	}
1516 	if (!CFBooleanGetValue(iscur)) {
1517 		return true;
1518 	}
1519 
1520 	/* get the URL */
1521 	if ((cx->id = CFDictionaryGetValue(dict, kColorSyncDeviceProfileID)) == NULL) {
1522 		debugrr("Failed to get current profile ID\n");
1523 		return true;
1524 	}
1525 	if ((cx->url = CFDictionaryGetValue(dict, kColorSyncDeviceProfileURL)) == NULL) {
1526 		debugrr("Failed to get current profile URL\n");
1527 		return true;
1528 	}
1529 	CFRetain(cx->id);
1530 	CFRetain(cx->url);
1531 
1532 	return false;
1533 }
1534 
1535 /* Return the url to the given displays current profile. */
1536 /* Return NULL on error. CFRelease when done. */
1537 /* Optionally return the ProfileID string */
cur_profile_url(CFStringRef * idp,dispwin * p)1538 static CFURLRef cur_profile_url(CFStringRef *idp, dispwin *p) {
1539 	diter_cntx_t cx;
1540 
1541 	if ((cx.dispuuid = CGDisplayCreateUUIDFromDisplayID(p->ddid)) == NULL) {
1542 		debugr2((errout,"CGDisplayCreateUUIDFromDisplayID() failed\n"));
1543 		return NULL;
1544 	}
1545 	cx.id = NULL;
1546 	cx.url = NULL;
1547 
1548 	ColorSyncIterateDeviceProfiles(diter_callback, (void *)&cx);
1549 
1550 	CFRelease(cx.dispuuid);
1551 
1552 	if (idp != NULL)
1553 		*idp = cx.id;
1554 	else
1555 		CFRelease(cx.id);
1556 	return cx.url;
1557 }
1558 
1559 /* Convert a URL into a local POSIX path string */
1560 /* Return NULL on error. Free returned string when done. */
url_to_path(CFURLRef url)1561 static char *url_to_path(CFURLRef url) {
1562 	CFStringRef urlstr;
1563 	CFIndex bufSize;
1564 	char *dpath = NULL;			/* return value */
1565 
1566 	urlstr = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
1567 	bufSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(urlstr),
1568 	                                              kCFStringEncodingUTF8) + 1;
1569 	if ((dpath = malloc(bufSize)) == NULL) {
1570 		debugrr("url_to_path: malloc failed\n");
1571 		CFRelease(urlstr);
1572 		return NULL;
1573 	}
1574 	if (!CFStringGetCString(urlstr, dpath, bufSize, kCFStringEncodingUTF8)) {
1575 		debugrr("url_to_path: CFStringGetCString failed\n");
1576 		CFRelease(urlstr);
1577 		return NULL;
1578 	}
1579 	CFRelease(urlstr);
1580 
1581 	return dpath;
1582 }
1583 
1584 /* Return the local POSIX path to the given displays current profile */
1585 /* Return NULL on error. Free returned string when done. */
cur_profile(dispwin * p)1586 static char *cur_profile(dispwin *p) {
1587 	CFURLRef url;
1588 	char *dpath = NULL;			/* return value */
1589 
1590 	if ((url = cur_profile_url(NULL, p)) == NULL) {
1591 		debugr2((errout,"cur_profile failed to find current profile\n"));
1592 		return NULL;
1593 	}
1594 
1595 	dpath = url_to_path(url);
1596 
1597 	CFRelease(url);
1598 
1599 	return dpath;
1600 }
1601 
1602 #endif /* >= 10.6 */
1603 
1604 /* Return a CMProfileRef/ColorSyncProfileRef for the */
1605 /* displays profile. Return NULL on error */
cur_colorsync_ref(dispwin * p)1606 static void *cur_colorsync_ref(dispwin *p) {
1607 	void *cspr = NULL;
1608 
1609 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
1610 	CFURLRef url;
1611 	ColorSyncProfileRef ref;
1612 	CFErrorRef ev;
1613 
1614 	if ((url = cur_profile_url(NULL, p)) == NULL) {
1615 		debugr2((errout,"cur_colorsync_ref got NULL URL\n"));
1616 		return NULL;
1617 	}
1618 
1619 	if ((ref = ColorSyncProfileCreateWithURL(url, &ev)) == NULL) {
1620 		debugr2((errout,"ColorSyncProfileCreateWithURL failed\n"));
1621 		return NULL;
1622 	}
1623 	CFRelease(url);
1624 
1625 	cspr = (void *)ref;
1626 
1627 #else	/* 10.5 and prior */
1628 	CMError ev;
1629 	CMDeviceProfileID curID;	/* Current Device Default profile ID */
1630 	CMProfileLocation cploc;	/* Current profile location */
1631 	CMProfileRef prof;
1632 
1633 	/* Get the default ID for the display */
1634 	if ((ev = CMGetDeviceDefaultProfileID(cmDisplayDeviceClass, (CMDeviceID)p->ddid, &curID)) != noErr) {
1635 		debugr2((errout,"CMGetDeviceDefaultProfileID() failed with error %d\n",ev));
1636 		return NULL;
1637 	}
1638 
1639 	/* Get the displays profile */
1640 	if ((ev = CMGetDeviceProfile(cmDisplayDeviceClass, (CMDeviceID)p->ddid, curID, &cploc)) != noErr) {
1641 		debugr2((errout,"CMGetDeviceDefaultProfileID() failed with error %d\n",ev));
1642 		return NULL;
1643 	}
1644 
1645 	if ((ev = CMOpenProfile(&prof, &cploc)) != noErr) {
1646 		debugr2((errout,"CMOpenProfile() failed with error %d\n",ev));
1647 		return NULL;
1648 	}
1649 	cspr = (void *)prof;
1650 
1651 #endif
1652 
1653 	return cspr;
1654 }
1655 
1656 #endif /* UNIX_APPLE */
1657 
1658 /* Set the RAMDAC values. */
1659 /* Return nz if not possible */
1660 /* Return 2 for OS X when the current profile is a system profile */
dispwin_set_ramdac(dispwin * p,ramdac * r,int persist)1661 static int dispwin_set_ramdac(dispwin *p, ramdac *r, int persist) {
1662 	int i, j;
1663 
1664 #ifdef NT
1665 	WORD vals[3][256];		/* 16 bit elements */
1666 
1667 	debugr("dispwin_set_ramdac called\n");
1668 
1669 #ifdef NEVER	/* Doesn't seem to return correct information on win2K systems */
1670 	if ((GetDeviceCaps(p->hdc, COLORMGMTCAPS) & CM_GAMMA_RAMP) == 0) {
1671 		debugr("dispwin_set_ramdac failed on GetDeviceCaps(CM_GAMMA_RAMP)\n");
1672 		return 1;
1673 	}
1674 #endif
1675 
1676 	for (j = 0; j < 3; j++) {
1677 		for (i = 0; i < r->nent; i++) {
1678 			double vv = r->v[j][i];
1679 
1680 			if (vv < 0.0)
1681 				vv = 0.0;
1682 			else if (vv > 1.0)
1683 				vv = 1.0;
1684 			vals[j][i] = (int)(65535.0 * vv + 0.5);
1685 		}
1686 	}
1687 
1688 	if (SetDeviceGammaRamp(p->hdc, vals) == 0) {
1689 		debugr2((errout,"dispwin_set_ramdac failed on SetDeviceGammaRamp() with error %d\n",GetLastError()));
1690 #ifdef NEVER
1691 		dispwin_dump_ramdac(stderr, r);
1692 #endif
1693 		return 1;
1694 	}
1695 	GdiFlush();
1696 #endif	/* NT */
1697 
1698 #ifdef UNIX_APPLE
1699 	{		/* Transient first */
1700 		CGGammaValue vals[3][16384];
1701 
1702 		debugr("dispwin_set_ramdac called\n");
1703 
1704 		for (j = 0; j < 3; j++) {
1705 			for (i = 0; i < r->nent; i++) {
1706 				double vv = r->v[j][i];
1707 				if (vv < 0.0)
1708 					vv = 0.0;
1709 				else if (vv > 1.0)
1710 					vv = 1.0;
1711 				vals[j][i] = vv;
1712 			}
1713 		}
1714 
1715 		if (CGSetDisplayTransferByTable(p->ddid, r->nent, vals[0], vals[1], vals[2]) != 0) {
1716 			debugr("CGSetDisplayTransferByTable failed\n");
1717 			return 1;
1718 		}
1719 
1720 	}
1721 
1722 	/* In theory IOFBSetGamma() might work - but maybe it needs root, and won't */
1723 	/* sync with OS X's view of what's loaded. */
1724 
1725 	/* By default the OSX RAMDAC access is transient, lasting only as long */
1726 	/* as the process setting it. To set a temporary but persistent beyond this process */
1727 	/* calibration, we fake up a profile and install it in such a way that it will disappear, */
1728 	/* restoring the previous display profile whenever the current ColorSync display profile */
1729 	/* is restored to the screen. NOTE that this trick will fail if it is not possible */
1730 	/* to rename the currently selected profile file, ie. because it is a system profile. */
1731 	/* [ Would a workaround be to use a link, or copy of the system file ? ] */
1732 	if (persist)
1733 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
1734 	if (persist) {					/* Persistent */
1735 		int rv = 0;
1736 		CFStringRef id;
1737 		CFURLRef url;
1738 		char *tpath;				/* Temporary profiles/original profiles path */
1739 		char *ppath;				/* Current/renamed profiles path */
1740 		icmFile *rd_fp, *wr_fp;
1741 		icc *icco;
1742 		CFUUIDRef dispuuid;
1743 		CFStringRef keys[1];
1744 		CFURLRef values[1];
1745 		CFDictionaryRef dict;
1746 
1747 		debugr("Set_ramdac persist\n");
1748 
1749 		/* Get the current installed profile */
1750 		if ((url = cur_profile_url(&id, p)) == NULL) {
1751 			debugr2((errout,"cur_profile_url failed for current profile\n"));
1752 			return 1;
1753 		}
1754 		if ((tpath = url_to_path(url)) == NULL) {
1755 			debugr2((errout,"url_to_path failed for current profile\n"));
1756 			CFRelease(id);
1757 			CFRelease(url);
1758 			return 1;
1759 		}
1760 
1761 		debugr2((errout, "Current profile path = '%s'\n",tpath));
1762 
1763 		/* Create a patched version with our calibration: */
1764 		/* (Hmm. I think icclib will cope with V4 OK) */
1765 
1766 		/* Open up the profile for reading */
1767 		if ((rd_fp = new_icmFileStd_name(tpath,"r")) == NULL) {
1768 			debugr2((errout,"Failed to open profile '%s'\n",tpath));
1769 			free(tpath);
1770 			CFRelease(id);
1771 			CFRelease(url);
1772 			return 1;
1773 		}
1774 
1775 		if ((icco = new_icc()) == NULL) {
1776 			debugr2((errout,"Creation of ICC object failed\n"));
1777 			rd_fp->del(rd_fp);
1778 			free(tpath);
1779 			CFRelease(id);
1780 			CFRelease(url);
1781 			return 1;
1782 		}
1783 
1784 		/* Read header etc. */
1785 		if ((rv = icco->read(icco,rd_fp,0)) != 0) {
1786 			debugr2((errout,"%d, %s",rv,icco->err));
1787 			icco->del(icco);
1788 			rd_fp->del(rd_fp);
1789 			free(tpath);
1790 			CFRelease(id);
1791 			CFRelease(url);
1792 			return 1;
1793 		}
1794 		/* Read every tag */
1795 		if (icco->read_all_tags(icco) != 0) {
1796 			debugr2((errout,"Unable to read all tags: %d, %s",icco->errc,icco->err));
1797 			icco->del(icco);
1798 			rd_fp->del(rd_fp);
1799 			free(tpath);
1800 			CFRelease(id);
1801 			CFRelease(url);
1802 			return 1;
1803 		}
1804 
1805 		rd_fp->del(rd_fp); rd_fp = NULL;
1806 
1807 		/* Replace the description */
1808 		{
1809 			icmTextDescription *wo;
1810 			char *dst = "Dispwin Temp";
1811 
1812 			if (icco->find_tag(icco, icSigProfileDescriptionTag) == 0) {
1813 				if (icco->delete_tag(icco, icSigProfileDescriptionTag) != 0) {
1814 					debugr2((errout,"Unable to delete Description tag: %d, %s",icco->errc,icco->err));
1815 					icco->del(icco);
1816 					free(tpath);
1817 					CFRelease(id);
1818 					CFRelease(url);
1819 					return 1;
1820 				}
1821 			}
1822 
1823 			if ((wo = (icmTextDescription *)icco->add_tag(
1824 			           icco, icSigProfileDescriptionTag,	icSigTextDescriptionType)) == NULL) {
1825 				debugr2((errout,"Unable to add Description tag: %d, %s",icco->errc,icco->err));
1826 				icco->del(icco);
1827 				free(tpath);
1828 				CFRelease(id);
1829 				CFRelease(url);
1830 				return 1;
1831 			}
1832 
1833 			wo->size = strlen(dst)+1; 	/* Allocated and used size of desc, inc null */
1834 			wo->allocate((icmBase *)wo);/* Allocate space */
1835 			strcpy(wo->desc, dst);		/* Copy the string in */
1836 		}
1837 		/* Replace the vcgt */
1838 		{
1839 			int c,i;
1840 			icmVideoCardGamma *wo;
1841 
1842 			if (icco->find_tag(icco, icSigVideoCardGammaTag) == 0) {
1843 				if (icco->delete_tag(icco, icSigVideoCardGammaTag) != 0) {
1844 					debugr2((errout,"Unable to delete VideoCardGamma tag: %d, %s",icco->errc,icco->err));
1845 					icco->del(icco);
1846 					free(tpath);
1847 					CFRelease(id);
1848 					CFRelease(url);
1849 					return 1;
1850 				}
1851 			}
1852 
1853 			if ((wo = (icmVideoCardGamma *)icco->add_tag(icco, icSigVideoCardGammaTag,
1854 			                                        icSigVideoCardGammaType)) == NULL) {
1855 				debugr2((errout,"Unable to add VideoCardGamma tag: %d, %s",icco->errc,icco->err));
1856 				icco->del(icco);
1857 				free(tpath);
1858 				CFRelease(id);
1859 				CFRelease(url);
1860 				return 1;
1861 			}
1862 
1863 			wo->tagType = icmVideoCardGammaTableType;
1864 			wo->u.table.channels = 3;			/* rgb */
1865 			wo->u.table.entryCount = r->nent;	/* number of calibration entries */
1866 			wo->u.table.entrySize = 2;			/* 16 bits */
1867 			wo->allocate((icmBase*)wo);
1868 
1869 			for (i = 0; i < r->nent; i++) {
1870 				for (j = 0; j < 3; j++) {
1871 					double vv = r->v[j][i];
1872 					int ivv;
1873 					if (vv < 0.0)
1874 						vv = 0.0;
1875 					else if (vv > 1.0)
1876 						vv = 1.0;
1877 					((unsigned short*)wo->u.table.data)[r->nent * j + i] = (int)(vv * 65535.0 + 0.5);
1878 				}
1879 			}
1880 		}
1881 
1882 		if ((ppath = malloc(strlen(tpath) + 6)) == NULL) {
1883 			debugr2((errout,"malloc failed for display '%s'\n",p->name));
1884 			icco->del(icco);
1885 			free(tpath);
1886 			CFRelease(id);
1887 			CFRelease(url);
1888 			return 1;
1889 		}
1890 		strcpy(ppath, tpath);
1891 		strcat(ppath,".orig");
1892 
1893 		/* Rename the currently installed profile temporarily. */
1894 		/* This will fail if current profile is a system profile and not writable by the user. */
1895 		/* This could be worked around by cloning the system profile to the user */
1896 		/* area and installing it before modifiying it. */
1897 		if (rename(tpath, ppath) != 0) {
1898 			debugr2((errout,"Unable to rename '%s' to '%s'\n",tpath,ppath));
1899 			icco->del(icco);
1900 			free(ppath);
1901 			free(tpath);
1902 			CFRelease(id);
1903 			CFRelease(url);
1904 			return 2;
1905 		}
1906 
1907 		/* Rename worked */
1908 
1909 		debugr2((errout,"Renamed current profile '%s' to '%s'\n",tpath,ppath));
1910 
1911 		/* Save the modified profile to the original profile name */
1912 		if ((wr_fp = new_icmFileStd_name(tpath,"w")) == NULL) {
1913 			debugr2((errout,"Failed to open '%s' for writing\n",tpath));
1914 			free(ppath);
1915 			icco->del(icco);
1916 			free(tpath);
1917 			CFRelease(id);
1918 			CFRelease(url);
1919 			return 1;
1920 		}
1921 
1922 		if ((rv = icco->write(icco,wr_fp,0)) != 0) {
1923 			debugr2((errout,"Write file: %d, %s",rv,icco->err));
1924 			free(ppath);
1925 			wr_fp->del(wr_fp);
1926 			icco->del(icco);
1927 			free(tpath);
1928 			CFRelease(id);
1929 			CFRelease(url);
1930 			return 1;
1931 		}
1932 
1933 		icco->del(icco);
1934 		wr_fp->del(wr_fp);
1935 
1936 		/* Update to the "current" profile, which is actually the modified profile */
1937 		if ((dispuuid = CGDisplayCreateUUIDFromDisplayID(p->ddid)) == NULL) {
1938 			debugr2((errout,"CGDisplayCreateUUIDFromDisplayID() failed\n"));
1939 			free(ppath);
1940 			free(tpath);
1941 			CFRelease(id);
1942 			CFRelease(url);
1943 			return 1;
1944 		}
1945 
1946 		keys[0] = id;
1947 		values[0] = url;
1948 
1949 		if ((dict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&keys,
1950 			                     (const void **)&values, 1, NULL, NULL)) == NULL) {
1951 			debugr2((errout,"CFDictionaryCreate() failed\n"));
1952 			CFRelease(dispuuid);
1953 			free(ppath);
1954 			free(tpath);
1955 			CFRelease(id);
1956 			CFRelease(url);
1957 			return 1;
1958 		}
1959 		if (!ColorSyncDeviceSetCustomProfiles(kColorSyncDisplayDeviceClass, dispuuid, dict)) {
1960 			debugr2((errout,"ColorSyncDeviceSetCustomProfiles() failed\n"));
1961 			CFRelease(dict);
1962 			CFRelease(dispuuid);
1963 			free(ppath);
1964 			free(tpath);
1965 			CFRelease(id);
1966 			CFRelease(url);
1967 			return 1;
1968 		}
1969 		CFRelease(dict);
1970 		CFRelease(dispuuid);
1971 		CFRelease(id);
1972 		CFRelease(url);
1973 
1974 		/* Delete the temporary profile */
1975 		unlink(tpath);
1976 
1977 		/* Rename the current profile back to it's correct name */
1978 		if (rename(ppath, tpath) != 0) {
1979 			debugr2((errout,"Renaming existing profile '%s' failed\n",ppath));
1980 			return 1;
1981 		}
1982 		debugr2((errout,"Restored '%s' back to '%s'\n",ppath,tpath));
1983 		free(ppath);
1984 		free(tpath);
1985 	}
1986 #else	/* < 10.6 */
1987 	if (persist) {					/* Persistent */
1988 		CMError ev;
1989 		CMProfileRef prof;			/* Current AVID profile */
1990 		CMProfileLocation ploc;		/* Current profile location */
1991 		UInt32 plocsz = sizeof(CMProfileLocation);
1992 		char *ppath;				/* Current/renamed profiles path */
1993 		char *tpath;				/* Temporary profiles/original profiles path */
1994 		CMProfileRef tprof;			/* Temporary profile */
1995 		CMProfileLocation tploc;	/* Temporary profile */
1996 		CMVideoCardGammaType *vcgt = NULL;	/* vcgt tag */
1997 		int size;
1998 		int i, j;
1999 
2000 		debugr("Set_ramdac persist\n");
2001 
2002 		/* Get the current installed profile */
2003 		if ((ev = CMGetProfileByAVID((CMDisplayIDType)p->ddid, &prof)) != noErr) {
2004 			debugr2((errout,"CMGetProfileByAVID() failed for display '%s' with error %d\n",p->name,ev));
2005 			return 1;
2006 		}
2007 
2008 		/* Get the current installed  profile's location */
2009 		if ((ev = NCMGetProfileLocation(prof, &ploc, &plocsz)) != noErr) {
2010 			debugr2((errout,"NCMGetProfileLocation() failed for display '%s' with error %d\n",p->name,ev));
2011 			return 1;
2012 		}
2013 
2014 		debugr2((errout, "Current profile path = '%s'\n",plocpath(&ploc)));
2015 
2016 		if ((tpath = plocpath(&ploc)) == NULL) {
2017 			debugr2((errout,"plocpath failed for display '%s'\n",p->name));
2018 			return 1;
2019 		}
2020 
2021 		if (strlen(tpath) > 255) {
2022 			debugr2((errout,"current profile path is too long\n"));
2023 			return 1;
2024 		}
2025 		if ((ppath = malloc(strlen(tpath) + 6)) == NULL) {
2026 			debugr2((errout,"malloc failed for display '%s'\n",p->name));
2027 			free(tpath);
2028 			return 1;
2029 		}
2030 		strcpy(ppath,tpath);
2031 		strcat(ppath,".orig");
2032 
2033 		/* Rename the currently installed profile temporarily */
2034 		if (rename(tpath, ppath) != 0) {
2035 			debugr2((errout,"Renaming existing profile '%s' failed\n",ppath));
2036 			return 2;
2037 		}
2038 		debugr2((errout,"Renamed current profile '%s' to '%s'\n",tpath,ppath));
2039 
2040 		/* Make a copy of the renamed current profile back to it's true name */
2041 		tploc.locType = cmPathBasedProfile;
2042 		strncpy(tploc.u.pathLoc.path, tpath, 255);
2043 		tploc.u.pathLoc.path[255] = '\000';
2044 
2045 		/* Make the temporary copy */
2046 		if ((ev = CMCopyProfile(&tprof, &tploc, prof)) != noErr) {
2047 			debugr2((errout,"CMCopyProfile() failed for display '%s' with error %d\n",p->name,ev));
2048 			CMCloseProfile(prof);
2049 			unlink(tpath);
2050 			rename(ppath, tpath);
2051 			return 1;
2052 		}
2053 		CMCloseProfile(prof);
2054 
2055 		if ((ev = CMSetProfileDescriptions(tprof, "Dispwin Temp", 13, NULL, 0, NULL, 0)) != noErr) {
2056 			debugr2((errout,"cmVideoCardGammaTag`() failed for display '%s' with error %d\n",p->name,ev));
2057 			CMCloseProfile(tprof);
2058 			unlink(tpath);
2059 			rename(ppath, tpath);
2060 			return 1;
2061 		}
2062 
2063 		/* Change the description and set the vcgt tag to the calibration */
2064 		if ((vcgt = malloc(size = (sizeof(CMVideoCardGammaType) - 1 + 3 * 2 * r->nent))) == NULL) {
2065 			debugr2((errout,"malloc of vcgt tag failed for display '%s' with error %d\n",p->name,ev));
2066 			CMCloseProfile(tprof);
2067 			unlink(tpath);
2068 			rename(ppath, tpath);
2069 			return 1;
2070 		}
2071 		cs_w32(&vcgt->typeDescriptor, cmSigVideoCardGammaType);
2072 		cs_w32(&vcgt->gamma.tagType, cmVideoCardGammaTableType);	/* Table, not formula */
2073 		cs_w16(&vcgt->gamma.u.table.channels, 3);
2074 		cs_w16(&vcgt->gamma.u.table.entryCount, r->nent);
2075 		cs_w16(&vcgt->gamma.u.table.entrySize, 2);
2076 
2077 		for (i = 0; i < r->nent; i++) {
2078 			for (j = 0; j < 3; j++) {
2079 				double vv = r->v[j][i];
2080 				int ivv;
2081 				if (vv < 0.0)
2082 					vv = 0.0;
2083 				else if (vv > 1.0)
2084 					vv = 1.0;
2085 				ivv = (int)(vv * 65535.0 + 0.5);
2086 				cs_w16(((unsigned short *)vcgt->gamma.u.table.data) + ((j * r->nent) + i), ivv);
2087 			}
2088 		}
2089 
2090 		/* Replace or add a vcgt tag */
2091 		if ((ev = CMSetProfileElement(tprof, cmVideoCardGammaTag, size, vcgt)) != noErr) {
2092 			debugr2((errout,"CMSetProfileElement vcgt tag failed with error %d\n",ev));
2093 			free(vcgt);
2094 			CMCloseProfile(tprof);
2095 			unlink(tpath);
2096 			rename(ppath, tpath);
2097 			return 1;
2098 		}
2099 		free(vcgt);
2100 
2101 		if ((ev = CMUpdateProfile(tprof)) != noErr) {
2102 			debugr2((errout,"CMUpdateProfile failed with error %d\n",ev));
2103 			CMCloseProfile(tprof);
2104 			unlink(tpath);
2105 			rename(ppath, tpath);
2106 			return 1;
2107 		}
2108 
2109 		/* Make temporary file the current profile - updates LUTs */
2110 		if ((ev = CMSetProfileByAVID((CMDisplayIDType)p->ddid, tprof)) != noErr) {
2111 			debugr2((errout,"CMSetProfileByAVID() failed for display '%s' with error %d\n",p->name,ev));
2112 			CMCloseProfile(tprof);
2113 			unlink(tpath);
2114 			rename(ppath, tpath);
2115 			return 1;
2116 		}
2117 		CMCloseProfile(tprof);
2118 		debugr2((errout,"Set display to use temporary profile '%s'\n",tpath));
2119 
2120 		/* Delete the temporary profile */
2121 		unlink(tpath);
2122 
2123 		/* Rename the current profile back to it's correct name */
2124 		if (rename(ppath, tpath) != 0) {
2125 			debugr2((errout,"Renaming existing profile '%s' failed\n",ppath));
2126 			return 1;
2127 		}
2128 		debugr2((errout,"Restored '%s' back to '%s'\n",ppath,tpath));
2129 	}
2130 #endif	/* < 10.6 */
2131 
2132 #endif /* UNIX_APPLE */
2133 
2134 #if defined(UNIX_X11)
2135 	unsigned short vals[3][16384];
2136 
2137 	debugr("dispwin_set_ramdac called\n");
2138 
2139 	for (j = 0; j < 3; j++) {
2140 		for (i = 0; i < r->nent; i++) {
2141 			double vv = r->v[j][i];
2142 			if (vv < 0.0)
2143 				vv = 0.0;
2144 			else if (vv > 1.0)
2145 				vv = 1.0;
2146 			vals[j][i] = (int)(vv * 65535.0 + 0.5);
2147 		}
2148 	}
2149 
2150 #if RANDR_MAJOR == 1 && RANDR_MINOR >= 2 && !defined(DISABLE_RANDR)
2151 	if (p->crtc != 0) {		/* Using Xrandr 1.2 */
2152 		XRRCrtcGamma *crtcgam;
2153 
2154 		debugr("Setting gamma using Randr 1.2\n");
2155 
2156 		if ((crtcgam = XRRAllocGamma(r->nent)) == NULL) {
2157 			debugr(" XRRAllocGamma failed\n");
2158 			return 1;
2159 		}
2160 
2161 		for (i = 0; i < r->nent; i++) {
2162 			crtcgam->red[i]   = vals[0][i];
2163 			crtcgam->green[i] = vals[1][i];
2164 			crtcgam->blue[i]  = vals[2][i];
2165 		}
2166 
2167 		XRRSetCrtcGamma(p->mydisplay, p->crtc, crtcgam);
2168 		XSync(p->mydisplay, False);		/* Flush the change out */
2169 
2170 		XRRFreeGamma(crtcgam);
2171 
2172 	} else
2173 #endif /* randr >= V 1.2 */
2174 	{
2175 		/* Some propietary multi-screen drivers (ie. TwinView & MergedFB) */
2176 		/* don't implement the XVidMode extenstion properly. */
2177 		if (XSetErrorHandler(null_error_handler) == 0) {
2178 			debugr("get_displays failed on XSetErrorHandler\n");
2179 			return 1;
2180 		}
2181 		if (XF86VidModeSetGammaRamp(p->mydisplay, p->myrscreen, r->nent, vals[0], vals[1], vals[2]) == 0) {
2182 			XSetErrorHandler(NULL);
2183 			debugr("XF86VidModeSetGammaRamp failed\n");
2184 			return 1;
2185 		}
2186 		XSync(p->mydisplay, False);		/* Flush the change out */
2187 		XSetErrorHandler(NULL);
2188 	}
2189 #endif	/* UNXI X11 */
2190 
2191 	debugr("dispwin_set_ramdac returning OK\n");
2192 
2193 	return 0;
2194 }
2195 
2196 
2197 /* Clone ourselves */
dispwin_clone_ramdac(ramdac * r)2198 ramdac *dispwin_clone_ramdac(ramdac *r) {
2199 	ramdac *nr;
2200 	int i, j;
2201 
2202 	debug("dispwin_clone_ramdac called\n");
2203 
2204 	/* Allocate a ramdac */
2205 	if ((nr = (ramdac *)calloc(sizeof(ramdac), 1)) == NULL) {
2206 		return NULL;
2207 	}
2208 
2209 	*nr = *r;		/* Structrure copy */
2210 
2211 	for (j = 0; j < 3; j++) {
2212 
2213 		if ((nr->v[j] = (double *)calloc(sizeof(double), r->nent)) == NULL) {
2214 			for (j--; j >= 0; j--)
2215 				free(nr->v[j]);
2216 			free(nr);
2217 			return NULL;
2218 		}
2219 	}
2220 
2221 	for (j = 0; j < 3; j++) {
2222 		for (i = 0; i < r->nent; i++) {
2223 			nr->v[j][i] = r->v[j][i];
2224 		}
2225 	}
2226 
2227 	debug("clone is done\n");
2228 	return nr;
2229 }
2230 
2231 /* Debug dump ramdac */
dispwin_dump_ramdac(FILE * fp,ramdac * r)2232 static void dispwin_dump_ramdac(FILE *fp, ramdac *r) {
2233 	int i, j;
2234 
2235 	fprintf(fp,"Ramdac fdepth %d, rdepth %d, ndepth %d, nent %d\n",
2236 	        r->fdepth, r->rdepth, r->ndepth, r->nent);
2237 
2238 	for (i = 0; i < r->nent; i++) {
2239 		int note = 0;
2240 		for (j = 0; j < 3; j++) {
2241 			if (r->v[j][i] < 0.0 || r->v[j][i] > 1.0
2242 			  || (i > 0 && r->v[j][i] < r->v[j][i-1]))
2243 			note = 1;
2244 		}
2245 		fprintf(fp," %d: %f %f %f%s\n",i, r->v[0][i], r->v[1][i], r->v[2][i], note ? " #" : "");
2246 	}
2247 }
2248 
2249 /* Set the ramdac values to linear */
dispwin_setlin_ramdac(ramdac * r)2250 void dispwin_setlin_ramdac(ramdac *r) {
2251 	int i, j;
2252 
2253 	debug("dispwin_setlin_ramdac called\n");
2254 
2255 	for (i = 0; i < r->nent; i++) {
2256 		double val = i/(r->nent - 1.0);
2257 		for (j = 0; j < 3; j++) {
2258 			r->v[j][i] = val;
2259 		}
2260 	}
2261 }
2262 
2263 /* We're done with a ramdac structure */
dispwin_del_ramdac(ramdac * r)2264 void dispwin_del_ramdac(ramdac *r) {
2265 	int j;
2266 
2267 	debug("dispwin_del_ramdac called\n");
2268 
2269 	for (j = 0; j < 3; j++) {
2270 		free(r->v[j]);
2271 	}
2272 
2273 	free(r);
2274 }
2275 
2276 /* ----------------------------------------------- */
2277 /* Useful function for X11 profile atom settings */
2278 
2279 #if defined(UNIX_X11)
2280 /* Return NZ on error */
set_X11_atom(dispwin * p,char * fname)2281 static int set_X11_atom(dispwin *p, char *fname) {
2282 	FILE *fp;
2283 	unsigned long psize, bread;
2284 	unsigned char *atomv;
2285 
2286 	debugr("Setting _ICC_PROFILE property\n");
2287 
2288 	/* Read in the ICC profile, then set the X11 atom value */
2289 #if !defined(O_CREAT) && !defined(_O_CREAT)
2290 # error "Need to #include fcntl.h!"
2291 #endif
2292 #if defined(O_BINARY) || defined(_O_BINARY)
2293 	if ((fp = fopen(fname,"rb")) == NULL)
2294 #else
2295 	if ((fp = fopen(fname,"r")) == NULL)
2296 #endif
2297 	{
2298 		debugr2((errout,"Can't open file '%s'\n",fname));
2299 		return 1;
2300 	}
2301 
2302 	/* Figure out how big it is */
2303 	if (fseek(fp, 0, SEEK_END)) {
2304 		debugr2((errout,"Seek '%s' to EOF failed\n",fname));
2305 		return 1;
2306 	}
2307 	psize = (unsigned long)ftell(fp);
2308 
2309 	if (fseek(fp, 0, SEEK_SET)) {
2310 		debugr2((errout,"Seek '%s' to SOF failed\n",fname));
2311 		return 1;
2312 	}
2313 
2314 	if ((atomv = (unsigned char *)malloc(psize)) == NULL) {
2315 		debugr2((errout,"Failed to allocate buffer for profile '%s'\n",fname));
2316 		return 1;
2317 	}
2318 
2319 	if ((bread = fread(atomv, 1, psize, fp)) != psize) {
2320 		debugr2((errout,"Failed to read profile '%s' into buffer\n",fname));
2321 		return 1;
2322 	}
2323 
2324 	fclose(fp);
2325 
2326 	XChangeProperty(p->mydisplay, RootWindow(p->mydisplay, 0), p->icc_atom,
2327 	                XA_CARDINAL, 8, PropModeReplace, atomv, psize);
2328 
2329 #if RANDR_MAJOR == 1 && RANDR_MINOR >= 2 && !defined(DISABLE_RANDR)
2330 	if (p->icc_out_atom != 0) {
2331 		/* If Xrandr 1.2, set property on output */
2332 		/* This seems to fail on some servers. Ignore the error ? */
2333 		if (XSetErrorHandler(null_error_handler) == 0) {
2334 			debugr("get_displays failed on XSetErrorHandler\n");
2335 			return 1;
2336 		}
2337 		g_error_handler_triggered = 0;
2338 		XRRChangeOutputProperty(p->mydisplay, p->output, p->icc_out_atom,
2339 	                XA_CARDINAL, 8, PropModeReplace, atomv, psize);
2340 		if (g_error_handler_triggered != 0) {
2341 			debugr("XRRChangeOutputProperty failed\n");
2342 			warning("Unable to set _ICC_PROFILE property on output");
2343 		}
2344 		XSync(p->mydisplay, False);		/* Flush the change out */
2345 		XSetErrorHandler(NULL);
2346 	}
2347 #endif /* randr >= V 1.2 */
2348 	free(atomv);
2349 
2350 	return 0;
2351 }
2352 #endif  /* UNXI X11 */
2353 
2354 /* ----------------------------------------------- */
2355 /* See if colord is available */
2356 
2357 #if defined(UNIX_X11) && defined(USE_UCMM)
2358 
2359 /* colord libcolordcompat.so shim functions */
2360 
2361 ucmm_error (*cd_edid_install_profile)(unsigned char *edid, int edid_len,
2362                                       ucmm_scope scope, char *profile_fn) = NULL;
2363 ucmm_error (*cd_edid_remove_profile)(unsigned char  *edid, int edid_len, char *profile_fn) = NULL;
2364 ucmm_error (*cd_edid_get_profile)(unsigned char *edid, int edid_len, char **profile_fn) = NULL;
2365 int cd_init = 0;		/* nz if we've looked for colord */
2366 void *cd_found = NULL;	/* .so handle if we've found colord */
2367 
2368 /* Return nz if found colord functions */
dispwin_checkfor_colord()2369 int dispwin_checkfor_colord() {
2370 
2371 	if (cd_init)
2372 		return (cd_found != NULL);
2373 
2374 	cd_found = NULL;
2375 
2376 	if ((cd_found = dlopen("libcolordcompat.so", RTLD_LAZY)) != NULL) {
2377 
2378 		cd_edid_install_profile = dlsym(cd_found, "cd_edid_install_profile");
2379 		cd_edid_remove_profile = dlsym(cd_found, "cd_edid_remove_profile");
2380 		cd_edid_get_profile = dlsym(cd_found, "cd_edid_get_profile");
2381 
2382 		if (cd_edid_install_profile == NULL
2383 		 || cd_edid_remove_profile == NULL
2384 		 || cd_edid_get_profile == NULL) {
2385 			cd_found = NULL;
2386 		}
2387 	}
2388 
2389 	cd_init = 1;
2390 
2391 	return (cd_found != NULL);
2392 }
2393 
2394 #endif
2395 
2396 
2397 
2398 /* ----------------------------------------------- */
2399 /* Install a display profile and make */
2400 /* it the default for this display. */
2401 /* Set the display to the calibration in the profile */
2402 /* (r == NULL if no calibration) */
2403 /* (We assume that the caller has checked that it's an ICC profile) */
2404 /* Return nz if failed */
dispwin_install_profile(dispwin * p,char * fname,ramdac * r,p_scope scope)2405 int dispwin_install_profile(dispwin *p, char *fname, ramdac *r, p_scope scope) {
2406 	debugr2((errout,"dispwin_install_profile '%s'\n",fname));
2407 #ifdef NT
2408 	{
2409 		char *fullpath;
2410 		char *basename;
2411 		char colpath[MAX_PATH];
2412 		unsigned long colpathlen = MAX_PATH;
2413 		WCS_PROFILE_MANAGEMENT_SCOPE wcssc;
2414 		unsigned short *wpath, *wbname, *wmonid;
2415 
2416 		if (GetColorDirectory(NULL, colpath, &colpathlen) == 0) {
2417 			debugr2((errout,"Getting color directory failed\n"));
2418 			return 1;
2419 		}
2420 
2421 		if ((fullpath = _fullpath(NULL, fname, 0)) == NULL) {
2422 			debugr2((errout,"_fullpath() failed\n"));
2423 			return 1;
2424 		}
2425 
2426 		if ((basename = PathFindFileNameX(fullpath)) == NULL) {
2427 			debugr2((errout,"Locating base name in '%s' failed\n",fname));
2428 			free(fullpath);
2429 			return 1;
2430 		}
2431 
2432 		if ((strlen(colpath) + strlen(basename) + 2) > MAX_PATH) {
2433 			debugr2((errout,"Installed profile path too long\n"));
2434 			free(fullpath);
2435 			return 1;
2436 		}
2437 		strcat(colpath, "\\");
2438 		strcat(colpath, basename);
2439 
2440 		/* Setup in case we're on Vista */
2441 		if (scope == p_scope_user)
2442 			wcssc = WCS_PROFILE_MANAGEMENT_SCOPE_CURRENT_USER;
2443 		else
2444 			wcssc = WCS_PROFILE_MANAGEMENT_SCOPE_SYSTEM_WIDE;
2445 
2446 		if ((wpath = char2wchar(fullpath)) == NULL) {
2447 			debugr2((errout,"char2wchar failed\n"));
2448 			free(fullpath);
2449 			return 1;
2450 		}
2451 
2452 		if ((wbname = char2wchar(basename)) == NULL) {
2453 			debugr2((errout,"char2wchar failed\n"));
2454 			free(wpath);
2455 			free(fullpath);
2456 			return 1;
2457 		}
2458 
2459 		if ((wmonid = char2wchar(p->monid)) == NULL) {
2460 			debugr2((errout,"char2wchar failed\n"));
2461 			free(wbname);
2462 			free(wpath);
2463 			free(fullpath);
2464 			return 1;
2465 		}
2466 
2467 		debugr2((errout,"Installing '%s'\n",fname));
2468 
2469 		/* Install doesn't replace an existing installed profile, */
2470 		/* so we need to try and delete this profile first */
2471 		if (pWcsDisassociateColorProfileFromDevice != NULL) {
2472 			(*pWcsDisassociateColorProfileFromDevice)(wcssc, wbname, wmonid);
2473 		} else {
2474 			DisassociateColorProfileFromDevice(NULL, basename, p->monid);
2475 		}
2476 		if (UninstallColorProfile(NULL,  basename, TRUE) == 0) {
2477 			/* UninstallColorProfile fails on Win2K */
2478 			_unlink(colpath);
2479 		}
2480 
2481 		if (InstallColorProfile(NULL, fullpath) == 0) {
2482 			debugr2((errout,"InstallColorProfile() failed for file '%s' with error %d\n",fname,GetLastError()));
2483 			free(wmonid);
2484 			free(wbname);
2485 			free(wpath);
2486 			free(fullpath);
2487 			return 1;
2488 		}
2489 
2490 		debugr2((errout,"Associating '%s' with '%s'\n",fullpath,p->monid));
2491 		if (pWcsAssociateColorProfileWithDevice != NULL) {
2492 			debugr("Using Vista Associate\n");
2493 			if ((*pWcsAssociateColorProfileWithDevice)(wcssc, wpath, wmonid) == 0) {
2494 				debugr2((errout,"WcsAssociateColorProfileWithDevice() failed for file '%s' with error %d\n",fullpath,GetLastError()));
2495 				free(wmonid);
2496 				free(wbname);
2497 				free(wpath);
2498 				free(fullpath);
2499 				return 1;
2500 			}
2501 		} else {
2502 			if (AssociateColorProfileWithDevice(NULL, fullpath, p->monid) == 0) {
2503 				debugr2((errout,"AssociateColorProfileWithDevice() failed for file '%s' with error %d\n",fullpath,GetLastError()));
2504 				free(wmonid);
2505 				free(wbname);
2506 				free(wpath);
2507 				free(fullpath);
2508 				return 1;
2509 			}
2510 		}
2511 
2512 		free(wmonid);
2513 		free(wbname);
2514 		free(wpath);
2515 		free(fullpath);
2516 		/* The default profile will be the last one associated */
2517 
2518 		/* MSWindows doesn't generally set the display to the current profile calibration, */
2519 		/* so we do it. */
2520 		if (p->set_ramdac(p,r,1))
2521 			error("Failed to set VideoLUT");
2522 
2523 		return 0;
2524 	}
2525 #endif /* NT */
2526 
2527 /* For Linux and OS X, make sure we don't create a file with the wrong owner */
2528 #if defined(UNIX)
2529 	/* If we're creating a user profile and running as root sudo */
2530 	if (scope == p_scope_user && geteuid() == 0) {
2531 		char *uids, *gids;
2532 		int uid, gid;
2533 
2534 		debugr("We're setting a user profile running as root - run as user\n");
2535 		if ((uids = getenv("SUDO_UID")) != NULL
2536 		 && (gids = getenv("SUDO_GID")) != NULL) {
2537 			uid = atoi(uids);
2538 			gid = atoi(gids);
2539 			if (setegid(gid) || seteuid(uid)) {
2540 				debugr("seteuid or setegid failed\n");
2541 			} else {
2542 				debug2((errout,"Set euid %d and egid %d\n",uid,gid));
2543 			}
2544 		}
2545 	/* If setting local system profile and not effective root, but sudo */
2546 	} else if (scope != p_scope_user && getuid() == 0 && geteuid() != 0) {
2547 		if (getenv("SUDO_UID") != NULL
2548 		 && getenv("SUDO_GID") != NULL) {
2549 
2550 			debugr("We're setting a system profile running as user - revert to root\n");
2551 			if (setegid(getgid()) || seteuid(getuid()))
2552 				debugr("seteuid or setegid failed\n");
2553 		}
2554 	}
2555 #endif /* OS X || Linux */
2556 
2557 #ifdef UNIX_APPLE
2558 
2559 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
2560 	{
2561 		char *dpath;		/* Install file path */
2562 		CFUUIDRef dispuuid;
2563 		CFStringRef cfprofpath;
2564 		CFStringRef keys[1];
2565 		CFURLRef values[1];
2566 		CFDictionaryRef dict;
2567 
2568 		if ((dispuuid = CGDisplayCreateUUIDFromDisplayID(p->ddid)) == NULL) {
2569 			debugr2((errout,"CGDisplayCreateUUIDFromDisplayID() failed\n"));
2570 			return 1;
2571 		}
2572 
2573 		/* Determine the location the profile will be installed into */
2574 		if ((dpath = iprof_path(scope, fname)) == NULL) {
2575 			debugr2((errout,"iprof_path() failed\n"));
2576 			CFRelease(dispuuid);
2577 			return 1;
2578 		}
2579 
2580 		debugr2((errout,"Source profile '%s'\n",fname));
2581 		debugr2((errout,"Destination profile '%s'\n",dpath));
2582 
2583 		/* Copy the new profile to the destination */
2584 		if (copyfile(fname, dpath, NULL, COPYFILE_ALL) != 0) {
2585 			debugr2((errout,"copyfile failed\n"));
2586 			free(dpath);
2587 			CFRelease(dispuuid);
2588 			return 1;
2589 		}
2590 
2591 		/* Register it with the OS and make it the default */
2592 		if ((cfprofpath = CFStringCreateWithCString(kCFAllocatorDefault, dpath,
2593 		                                        kCFStringEncodingUTF8)) == NULL) {
2594 			debugr2((errout,"CFStringCreateWithCString() failed\n"));
2595 			free(dpath);
2596 			CFRelease(dispuuid);
2597 			return 1;
2598 		}
2599 		free(dpath); dpath = NULL;
2600 
2601 		keys[0] = kColorSyncDeviceDefaultProfileID;
2602 		if ((values[0] = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, cfprofpath,
2603 		                                         kCFURLPOSIXPathStyle, false)) == NULL) {
2604 			debugr2((errout,"CFURLCreateWithFileSystemPath() failed\n"));
2605 			CFRelease(cfprofpath);
2606 			CFRelease(dispuuid);
2607 			return 1;
2608 		}
2609 
2610 		if ((dict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&keys,
2611 			                     (const void **)&values, 1, NULL, NULL)) == NULL) {
2612 			debugr2((errout,"CFDictionaryCreate() failed\n"));
2613 			CFRelease(values[0]);
2614 			CFRelease(cfprofpath);
2615 			CFRelease(dispuuid);
2616 			return 1;
2617 		}
2618 		if (!ColorSyncDeviceSetCustomProfiles(kColorSyncDisplayDeviceClass, dispuuid, dict)) {
2619 			debugr2((errout,"ColorSyncDeviceSetCustomProfiles() failed\n"));
2620 			CFRelease(dict);
2621 			CFRelease(values[0]);
2622 			CFRelease(cfprofpath);
2623 			CFRelease(dispuuid);
2624 			return 1;
2625 		}
2626 		CFRelease(dict);
2627 		CFRelease(values[0]);
2628 		CFRelease(cfprofpath);
2629 		CFRelease(dispuuid);
2630 
2631 		return 0;
2632 	}
2633 #else	/* 10.6 and prior */
2634 		// Switch to using iprof_path() to simplify ?
2635 	{
2636 		CMError ev;
2637 		short vref;
2638 		FSRef dirref;
2639 		char dpath[FILENAME_MAX];
2640 		char *basename;
2641 
2642 		CMProfileLocation ploc;		/* Source profile location */
2643 		CMProfileRef prof;			/* Source profile */
2644 		CMProfileLocation dploc;	/* Destinaion profile location */
2645 		CMProfileRef dprof;			/* Destinaion profile */
2646 
2647 		if (scope == p_scope_network)
2648 			vref = kNetworkDomain;
2649 		else if (scope == p_scope_system)
2650 			vref = kSystemDomain;
2651 		else if (scope == p_scope_local)
2652 			vref = kLocalDomain;
2653 		else
2654 			vref = kUserDomain;
2655 
2656 		/* Locate the appropriate ColorSync path */
2657 		if ((ev = FSFindFolder(vref, kColorSyncProfilesFolderType, kCreateFolder, &dirref)) != noErr) {
2658 			debugr2((errout,"FSFindFolder() failed with error %d\n",ev));
2659 			return 1;
2660 		}
2661 
2662 		/* Convert to POSIX path */
2663 		if ((ev = FSRefMakePath(&dirref, (unsigned char *)dpath, FILENAME_MAX)) != noErr) {
2664 			debugr2((errout,"FSRefMakePath failed with error %d\n",ev));
2665 			return 1;
2666 		}
2667 
2668 		/* Locate the base filename in the fname */
2669 		for (basename = fname + strlen(fname);  ; basename--) {
2670 			if (basename <= fname || basename[-1] == '/')
2671 				break;
2672 		}
2673 
2674 		/* Append the basename to the ColorSync directory path */
2675 		if ((strlen(dpath) + strlen(basename) + 2) > FILENAME_MAX
2676 		 || (strlen(dpath) + strlen(basename) + 2) > 256) {
2677 			debugr2((errout,"ColorSync dir + profile name too long\n"));
2678 			return 1;
2679 		}
2680 		strcat(dpath, "/");
2681 		strcat(dpath, basename);
2682 
2683 		debugr2((errout,"Source profile '%s'\n",fname));
2684 		debugr2((errout,"Destination profile '%s'\n",dpath));
2685 
2686 		/* Open the profile we want to install */
2687 		ploc.locType = cmPathBasedProfile;
2688 		strncpy(ploc.u.pathLoc.path, fname, 255);
2689 		ploc.u.pathLoc.path[255] = '\000';
2690 
2691 		if ((ev = CMOpenProfile(&prof, &ploc)) != noErr) {
2692 			debugr2((errout,"CMOpenProfile() failed for file '%s' with error %d\n",fname,ev));
2693 			return 1;
2694 		}
2695 
2696 		/* Delete any current profile */
2697 		unlink(dpath);
2698 
2699 		/* Make a copy of it to the ColorSync directory */
2700 		dploc.locType = cmPathBasedProfile;
2701 		strncpy(dploc.u.pathLoc.path, dpath, 255);
2702 		dploc.u.pathLoc.path[255] = '\000';
2703 
2704 		if ((ev = CMCopyProfile(&dprof, &dploc, prof)) != noErr) {
2705 			debugr2((errout,"CMCopyProfile() failed for file '%s' with error %d\n",dpath,ev));
2706 			return 1;
2707 		}
2708 
2709 		/* Make it the current profile - updates LUTs */
2710 		if ((ev = CMSetProfileByAVID((CMDisplayIDType)p->ddid, dprof)) != noErr) {
2711 			debugr2((errout,"CMSetProfileByAVID() failed for file '%s' with error %d\n",fname,ev));
2712 			return 1;
2713 		}
2714 		CMCloseProfile(prof);
2715 		CMCloseProfile(dprof);
2716 
2717 		return 0;
2718 	}
2719 #endif	/* 10.6 and prior */
2720 #endif /*  UNIX_APPLE */
2721 
2722 #if defined(UNIX_X11) && defined(USE_UCMM)
2723 	{
2724 		ucmm_error ev;
2725 		ucmm_scope sc;
2726 		FILE *fp;
2727 		unsigned long psize, bread;
2728 		unsigned char *atomv;
2729 		int rv;
2730 
2731 		if (scope == p_scope_network
2732 		 || scope == p_scope_system
2733 		 || scope == p_scope_local)
2734 			sc = ucmm_local_system;
2735 		else
2736 			sc = ucmm_user;
2737 
2738 		if (cd_found)
2739 			ev = cd_edid_install_profile(p->edid, p->edid_len, sc, fname);
2740 			// Hmm. We're relying on colord error codes being in sync with ucmm.
2741 		else
2742 			ev = ucmm_install_monitor_profile(sc, p->edid, p->edid_len, p->name, fname);
2743 
2744 		if (ev != ucmm_ok) {
2745 			debugr2((errout,"Installing profile '%s' failed with error %d '%s'\n",fname,ev,ucmm_error_string(ev)));
2746 			return 1;
2747 		}
2748 
2749 		if ((rv = set_X11_atom(p, fname)) != 0) {
2750 			debugr2((errout,"Setting X11 atom failed"));
2751 			return 1;
2752 		}
2753 
2754 		/* X11 doesn't set the display to the current profile calibration, */
2755 		/* so we do it. */
2756 		if (p->set_ramdac(p,r,1)) {
2757 			debugr2((errout,"Failed to set VideoLUT"));
2758 			return 1;
2759 		}
2760 		return 0;
2761 	}
2762 #endif  /* UNXI X11 */
2763 
2764 	return 1;
2765 }
2766 
2767 /* Un-Install a display profile */
2768 /* Return nz if failed, */
2769 /* 1 if not sucessfully deleted */
2770 /* 2 if profile not found */
dispwin_uninstall_profile(dispwin * p,char * fname,p_scope scope)2771 int dispwin_uninstall_profile(dispwin *p, char *fname, p_scope scope) {
2772 	debugr2((errout,"dispwin_uninstall_profile '%s'\n", fname));
2773 #ifdef NT
2774 	{
2775 		char *fullpath;
2776 		char *basename;
2777 		char colpath[MAX_PATH];
2778 		unsigned long colpathlen = MAX_PATH;
2779 		WCS_PROFILE_MANAGEMENT_SCOPE wcssc;
2780 		unsigned short *wbname, *wmonid;
2781 
2782 		if (GetColorDirectory(NULL, colpath, &colpathlen) == 0) {
2783 			debugr2((errout,"Getting color directory failed\n"));
2784 			return 1;
2785 		}
2786 
2787 		if ((fullpath = _fullpath(NULL, fname, 0)) == NULL) {
2788 			debugr2((errout,"_fullpath() failed\n"));
2789 			return 1;
2790 		}
2791 
2792 		if ((basename = PathFindFileNameX(fullpath)) == NULL) {
2793 			debugr2((errout,"Locating base name in '%s' failed\n",fname));
2794 			free(fullpath);
2795 			return 1;
2796 		}
2797 
2798 		if ((strlen(colpath) + strlen(basename) + 2) > MAX_PATH) {
2799 			debugr2((errout,"Installed profile path too long\n"));
2800 			free(fullpath);
2801 			return 1;
2802 		}
2803 		strcat(colpath, "\\");
2804 		strcat(colpath, basename);
2805 
2806 		/* Setup in case we're on Vista */
2807 		if (scope == p_scope_user)
2808 			wcssc = WCS_PROFILE_MANAGEMENT_SCOPE_CURRENT_USER;
2809 		else
2810 			wcssc = WCS_PROFILE_MANAGEMENT_SCOPE_SYSTEM_WIDE;
2811 
2812 		if ((wbname = char2wchar(basename)) == NULL) {
2813 			debugr2((errout,"char2wchar failed\n"));
2814 			free(fullpath);
2815 			return 1;
2816 		}
2817 
2818 		if ((wmonid = char2wchar(p->monid)) == NULL ) {
2819 			debugr2((errout,"char2wchar failed\n"));
2820 			free(wbname);
2821 			free(fullpath);
2822 			return 1;
2823 		}
2824 
2825 		debugr2((errout,"Disassociating '%s' from '%s'\n",basename,p->monid));
2826 
2827 		if (pWcsDisassociateColorProfileFromDevice != NULL) {
2828 			debugr("Using Vista Disassociate\n");
2829 			/* Ignore error if profile is already disasociated or doesn't exist */
2830 			if ((*pWcsDisassociateColorProfileFromDevice)(wcssc, wbname, wmonid) == 0
2831 			 && GetLastError() != 2015 && GetLastError() != 2011) {
2832 				debugr2((errout,"WcsDisassociateColorProfileWithDevice() failed for file '%s' with error %d\n",basename,GetLastError()));
2833 				free(wmonid);
2834 				free(wbname);
2835 				free(fullpath);
2836 				return 1;
2837 			}
2838 		} else {
2839 			/* Ignore error if profile is already disasociated or doesn't exist */
2840 			if (DisassociateColorProfileFromDevice(NULL, basename, p->monid) == 0
2841 			 && GetLastError() != 2015 && GetLastError() != 2011) {
2842 				debugr2((errout,"DisassociateColorProfileWithDevice() failed for file '%s' with error %d\n",basename,GetLastError()));
2843 				free(wmonid);
2844 				free(wbname);
2845 				free(fullpath);
2846 				return 1;
2847 			}
2848 		}
2849 
2850 		if (UninstallColorProfile(NULL, basename, TRUE) == 0) {
2851 			/* This can happen when some other program has the profile open */
2852 			int ev;
2853 			struct _stat sbuf;
2854 			debugr2((errout,"Warning, uninstallColorProfile() failed for file '%s' with error %d\n", basename,GetLastError()));
2855 			free(wmonid);
2856 			free(wbname);
2857 			free(fullpath);
2858 			return 2;
2859 		}
2860 
2861 		free(wmonid);
2862 		free(wbname);
2863 		free(fullpath);
2864 
2865 		return 0;
2866 	}
2867 #endif /* NT */
2868 
2869 /* For Linux and OS X, make sure we don't create a file with the wrong owner */
2870 #if defined(UNIX)
2871 	/* If we're creating a user profile and running as root sudo */
2872 	if (scope == p_scope_user && geteuid() == 0) {
2873 		char *uids, *gids;
2874 		int uid, gid;
2875 
2876 		debugr("We're setting a user profile running as root - run as user\n");
2877 		if ((uids = getenv("SUDO_UID")) != NULL
2878 		 && (gids = getenv("SUDO_GID")) != NULL) {
2879 			uid = atoi(uids);
2880 			gid = atoi(gids);
2881 			if (setegid(gid) || seteuid(uid)) {
2882 				debugr("seteuid or setegid failed\n");
2883 			}
2884 			debug2((errout,"Set euid %d and egid %d\n",uid,gid));
2885 		}
2886 	/* If setting local system proile and not effective root, but sudo */
2887 	} else if (scope != p_scope_user && getuid() == 0 && geteuid() != 0) {
2888 		if (getenv("SUDO_UID") != NULL
2889 		 && getenv("SUDO_GID") != NULL) {
2890 
2891 			debugr("We're setting a system profile running as user - revert to root\n");
2892 			setegid(getgid());
2893 			seteuid(getuid());
2894 		}
2895 	}
2896 #endif /* OS X || Linux */
2897 #ifdef UNIX_APPLE
2898 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
2899 	{
2900 		char *dpath;		/* Un-install file path */
2901 		struct stat sbuf;
2902 		int ev;
2903 		CFStringRef keys[1];
2904 		CFURLRef values[1];
2905 		CFDictionaryRef dict;
2906 		CFUUIDRef dispuuid;
2907 
2908 		/* Determine the location the profile will be installed into */
2909 		if ((dpath = iprof_path(scope, fname)) == NULL) {
2910 			debugr2((errout,"iprof_path() failed\n"));
2911 			return 1;
2912 		}
2913 
2914 		debugr2((errout,"Profile to delete '%s'\n",dpath));
2915 
2916 		if (stat(dpath, &sbuf) != 0) {
2917 			debugr2((errout,"delete '%s' profile doesn't exist\n",dpath));
2918 			return 2;
2919 		}
2920 		if ((ev = unlink(dpath)) != 0) {
2921 			debugr2((errout,"delete '%s' failed with %d\n",dpath,ev));
2922 			return 1;
2923 		}
2924 
2925 		free(dpath); dpath = NULL;
2926 
2927 		/* Make ColorSync notice that it's gone */
2928 		/* (Works on 10.7, but not 10.6 ? */
2929 		if ((dispuuid = CGDisplayCreateUUIDFromDisplayID(p->ddid)) == NULL) {
2930 			debugr2((errout,"CGDisplayCreateUUIDFromDisplayID() failed\n"));
2931 			return 1;
2932 		}
2933 
2934 		keys[0] = kColorSyncDeviceDefaultProfileID;
2935 		values[0] = (CFURLRef)kCFNull;
2936 
2937 		if ((dict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&keys,
2938 			                     (const void **)&values, 1, NULL, NULL)) == NULL) {
2939 			debugr2((errout,"CFDictionaryCreate() failed\n"));
2940 			CFRelease(dispuuid);
2941 			return 1;
2942 		}
2943 
2944 		if (!ColorSyncDeviceSetCustomProfiles(kColorSyncDisplayDeviceClass, dispuuid, dict)) {
2945 			debugr2((errout,"ColorSyncDeviceSetCustomProfiles() failed\n"));
2946 			CFRelease(dict);
2947 			CFRelease(dispuuid);
2948 			return 1;
2949 		}
2950 		CFRelease(dict);
2951 		CFRelease(dispuuid);
2952 
2953 		return 0;
2954 	}
2955 #else	/* 10.6 and prior */
2956 		// ~~~ can use above code
2957 	{
2958 		CMError cmev;
2959 		int ev;
2960 		short vref;
2961 		char dpath[FILENAME_MAX];
2962 		char *basename;
2963 		FSRef dirref;
2964 		struct stat sbuf;
2965 
2966 		if (scope == p_scope_network)
2967 			vref = kNetworkDomain;
2968 		else if (scope == p_scope_system)
2969 			vref = kSystemDomain;
2970 		else if (scope == p_scope_local)
2971 			vref = kLocalDomain;
2972 		else
2973 			vref = kUserDomain;
2974 
2975 		/* Locate the appropriate ColorSync path */
2976 		if ((cmev = FSFindFolder(vref, kColorSyncProfilesFolderType, kCreateFolder, &dirref)) != noErr) {
2977 			debugr2((errout,"FSFindFolder() failed with error %d\n",cmev));
2978 			return 1;
2979 		}
2980 
2981 		/* Convert to POSIX path */
2982 		if ((cmev = FSRefMakePath(&dirref, (unsigned char *)dpath, FILENAME_MAX)) != noErr) {
2983 			debugr2((errout,"FSRefMakePath failed with error %d\n",cmev));
2984 			return 1;
2985 		}
2986 
2987 		/* Locate the base filename in the fname */
2988 		for (basename = fname + strlen(fname);  ; basename--) {
2989 			if (basename <= fname || basename[-1] == '/')
2990 				break;
2991 		}
2992 
2993 		/* Append the basename to the ColorSync directory path */
2994 		if ((strlen(dpath) + strlen(basename) + 2) > FILENAME_MAX
2995 		 || (strlen(dpath) + strlen(basename) + 2) > 256) {
2996 			debugr2((errout,"ColorSync dir + profile name too long\n"));
2997 			return 1;
2998 		}
2999 		strcat(dpath, "/");
3000 		strcat(dpath, basename);
3001 		debugr2((errout,"Profile to delete '%s'\n",dpath));
3002 
3003 		if (stat(dpath, &sbuf) != 0) {
3004 			debugr2((errout,"delete '%s' profile doesn't exist\n",dpath));
3005 			return 2;
3006 		}
3007 		if ((ev = unlink(dpath)) != 0) {
3008 			debugr2((errout,"delete '%s' failed with %d\n",dpath,ev));
3009 			return 1;
3010 		}
3011 
3012 		return 0;
3013 	}
3014 #endif	/* 10.6 and prior */
3015 #endif /*  UNIX_APPLE */
3016 
3017 #if defined(UNIX_X11) && defined(USE_UCMM)
3018 	{
3019 		ucmm_error ev;
3020 		ucmm_scope sc;
3021 
3022 		if (scope == p_scope_network
3023 		 || scope == p_scope_system
3024 		 || scope == p_scope_local)
3025 			sc = ucmm_local_system;
3026 		else
3027 			sc = ucmm_user;
3028 
3029 		if (cd_found)
3030 			ev = cd_edid_remove_profile(p->edid, p->edid_len, fname);
3031 		else
3032 			ev = ucmm_uninstall_monitor_profile(sc, p->edid, p->edid_len, p->name, fname);
3033 
3034 		if (ev != ucmm_ok) {
3035 			debugr2((errout,"Installing profile '%s' failed with error %d '%s'\n",fname,ev,ucmm_error_string(ev)));
3036 			return 1;
3037 		}
3038 
3039 		XDeleteProperty(p->mydisplay, RootWindow(p->mydisplay, 0), p->icc_atom);
3040 
3041 #if RANDR_MAJOR == 1 && RANDR_MINOR >= 2 && !defined(DISABLE_RANDR)
3042 		/* If Xrandr 1.2, set property on output */
3043 		if (p->icc_out_atom != 0) {
3044 			XRRDeleteOutputProperty(p->mydisplay, p->output, p->icc_out_atom);
3045 		}
3046 #endif /* randr >= V 1.2 */
3047 		return 0;
3048 	}
3049 #endif  /* UNXI X11 */
3050 
3051 	return 1;
3052 }
3053 
3054 /* Get the currently installed display profile and return it as an icmFile. */
3055 /* Return the name as well, up to mxlen chars, excluding nul. */
3056 /* Return NULL if failed. */
dispwin_get_profile(dispwin * p,char * name,int mxlen)3057 icmFile *dispwin_get_profile(dispwin *p, char *name, int mxlen) {
3058 		icmFile *rd_fp = NULL;
3059 
3060 #ifdef NT
3061 	{
3062 		char buf[MAX_PATH];
3063 		DWORD blen = MAX_PATH;
3064 
3065 		if (GetICMProfile(p->hdc, &blen, buf) == 0) {
3066 			debugr2((errout, "GetICMProfile failed, lasterr = %d\n",GetLastError()));
3067 			return NULL;
3068 		}
3069 
3070 		debugr2((errout,"Loading default profile '%s'\n",buf));
3071 		if ((rd_fp = new_icmFileStd_name(buf,"r")) == NULL)
3072 			debugr2((errout,"Can't open file '%s'",buf));
3073 
3074 		return rd_fp;
3075 	}
3076 #endif /* NT */
3077 
3078 #ifdef UNIX_APPLE
3079 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
3080 	{
3081 		char *dpath; 			/* Read file path */
3082 		struct stat sbuf;
3083 		icmAlloc *al;
3084 		void *buf;
3085 		FILE *fp;
3086 
3087 		if ((dpath = cur_profile(p)) == NULL) {
3088 			debugr2((errout,"cur_profile() failed\n"));
3089 			return NULL;
3090 		}
3091 
3092 		/* Get the profile size */
3093 		if (stat(dpath, &sbuf) != 0) {
3094 			debugr2((errout,"Failed to open profile '%s'\n",dpath));
3095 			free(dpath);
3096 			return NULL;
3097 		}
3098 
3099 		if ((al = new_icmAllocStd()) == NULL) {
3100 			debugr("new_icmAllocStd failed\n");
3101 			free(dpath);
3102 		    return NULL;
3103 		}
3104 		if ((buf = al->malloc(al, sbuf.st_size)) == NULL) {
3105 			debugr("malloc of profile buffer failed\n");
3106 			free(dpath);
3107 		    return NULL;
3108 		}
3109 
3110 		if ((fp = fopen(dpath, "r")) == NULL) {
3111 			debugr2((errout,"opening '%s' failed\n",dpath));
3112 			al->free(al, buf);
3113 			free(dpath);
3114 			return NULL;
3115 		}
3116 		if (fread(buf, 1, sbuf.st_size, fp) != sbuf.st_size) {
3117 			debugr2((errout,"reading '%s' failed\n",dpath));
3118 			al->free(al, buf);
3119 			fclose(fp);
3120 			free(dpath);
3121 			return NULL;
3122 		}
3123 		fclose(fp);
3124 		free(dpath); dpath = NULL;
3125 
3126 		/* Memory File fp that will free the buffer when deleted: */
3127 		if ((rd_fp = new_icmFileMem_ad(buf, sbuf.st_size, al)) == NULL) {
3128 			debugr("Creating memory file profile failed");
3129 			al->free(al, buf);
3130 			al->del(al);
3131 			return NULL;
3132 		}
3133 
3134 		if (name != NULL) {
3135 			strncpy(name, "Display", mxlen);
3136 			name[mxlen] = '\000';
3137 		}
3138 
3139 		return rd_fp;
3140 	}
3141 
3142 #else	/* 10.5 and prior */
3143 	{
3144 		CMError ev;
3145 		CMProfileRef prof, dprof;		/* Source profile */
3146 		CMProfileLocation dploc;		/* Destinaion profile location */
3147 		CMAppleProfileHeader hdr;
3148 		icmAlloc *al;
3149 
3150 #ifdef NEVER
3151 		/* Get the current display profile */
3152 		if ((ev = CMGetProfileByAVID((CMDisplayIDType)p->ddid, &prof)) != noErr) {
3153 			debugr2((errout,"CMGetProfileByAVID() failed with error %d\n",ev));
3154 			return NULL;
3155 		}
3156 #else
3157 		CMDeviceProfileID curID;	/* Current Device Default profile ID */
3158 		CMProfileLocation cploc;	/* Current profile location */
3159 
3160 		/* Get the default ID for the display */
3161 		if ((ev = CMGetDeviceDefaultProfileID(cmDisplayDeviceClass, (CMDeviceID)p->ddid, &curID)) != noErr) {
3162 			debugr2((errout,"CMGetDeviceDefaultProfileID() failed with error %d\n",ev));
3163 			return NULL;
3164 		}
3165 
3166 		/* Get the displays profile */
3167 		if ((ev = CMGetDeviceProfile(cmDisplayDeviceClass, (CMDeviceID)p->ddid, curID, &cploc)) != noErr) {
3168 			debugr2((errout,"CMGetDeviceDefaultProfileID() failed with error %d\n",ev));
3169 			return NULL;
3170 		}
3171 
3172 		if ((ev = CMOpenProfile(&prof, &cploc)) != noErr) {
3173 			debugr2((errout,"CMOpenProfile() failed with error %d\n",ev));
3174 			return NULL;
3175 		}
3176 #endif
3177 
3178 		/* Get the profile size */
3179 		if ((ev = CMGetProfileHeader(prof, &hdr)) != noErr) {
3180 			debugr2((errout,"CMGetProfileHeader() failed with error %d\n",ev));
3181 			return NULL;
3182 		}
3183 
3184 		/* Make a copy of the profile to a memory buffer */
3185 		dploc.locType = cmBufferBasedProfile;
3186 		dploc.u.bufferLoc.size = hdr.cm1.size;
3187 		if ((al = new_icmAllocStd()) == NULL) {
3188 			debugr("new_icmAllocStd failed\n");
3189 		    return NULL;
3190 		}
3191 		if ((dploc.u.bufferLoc.buffer = al->malloc(al, dploc.u.bufferLoc.size)) == NULL) {
3192 			debugr("malloc of profile buffer failed\n");
3193 		    return NULL;
3194 		}
3195 
3196 		if ((ev = CMCopyProfile(&dprof, &dploc, prof)) != noErr) {
3197 			debugr2((errout,"CMCopyProfile() failed for AVID to buffer with error %d\n",ev));
3198 			return NULL;
3199 		}
3200 
3201 		/* Memory File fp that will free the buffer when deleted: */
3202 		if ((rd_fp = new_icmFileMem_ad((void *)dploc.u.bufferLoc.buffer, dploc.u.bufferLoc.size, al)) == NULL) {
3203 			debugr("Creating memory file from CMProfileLocation failed");
3204 			al->free(al, dploc.u.bufferLoc.buffer);
3205 			al->del(al);
3206 			CMCloseProfile(prof);
3207 			CMCloseProfile(dprof);
3208 			return NULL;
3209 		}
3210 
3211 		if (name != NULL) {
3212 			strncpy(name, "Display", mxlen);
3213 			name[mxlen] = '\000';
3214 		}
3215 
3216 		CMCloseProfile(prof);
3217 		CMCloseProfile(dprof);
3218 
3219 		return rd_fp;
3220 	}
3221 #endif	/* 10.5 and prior */
3222 #endif /*  UNIX_APPLE */
3223 
3224 #if defined(UNIX_X11) && defined(USE_UCMM)
3225 	/* Try and get the currently installed profile from ucmm */
3226 	{
3227 		ucmm_error ev;
3228 		char *profile = NULL;
3229 
3230 		debugr2((errout,"dispwin_get_profile called\n"));
3231 
3232 		if (cd_found)
3233 			ev = cd_edid_get_profile(p->edid, p->edid_len, &profile);
3234 		else
3235 			ev = ucmm_get_monitor_profile(p->edid, p->edid_len, p->name, &profile);
3236 
3237 		if (ev == ucmm_ok) {
3238 
3239 			if (name != NULL) {
3240 				strncpy(name, profile, mxlen);
3241 				name[mxlen] = '\000';
3242 			}
3243 
3244 			debugr2((errout,"Loading current profile '%s'\n",profile));
3245 			if ((rd_fp = new_icmFileStd_name(profile,"r")) == NULL) {
3246 				debugr2((errout,"Can't open file '%s'",profile));
3247 				free(profile);
3248 				return NULL;
3249 			}
3250 
3251 			/* Implicitly we set the X11 atom to be the profile we just got */
3252 			debugr2((errout,"Setting X11 atom to current profile '%s'\n",profile));
3253 			if (set_X11_atom(p, profile) != 0) {
3254 				debugr2((errout,"Setting X11 atom to profile '%s' failed",profile));
3255 				/* Hmm. We ignore this error */
3256 			}
3257 			return rd_fp;
3258 		}
3259 		if (ev != ucmm_no_profile) {
3260 			debugr2((errout,"Got ucmm error %d '%s'\n",ev,ucmm_error_string(ev)));
3261 			return NULL;
3262 		}
3263 		debugr2((errout,"Failed to get configured profile, so use X11 atom\n"));
3264 		/* Drop through to using the X11 root window atom */
3265 	}
3266 	{
3267 		Atom ret_type;
3268 		int ret_format;
3269 		long ret_len, ret_togo;
3270 		char aname[30];
3271 		unsigned char *atomv = NULL;	/* Profile loaded from/to atom */
3272 		unsigned char *buf;
3273 		icmAlloc *al;
3274 
3275 		atomv = NULL;
3276 
3277 		strcpy(aname, "_ICC_PROFILE");
3278 
3279 #if RANDR_MAJOR == 1 && RANDR_MINOR >= 2 && !defined(DISABLE_RANDR)
3280 		/* If Xrandr 1.2, get property on output */
3281 		if (p->icc_out_atom != 0) {
3282 
3283 			/* Get the ICC profile property */
3284 			if (XRRGetOutputProperty(p->mydisplay, p->output, p->icc_out_atom,
3285 			            0, 0x7ffffff, False, False, XA_CARDINAL,
3286    	                     &ret_type, &ret_format, &ret_len, &ret_togo, &atomv) != Success || ret_len == 0) {
3287 				debugr("Failed to read ICC_PROFILE property from Xranr output\n");
3288 			}
3289 
3290 		}
3291 #endif /* randr >= V 1.2 */
3292 
3293 		if (atomv == NULL) {
3294 			if (p->myuscreen != 0)
3295 				sprintf(aname, "_ICC_PROFILE_%d",p->myuscreen);
3296 
3297 			/* Get the ICC profile property */
3298 			if (XGetWindowProperty(p->mydisplay, RootWindow(p->mydisplay, 0), p->icc_atom,
3299 			            0, 0x7ffffff, False, XA_CARDINAL,
3300    	                     &ret_type, &ret_format, &ret_len, &ret_togo, &atomv) != Success || ret_len == 0) {
3301 				debugr2((errout,"Getting property '%s' from RootWindow\n", aname));
3302 				return NULL;
3303 			}
3304 		}
3305 
3306 		/* This is a bit of a fiddle to keep the memory allocations */
3307 		/* straight. (We can't assume that X11 and icc are using the */
3308 		/* same allocators) */
3309 		if ((al = new_icmAllocStd()) == NULL) {
3310 			debugr("new_icmAllocStd failed\n");
3311 		    return NULL;
3312 		}
3313 		if ((buf = al->malloc(al, ret_len)) == NULL) {
3314 			debugr("malloc of profile buffer failed\n");
3315 		    return NULL;
3316 		}
3317 		memmove(buf, atomv, ret_len);
3318 		XFree(atomv);
3319 
3320 		/* Memory File fp that will free the buffer when deleted: */
3321 		if ((rd_fp = new_icmFileMem_ad((void *)buf, ret_len, al)) == NULL) {
3322 			debugr("Creating memory file from X11 atom failed");
3323 			al->free(al, buf);
3324 			al->del(al);
3325 			return NULL;
3326 		}
3327 
3328 		if (name != NULL) {
3329 			strncpy(name, aname, mxlen);
3330 			name[mxlen] = '\000';
3331 		}
3332 		return rd_fp;
3333 	}
3334 #endif	  /* UNXI X11 */
3335 
3336 	return NULL;
3337 }
3338 
3339 /* ----------------------------------------------- */
3340 
3341 /* Restore the display state and free ramdacs */
restore_display(dispwin * p)3342 static void restore_display(dispwin *p) {
3343 
3344 	if (p->oor != NULL) {
3345 		p->oor->del(p->oor);
3346 		p->oor = NULL;
3347 	}
3348 	/* Restore the ramdac */
3349 	if (p->or != NULL) {
3350 		p->set_ramdac(p, p->or, 0);
3351 		p->set_ramdac(p, p->or, 0);		/* Hmm. To be sure to be sure... */
3352 		p->or->del(p->or);
3353 		p->or = NULL;
3354 		debugr("Restored original ramdac\n");
3355 	}
3356 	if (p->r != NULL) {
3357 		p->r->del(p->r);
3358 		p->r = NULL;
3359 	}
3360 
3361 #if defined(UNIX_X11)
3362 
3363 #if ScreenSaverMajorVersion >= 1 && ScreenSaverMinorVersion >= 1	/* X11R7.1 */
3364 	if (p->xsssuspend) {
3365 		XScreenSaverSuspend(p->mydisplay, False);
3366 		p->xsssuspend = 0;
3367 	}
3368 #endif
3369 	if (p->xssvalid) {
3370 		/* Restore the X11 screen saver state */
3371 		XSetScreenSaver(p->mydisplay, p->timeout, p->interval,
3372 	                p->prefer_blanking, p->allow_exposures);
3373 	}
3374 
3375 	/* Restore the xscreensaver */
3376 	if (p->xssrunning) {
3377 		system("xscreensaver -nosplash 2>/dev/null >/dev/null&");
3378 	}
3379 
3380 	if (p->gnomessrunning && p->gnomepid != -1) {
3381 		kill(p->gnomepid, SIGKILL);		/* Kill the process inhibiting the screen saver */
3382 	}
3383 
3384 	/* Restore the KDE screen saver state */
3385 	if (p->kdessrunning) {
3386 		system("dcop kdesktop KScreensaverIface enable true 2>&1 >/dev/null");
3387 	}
3388 
3389 	/* Restore DPMS */
3390 	if (p->dpmsenabled) {
3391 		DPMSEnable(p->mydisplay);
3392 	}
3393 
3394 	/* Flush any changes out */
3395 	XSync(p->mydisplay, False);
3396 #endif /* UNIX_X11 */
3397 }
3398 
3399 /* ----------------------------------------------- */
3400 /* On something killing our process, deal with Ramac & ScreenSaver cleanup */
3401 
3402 #ifdef NT
3403 void (__cdecl *dispwin_int)(int sig) = SIG_DFL;
3404 void (__cdecl *dispwin_term)(int sig) = SIG_DFL;
3405 #endif
3406 #ifdef UNIX
3407 void (*dispwin_hup)(int sig) = SIG_DFL;
3408 void (*dispwin_int)(int sig) = SIG_DFL;
3409 void (*dispwin_term)(int sig) = SIG_DFL;
3410 #endif
3411 
3412 /* List of dispwin's to clean up */
3413 static dispwin *signal_dispwin = NULL;
3414 
dispwin_sighandler(int arg)3415 static void dispwin_sighandler(int arg) {
3416 	static amutex_static(lock);
3417 	dispwin *pp, *np;
3418 
3419 	/* Make sure we don't re-enter */
3420 	if (amutex_trylock(lock)) {
3421 		return;
3422 	}
3423 
3424 	/* Restore all dispwin's Ramdacs & screen savers */
3425 	for (pp = signal_dispwin; pp != NULL; pp = np) {
3426 	    np = pp->next;
3427 		restore_display(pp);
3428 	}
3429 
3430 	/* Call through to previous handler */
3431 #ifdef UNIX
3432 	if (arg == SIGHUP && dispwin_hup != SIG_DFL && dispwin_hup != SIG_IGN)
3433 		dispwin_hup(arg);
3434 #endif
3435 	if (arg == SIGINT && dispwin_int != SIG_DFL && dispwin_int != SIG_IGN)
3436 		dispwin_int(arg);
3437 	if (arg == SIGTERM && dispwin_term != SIG_DFL && dispwin_term != SIG_IGN)
3438 		dispwin_term(arg);
3439 
3440 	amutex_unlock(lock);
3441 	exit(0);
3442 }
3443 
dispwin_install_signal_handlers(dispwin * p)3444 static void dispwin_install_signal_handlers(dispwin *p) {
3445 
3446 	if (signal_dispwin == NULL) {
3447 		/* Install the signal handler to ensure cleanup */
3448 #ifdef UNIX
3449 		dispwin_hup = signal(SIGHUP, dispwin_sighandler);
3450 #endif /* UNIX */
3451 		dispwin_int = signal(SIGINT, dispwin_sighandler);
3452 		dispwin_term = signal(SIGTERM, dispwin_sighandler);
3453 	}
3454 
3455 	/* Add this one to the list */
3456 	p->next = signal_dispwin;
3457 	signal_dispwin = p;
3458 }
3459 
dispwin_uninstall_signal_handlers(dispwin * p)3460 static void dispwin_uninstall_signal_handlers(dispwin *p) {
3461 
3462 	/* Find it and delete it from our static cleanup list */
3463 	if (signal_dispwin != NULL) {
3464 		if (signal_dispwin == p) {
3465 			signal_dispwin = p->next;
3466 			if (signal_dispwin == NULL) {
3467 #if defined(UNIX)
3468 				signal(SIGHUP, dispwin_hup);
3469 #endif /* UNIX */
3470 				signal(SIGINT, dispwin_int);
3471 				signal(SIGTERM, dispwin_term);
3472 			}
3473 		} else {
3474 			dispwin *pp;
3475 			for (pp = signal_dispwin; pp != NULL; pp = pp->next) {
3476 				if (pp->next == p) {
3477 					pp->next = p->next;
3478 					break;
3479 				}
3480 			}
3481 		}
3482 	}
3483 	p->next = NULL;
3484 }
3485 
3486 /* ----------------------------------------------- */
3487 /* Test patch window specific declarations */
3488 
3489 #ifdef UNIX_APPLE
3490 
3491 @class DWWin;
3492 @class DWView;
3493 
3494 /* Our OS X window specific information */
3495 typedef struct {
3496 	dispwin *p;
3497     DWWin *window;      		/* Our NSWindow */
3498     DWView *view;       		/* Our NSView */
3499 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1040
3500 	NSColorSpace *nscs;			/* Colorspace from profile */
3501 #endif
3502 	NSRect rect;				/* Size and position to create window */
3503 	int err;					/* Error code */
3504 } osx_cntx_t;
3505 
3506 // - - - - - - - - - - - - - - - - - - - - - - - - -
3507 
3508 
3509 @interface DWView : NSView {
3510 	osx_cntx_t *cntx;
3511 }
3512 - (void)setCntx:(osx_cntx_t *)cntx;
3513 @end
3514 
3515 @implementation DWView
3516 
3517 - (void)setCntx:(osx_cntx_t *)val {
3518 	cntx = val;
3519 }
3520 
3521 // A transparent 1x1 GIF: (43 bytes)
3522 unsigned char emptyCursor[43] = {
3523 	0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0xff, 0xff, 0xff,
3524 	0x00, 0x00, 0x00, 0x21, 0xf9, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00,
3525 	0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3b };
3526 
3527 /* Make cursor invisible over our window */
3528 /*
3529  * This doesn't work very well. The only way to work it properly
3530  * is to create a CGEventTap and do a hide/unhide cursor when
3531  * the mouse enters the window. This needs the main thread
3532  * to be dedicated to running the event loop, so would involve
3533  * some trickiness after main() in every program.
3534  */
3535 - (void)resetCursorRects {
3536 	[super resetCursorRects];
3537 //	[self addCursorRect:[self bounds] cursor:[NSCursor crosshairCursor]];
3538 	NSData *idata = [NSData dataWithBytes:(void *)emptyCursor length:43];
3539 	NSImage *img = [NSImage alloc];
3540 	img = [img initWithData:idata];
3541 	NSCursor *curs = [NSCursor alloc];
3542 	curs = [curs initWithImage:img hotSpot:NSMakePoint(0,0)];
3543 	[self addCursorRect:[self bounds] cursor:curs];
3544 }
3545 
3546 - (void)drawRect:(NSRect)rect {
3547 	osx_cntx_t *cx = cntx;
3548 	dispwin *p = cx->p;
3549 	NSRect frect;
3550 	NSBezierPath* aPath = [NSBezierPath bezierPath];
3551 
3552 	frect = NSMakeRect(p->tx, p->ty, (1.0 + p->tw), (1.0 + p->th));
3553 
3554 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1040
3555 	/* Use matching profile to (hopefully) trigger null color transform. */
3556 	/* This doesn't work on < 10.6 though. */
3557 	if (cx->nscs != NULL) {
3558 		CGFloat rgb[4];
3559 		NSInteger cnt = 4;
3560 		rgb[0] = p->r_rgb[0];
3561 		rgb[1] = p->r_rgb[1];
3562 		rgb[2] = p->r_rgb[2];
3563 		rgb[3] = 1.0;
3564 		cnt = [ cx->nscs numberOfColorComponents ] + 1;
3565 		[[NSColor colorWithColorSpace: cx->nscs components: rgb count: cnt] setFill];
3566 
3567 	/* This works for < 10.6, but not for >= 10.6 on non-primary display */
3568 	} else
3569 #endif
3570 	{
3571 		[[NSColor colorWithDeviceRed: p->r_rgb[0]
3572    	                        green: p->r_rgb[1]
3573    	                         blue: p->r_rgb[2]
3574    	                        alpha: 1.0] setFill];
3575 	}
3576 	[aPath appendBezierPathWithRect:frect];
3577 	[aPath fill];
3578 }
3579 
3580 @end
3581 
3582 /* Function called back by main thread to trigger a drawRect */
3583 
doSetNeedsDisplay(void * cntx)3584 static void doSetNeedsDisplay(void *cntx) {
3585 	osx_cntx_t *cx = (osx_cntx_t *)cntx;
3586 
3587 	[cx->view setNeedsDisplay: YES ];
3588 
3589 	cx->err = 0;
3590 }
3591 // - - - - - - - - - - - - - - - - - - - - - - - - -
3592 
3593 @interface DWWin : NSWindow {
3594 	osx_cntx_t *cntx;
3595 }
3596 - (void)setCntx:(osx_cntx_t *)cntx;
3597 @end
3598 
3599 @implementation DWWin
3600 
3601 - (void)setCntx:(osx_cntx_t *)val {
3602 	cntx = val;
3603 }
3604 
3605 - (BOOL)canBecomeMainWindow {
3606 	return NO;
3607 }
3608 
3609 /* So that we can change the cursor on a borderless window: */
3610 - (BOOL)canBecomeKeyWindow {
3611     return YES;
3612 }
3613 
3614 - (BOOL)isMoveable {
3615 	return NO;
3616 }
3617 
3618 - (BOOL)windowShouldClose:(id)sender {
3619 //	printf("Got Window windowShouldClose\n");
3620 
3621 //	[NSApp terminate: nil];
3622     return YES;
3623 }
3624 
3625 @end
3626 
3627 /* Create our window */
3628 /* We run this on the main thread using a custom message */
create_my_win(void * cntx)3629 static void create_my_win(void *cntx) {
3630 	osx_cntx_t *cx = (osx_cntx_t *)cntx;
3631 	dispwin *p = cx->p;
3632 	SInt32 MacMajVers, MacMinVers, MacBFVers;
3633 	void *cspr = NULL;			/* ColorSync profile ref. */
3634 	int i;
3635 
3636 	/* We need to locate the NSScreen that corresponds to the */
3637 	/* CGDirectDisplayID - look through them for a match. */
3638 	NSScreen *screen = nil;
3639 	NSArray *screenArray = [NSScreen screens];
3640 	for (i = 0; i < [screenArray count]; i++) {
3641 		NSDictionary *screenDescription = [[screenArray objectAtIndex:i] deviceDescription];
3642 
3643 		// CFShow(screenDescription);	// Dump it out
3644 		/* Hmm. Also has "NSDeviceBitsPerSample" entry with value 8 in dict. */
3645 
3646 	    NSNumber* screenID = [screenDescription objectForKey:@"NSScreenNumber"];
3647 	    CGDirectDisplayID ddid = (CGDirectDisplayID)[screenID unsignedIntValue];
3648 		if (ddid == p->ddid) {
3649 			screen = [screenArray objectAtIndex:i];
3650 			break;
3651 		}
3652 	}
3653 
3654 	/* Create Window */
3655 	cx->window = [[DWWin alloc] initWithContentRect: cx->rect
3656                                         styleMask: NSBorderlessWindowMask
3657                                           backing: NSBackingStoreBuffered
3658                                             defer: YES
3659 	                                       screen: screen];
3660 
3661 	[cx->window setLevel: NSScreenSaverWindowLevel];
3662 
3663 	[cx->window setBackgroundColor: [NSColor blackColor]];
3664 
3665 	[cx->window setTitle: @"Argyll Window"];
3666 
3667 	/* Use our view for the whole window to draw plot */
3668 	cx->view = [DWView new];
3669 	[cx->view setCntx:(void *)cx];
3670 	[cx->window setContentView: cx->view];
3671 
3672 	/* Moves the window to the front of the screen list within its level, */
3673 	/* and show the window (i.e. make it "key") */
3674 	/* Trigger warning on OS X 10.11 El Capitan ? */
3675 	/* (Doesn't happen using 1.6.3 which ran everything in the main thread.) */
3676 	[cx->window makeKeyAndOrderFront: nil];
3677 
3678 	/* Use a null color transform to ensure device values */
3679 	/* on non-primary display for 10.6+ */
3680 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
3681 
3682 	/* Get the ColorSync profile for this display */
3683 	if ((cspr = cur_colorsync_ref(p)) == NULL) {
3684 		debugr2((errout,"cur_colorsync_ref failed\n"));
3685 
3686 	} else {
3687 
3688 #ifdef NEVER
3689 		/* This is buggy on 10.6 (returns gray space). it does work on 10.4-10.5 & 10.7+ */
3690 		cx->nscs = [[NSColorSpace alloc] initWithColorSyncProfile: cspr];
3691 #else
3692 		CGColorSpaceRef cgref;
3693 
3694 		/* This works on 10.6+ */
3695 		if ((cgref = CGColorSpaceCreateWithPlatformColorSpace(cspr)) == NULL) {
3696 			debugr2((errout,"CGColorSpaceCreateWithPlatformColorSpace failed\n"));
3697 		} else {
3698 			/* 10.4 doesn't declare initWithCGColorSpace, but does implement it */
3699 			cx->nscs = [[NSColorSpace alloc] initWithCGColorSpace: cgref];
3700 		}
3701 		CFRelease(cgref);
3702 #endif
3703 		if (cx->nscs == NULL) {
3704 			debugr2((errout,"initWithColorSyncProfile failed\n"));
3705 		} else {
3706 			if ([ cx->nscs numberOfColorComponents ] != 3) {
3707 				[cx->nscs release];
3708 				cx->nscs = NULL;
3709 			}
3710 		}
3711 
3712 		if (cspr != NULL) {
3713 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
3714 			CFRelease((ColorSyncProfileRef)cspr);
3715 #else
3716 			CMCloseProfile((CMProfileRef)cspr);
3717 #endif
3718 		}
3719 	}
3720 #endif /* >= 10.6 */
3721 
3722 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1040
3723 	/* >= 10.6+ device colors don't work on secondary display, need null transform. */
3724 	/*  < 10.6 null transform doesn't work. */
3725 
3726 	if (Gestalt(gestaltSystemVersionMajor,  &MacMajVers) == noErr
3727 	 && Gestalt(gestaltSystemVersionMinor,  &MacMinVers) == noErr
3728 	 && Gestalt(gestaltSystemVersionBugFix, &MacBFVers) == noErr
3729 	 && MacMajVers >= 10 && MacMinVers >= 6
3730 	 && cx->nscs == NULL) {
3731 		warning("Unable to create null color transform - test colors may be wrong!");
3732 	}
3733 #endif
3734 
3735 	cx->err = 0;
3736 }
3737 
3738 #endif /* UNIX_APPLE */
3739 
3740 /* ----------------------------------------------- */
3741 
3742 /* Change the window color. */
3743 /* Return 1 on error, 2 on window being closed */
dispwin_set_color(dispwin * p,double r,double g,double b)3744 static int dispwin_set_color(
3745 dispwin *p,
3746 double r, double g, double b	/* Color values 0.0 - 1.0 */
3747 ) {
3748 	int j;
3749 	double orgb[3];		/* Previous RGB value */
3750 	double kr, kf;
3751 	int update_delay = 0;
3752 
3753 	debugr("dispwin_set_color called\n");
3754 
3755 	if (p->nowin) {
3756 		return 1;
3757 	}
3758 
3759 	orgb[0] = p->rgb[0]; p->rgb[0] = r;
3760 	orgb[1] = p->rgb[1]; p->rgb[1] = g;
3761 	orgb[2] = p->rgb[2]; p->rgb[2] = b;
3762 
3763 //printf("\n set rgb %f %f %f\n",p->rgb[0], p->rgb[1], p->rgb[2]);
3764 
3765 	for (j = 0; j < 3; j++) {
3766 		if (p->rgb[j] < 0.0)
3767 			p->rgb[j] = 0.0;
3768 		else if (p->rgb[j] > 1.0)
3769 			p->rgb[j] = 1.0;
3770 		p->r_rgb[j] = p->s_rgb[j] = p->rgb[j];
3771 		if (p->out_tvenc) {
3772 			p->r_rgb[j] = p->s_rgb[j] = ((235.0 - 16.0) * p->s_rgb[j] + 16.0)/255.0;
3773 
3774 			/* For video encoding the extra bits of precision are created by bit shifting */
3775 			/* rather than scaling, so we need to scale the fp value to account for this. */
3776 			if (p->edepth > 8)
3777 				p->r_rgb[j] = (p->s_rgb[j] * 255 * (1 << (p->edepth - 8)))
3778 				            /((1 << p->edepth) - 1.0);
3779 		}
3780 	}
3781 
3782 //if (p->out_tvenc) {
3783 //printf(" %d: 8 bit tv = s_rgb %f %f %f\n",j, p->s_rgb[0], p->s_rgb[1], p->s_rgb[2]);
3784 //printf(" %d: %d bitraster r_rgb %f %f %f\n",j, p->edepth,p->r_rgb[0], p->r_rgb[1], p->r_rgb[2]);
3785 //}
3786 
3787 	/* Use ramdac for high precision native output. */
3788 	/* The ramdac is used to hold the lsb that the frame buffer */
3789 	/* doesn't hold. */
3790 	if ((p->native & 1) == 1) {
3791 		double frange = (1 << p->fdepth) - 1.0;
3792 		double rrange = (1 << p->rdepth) - 1.0;
3793 		double nrange = p->nent - 1.0;
3794 
3795 		p->r->setlin(p->r);			/* In case something else altered this */
3796 
3797 		for (j = 0; j < 3; j++) {
3798 			int tt;
3799 			double vv;
3800 
3801 			vv = p->s_rgb[j];
3802 
3803 			/* For video encoding the extra bits of precision are created by bit shifting */
3804 			/* rather than scaling, so we need to scale the fp value to account for this. */
3805 			if (p->out_tvenc && p->edepth > 8)
3806 				vv = (vv * 255 * (1 << (p->edepth - 8)))/((1 << p->edepth) - 1.0);
3807 
3808 			/* Determine the ramdac index that the quantized frame buffer */
3809 			/* value will make use of */
3810 #ifdef NT
3811 			/* Assume all depths match, or linear mapping between them */
3812 			tt = (int)(vv * frange + 0.5);			/* Frame buffer value */
3813 			p->r_rgb[j] = (double)tt/frange;		/* Double frame buffer value */
3814 			tt = (int)(tt/frange * rrange + 0.5);	/* expected RAMDAC index */
3815 			tt = (int)(tt/rrange * nrange + 0.5);	/* actual RAMDAC index */
3816 #endif
3817 #ifdef UNIX_APPLE
3818 			/* We assume linear mapping with perfect rounding between rdepth and ndepth */
3819 			tt = (int)(vv * frange + 0.5);			/* Frame buffer value */
3820 			p->r_rgb[j] = (double)tt/frange;		/* Double frame buffer value */
3821 			tt = (int)(tt/frange * rrange + 0.5);	/* expected RAMDAC index */
3822 			tt = (int)(tt/rrange * nrange + 0.5);	/* actual RAMDAC index */
3823 #endif
3824 #if defined(UNIX_X11)
3825 			/* We assume linear mapping with perfect rounding between rdepth and ndepth */
3826 			tt = (int)(vv * frange + 0.5);			/* Frame buffer value */
3827 			p->r_rgb[j] = (double)tt/frange;		/* Double frame buffer value */
3828 			tt = p->rmap[j][tt];					/* expected RAMDAC index */
3829 			tt = (int)(tt/rrange * nrange + 0.5);	/* actual RAMDAC index */
3830 #endif
3831 
3832 #ifdef NEVER	// Just set entry we think will get hit
3833 			p->r->v[j][tt] = vv;
3834 #else
3835 
3836 			/* Set the three entries around target and create ramp either side,
3837 			   to allow for some video cards not having a precise
3838 			   definition of what value translates to what frame buffer value. */
3839 			{
3840 				int i;
3841 				double maxv = 1.0;
3842 
3843 				if (p->out_tvenc && p->edepth > 8)
3844 					maxv = (maxv * 255 * (1 << (p->edepth - 8)))/((1 << p->edepth) - 1.0);
3845 
3846 				if ((tt-1) == 0) {
3847 					p->r->v[j][tt-1] = vv;
3848 				} else {
3849 					for (i = 0; i <= (tt-1); i++)
3850 						p->r->v[j][i] = vv * i/(tt-1);
3851 				}
3852 
3853 				p->r->v[j][tt] = vv;
3854 
3855 				if ((tt+1) == (p->r->nent-1)) {
3856 					p->r->v[j][tt+1] = vv;
3857 				} else {
3858 					for (i = tt+1; i < p->r->nent; i++)
3859 						p->r->v[j][i] = vv + (maxv - vv) * (i - (tt+1))/((p->r->nent-1) - (tt+1));
3860 				}
3861 
3862 #ifdef NEVER
3863 				for (i = 0; i < p->r->nent; i++)
3864 					printf("~1 %d, %d -> %f\n",j,i,p->r->v[j][i]);
3865 #endif
3866 			}
3867 #endif
3868 
3869 //printf(" cell[%d] = r_rgb %f, cell val %f\n",tt, p->r_rgb[j], vv);
3870 		}
3871 		if (p->set_ramdac(p, p->r, 0)) {
3872 			debugr("set_ramdac() failed\n");
3873 			return 1;
3874 		}
3875 	}
3876 
3877 	/* - - - - - - - - - - - - - - */
3878 #ifdef NT
3879 	{
3880 		MSG msg;
3881 		INPUT fip;
3882 
3883 		/* Stop the system going to sleep */
3884 
3885 		/* This used to work OK in reseting the screen saver on */
3886 		/* all versions of MSWin. */
3887 		SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
3888 
3889 #ifdef NEVER
3890 		/* (Some people use ES_CONTINUOUS | ES_AWAYMODE_REQUIRED as well ?? */
3891 		/*  See also setting SPI_SETSCREENSAVEACTIVE? ) */
3892 
3893 		/* Another approach is a fake mouse non-movementr. */
3894 		SystemParametersInfo(SPI_SETBLOCKSENDINPUTRESETS, FALSE, NULL, 0);
3895 		fip.type = INPUT_MOUSE;
3896 		fip.mi.dx = 0;
3897 		fip.mi.dy = 0;
3898 		fip.mi.dwFlags = MOUSEEVENTF_MOVE;
3899 		fip.mi.time = 0;
3900 		fip.mi.dwExtraInfo = 0;
3901 		SendInput(1, &fip, sizeof(INPUT));
3902 #endif
3903 
3904 		p->colupd++;
3905 
3906 		debugr2((errout,"dispwin_set_color about to paint color %f %f %f\n",
3907 		         p->r_rgb[0], p->r_rgb[1], p->r_rgb[2]));
3908 
3909 		/* Trigger a WM_PAINT */
3910 		if (!InvalidateRect(p->hwnd, NULL, FALSE)) {
3911 			debugr2((errout,"InvalidateRect failed, lasterr = %d\n",GetLastError()));
3912 			return 1;
3913 		}
3914 		UpdateWindow(p->hwnd);
3915 
3916 //printf("~1 waiting for paint\n");
3917 		/* Wait for WM_PAINT to be executed */
3918 		while (p->colupd != p->colupde && p->cberror == 0) {
3919 			msec_sleep(10);
3920 		}
3921 
3922 		debugr2((errout,"dispwin_set_color paint done\n"));
3923 	}
3924 #endif /* NT */
3925 
3926 	/* - - - - - - - - - - - - - - */
3927 
3928 #ifdef UNIX_APPLE
3929 
3930 	if (p->winclose) {
3931 		return 2;
3932 	}
3933 
3934 	/* If we may be in a different thread to the main thread or */
3935 	/* the application thread, establish our own pool. */
3936 	NSAutoreleasePool *tpool = nil;
3937 	if (pthread_self() != ui_thid
3938 	 && pthread_self() != ui_main_thid)
3939 		tpool = [NSAutoreleasePool new];
3940 
3941 	/* Stop the system going to sleep */
3942     UpdateSystemActivity(OverallAct);
3943 
3944 	/* Make sure our window is brought to the front at least once, */
3945 	/* but not every time, in case the user wants to kill the application. */
3946 	if (p->btf == 0){
3947 		OSStatus stat;
3948 		ProcessSerialNumber cpsn;
3949 		if ((stat = GetCurrentProcess(&cpsn)) != noErr) {
3950 			debugr2((errout,"GetCurrentProcess returned error %d\n",stat));
3951 		} else {
3952 			// [window makeGetAndOrderFront:] 	??
3953 			if ((stat = SetFrontProcess(&cpsn)) != noErr) {
3954 				debugr2((errout,"SetFrontProcess returned error %d\n",stat));
3955 			}
3956 		}
3957 		p->btf = 1;
3958 	}
3959 
3960 	/* Prepare to wait for events */
3961 	ui_aboutToWait();
3962 
3963 	debugr2((errout,"dispwin_set_color about to paint color %f %f %f\n",
3964 	         p->r_rgb[0], p->r_rgb[1], p->r_rgb[2]));
3965 
3966 //	[((osx_cntx_t *)(p->osx_cntx))->view setNeedsDisplay: YES ];
3967 
3968 	/* Run the window creation in the main thread and wait for it */
3969 	ui_runInMainThreadAndWait((void *)p->osx_cntx, doSetNeedsDisplay);
3970 
3971 	/* Wait for any events generated by paint to complete */
3972 	ui_waitForEvents();
3973 
3974 	debugr2((errout,"dispwin_set_color paint done\n"));
3975 
3976 	if (tpool != nil)
3977 		[tpool release];
3978 
3979 #endif /* UNIX_APPLE */
3980 
3981 	/* - - - - - - - - - - - - - - */
3982 
3983 #if defined(UNIX_X11)
3984 	{
3985 		unsigned int vali[3];
3986 		unsigned long fbval;
3987 
3988 		/* Indicate that we've got activity to the X11 Screensaver */
3989 		XResetScreenSaver(p->mydisplay);
3990 
3991 		/* Quantize to frame buffer component depth */
3992 		for (j = 0; j < 3; j++)
3993 			vali[j] = (int)(((1 << p->fdepth)-1.0) * p->r_rgb[j] + 0.5);
3994 
3995 		/* Compose frame buffer pixel value */
3996 		fbval = (vali[0] << p->shift[0])
3997 		      | (vali[1] << p->shift[1])
3998 		      | (vali[2] << p->shift[2]);
3999 		XSetForeground(p->mydisplay, p->mygc, fbval);
4000 
4001 		debugr2((errout,"dispwin_set_color about to paint color %f %f %f\n",
4002 		         p->r_rgb[0], p->r_rgb[1], p->r_rgb[2]));
4003 
4004 		XFillRectangle(p->mydisplay, p->mywindow, p->mygc,
4005 		               p->tx, p->ty, p->tw, p->th);
4006 
4007 		XSync(p->mydisplay, False);		/* Make sure it happens */
4008 
4009 		debugr2((errout,"dispwin_set_color paint done\n"));
4010 	}
4011 #endif	/* UNXI X11 */
4012 
4013 	if (p->callout != NULL) {
4014 		int rv;
4015 		char *cmd;
4016 
4017 		if ((cmd = malloc(strlen(p->callout) + 200)) == NULL)
4018 			error("Malloc of command string failed");
4019 
4020 		sprintf(cmd, "%s %d %d %d %f %f %f",p->callout,
4021 			        (int)(r * 255.0 + 0.5),(int)(g * 255.0 + 0.5),(int)(b * 255.0 + 0.5), r, g, b);
4022 		if ((rv = system(cmd)) != 0)
4023 			warning("System command '%s' failed with %d",cmd,rv);
4024 		free(cmd);
4025 	}
4026 
4027 	update_delay = dispwin_compute_delay(p, orgb);
4028 
4029 	/* Allow some time for the display & instrument to update */
4030 	/* before  a measurement can take place. This allows for CRT refresh, */
4031 	/* or LCD processing/update time, + display settling time (quite long for */
4032 	/* smaller LCD changes), and any instrument reaction time. */
4033 	debugr2((errout, "dispwin_set_color delaying %d msec\n",update_delay));
4034 	msec_sleep(update_delay);
4035 
4036 	if (p->cberror) {	/* Callback routine failed */
4037 		return 1;
4038 	}
4039 
4040 	return 0;
4041 }
4042 
4043 
4044 /* ----------------------------------------------- */
4045 
4046 /* Set a patch delay and instrument reaction time values. */
4047 /* The overall delay between patch change and triggering */
4048 /* the instrument is (patch_delay + display_settle - inst_reaction) */
4049 /* and will never be less than the min_update_delay value. */
dispwin_set_update_delay(dispwin * p,int patch_delay,int inst_reaction)4050 void dispwin_set_update_delay(dispwin *p, int patch_delay, int inst_reaction) {
4051 	p->patch_delay = patch_delay;
4052 	p->inst_reaction = inst_reaction;
4053 }
4054 
4055 /* Set/unset the full screen black flag */
4056 /* Return nz on error */
dispwin_set_fc(dispwin * p,int fullscreen)4057 static int dispwin_set_fc(dispwin *p, int fullscreen) {
4058 	return 1;		/* Need to re-create window */
4059 }
4060 
4061 /* Set the display settling time constants. Use -ve value to leave current value */
4062 /* unchanged. (These values are used as part of the update delay calculations - see above). */
dispwin_set_settling_delay(dispwin * p,double rise_time,double fall_time,double de_aim)4063 void dispwin_set_settling_delay(dispwin *p, double rise_time, double fall_time, double de_aim) {
4064 	if (rise_time >= 0.0)
4065 		p->rise_time = rise_time;
4066 	if (fall_time >= 0.0)
4067 		p->fall_time = fall_time;
4068 	if (de_aim >= 0.0)
4069 		p->de_aim = de_aim;
4070 }
4071 
4072 /* Enable or disable the update delay. This is used to disable the update delay */
4073 /* when measuring the patch_delay and inst_reaction */
dispwin_enable_update_delay(dispwin * p,int enable)4074 void dispwin_enable_update_delay(dispwin *p, int enable) {
4075 	p->do_update_del = enable;
4076 }
4077 
4078 /* Return the update delay we should use (msec) */
dispwin_compute_delay(dispwin * p,double * orgb)4079 int dispwin_compute_delay(dispwin *p, double *orgb) {
4080 	int update_delay = 0, disp_settle = 0;
4081 
4082 	if (p->do_update_del == 0) {
4083 		if (p->ddebug) fprintf(stderr,"dispwin: update delay disabled\n");
4084 		return 0;
4085 	}
4086 
4087 	if (p->do_resp_time_del) {
4088 		double xdelay;
4089 
4090 		/* Compute am expected response time for the change in level, */
4091 		/* to achieve 0.1 delta E */
4092 		xdelay = disp_settle_time(orgb, p->rgb, p->rise_time * p->settle_mult,
4093 		                                        p->fall_time * p->settle_mult, p->de_aim);
4094 		disp_settle = (int)(xdelay * 1000.0 + 0.5);
4095 	}
4096 
4097 	update_delay = p->patch_delay + disp_settle - p->inst_reaction;
4098 
4099 	/* Enforce minimum delay */
4100 	if (update_delay < p->min_update_delay)
4101 		update_delay = p->min_update_delay;
4102 
4103 	if (p->ddebug) fprintf(stderr,"dispwin: update delay %d msec = patch_delay %d + disp_settle %d  - inst_reaction %d\n", update_delay, p->patch_delay, disp_settle, p->inst_reaction);
4104 
4105 	return update_delay;
4106 }
4107 
4108 
4109 /* ----------------------------------------------- */
4110 /* Set the shell set color callout */
dispwin_set_callout(dispwin * p,char * callout)4111 void dispwin_set_callout(
4112 dispwin *p,
4113 char *callout
4114 ) {
4115 	debugr2((errout,"dispwin_set_callout called with '%s'\n",callout));
4116 
4117 	p->callout = strdup(callout);
4118 }
4119 
4120 /* ----------------------------------------------- */
4121 /* Destroy ourselves */
dispwin_del(dispwin * p)4122 void dispwin_del(
4123 dispwin *p
4124 ) {
4125 
4126 	debugr("dispwin_del called\n");
4127 
4128 	if (p == NULL)
4129 		return;
4130 
4131 	/* Restore original RAMDAC if we were in native mode, */
4132 	/* and restore screensaver */
4133 	restore_display(p);
4134 	dispwin_uninstall_signal_handlers(p);
4135 
4136 	/* -------------------------------------------------- */
4137 #ifdef NT
4138 
4139 	if (p->hwnd != NULL) {
4140 
4141 		p->quit = 1;
4142 		if (PostMessage(p->hwnd, WM_CLOSE, (WPARAM)NULL, (LPARAM)NULL) != 0) {
4143 			while(p->hwnd != NULL)
4144 				msec_sleep(20);
4145 		} else {
4146 			debugr2((errout, "PostMessage(WM_GETICON failed, lasterr = %d\n",GetLastError()));
4147 		}
4148 //		DestroyCursor(p->curs);
4149 
4150 		if (p->mth != NULL) {			/* Message thread */
4151 			p->mth->del(p->mth);
4152 		}
4153 		p->hwnd = NULL;
4154 	}
4155 
4156 	if (p->hdc != NULL)
4157 		DeleteDC(p->hdc);
4158 
4159 #endif /* NT */
4160 	/* -------------------------------------------------- */
4161 
4162 	/* -------------------------------------------------- */
4163 #ifdef UNIX_APPLE
4164 	if (p->nowin == 0) {	/* We have a window up */
4165 		restore_display(p);
4166 		if (p->osx_cntx != NULL) {	/* And we've allocated a context */
4167 			osx_cntx_t *cx = (osx_cntx_t *)p->osx_cntx;
4168 
4169 			p->winclose = 1;
4170 
4171 			[cx->window release];
4172 
4173 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1040
4174 			if (cx->nscs != NULL)
4175 				[cx->nscs release];
4176 #endif
4177 
4178 			free(p->osx_cntx);
4179 			p->osx_cntx = NULL;
4180 		}
4181 	}
4182 
4183 // ~~
4184 //	CGDisplayShowCursor(p->ddid);
4185 
4186 #endif /* UNIX_APPLE */
4187 	/* -------------------------------------------------- */
4188 
4189 	/* -------------------------------------------------- */
4190 #if defined(UNIX_X11)
4191 	debugr("About to close display\n");
4192 
4193 	if (p->mydisplay != NULL) {
4194 		if (p->nowin == 0) {	/* We have a window up */
4195 
4196 			if (p->mygc != 0)
4197 				XFreeGC(p->mydisplay, p->mygc);
4198 			if (p->mywindow != 0)
4199 				XDestroyWindow(p->mydisplay, p->mywindow);
4200 		}
4201 		XCloseDisplay(p->mydisplay);
4202 		p->mydisplay = NULL;
4203 	}
4204 	{
4205 		int j;
4206 		for (j = 0; j < 3; j++) {
4207 			if (p->rmap[j] != NULL) {
4208 				free(p->rmap[j]);
4209 				p->rmap[j] = NULL;
4210 			}
4211 		}
4212 	}
4213 	debugr("finished\n");
4214 
4215 	if (p->edid != NULL)
4216 		free(p->edid);
4217 
4218 #endif	/* UNXI X11 */
4219 	/* -------------------------------------------------- */
4220 
4221 	if (p->name != NULL) {
4222 		free(p->name);
4223 		p->name = NULL;
4224 	}
4225 	if (p->description != NULL) {
4226 		free(p->description);
4227 		p->description = NULL;
4228 	}
4229 	if (p->callout != NULL) {
4230 		free(p->callout);
4231 		p->callout = NULL;
4232 	}
4233 
4234 	free(p);
4235 }
4236 
4237 /* ----------------------------------------------- */
4238 /* Event handler callbacks */
4239 
4240 #ifdef NT
4241 
4242 /* Undocumented flag. Set when minimized/maximized etc. */
4243 #ifndef SWP_STATECHANGED
4244 #define SWP_STATECHANGED 0x8000
4245 #endif
4246 
4247 /* MingW doesn't seem to have this, even though it's been there sine Win2k ... */
4248 #ifndef ICM_DONE_OUTSIDEDC
4249 # define ICM_DONE_OUTSIDEDC 4
4250 #endif
4251 
MainWndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)4252 static LRESULT CALLBACK MainWndProc(
4253 	HWND hwnd,
4254 	UINT message,
4255 	WPARAM wParam,
4256 	LPARAM lParam
4257 ) {
4258 	debugrr2l(4, (stderr, "Handling message type 0x%x\n",message));
4259 
4260 	if (message >= WM_APP) {
4261 		debugrr2l(4, (stderr, "Message ignored\n"));
4262 		return 0;
4263 	}
4264 
4265 	switch(message) {
4266 		case WM_PAINT: {
4267 			dispwin *p = NULL;
4268 			int vali[3];
4269 			HDC hdc;
4270 			PAINTSTRUCT ps;
4271 			RECT rect;
4272 			HBRUSH hbr;
4273 			int j;
4274 
4275 #ifdef _WIN64
4276 			if ((p = (dispwin *)GetWindowLongPtr(hwnd, GWLP_USERDATA)) == NULL)
4277 #else
4278 			if ((p = (dispwin *)GetWindowLong(hwnd, GWL_USERDATA)) == NULL)
4279 #endif
4280 			{
4281 				debugrr2l(4,(stderr, "GetWindowLongPtr failed, lasterr = %d\n",GetLastError()));
4282 				hdc = BeginPaint(hwnd, &ps);
4283 				/* Don't know where to paint */
4284 				EndPaint(hwnd, &ps);
4285 				return 0;
4286 			}
4287 
4288 			/* Check that there is something to paint */
4289 			if (GetUpdateRect(hwnd, NULL, FALSE) == 0) {
4290 				debugrr2l(4, (stderr,"The update region was empty\n"));
4291 			}
4292 
4293 			/* Frame buffer Quantize 8 bit color */
4294 			for (j = 0; j < 3; j++) {
4295 				vali[j] = (int)(255.0 * p->r_rgb[j] + 0.5);
4296 			}
4297 
4298 			if ((hdc = BeginPaint(hwnd, &ps)) == NULL) {
4299 				debugrr2l(4, (stderr,"BeginPaint failed\n"));
4300 				EndPaint(hwnd, &ps);
4301 				p->cberror = 2;
4302 				return 0;
4303 			}
4304 
4305 			if (SaveDC(hdc) == 0) {
4306 				debugrr2l(4, (stderr,"SaveDC failed\n"));
4307 				EndPaint(hwnd, &ps);
4308 				p->cberror = 3;
4309 				return 0;
4310 			}
4311 
4312 			/* Try and turn ICM & WCS off */
4313 			if (!SetICMMode(hdc, ICM_DONE_OUTSIDEDC)) {
4314 				OSVERSIONINFO osver;
4315 				osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
4316 				osver.dwMajorVersion = 5;
4317 				GetVersionEx(&osver);
4318 				/* This seems to fail with "invalid handle" under NT4 */
4319 				/* so only report an error if Win2K or more */
4320 				if (osver.dwMajorVersion >= 5)
4321 					printf("SetICMMode failed, lasterr = %d\n",GetLastError());
4322 			}
4323 
4324 			if ((hbr = CreateSolidBrush(RGB(vali[0],vali[1],vali[2]))) == NULL) {
4325 				debugrr2l(4, (stderr,"CreateSolidBrush failed\n"));
4326 				RestoreDC(hdc,-1);
4327 				EndPaint(hwnd, &ps);
4328 				p->cberror = 4;
4329 				return 0;
4330 			}
4331 			if (SelectObject(hdc, hbr) == NULL
4332 			 || SetRect(&rect, p->tx, p->ty, p->tx + p->tw, p->ty + p->th) == 0
4333 			 || FillRect(hdc, &rect, hbr) == 0) {
4334 				debugrr2l(4, (stderr,"SelectObject/SetRect/FillRect failed\n"));
4335 				p->cberror = 5;
4336 			}
4337 
4338 			DeleteObject(hbr);
4339 			RestoreDC(hdc,-1);
4340 			EndPaint(hwnd, &ps);
4341 			GdiFlush();
4342 
4343 			p->colupde = p->colupd;		/* We're updated to this color */
4344 
4345 			return 0;
4346 		}
4347 
4348 		/* Prevent any changes in position of the window */
4349 		case WM_WINDOWPOSCHANGING: {
4350 			WINDOWPOS *wpos = (WINDOWPOS *)lParam;
4351 			debugrr2l(4,(stderr, "It's a windowposchange, flags = 0x%x, x,y %d %d, w,h %d %d\n",wpos->flags, wpos->x, wpos->y, wpos->cx, wpos->cy));
4352 			wpos->flags &= ~SWP_FRAMECHANGED & ~SWP_NOREDRAW;
4353 			wpos->flags |= SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER
4354 			            |  SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_SHOWWINDOW;
4355 			debugrr2l(4,(stderr, "flags now = 0x%x\n",wpos->flags));
4356 			return DefWindowProc(hwnd, message, wParam, lParam);
4357 		}
4358 		case WM_WINDOWPOSCHANGED: {
4359 			WINDOWPOS *wpos = (WINDOWPOS *)lParam;
4360 			debugrr2l(4,(stderr, "It's a windowposchanged, flags = 0x%x, x,y %d %d, w,h %d %d\n",wpos->flags, wpos->x, wpos->y, wpos->cx, wpos->cy));
4361 			debugrr2l(4,(stderr, "It's a windowposchanged, flags = 0x%x\n",wpos->flags));
4362 			return 0;
4363 		}
4364 		case WM_CLOSE:
4365 			DestroyWindow(hwnd);
4366 			return 0;
4367 
4368 		case WM_DESTROY:
4369 		    PostQuitMessage(0);
4370 			return 0;
4371 	}
4372 
4373 	debugrr2l(4,(stderr, "Handle message using DefWindowProc()\n"));
4374 
4375 	return DefWindowProc(hwnd, message, wParam, lParam);
4376 }
4377 
4378 #endif /* NT */
4379 
4380 #ifdef UNIX_APPLE
4381 
4382 #endif /* UNIX_APPLE */
4383 
4384 #if defined(UNIX_X11)
4385 	/* None */
4386 #endif	/* UNXI X11 */
4387 
4388 /* ----------------------------------------------- */
4389 #ifdef NT
4390 
4391 /* Thread to create the window if it doesn'r exist, */
4392 /* and handle message processing, so that there is no delay */
4393 /* when the main thread is doing other things. */
win_message_thread(void * pp)4394 int win_message_thread(void *pp) {
4395 	dispwin *p = (dispwin *)pp;
4396 	MSG msg;
4397 	WNDCLASS wc;
4398 
4399 	debugrr2l(4, (stderr, "win_message_thread started\n"));
4400 
4401 	/* Fill in window class structure with parameters that describe the */
4402 	/* main window. */
4403 	wc.style         = 0 ;			/* Class style(s). */
4404 	wc.lpfnWndProc   = MainWndProc;	/* Function to retrieve messages for windows of this class. */
4405 	wc.cbClsExtra    = 0;			/* No per-class extra data. */
4406 	wc.cbWndExtra    = 0;			/* No per-window extra data. */
4407 	wc.hInstance     = NULL;		/* Application that owns the class. */
4408 	wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
4409 	wc.hCursor       = LoadCursor(NULL, IDC_CROSS);
4410 	wc.hbrBackground = GetStockObject(BLACK_BRUSH);		/* So full screen black works */
4411 	wc.lpszMenuName  = NULL;
4412 	wc.lpszClassName = p->AppName;
4413 
4414 	/* Make the cursor disapear over our window */
4415 	/* (How does it know our window ??) */
4416 	ShowCursor(FALSE);
4417 
4418 	if ((p->arv = RegisterClass(&wc)) == 0) {
4419 		debugr2((errout, "RegisterClass failed, lasterr = %d\n",GetLastError()));
4420 		p->inited = 2;
4421 		return 0;
4422 	}
4423 
4424 #ifndef WS_EX_NOACTIVATE
4425 # define WS_EX_NOACTIVATE 0x08000000L
4426 #endif
4427 	p->hwnd = CreateWindowEx(
4428 		WS_EX_NOACTIVATE | WS_EX_TOPMOST,
4429 //				0,
4430 		p->AppName,
4431 		"Argyll Display Calibration Window",
4432 		WS_VISIBLE | WS_DISABLED | WS_POPUP,
4433 //		WS_EX_TOPMOST
4434 //		WS_EX_PALETTEWINDOW
4435 //		WS_OVERLAPPEDWINDOW | WS_VISIBLE
4436 //		WS_POPUPWINDOW | WS_VISIBLE,
4437 		p->xo, p->yo,			/* Location */
4438 		p->wi, p->he,			/* Size */
4439 		NULL,					/* Handle to parent or owner */
4440 		NULL,					/* Handle to menu or child window */
4441 		NULL, 					/* hInstance Handle to appication instance */
4442 		NULL);					/* pointer to window creation data */
4443 
4444 	if (!p->hwnd) {
4445 		debugr2((errout, "CreateWindow failed, lasterr = %d\n",GetLastError()));
4446 		p->inited = 2;
4447 		return 0;
4448 	}
4449 
4450 	/* Associate the dispwin object with the window, */
4451 	/* so that the event callback can access it */
4452 #ifdef _WIN64
4453 	SetWindowLongPtr(p->hwnd, GWLP_USERDATA, (LONG_PTR)p);
4454 #else
4455 	SetWindowLong(p->hwnd, GWL_USERDATA, (LONG)p);
4456 #endif
4457 
4458 	/*
4459 		Should we call BOOL SystemParametersInfo()
4460 		to disable high contrast, powertimout and screensaver timeout ?
4461 
4462 	 */
4463 
4464 	debugrr2l(4, (stderr, "win_message_thread initialized - about to process messages\n"));
4465 	p->inited = 1;
4466 
4467 	for (;;) {
4468 		if (GetMessage(&msg, NULL, 0, 0)) {
4469 			TranslateMessage(&msg);
4470 			DispatchMessage(&msg);
4471 
4472 			if (p->quit != 0) {
4473 				/* Process any pending messages */
4474 				while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
4475 					TranslateMessage(&msg);
4476 					DispatchMessage(&msg);
4477 				}
4478 				break;
4479 			}
4480 		}
4481 	}
4482 
4483 	if (UnregisterClass(p->AppName, NULL) == 0) {
4484 		warning("UnregisterClass failed, lasterr = %d",GetLastError());
4485 	}
4486 
4487 	p->hwnd = NULL;		/* Signal it's been deleted */
4488 
4489 	return 0;
4490 }
4491 
4492 #endif /* NT */
4493 
4494 
4495 /* Set the defauly update delay values */
dispwin_set_default_delays(dispwin * p)4496 void dispwin_set_default_delays(dispwin *p) {
4497 	char *cp;
4498 
4499 	p->min_update_delay = 20;
4500 
4501 	if ((cp = getenv("ARGYLL_MIN_DISPLAY_UPDATE_DELAY_MS")) != NULL) {
4502 		p->min_update_delay = atoi(cp);
4503 		if (p->min_update_delay < 20)
4504 			p->min_update_delay = 20;
4505 		if (p->min_update_delay > 60000)
4506 			p->min_update_delay = 60000;
4507 		debugr2((errout, "new_dispwin: Minimum display update delay set to %d msec\n",p->min_update_delay));
4508 	}
4509 
4510 	p->settle_mult = 1.0;
4511 
4512 	if ((cp = getenv("ARGYLL_DISPLAY_SETTLE_TIME_MULT")) != NULL) {
4513 		p->settle_mult = atof(cp);
4514 		if (p->settle_mult < 1e-6)
4515 			p->settle_mult = 1e-6;
4516 		if (p->settle_mult > 1e4)
4517 			p->settle_mult = 1e4;
4518 		debugr2((errout, "new_dispwin: Settling time multiplier %f\n",p->settle_mult));
4519 	}
4520 
4521 	p->patch_delay = PATCH_UPDATE_DELAY;			/* Default patch delay */
4522 	p->inst_reaction = INSTRUMENT_REACTIONTIME;		/* Default inst delay */
4523 	p->rise_time = DISPLAY_RISE_TIME;
4524 	p->fall_time = DISPLAY_FALL_TIME;
4525 	p->de_aim = DISPLAY_SETTLE_AIM;
4526 
4527 	p->do_resp_time_del = 1;		/* Default this to on */
4528 	p->do_update_del = 1;			/* Default this to on */
4529 }
4530 
4531 
4532 /* Create a RAMDAC access and display test window, default grey */
new_dispwin(disppath * disp,double width,double height,double hoff,double voff,int nowin,int native,int * noramdac,int * nocm,int out_tvenc,int fullscreen,int override,int ddebug)4533 dispwin *new_dispwin(
4534 disppath *disp,					/* Display to calibrate. */
4535 double width, double height,	/* Width and height in mm */
4536 double hoff, double voff,		/* Offset from center in fraction of screen, range -1.0 .. 1.0 */
4537 int nowin,						/* NZ if no window should be created - RAMDAC access only */
4538 int native,						/* X0 = use current per channel calibration curve */
4539 								/* X1 = set native linear output and use ramdac high precn. */
4540 								/* 0X = use current color management cLut (MadVR) */
4541 								/* 1X = disable color management cLUT (MadVR) */
4542 int *noramdac,					/* Return nz if no ramdac access. native is set to X0 */
4543 int *nocm,						/* Return nz if no CM cLUT access. native is set to 0X */
4544 int out_tvenc,					/* 1 = use RGB Video Level encoding */
4545 int fullscreen,					/* NZ if whole screen should be filled with black */
4546 int override,					/* NZ if override_redirect is to be used on X11 */
4547 int ddebug						/* >0 to print debug statements to stderr */
4548 ) {
4549 	dispwin *p = NULL;
4550 
4551 #ifndef DEBUG
4552 	if (ddebug)
4553 #endif
4554 	fprintf(errout, "new_dispwin called\n");
4555 
4556 #if defined(UNIX_X11) && defined(USE_UCMM)
4557 	dispwin_checkfor_colord();		/* Make colord functions available */
4558 	if (ddebug) {
4559 		if (cd_found)
4560 			fprintf(stderr,"using colord for profile installation\n");
4561 		else
4562 			fprintf(stderr,"using ucmm for profile installation\n");
4563 	}
4564 #endif
4565 
4566 	if ((p = (dispwin *)calloc(sizeof(dispwin), 1)) == NULL) {
4567 		if (ddebug) fprintf(stderr,"new_dispwin failed because malloc failed\n");
4568 		return NULL;
4569 	}
4570 
4571 	/* !!!! Make changes in webwin.c as well !!!! */
4572 	p->width = width;
4573 	p->height = height;
4574 	p->nowin = nowin;
4575 	p->native = native;
4576 	p->out_tvenc = out_tvenc;
4577 	p->fullscreen = fullscreen;
4578 	p->ddebug = ddebug;
4579 	p->get_ramdac          = dispwin_get_ramdac;
4580 	p->set_ramdac          = dispwin_set_ramdac;
4581 	p->install_profile     = dispwin_install_profile;
4582 	p->uninstall_profile   = dispwin_uninstall_profile;
4583 	p->get_profile         = dispwin_get_profile;
4584 	p->set_color           = dispwin_set_color;
4585 	p->set_fc              = dispwin_set_fc;
4586 	p->set_update_delay    = dispwin_set_update_delay;
4587 	p->set_settling_delay  = dispwin_set_settling_delay;
4588 	p->enable_update_delay = dispwin_enable_update_delay;
4589 	p->set_callout         = dispwin_set_callout;
4590 	p->del                 = dispwin_del;
4591 
4592 	p->rgb[0] = p->rgb[1] = p->rgb[2] = 0.5;	/* Set Grey as the initial test color */
4593 
4594 	dispwin_set_default_delays(p);
4595 
4596 	/* Basic object is initialised, so create a window */
4597 
4598 	ui_UsingGUI();
4599 
4600 	/* -------------------------------------------------- */
4601 #ifdef NT
4602 	{
4603 		WNDCLASS wc;
4604 		int disp_hsz, disp_vsz;		/* Display horizontal/vertical size in mm */
4605 		int disp_hrz, disp_vrz;		/* Display horizontal/vertical resolution in pixels */
4606 		int wi, he;					/* Width and height of window in pixels */
4607 		int xo, yo;					/* Window location in pixels */
4608 		int bpp;
4609 
4610 		p->AppName = "Argyll Test Window";
4611 
4612 		debugr2((errout, "new_dispwin: About to open display '%s'\n",disp->name));
4613 
4614 		/* Get device context to main display */
4615 		/* (This is the recommended way of doing this, and works on Vista) */
4616 		if ((p->hdc = CreateDC(disp->name, NULL, NULL, NULL)) == NULL) {
4617 			debugr2((errout, "new_dispwin: CreateDC failed, lasterr = %d\n",GetLastError()));
4618 			dispwin_del(p);
4619 			return NULL;
4620 		}
4621 
4622 		if ((p->name = strdup(disp->name)) == NULL) {
4623 			debugr2((errout, "new_dispwin: Malloc failed\n"));
4624 			dispwin_del(p);
4625 			return NULL;
4626 		}
4627 		if ((p->description = strdup(disp->description)) == NULL) {
4628 			debugr2((errout, "new_dispwin: Malloc failed\n"));
4629 			dispwin_del(p);
4630 			return NULL;
4631 		}
4632 		strcpy(p->monid, disp->monid);
4633 
4634 		disp_hsz = GetDeviceCaps(p->hdc, HORZSIZE);	/* mm */
4635 		disp_vsz = GetDeviceCaps(p->hdc, VERTSIZE);
4636 		disp_hrz = GetDeviceCaps(p->hdc, HORZRES);	/* pixels */
4637 		disp_vrz = GetDeviceCaps(p->hdc, VERTRES);
4638 
4639 		wi = (int)(width * (double)disp_hrz/(double)disp_hsz + 0.5);
4640 		if (wi > disp_hrz)
4641 			wi = disp_hrz;
4642 		he = (int)(height * (double)disp_vrz/(double)disp_vsz + 0.5);
4643 		if (he > disp_vrz)
4644 			he = disp_vrz;
4645 
4646 		if (p->fullscreen) {	/* Window fills the screen, test area is within it */
4647 			p->tx = (int)((hoff * 0.5 + 0.5) * (disp->sw - wi) + 0.5);
4648 			p->ty = (int)((voff * 0.5 + 0.5) * (disp->sh - he) + 0.5);
4649 			p->tw = wi;
4650 			p->th = he;
4651 			wi = disp->sw;
4652 			he = disp->sh;
4653 			xo = disp->sx;
4654 			yo = disp->sy;
4655 		} else {			/* Test area completely fills the window */
4656 			p->tx = 0;
4657 			p->ty = 0;
4658 			p->tw = wi;
4659 			p->th = he;
4660 			xo = disp->sx + (int)((hoff * 0.5 + 0.5) * (disp->sw - wi) + 0.5);
4661 			yo = disp->sy + (int)((voff * 0.5 + 0.5) * (disp->sh - he) + 0.5);
4662 		}
4663 		p->ww = wi;
4664 		p->wh = he;
4665 
4666 		/* It's a bit difficult to know how windows defines the display */
4667 		/* depth. Microsoft's doco is fuzzy, and typical values */
4668 		/* for BITSPIXEL and PLANES are confusing (What does "32" and "1" */
4669 		/* mean ?) NUMCOLORS seems to be -1 on my box, and perhaps */
4670 		/* is only applicable to up to 256 paletized colors. The doco */
4671 		/* for COLORRES is also fuzzy, but it returns a meaningful number */
4672 		/* on my box (24) */
4673 		if (p->ddebug) {
4674 			fprintf(errout,"Windows display RASTERCAPS 0x%x, BITSPIXEL %d, PLANES %d, NUMCOLORS %d, COLORRES %d\n",
4675 			        GetDeviceCaps(p->hdc, RASTERCAPS),
4676 			        GetDeviceCaps(p->hdc, BITSPIXEL),GetDeviceCaps(p->hdc, PLANES),
4677 			        GetDeviceCaps(p->hdc, NUMCOLORS),GetDeviceCaps(p->hdc, COLORRES));
4678 		}
4679 		if (GetDeviceCaps(p->hdc, RASTERCAPS) & RC_PALETTE) {
4680 			debugr2((errout, "new_dispwin: can't calibrate palette based device!\n"));
4681 			dispwin_del(p);
4682 			return NULL;
4683 		}
4684 		bpp = GetDeviceCaps(p->hdc, COLORRES);
4685 		if (bpp <= 0)
4686 			p->fdepth = 8;		/* Assume this is so */
4687 		else
4688 			p->fdepth = bpp/3;
4689 
4690 		p->rdepth = p->fdepth;		/* Assume this is so */
4691 		p->ndepth = 8;				/* Assume this is so */
4692 		p->nent = (1 << p->ndepth);
4693 		p->edepth = 16;
4694 
4695 		debugr2((errout,"new_dispwin: fdepth %d, rdepth %d, ndepth %d, edepth %d\n", p->fdepth,p->rdepth,p->ndepth,p->edepth));
4696 
4697 		if (nowin == 0) {
4698 
4699 			/* We use a thread to process the window messages, so that */
4700 			/* Task Manager doesn't think it's not responding. */
4701 
4702 			/* Because messages only get delivered to same thread that created window, */
4703 			/* the thread needs to create window. :-( */
4704 
4705 			p->xo = xo;		/* Pass info to thread */
4706 			p->yo = yo;
4707 			p->wi = wi;
4708 			p->he = he;
4709 
4710 			debugr2((errout,"new_dispwin about to create window\n"));
4711 
4712 			/* Create the window and then process events */
4713 			if ((p->mth = new_athread(win_message_thread, (void *)p)) == NULL) {
4714 				debugr2((errout, "new_dispwin: new_athread failed\n"));
4715 				dispwin_del(p);
4716 				return NULL;
4717 			}
4718 
4719 			/* Wait for thread to run */
4720 			/* (Hmm. This doesn't actually gurantee that our window has */
4721 			/* been created yet ? */
4722 			// ~~ should sync by sending a custom message and getting notified ? */
4723 			while (p->inited == 0) {
4724 				msec_sleep(20);
4725 			}
4726 			/* If thread errored */
4727 			if (p->inited != 1) {		/* Error */
4728 				debugr2((errout, "new_dispwin: new_athread returned error\n"));
4729 				dispwin_del(p);
4730 				return NULL;
4731 			}
4732 
4733 			debugr2((errout,"new_dispwin window created\n"));
4734 		}
4735 
4736 		/* Install the signal handler to ensure cleanup */
4737 		dispwin_install_signal_handlers(p);
4738 	}
4739 
4740 #endif /* NT */
4741 	/* -------------------------------------------------- */
4742 
4743 	/* -------------------------------------------------- */
4744 #ifdef UNIX_APPLE
4745 
4746 	if ((p->name = strdup(disp->name)) == NULL) {
4747 		debugr2((errout,"new_dispwin: Malloc failed\n"));
4748 		dispwin_del(p);
4749 		return NULL;
4750 	}
4751 	p->ddid = disp->ddid;		/* Display we're working on */
4752 
4753 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
4754 	{
4755 		CGDisplayModeRef dispmode;
4756 		CFStringRef pixenc;
4757 		int fbdepth = 0;
4758 
4759 		/* Get frame buffer depth  */
4760 		dispmode = CGDisplayCopyDisplayMode(p->ddid);
4761 		pixenc = CGDisplayModeCopyPixelEncoding(dispmode);
4762 
4763 		/* Hmm. Don't know what to do with kIO16BitFloatPixels or kIO32BitFloatPixels */
4764 		if (CFStringCompare(pixenc, CFSTR(kIO64BitDirectPixels), kCFCompareCaseInsensitive)
4765 		                                                             == kCFCompareEqualTo)
4766 			fbdepth = 16;
4767 		else if (CFStringCompare(pixenc, CFSTR(kIO30BitDirectPixels), kCFCompareCaseInsensitive)
4768 		                                                             == kCFCompareEqualTo)
4769 			fbdepth = 10;
4770 		else if (CFStringCompare(pixenc, CFSTR(IO32BitDirectPixels), kCFCompareCaseInsensitive)
4771 		                                                             == kCFCompareEqualTo)
4772 			fbdepth = 8;
4773 		else if (CFStringCompare(pixenc, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive)
4774 			                                                             == kCFCompareEqualTo)
4775 			fbdepth = 5;
4776 		else
4777 			fbdepth = 8;		/* Assume */
4778 
4779 #ifndef DEBUG
4780 		if (p->ddebug)
4781 #endif
4782 		{
4783 			char buf[200];
4784 			CFStringGetCString(pixenc, buf, 200, kCFStringEncodingUTF8);
4785 			debugr2((errout,"new_dispwin: CGDisplayModePixelEncoding = '%s'\n",buf));
4786 		}
4787 
4788 		CFRelease(pixenc);
4789 		CGDisplayModeRelease(dispmode);
4790 
4791 		p->fdepth = fbdepth;
4792 		p->rdepth = p->fdepth;		/* We don't know of any HW between frame buffer and VideoLUT */
4793 
4794 		/* Get CGDisplayGammaTable size */
4795 		p->nent = CGDisplayGammaTableCapacity(p->ddid);
4796 
4797 		/* Compute GammaTable/VideoLUT/RAMDAC depth */
4798 		{
4799 			for (p->ndepth = 1; p->ndepth < 17; p->ndepth++) {
4800 				if ((1 << p->ndepth) >= p->nent)
4801 					break;
4802 			}
4803 			if (p->ndepth >= 17) {
4804 				debugr2((errout,"new_dispwin: failed to extract depth from GammaTableCapacity %d\n",p->nent));
4805 				dispwin_del(p);
4806 				return NULL;
4807 			}
4808 			debugr2((errout,"new_dispwin: found actual VideoLUT depth %d bits\n",p->ndepth));
4809 		}
4810 
4811 		if (p->rdepth > p->ndepth) {
4812 			debugr2((errout,"new_dispwin: Frame buffer depth %d > VideoLUT depth %d\n",p->rdepth, p->ndepth));
4813 			dispwin_del(p);
4814 			return NULL;
4815 		}
4816 
4817 		if (p->rdepth != p->ndepth) {
4818 			if (!p->warned) {
4819 				warning("new_dispwin: Frame buffer depth %d doesn't matcv VideoLUT %d",p->rdepth, p->ndepth);
4820 				p->warned = 1;
4821 			}
4822 		}
4823 	}
4824 #else
4825 	/* Life is simple */
4826 	p->fdepth = CGDisplayBitsPerSample(p->ddid);
4827 	p->rdepth = p->fdepth;
4828 	p->ndepth = p->rdepth;
4829 	p->nent = (1 << p->ndepth);
4830 #endif
4831 
4832 	p->edepth = 16;				/* By experiment it seems to be 16 bits too */
4833 
4834 	debugr2((errout,"new_dispwin: fdepth %d, rdepth %d, ndepth %d, edepth %d\n", p->fdepth,p->rdepth,p->ndepth,p->edepth));
4835 
4836 	if (nowin == 0) {			/* Create a window */
4837 		osx_cntx_t *cx;
4838 		CGSize sz;				/* Display size in mm */
4839 		int wi, he;				/* Width and height in pixels */
4840 		int xo, yo;				/* Window location */
4841 
4842 		debugr2((errout, "new_dispwin: About to open display '%s'\n",disp->name));
4843 
4844 		/* If we may be in a different thread to the main thread or */
4845 		/* the application thread, establish our own pool. */
4846 		NSAutoreleasePool *tpool = nil;
4847 		if (pthread_self() != ui_thid
4848 		 && pthread_self() != ui_main_thid)
4849 			tpool = [NSAutoreleasePool new];
4850 
4851 		/* If there is no NSApp, then we haven't run main() in libui before */
4852 		/* main() in the application. */
4853 		if (NSApp == nil) {
4854 			fprintf(stderr,"NSApp is nil - need to rename main() to main() and link with libui !\n");
4855 			exit(1);
4856 		}
4857 
4858 		if ((cx = (osx_cntx_t *)calloc(sizeof(osx_cntx_t), 1)) == NULL) {
4859 			if (tpool != nil)
4860 				[tpool release];
4861 			debugr2((errout,"new_dispwin: Malloc failed (osx_cntx_t)\n"));
4862 			dispwin_del(p);
4863 			return NULL;
4864 		}
4865 		cx->p = p;
4866 		p->osx_cntx = cx;
4867 
4868 		sz = CGDisplayScreenSize(p->ddid);
4869 		debugr2((errout," Display size = %f x %f mm\n",sz.width,sz.height));
4870 
4871 		wi = (int)(width * disp->sw/sz.width + 0.5);
4872 		if (wi > disp->sw)
4873 			wi = disp->sw;
4874 		he = (int)(height * disp->sh/sz.height + 0.5);
4875 		if (he > disp->sh)
4876 			he = disp->sh;
4877 
4878 		/* (Because Cocoa origin is botton left, we flip voff) */
4879 		/* (Cocoa doesn't use disp->sx/sy either - each screen origin is at 0,0) */
4880 		if (p->fullscreen) {	/* Window fills the screen, test area is within it */
4881 			p->tx = (int)((hoff * 0.5 + 0.5) * (disp->sw - wi) + 0.5);
4882 			p->ty = (int)((-voff * 0.5 + 0.5) * (disp->sh - he) + 0.5);
4883 			p->tw = wi;
4884 			p->th = he;
4885 			wi = disp->sw;
4886 			he = disp->sh;
4887 			xo = 0;
4888 			yo = 0;
4889 		} else {			/* Test area completely fills the window */
4890 			p->tx = 0;
4891 			p->ty = 0;
4892 			p->tw = wi;
4893 			p->th = he;
4894 			xo = (int)((hoff * 0.5 + 0.5) * (disp->sw - wi) + 0.5);
4895 			yo = (int)((-voff * 0.5 + 0.5) * (disp->sh - he) + 0.5);
4896 		}
4897 		p->ww = wi;
4898 		p->wh = he;
4899 
4900 		cx->rect.origin.x = xo;
4901 		cx->rect.origin.y = yo;
4902 		cx->rect.size.width = wi;
4903 		cx->rect.size.height = he;
4904 
4905 		debugr2((errout,"new_dispwin about to create window\n"));
4906 
4907 		/* Prepare to wait for events */
4908 		ui_aboutToWait();
4909 
4910 		/* Run the window creation in the main thread and wait for it */
4911 		ui_runInMainThreadAndWait((void *)cx, create_my_win);
4912 
4913 		/* Wait for events generated by window creation to complete */
4914 		ui_waitForEvents();
4915 
4916 		if (tpool != nil)
4917 			[tpool release];
4918 
4919 		p->winclose = 0;
4920 
4921 		debugr2((errout,"new_dispwin window created\n"));
4922 	}
4923 
4924 	/* Install the signal handler to ensure cleanup */
4925 	dispwin_install_signal_handlers(p);
4926 #endif /* UNIX_APPLE */
4927 	/* -------------------------------------------------- */
4928 
4929 	/* -------------------------------------------------- */
4930 #if defined(UNIX_X11)
4931 	{
4932 		/* NOTE: That we're not doing much to detect if the display/window
4933 		   we open is unsuitable for high quality color (ie. at least
4934 		   24 bit etc.
4935 		 */
4936 
4937 #ifdef NEVER		// ?? is this for a specific reason ??
4938 		if (signal_dispwin != NULL) {
4939 			debugr2((errout,"new_dispwin: Attempting to open more than one dispwin!\n"));
4940 			dispwin_del(p);
4941 			return NULL;
4942 		}
4943 #endif
4944 
4945 		/* stuff for X windows */
4946 		char *pp, *bname;               /* base display name */
4947 		Window rootwindow;
4948 		char *appname = "TestWin";
4949 		Visual *myvisual;
4950 		XVisualInfo template, *vinfo;
4951 		int nitems = 0;
4952 		XSetWindowAttributes myattr;
4953 		XEvent      myevent;
4954 		XTextProperty myappname;
4955 		XSizeHints  mysizehints;
4956 		XWMHints    mywmhints;
4957 		int evb = 0, erb = 0;		/* Extension version */
4958 		unsigned long myforeground,mybackground;
4959 		int disp_hsz, disp_vsz;		/* Display horizontal/vertical size in mm */
4960 		int disp_hrz, disp_vrz;		/* Display horizontal/vertical resolution in pixels (virtual screen) */
4961 		int wi, he;				/* Width and height of window in pixels */
4962 		int xo, yo;				/* Window location in pixels */
4963 
4964 		/* Create the base display name (in case of Xinerama, XRandR) */
4965 		if ((bname = strdup(disp->name)) == NULL) {
4966 			debugr2((errout,"new_dispwin: Malloc failed\n"));
4967 			dispwin_del(p);
4968 			return NULL;
4969 		}
4970 		if ((pp = strrchr(bname, ':')) != NULL) {
4971 			if ((pp = strchr(pp, '.')) != NULL) {
4972 				sprintf(pp,".%d",disp->screen);
4973 			}
4974 		}
4975 
4976 		/* open the display */
4977 		p->mydisplay = XOpenDisplay(bname);
4978 		if(!p->mydisplay) {
4979 			debugr2((errout,"new_dispwin: Unable to open display '%s'\n",bname));
4980 			dispwin_del(p);
4981 			free(bname);
4982 			return NULL;
4983 		}
4984 		free(bname);
4985 		debugr("new_dispwin: Opened display OK\n");
4986 
4987 		if ((p->name = strdup(disp->name)) == NULL) {
4988 			debugr2((errout,"new_dispwin: Malloc failed\n"));
4989 			dispwin_del(p);
4990 			return NULL;
4991 		}
4992 		p->myscreen = disp->screen;
4993 		p->myuscreen = disp->uscreen;
4994 		p->myrscreen = disp->rscreen;
4995 
4996 #if RANDR_MAJOR == 1 && RANDR_MINOR >= 2 && !defined(DISABLE_RANDR)
4997 		/* These will be NULL otherwise */
4998 		p->icc_atom = disp->icc_atom;
4999 		p->crtc = disp->crtc;
5000 		p->output = disp->output;
5001 		p->icc_out_atom = disp->icc_out_atom;
5002 #endif /* randr >= V 1.2 */
5003 
5004 		if (disp->edid != NULL) {
5005 			if ((p->edid = malloc(sizeof(unsigned char) * disp->edid_len)) == NULL) {
5006 				debugr2((errout,"new_dispwin: Malloc failed\n"));
5007 				dispwin_del(p);
5008 				return NULL;
5009 			}
5010 			p->edid_len = disp->edid_len;
5011 			memmove(p->edid, disp->edid, p->edid_len);
5012 		}
5013 
5014 #ifdef NEVER
5015 #pragma message("######### dispwin.c DirectColor test is defined! ########")
5016 
5017 		/* To test DirectColor Visual when it's not the default:*/
5018 		debugr2((errout,"new_dispwin: Testing DirectColor Visual\n"));
5019 
5020 		// test DirectColor visual
5021 		template.class = DirectColor;
5022 		if ((vinfo = XGetVisualInfo(p->mydisplay, VisualClassMask, &template, &nitems)) == NULL) {
5023 			debugr2((errout,"new_dispwin: Unable to find a DirectColor Visual\n"));
5024 			dispwin_del(p);
5025 			return NULL;
5026 		}
5027 		myvisual = vinfo->visual;
5028 
5029 #else
5030 		myvisual = DefaultVisual(p->mydisplay, p->myscreen);
5031 #endif
5032 
5033 		/* Expect TrueColor (fixed/no map) or DirectColor (read/write map) Visual - */
5034 		/* anything else is not suitable for high quality color. */
5035 
5036 		/* Get the VisualInfo */
5037 		template.visualid = myvisual->visualid;
5038 		vinfo = XGetVisualInfo(p->mydisplay, VisualIDMask, &template, &nitems);
5039 
5040 		if (nitems < 1) {
5041 			debugr2((errout,"new_dispwin: Failed to get XGetVisualInfo of defalt Visual\n"));
5042 			dispwin_del(p);
5043 			return NULL;
5044 		}
5045 
5046 		if (vinfo->class != TrueColor && vinfo->class != DirectColor) {
5047 			debugr2((errout,"new_dispwin: Default Visual is not TrueColor or DirectColor\n"));
5048 			XFree(vinfo);
5049 			dispwin_del(p);
5050 			return NULL;
5051 		}
5052 
5053 		/* Compute per component frame buffer depth */
5054 		{
5055 			for (p->fdepth = 1; p->fdepth < 17; p->fdepth++) {
5056 				if ((1 << p->fdepth) >= myvisual->map_entries)
5057 					break;
5058 			}
5059 			if (p->fdepth >= 17) {
5060 				debugr2((errout,"new_dispwin: failed to extract depth from Visual bits_per_rgb %d\n",myvisual->map_entries));
5061 				XFree(vinfo);
5062 				dispwin_del(p);
5063 				return NULL;
5064 			}
5065 		}
5066 		p->rdepth = myvisual->bits_per_rgb;		/* X11 colormap entry size */
5067 		p->edepth = 16;							/* Assumed */
5068 
5069 		/* Check that vinfo->red_mask, green_mask & blue_mask all have */
5070 		/* myvisual->map_entries number of bits set, and determine the shift. */
5071 		{
5072 			unsigned long bit;
5073 			int depth;
5074 
5075 			if (vinfo->red_mask == 0 || vinfo->green_mask == 0 || vinfo->blue_mask == 0) {
5076 				debugr2((errout,"new_dispwin: one of default Visual r/g/b masks is 0\n"));
5077 				XFree(vinfo);
5078 				dispwin_del(p);
5079 				return NULL;
5080 			}
5081 
5082 			for (p->shift[0] = 0, bit = 1; (bit & vinfo->red_mask) == 0; p->shift[0]++, bit <<= 1)
5083 				;
5084 
5085 			for (depth = 0; (bit & vinfo->red_mask) != 0; depth++, bit <<= 1)
5086 				;
5087 
5088 			if (depth != p->fdepth) {
5089 				debugr2((errout,"new_dispwin: Default Visual red mask 0x%x is not %d bits\n",vinfo->red_mask,p->fdepth));
5090 				XFree(vinfo);
5091 				dispwin_del(p);
5092 				return NULL;
5093 			}
5094 
5095 			for (p->shift[1] = 0, bit = 1; (bit & vinfo->green_mask) == 0; p->shift[1]++, bit <<= 1)
5096 				;
5097 
5098 			for (depth = 0; (bit & vinfo->green_mask) != 0; depth++, bit <<= 1)
5099 				;
5100 
5101 			if (depth != p->fdepth) {
5102 				debugr2((errout,"new_dispwin: Default Visual green mask 0x%x is not %d bits\n",vinfo->red_mask,p->fdepth));
5103 				XFree(vinfo);
5104 				dispwin_del(p);
5105 				return NULL;
5106 			}
5107 
5108 			for (p->shift[2] = 0, bit = 1; (bit & vinfo->blue_mask) == 0; p->shift[2]++, bit <<= 1)
5109 				;
5110 
5111 			for (depth = 0; (bit & vinfo->blue_mask) != 0; depth++, bit <<= 1)
5112 				;
5113 
5114 			if (depth != p->fdepth) {
5115 				debugr2((errout,"new_dispwin: Default Visual blue mask 0x%x is not %d bits\n",vinfo->red_mask,p->fdepth));
5116 				XFree(vinfo);
5117 				dispwin_del(p);
5118 				return NULL;
5119 			}
5120 		}
5121 
5122 		/* Check the VideoLUT depth */
5123 #if RANDR_MAJOR == 1 && RANDR_MINOR >= 2 && !defined(DISABLE_RANDR)
5124 		if (p->crtc != 0) {		/* Using Xrandr 1.2 */
5125 
5126 			if ((p->nent = XRRGetCrtcGammaSize(p->mydisplay, p->crtc)) <= 0) {
5127 				p->nent = 0;
5128 			}
5129 		} else
5130 #endif /* randr >= V 1.2 */
5131 		{
5132 			p->nent = 0;
5133 
5134 			if (XF86VidModeQueryExtension(p->mydisplay, &evb, &erb) != 0) {
5135 				int nent = -1;
5136 
5137 				/* Some propietary multi-screen drivers (ie. TwinView & MergedFB) */
5138 				/* don't implement the XVidMode extenstion properly. */
5139 				if (XSetErrorHandler(null_error_handler) == 0) {
5140 					debugr("new_dispwin failed on XSetErrorHandler\n");
5141 					XSetErrorHandler(NULL);		/* Restore handler */
5142 					XFree(vinfo);
5143 					dispwin_del(p);
5144 					return NULL;
5145 				}
5146 				if (XF86VidModeGetGammaRampSize(p->mydisplay, p->myrscreen, &nent) != 0
5147 				 && nent != -1) {
5148 					p->nent = nent;
5149 				}
5150 				XSetErrorHandler(NULL);		/* Restore handler */
5151 			}
5152 		}
5153 
5154 		if (p->nent == 0) {
5155 			p->ndepth = 0;
5156 
5157 			if (!p->warned) {
5158 				warning("new_dispwin: VideoLUT is not accessible");
5159 				p->warned = 1;
5160 			}
5161 
5162 		} else {
5163 			if (p->nent > 16384) {
5164 				debugr("VideoLUT has more entries than we can handle\n");
5165 				XFree(vinfo);
5166 				dispwin_del(p);
5167 				return NULL;
5168 			}
5169 
5170 			/* Compute actual ramdac depth */
5171 			for (p->ndepth = 1; p->ndepth < 17; p->ndepth++) {
5172 				if ((1 << p->ndepth) >= p->nent)
5173 					break;
5174 			}
5175 
5176 			if (p->nent != (1 << p->rdepth)) {
5177 				if (!p->warned) {
5178 					warning("new_dispwin: Expected VideoLUT depth %d doesn't match actual %d",p->rdepth, p->ndepth);
5179 					p->warned = 1;
5180 				}
5181 			}
5182 		}
5183 
5184 		debugr2((errout,"new_dispwin: %s fdepth %d, rdepth %d, ndepth %d, edepth %d, r/g/b shifts %d %d %d\n", vinfo->class != TrueColor ? "TreuColor" : "DirectColor", p->fdepth,p->rdepth,p->ndepth,p->edepth, p->shift[0], p->shift[1], p->shift[2]));
5185 
5186 		if (nowin == 0) {			/* Create a window */
5187 			unsigned long attrmask = 0;
5188 			XWindowAttributes mywa;
5189 			Colormap mycmap = None;
5190 			int ncolors, i;
5191 
5192 			rootwindow = RootWindow(p->mydisplay, p->myscreen);
5193 
5194 			myforeground = BlackPixel(p->mydisplay, p->myscreen);
5195 			mybackground = BlackPixel(p->mydisplay, p->myscreen);
5196 
5197 			/* Get device context to main display */
5198 			disp_hsz = DisplayWidthMM(p->mydisplay, p->myscreen);
5199 			disp_vsz = DisplayHeightMM(p->mydisplay, p->myscreen);
5200 			disp_hrz = DisplayWidth(p->mydisplay, p->myscreen);
5201 			disp_vrz = DisplayHeight(p->mydisplay, p->myscreen);
5202 
5203 			/* Compute width and offset from overal display in case Xinerama is active */
5204 			wi = (int)(width * (double)disp_hrz/(double)disp_hsz + 0.5);
5205 			if (wi > disp_hrz)
5206 				wi = disp_hrz;
5207 			he = (int)(height * (double)disp_vrz/(double)disp_vsz + 0.5);
5208 			if (he > disp_vrz)
5209 				he = disp_vrz;
5210 
5211 			if (p->fullscreen) {	/* Window fills the screen, test area is within it */
5212 				p->tx = (int)((hoff * 0.5 + 0.5) * (disp->sw - wi) + 0.5);
5213 				p->ty = (int)((voff * 0.5 + 0.5) * (disp->sh - he) + 0.5);
5214 				p->tw = wi;
5215 				p->th = he;
5216 				wi = disp->sw;
5217 				he = disp->sh;
5218 				xo = disp->sx;
5219 				yo = disp->sy;
5220 			} else {			/* Test area completely fills the window */
5221 				p->tx = 0;
5222 				p->ty = 0;
5223 				p->tw = wi;
5224 				p->th = he;
5225 				xo = disp->sx + (int)((hoff * 0.5 + 0.5) * (disp->sw - wi) + 0.5);
5226 				yo = disp->sy + (int)((voff * 0.5 + 0.5) * (disp->sh - he) + 0.5);
5227 			}
5228 			p->ww = wi;
5229 			p->wh = he;
5230 
5231 			/* Setup Size Hints */
5232 			mysizehints.flags = PPosition | USSize;
5233 			mysizehints.x = xo;
5234 			mysizehints.y = yo;
5235 			mysizehints.width = wi;
5236 			mysizehints.height = he;
5237 
5238 			/* Setup Window Manager Hints */
5239 			mywmhints.flags = InputHint | StateHint;
5240 			mywmhints.input = 0;
5241 			mywmhints.initial_state = NormalState;
5242 
5243 			/* Setup Window Attributes */
5244 			myattr.background_pixel = mybackground;
5245 			myattr.bit_gravity = CenterGravity;
5246 			myattr.win_gravity = CenterGravity;
5247 			myattr.backing_store = WhenMapped;		/* Since we aren't listning to events */
5248 			if (override)
5249 				myattr.override_redirect = True;		/* Takes the WM out of the picture */
5250 			else
5251 				myattr.override_redirect = False;
5252 
5253 			attrmask |= CWBackPixel | CWBitGravity	/* Attributes Valumask */
5254 			          | CWWinGravity | CWBackingStore | CWOverrideRedirect;
5255 
5256 			/* For a DirectColor Visual, set the X11 color map */
5257 			if (vinfo->class == DirectColor) {
5258 				Colormap mycmap = None;
5259 				XColor *colors;
5260 				int ncolors = (1 << p->fdepth), i;
5261 
5262 				debugr2((errout,"new_dispwin: setting DirectColor colormap\n"));
5263 
5264 				if ((mycmap = XCreateColormap(p->mydisplay, rootwindow, myvisual, AllocAll)) == None) {
5265 					debugr2((errout,"new_dispwin: XCreateColormap failed\n"));
5266 					XFree(vinfo);
5267 					dispwin_del(p);
5268 					return NULL;
5269 				}
5270 
5271 				if ((colors = malloc(sizeof(XColor) * ncolors)) == NULL) {
5272 					debugr2((errout,"new_dispwin: Malloc failed for XColors\n"));
5273 					XFree(vinfo);
5274 					dispwin_del(p);
5275 					return NULL;
5276 				}
5277 
5278 				/* Set a linear mapping */
5279 				for (i = 0; i < ncolors; i++) {
5280 					colors[i].pixel = i << p->shift[0] | i << p->shift[1] | i << p->shift[2];
5281 					colors[i].red =
5282 					colors[i].green =
5283 					colors[i].blue = (unsigned short) (65535.0 * i/(ncolors-1.0) + 0.5);
5284 					colors[i].flags = DoRed | DoGreen | DoBlue;
5285 				}
5286 
5287 				if (!XStoreColors(p->mydisplay, mycmap, colors, ncolors)) {
5288 					debugr2((errout,"new_dispwin: DirectColor XStoreColors failed\n"));
5289 					free(colors);
5290 					XFree(vinfo);
5291 					dispwin_del(p);
5292 					return NULL;
5293 				}
5294 				free(colors);
5295 
5296 				myattr.colormap = mycmap;
5297 				attrmask |= CWColormap;
5298 			}
5299 
5300 			debugr("Opening window\n");
5301 			p->mywindow = XCreateWindow(
5302 				p->mydisplay, rootwindow,
5303 				mysizehints.x,mysizehints.y,mysizehints.width,mysizehints.height,
5304 				0,							/* Border width */
5305 				CopyFromParent,				/* Depth */
5306 				InputOutput,				/* Class */
5307 //				CopyFromParent,				/* Visual */
5308 				myvisual,					/* Visual */
5309 				attrmask,					/* Attributes Valumask */
5310 				&myattr						/* Attribute details */
5311 			);
5312 
5313 #ifdef NEVER
5314 			XWindowAttributes mywattributes;
5315 
5316 			/* Get the windows attributes */
5317 			if (XGetWindowAttributes(
5318 				p->mydisplay, p->mywindow,
5319 				&mywattributes) == 0) {
5320 				debugr("new_dispwin: XGetWindowAttributes failed\n");
5321 				XFree(vinfo);
5322 				dispwin_del(p);
5323 				return NULL;
5324 			}
5325 			p->fdepth = mywattributes.depth/3;
5326 			p->rdepth = p->fdepth;
5327 #endif
5328 
5329 			/* Setup TextProperty */
5330 			XStringListToTextProperty(&appname, 1, &myappname);
5331 
5332 			XSetWMProperties(
5333 				p->mydisplay, p->mywindow,
5334 				&myappname,				/* Window name */
5335 				&myappname,				/* Icon name */
5336 				NULL, 0,				/* argv, argc */
5337 				&mysizehints,
5338 				&mywmhints,
5339 				NULL);					/* No class hints */
5340 
5341 			// ~1 should free myappname, but there doesn't seem to be
5342 			// a XFreeXTextProperty(&myappname); ???
5343 
5344 			/* Set aditional properties */
5345 			{
5346 				Atom optat;
5347 				unsigned int opaque = 0xffffffff;
5348 				unsigned int xid = (unsigned int)rootwindow;	/* Hope this is 32 bit */
5349 				XChangeProperty(
5350 					p->mydisplay, p->mywindow,
5351 					XA_WM_TRANSIENT_FOR,		/* Property */
5352 					XA_WINDOW,					/* Type */
5353 					32,							/* format = bits in type of unsigned int */
5354 					PropModeReplace,			/* Change mode */
5355 					(char *)(&xid),				/* Data is Root Window XID */
5356 					1							/* Number of elements of data */
5357 				);
5358 
5359 				/* Set hint for compositing WMs that the window must be opaque */
5360 				if ((optat = XInternAtom(p->mydisplay, "_NET_WM_WINDOW_OPACITY", False)) != None) {
5361 					XChangeProperty(p->mydisplay, p->mywindow, optat,
5362 	           				     XA_CARDINAL, 32, PropModeReplace, (char *)(&opaque), 1);
5363 				}
5364 				if ((optat = XInternAtom(p->mydisplay, "_NET_WM_WINDOW_OPACITY_LOCKED", False)) != None) {
5365 					XChangeProperty(p->mydisplay, p->mywindow, optat,
5366 	           				     XA_CARDINAL, 32, PropModeReplace, (char *)(&opaque), 1);
5367 				}
5368 			}
5369 
5370 			p->mygc = XCreateGC(p->mydisplay,p->mywindow,0,0);
5371 			XSetBackground(p->mydisplay,p->mygc,mybackground);
5372 			XSetForeground(p->mydisplay,p->mygc,myforeground);
5373 
5374 			/* Create an invisible cursor over our window */
5375 			{
5376 				Cursor mycursor;
5377 				Pixmap mypixmap;
5378 				Colormap mycmap;
5379 				XColor col;
5380 				char pmdata[1] = { 0 };
5381 
5382 				col.red = col.green = col.blue = 0;
5383 
5384 				mycmap = DefaultColormap(p->mydisplay, p->myscreen);
5385 				XAllocColor(p->mydisplay, mycmap, &col);
5386 				mypixmap = XCreatePixmapFromBitmapData(p->mydisplay, p->mywindow, pmdata, 1, 1, 0, 0, 1);
5387 				mycursor = XCreatePixmapCursor(p->mydisplay, mypixmap, mypixmap, &col, &col, 0,0);
5388 				XDefineCursor(p->mydisplay, p->mywindow, mycursor);
5389 			}
5390 
5391 			XSelectInput(p->mydisplay,p->mywindow, ExposureMask);
5392 
5393 			debugr2((errout,"new_dispwin about to raise window\n"));
5394 			XMapRaised(p->mydisplay,p->mywindow);
5395 
5396 			/* ------------------------------------------------------- */
5397 			/* Suspend any screensavers if we can */
5398 
5399 			/* Install the signal handler to ensure cleanup */
5400 			dispwin_install_signal_handlers(p);
5401 
5402 #if ScreenSaverMajorVersion >= 1 && ScreenSaverMinorVersion >= 1	/* X11R7.1 ??? */
5403 
5404 			/* Disable any screensavers that work properly with XScreenSaverSuspend() */
5405 			if (XScreenSaverQueryExtension (p->mydisplay, &evb, &erb) != 0) {
5406 				int majv, minv;
5407 				XScreenSaverSuspend(p->mydisplay, True);
5408 				p->xsssuspend = 1;
5409 
5410 				/* Else we'd have to register as a screensaver to */
5411 				/* prevent another one activating ?? */
5412 			}
5413 #endif	/* X11R7.1 screensaver extension */
5414 
5415 			/* Disable the native X11 screensaver */
5416 			if (p->xsssuspend == 0) {
5417 
5418 				/* Save the screensaver state, and then disable it */
5419 				XGetScreenSaver(p->mydisplay, &p->timeout, &p->interval,
5420 				                &p->prefer_blanking, &p->allow_exposures);
5421 				XSetScreenSaver(p->mydisplay, 0, 0, DefaultBlanking, DefaultExposures);
5422 				p->xssvalid = 1;
5423 			}
5424 
5425 			/* Disable xscreensaver if it is running */
5426 			if (p->xssrunning == 0) {
5427 				p->xssrunning = (system("xscreensaver-command -version 2>/dev/null >/dev/null") == 0);
5428 				if (p->xssrunning)
5429 					system("xscreensaver-command -exit 2>/dev/null >/dev/null");
5430 			}
5431 
5432 			/* Disable gnomescreensaver if it is running */
5433 			if (p->gnomessrunning == 0) {
5434 				p->gnomessrunning = (system("gnome-screensaver-command -q "
5435 				                     "2>/dev/null >/dev/null") == 0);
5436 				if (p->gnomessrunning) {
5437 					sigset_t nsm, osm;
5438 					/* Ensure that other process doesn't get the signals we want to catch */
5439 					sigemptyset(&nsm);
5440 					sigaddset(&nsm,SIGHUP);
5441 					sigaddset(&nsm,SIGINT);
5442 					sigaddset(&nsm,SIGTERM);
5443 					sigprocmask(SIG_BLOCK, &nsm, &osm);
5444 
5445 					if ((p->gnomepid = fork()) == 0) {
5446 						freopen("/dev/null", "r", stdin);
5447 						freopen("/dev/null", "a", stdout);		/* Hide output */
5448 						freopen("/dev/null", "a", stderr);
5449 						execlp("gnome-screensaver-command", "gnome-screensaver-command","-i","-n","argyll","-r","measuring screen",NULL);
5450 
5451 						_exit(0);
5452 					}
5453 					sigprocmask(SIG_SETMASK, &osm, NULL);		/* restore the signals */
5454 				}
5455 			}
5456 
5457 			/* kscreensaver > 3.5.9 obeys XResetScreenSaver(), but earlier versions don't. */
5458 			/* Disable any KDE screen saver if it's active */
5459 			if (p->kdessrunning == 0) {
5460 				/* dcop is very slow if we're not actually running kde. */
5461 				/* Check that kde is running first */
5462 				if (system("ps -e 2>/dev/null | grep kdesktop 2>/dev/null >/dev/null") == 0) {
5463 					p->kdessrunning = (system("dcop kdesktop KScreensaverIface isEnabled "
5464 			                "2>/dev/null | grep true 2>/dev/null >/dev/null") == 0);
5465 				}
5466 				if (p->kdessrunning) {
5467 					system("dcop kdesktop KScreensaverIface enable false 2>&1 >/dev/null");
5468 				}
5469 			}
5470 
5471 			/* If DPMS is enabled, disable it */
5472 			if (DPMSQueryExtension(p->mydisplay, &evb, &erb) != 0) {
5473 				CARD16 power_level;
5474 				BOOL state;
5475 
5476 				if (DPMSInfo(p->mydisplay, &power_level, &state)) {
5477 					if ((p->dpmsenabled = state) != 0)
5478 						DPMSDisable(p->mydisplay);
5479 				}
5480 			}
5481 
5482 			/* Deal with any pending events */
5483 			debugr("About to enter main loop\n");
5484 			while(XPending(p->mydisplay) > 0) {
5485 				XNextEvent(p->mydisplay, &myevent);
5486 				switch(myevent.type) {
5487 					case Expose:
5488 						if(myevent.xexpose.count == 0) {	/* Repare the exposed region */
5489 							debug("Servicing final expose\n");
5490 							XFillRectangle(p->mydisplay, p->mywindow, p->mygc,
5491 							               p->tx, p->ty, p->tw, p->th);
5492 							debug("Finished expose\n");
5493 						}
5494 						break;
5495 				}
5496 			}
5497 
5498 			/* Deal with Colormaps */
5499 			debugr2((errout,"new_dispwin: window created - dealling with colormap\n"));
5500 
5501 			if (!XGetWindowAttributes(p->mydisplay, p->mywindow, &mywa)) {
5502 				debugr2((errout,"new_dispwin: XGetWindowAttributes failed\n"));
5503 				XFree(vinfo);
5504 				dispwin_del(p);
5505 				return NULL;
5506 			}
5507 
5508 			mycmap = mywa.colormap;
5509 			ncolors = (1 << p->fdepth);
5510 
5511 			for (i = 0; i < 3; i++) {
5512 				if ((p->rmap[i] = malloc(sizeof(int) * ncolors)) == NULL) {
5513 					debugr2((errout,"new_dispwin: Malloc failed for rmap[%d]\n",i));
5514 					XFree(vinfo);
5515 					dispwin_del(p);
5516 					return NULL;
5517 				}
5518 			}
5519 
5520 			/* Get the X11 colormaps, so that we know how to translate */
5521 			/* between the frame buffer pixel value and the ramdac */
5522 			/* index number. */
5523 			if (mycmap != None) {
5524 				XColor *colors;
5525 
5526 				debugr2((errout,"new_dispwin: getting colormap\n"));
5527 
5528 				if ((colors = malloc(sizeof(XColor) * ncolors)) == NULL) {
5529 					debugr2((errout,"new_dispwin: Malloc failed for XColors\n"));
5530 					XFree(vinfo);
5531 					dispwin_del(p);
5532 					return NULL;
5533 				}
5534 
5535 				for (i = 0; i < ncolors; i++) {
5536 					colors[i].pixel = i << p->shift[0] | i << p->shift[1] | i << p->shift[2];
5537 					colors[i].flags = DoRed | DoGreen | DoBlue;
5538 				}
5539 
5540 				if (!XQueryColors(p->mydisplay, mycmap, colors, ncolors)) {
5541 					debugr2((errout,"new_dispwin: DirectColor XQueryColors failed\n"));
5542 					free(colors);
5543 					XFree(vinfo);
5544 					dispwin_del(p);
5545 					return NULL;
5546 				}
5547 
5548 				/* Map from frame buffer value to ramdac index */
5549 				for (i = 0; i < ncolors; i++) {
5550 					p->rmap[0][i] = (int)(colors[i].red/65535.0 * ((1 << p->rdepth)-1.0) + 0.5);
5551 					p->rmap[1][i] = (int)(colors[i].green/65535.0 * ((1 << p->rdepth)-1.0) + 0.5);
5552 					p->rmap[2][i] = (int)(colors[i].blue/65535.0 * ((1 << p->rdepth)-1.0) + 0.5);
5553 
5554 //printf("%d: %d %d %d\n",i,p->rmap[0][i],p->rmap[1][i],p->rmap[2][i]);
5555 				}
5556 
5557 				free(colors);
5558 
5559 			/* Assume a default linear mapping */
5560 			} else {
5561 				debugr2((errout,"new_dispwin: assuming a linear colormap\n"));
5562 				for (i = 0; i < ncolors; i++) {
5563 					p->rmap[0][i] = (int)(i/((1 << p->fdepth)-1.0) * ((1 << p->rdepth)-1.0) + 0.5);
5564 					p->rmap[1][i] = (int)(i/((1 << p->fdepth)-1.0) * ((1 << p->rdepth)-1.0) + 0.5);
5565 					p->rmap[2][i] = (int)(i/((1 << p->fdepth)-1.0) * ((1 << p->rdepth)-1.0) + 0.5);
5566 				}
5567 
5568 				if (p->fdepth != p->rdepth) {
5569 					static int warned  = 0;
5570 					if (!warned) {
5571 						warning("new_dispwin: frame buffer depth %d != VideoLUT depth %d",p->fdepth, p->rdepth);
5572 						warned = 1;
5573 					}
5574 				}
5575 			}
5576 
5577 			debugr2((errout,"new_dispwin: dealt with colormap\n"));
5578 
5579 		} else {
5580 			/* Install the signal handler to ensure cleanup */
5581 			dispwin_install_signal_handlers(p);
5582 		}
5583 
5584 		XFree(vinfo); vinfo = NULL;
5585 	}
5586 #endif	/* UNIX X11 */
5587 	/* -------------------------------------------------- */
5588 
5589 	/* Save the original ramdac, which gets restored on exit */
5590 	if ((p->or = p->get_ramdac(p)) != NULL) {
5591 
5592 		if (noramdac != NULL)
5593 			*noramdac = 0;
5594 
5595 		debugr("Saved original VideoLUT\n");
5596 
5597 		/* Copy original ramdac that never gets altered */
5598 		if ((p->oor = p->or->clone(p->or)) == NULL) {
5599 			dispwin_del(p);
5600 			debugr("ramdac clone failed - memory ?\n");
5601 			return NULL;
5602 		}
5603 
5604 		/* Create a working ramdac for native or other use */
5605 		if ((p->r = p->or->clone(p->or)) == NULL) {
5606 			dispwin_del(p);
5607 			debugr("ramdac clone failed - memory ?\n");
5608 			return NULL;
5609 		}
5610 
5611 	} else {
5612 		debugr("Unable to access VideoLUT\n");
5613 		if (noramdac != NULL)
5614 			*noramdac = 1;
5615 		p->native = native &= ~1;
5616 		p->oor = p->or = p->r = NULL;
5617 	}
5618 
5619 	if (!p->nowin) {
5620 
5621 		/* Make sure initial test color is displayed */
5622 		dispwin_set_color(p, p->rgb[0], p->rgb[1], p->rgb[2]);
5623 
5624 		/* Hmm. Could we add this ?? */
5625 		/* Hard to know whether OS CM is active though. By default */
5626 		/* dispwin disables it. */
5627 		if (nocm != NULL)
5628 			*nocm = 1;
5629 
5630 		p->native = native &= ~2;
5631 	}
5632 
5633 	debugr("new_dispwin: return sucessfully\n");
5634 	return p;
5635 }
5636 
5637 /* ================================================================ */
5638 #if defined(UNIX_X11)
5639 /* Process to continuously monitor XRandR events, */
5640 /* and load the appropriate calibration and profiles */
5641 /* for each monitor. */
x11_daemon_mode(disppath * disp,int verb,int ddebug)5642 int x11_daemon_mode(disppath *disp, int verb, int ddebug) {
5643 
5644 #if RANDR_MAJOR == 1 && RANDR_MINOR >= 2 && !defined(DISABLE_RANDR)
5645 	char *dname;
5646 	char *pp;
5647 	char dnbuf[100];
5648 	Display *mydisplay;
5649 	int majv, minv;			/* Version */
5650 	int evb = 0, erb = 0;
5651 	int dopoll = 1;				/* Until XRandR is fixed */
5652 	XEvent myevent;
5653 	int update_profiles = 1;	/* Do it on entry */
5654 
5655 	/* Open the base display */
5656 	strncpy(dnbuf,disp->name,99); dnbuf[99] = '\000';
5657 	if ((pp = strrchr(dnbuf, ':')) != NULL) {
5658 		if ((pp = strchr(pp, '.')) == NULL)
5659 			strcat(dnbuf,".0");
5660 		else  {
5661 			if (pp[1] == '\000')
5662 				strcat(dnbuf,"0");
5663 			else {
5664 				pp[1] = '0';
5665 				pp[2] = '\000';
5666 			}
5667 		}
5668 	}
5669 
5670 	if ((mydisplay = XOpenDisplay(dnbuf)) == NULL) {
5671 		debug2((errout, "x11_daemon_mode: failed to open display '%s'\n",dnbuf));
5672 		return -1;
5673 	}
5674 
5675 	if (verb) printf("Opened display '%s'\n",dnbuf);
5676 
5677 	/* !!!! we want to create a test here, to see if we have to poll, */
5678 	/* !!!! or whether we spontainously get events when the EDID changes. */
5679 
5680 	/* Use Xrandr 1.2 if it's available and not disabled */
5681 	if (getenv("ARGYLL_IGNORE_XRANDR1_2") == NULL
5682 	 && XRRQueryExtension(mydisplay, &evb, &erb) != 0
5683 	 && XRRQueryVersion(mydisplay, &majv, &minv)
5684 	 && majv == 1 && minv >= 2) {
5685 		static void *xrr_found = NULL;	/* .so handle */
5686 		static XRRScreenResources *(*_XRRGetScreenResourcesCurrent)
5687 				                  (Display *dpy, Window window) = NULL;
5688 		XRRScreenResources *scrnres;
5689 
5690 		if (minv >= 3 && xrr_found == NULL) {
5691 			if ((xrr_found = dlopen("libXrandr.so", RTLD_LAZY)) != NULL)
5692 				_XRRGetScreenResourcesCurrent = dlsym(xrr_found, "XRRGetScreenResourcesCurrent");
5693 		}
5694 
5695 		if (verb) printf("Found XRandR 1.2 or latter\n");
5696 
5697 		XRRSelectInput(mydisplay,RootWindow(mydisplay,0),
5698 				RRScreenChangeNotifyMask
5699 			|	RRCrtcChangeNotifyMask
5700 			|	RROutputChangeNotifyMask
5701 			|	RROutputPropertyNotifyMask
5702 		);
5703 
5704 		/* Deal with any pending events */
5705 		if (verb) printf("About to enter main loop waiting for XRandR changes\n");
5706 		for(;;) {
5707 
5708 			if (update_profiles == 0) {
5709 				if (dopoll) {
5710 					for (;;) {
5711 						if (minv >= 3 && _XRRGetScreenResourcesCurrent != NULL) {
5712 							_XRRGetScreenResourcesCurrent(mydisplay, RootWindow(mydisplay,0));
5713 
5714 						} else {
5715 							XRRGetScreenResources(mydisplay, RootWindow(mydisplay,0));
5716 						}
5717 						if(XPending(mydisplay) > 0)
5718 							break;
5719 						sleep(2);
5720 					}
5721 				} else {
5722 					/* Sleep until there is an event */
5723 					XPeekEvent(mydisplay, &myevent);
5724 				}
5725 			}
5726 
5727 			/* Get all our events until we run out */
5728 			while (XPending(mydisplay) > 0) {
5729 				XNextEvent(mydisplay, &myevent);
5730 				if (myevent.type == evb + RRScreenChangeNotify) {
5731 //					printf("~1 Got RRScreenChangeNotify\n");
5732 					update_profiles = 1;
5733 				} else if (myevent.type == evb + RRNotify) {
5734 					update_profiles = 1;
5735 					XRRNotifyEvent *rrne = (XRRNotifyEvent *)(&myevent);
5736 					if (rrne->subtype == RRNotify_CrtcChange) {
5737 //						printf("~1 Got RRCrtcChangeNotify\n");
5738 					}
5739 					else if (rrne->subtype == RRNotify_OutputChange) {
5740 //						printf("~1 Got RROutputChangeNotify\n");
5741 					}
5742 					else if (rrne->subtype == RRNotify_OutputProperty) {
5743 //						printf("~1 Got RROutputPropertyNotify\n");
5744 					}
5745 				}
5746 			}
5747 
5748 			if (update_profiles) {
5749 				disppath **dp;
5750 				ramdac *r = NULL;
5751 
5752 				if (verb) printf("Updating profiles for display '%s'\n",dnbuf);
5753 
5754 				dp = get_displays();
5755 				if (dp == NULL || dp[0] == NULL) {
5756 					if (verb) printf("Failed to enumerate all the screens for display '%s'\n",dnbuf);
5757 						continue;
5758 				} else {
5759 					int i, j;
5760 					dispwin *dw;
5761 					char calname[MAXNAMEL+1] = "\000";	/* Calibration file name */
5762 					icmFile *rd_fp = NULL;
5763 					icc *icco = NULL;
5764 					icmVideoCardGamma *wo;
5765 					double iv;
5766 
5767 					for (i = 0; ; i++) {
5768 						if (dp[i] == NULL)
5769 							break;
5770 						if (verb) printf("Updating display %d = '%s'\n",i+1,dp[i]->description);
5771 
5772 						if ((dw = new_dispwin(dp[i], 0.0, 0.0, 0.0, 0.0, 1, 0, NULL, NULL, 0, 0, 0, ddebug)) == NULL) {
5773 							if (verb) printf("Failed to access screen %d of display '%s'\n",i+1,dnbuf);
5774 							continue;
5775 						}
5776 						if ((r = dw->get_ramdac(dw)) == NULL) {
5777 							if (verb) printf("Failed to access VideoLUT of screen %d for display '%s'\n",i+1,dnbuf);
5778 							dw->del(dw);
5779 							continue;
5780 						}
5781 
5782 						/* Grab the installed profile from the ucmm */
5783 						if ((rd_fp = dw->get_profile(dw, calname, MAXNAMEL)) == NULL) {
5784 							if (verb) printf("Failed to find profile of screen %d for display '%s'\n",i+1,dnbuf);
5785 							r->del(r);
5786 							dw->del(dw);
5787 							continue;
5788 						}
5789 
5790 						if ((icco = new_icc()) == NULL) {
5791 							if (verb) printf("Failed to create profile object for screen %d for display '%s'\n",i+1,dnbuf);
5792 							rd_fp->del(rd_fp);
5793 							r->del(r);
5794 							dw->del(dw);
5795 							continue;
5796 						}
5797 
5798 						/* Read header etc. */
5799 						if (icco->read(icco, rd_fp,0) != 0) {		/* Read ICC OK */
5800 							if (verb) printf("Failed to read profile for screen %d for display '%s'\n",i+1,dnbuf);
5801 							icco->del(icco);
5802 							rd_fp->del(rd_fp);
5803 							r->del(r);
5804 							dw->del(dw);
5805 							continue;
5806 						}
5807 
5808 						if ((wo = (icmVideoCardGamma *)icco->read_tag(icco, icSigVideoCardGammaTag)) == NULL) {
5809 							if (verb) printf("Failed to find vcgt tagd in profile for screen %d for display '%s' so setting linear\n",i+1,dnbuf);
5810 							for (j = 0; j < r->nent; j++) {
5811 								iv = j/(r->nent-1.0);
5812 								r->v[0][j] = iv;
5813 								r->v[1][j] = iv;
5814 								r->v[2][j] = iv;
5815 							}
5816 						} else {
5817 							if (wo->u.table.channels == 3) {
5818 								for (j = 0; j < r->nent; j++) {
5819 									iv = j/(r->nent-1.0);
5820 									r->v[0][j] = wo->lookup(wo, 0, iv);
5821 									r->v[1][j] = wo->lookup(wo, 1, iv);
5822 									r->v[2][j] = wo->lookup(wo, 2, iv);
5823 								}
5824 							} else if (wo->u.table.channels == 1) {
5825 								for (j = 0; j < r->nent; j++) {
5826 									iv = j/(r->nent-1.0);
5827 									r->v[0][j] =
5828 									r->v[1][j] =
5829 									r->v[2][j] = wo->lookup(wo, 0, iv);
5830 								}
5831 								debug("Got monochrom vcgt calibration\n");
5832 							} else {
5833 								if (verb) printf("vcgt tag is unrecognized in profile for screen %d for display '%s'\n",i+1,dnbuf);
5834 								icco->del(icco);
5835 								rd_fp->del(rd_fp);
5836 								r->del(r);
5837 								dw->del(dw);
5838 								continue;
5839 							}
5840 						}
5841 						if (dw->set_ramdac(dw,r,1) != 0) {
5842 							if (verb) printf("Unable to set vcgt tag for screen %d for display '%s'\n",i+1,dnbuf);
5843 							icco->del(icco);
5844 							rd_fp->del(rd_fp);
5845 							r->del(r);
5846 							dw->del(dw);
5847 							continue;
5848 						}
5849 						if (verb) printf("Loaded profile and calibration for screen %d for display '%s'\n",i+1,dnbuf);
5850 						icco->del(icco);
5851 						rd_fp->del(rd_fp);
5852 						r->del(r);
5853 						dw->del(dw);
5854 					}
5855 				}
5856 				free_disppaths(dp);
5857 				update_profiles = 0;
5858 			}
5859 		}
5860 	} else
5861 #endif /* randr >= V 1.2 */
5862 
5863 	if (verb) printf("XRandR 1.2 is not available - quitting\n");
5864 	return -1;
5865 }
5866 
5867 #endif 	/* UNIX_X11 */
5868 
5869 
5870 /* ================================================================ */
5871 #ifdef STANDALONE_TEST
5872 /* test/utility code */
5873 
5874 #if defined(__APPLE__) && defined(__POWERPC__)
5875 
5876 /* Workaround for a ppc gcc 3.3 optimiser bug... */
5877 /* It seems to cause a segmentation fault instead of */
5878 /* converting an integer loop index into a float, */
5879 /* when there are sufficient variables in play. */
gcc_bug_fix(int i)5880 static int gcc_bug_fix(int i) {
5881 	static int nn;
5882 	nn += i;
5883 	return nn;
5884 }
5885 #endif	/* APPLE */
5886 
5887 #include "numlib.h"
5888 
5889 /* Flag = 0x0000 = default */
5890 /* Flag & 0x0001 = list ChromCast's */
usage(int flag,char * diag,...)5891 static void usage(int flag, char *diag, ...) {
5892 	disppath **dp;
5893 	fprintf(stderr,"Test display patch window, Set Video LUTs, Install profiles, Version %s\n",ARGYLL_VERSION_STR);
5894 	fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n");
5895 	if (diag != NULL) {
5896 		va_list args;
5897 		fprintf(stderr,"Diagnostic: ");
5898 		va_start(args, diag);
5899 		vfprintf(stderr, diag, args);
5900 		va_end(args);
5901 		fprintf(stderr,"\n");
5902 	}
5903 	fprintf(stderr,"usage: dispwin [options] [calfile] \n");
5904 	fprintf(stderr," -v                   Verbose mode\n");
5905 #if defined(UNIX_X11)
5906 	fprintf(stderr," -display displayname Choose X11 display name\n");
5907 	fprintf(stderr," -d n[,m]             Choose the display n from the following list (default 1)\n");
5908 	fprintf(stderr,"                      Optionally choose different display m for Video LUT access\n");
5909 #else
5910 	fprintf(stderr," -d n                 Choose the display from the following list (default 1)\n");
5911 #endif
5912 	dp = get_displays();
5913 	if (dp == NULL || dp[0] == NULL) {
5914 		fprintf(stderr,"    ** No displays found **\n");
5915 	} else {
5916 		int i;
5917 		for (i = 0; ; i++) {
5918 			if (dp[i] == NULL)
5919 				break;
5920 			fprintf(stderr,"    %d = '%s'\n",i+1,dp[i]->description);
5921 		}
5922 	}
5923 	free_disppaths(dp);
5924 	fprintf(stderr," -dweb[:port]         Display via web server at port (default 8080)\n");
5925 	fprintf(stderr," -dcc[:n]             Display via n'th ChromeCast (default 1, ? for list)\n");
5926 	if (flag & 0x001) {
5927 		ccast_id **ids;
5928 		if ((ids = get_ccids()) == NULL) {
5929 			fprintf(stderr,"    ** Error discovering ChromCasts **\n");
5930 		} else {
5931 			if (ids[0] == NULL)
5932 				fprintf(stderr,"    ** No ChromCasts found **\n");
5933 			else {
5934 				int i;
5935 				for (i = 0; ids[i] != NULL; i++)
5936 					fprintf(stderr,"    %d = '%s'\n",i+1,ids[i]->name);
5937 				free_ccids(ids);
5938 			}
5939 		}
5940 	}
5941 #ifdef NT
5942 	fprintf(stderr," -dmadvr              Display via MadVR Video Renderer\n");
5943 #endif
5944 
5945 	fprintf(stderr," -P ho,vo,ss[,vs]     Position test window and scale it\n");
5946 	fprintf(stderr," -F                   Fill whole screen with black background\n");
5947 	fprintf(stderr," -i                   Run forever with random values\n");
5948 	fprintf(stderr," -G filename          Display RGB colors from CGATS (ie .ti1) file\n");
5949 	fprintf(stderr," -C r.rr,g.gg,b.bb    Add this RGB color to list to be displayed\n");
5950 	fprintf(stderr," -m                   Manually cycle through values\n");
5951 	fprintf(stderr," -f                   Test grey ramp fade\n");
5952 	fprintf(stderr," -r                   Test just Video LUT loading & Beeps\n");
5953 	fprintf(stderr," -n                   Test native output (rather than through Video LUT and C.M.)\n");
5954 	fprintf(stderr," -s filename          Save the currently loaded Video LUT to 'filename'\n");
5955 	fprintf(stderr," -c                   Load a linear display calibration\n");
5956 	fprintf(stderr," -V                   Verify that calfile/profile cal. is currently loaded in LUT\n");
5957 	fprintf(stderr," -I                   Install profile for display and use it's calibration\n");
5958 	fprintf(stderr," -U                   Un-install profile for display\n");
5959 	fprintf(stderr," -S d                 Specify the install/uninstall scope for OS X [nlu] or X11/Vista [lu]\n");
5960 	fprintf(stderr,"                      d is one of: n = network, l = local system, u = user (default)\n");
5961 	fprintf(stderr," -L                   Load installed profiles cal. into Video LUT\n");
5962 #if defined(UNIX_X11)
5963 	fprintf(stderr," -X                   Run in daemon loader mode for given X11 server\n");
5964 #endif /* X11 */
5965 	fprintf(stderr," -D [level]           Print debug diagnostics to stderr\n");
5966 	fprintf(stderr," calfile              Load calibration (.cal or %s) into Video LUT\n",ICC_FILE_EXT);
5967 	exit(1);
5968 }
5969 
5970 /* 32 bit pseudo random sequencer based on XOR feedback */
5971 /* generates number between 1 and 4294967295 */
5972 #define PSRAND32(S) (((S) & 0x80000000) ? (((S) << 1) ^ 0xa398655d) : ((S) << 1))
5973 
5974 int
main(int argc,char * argv[])5975 main(int argc, char *argv[]) {
5976 	int fa, nfa, mfa;			/* current argument we're looking at */
5977 	int verb = 0;				/* Verbose flag */
5978 	int ddebug = 0;				/* debug level */
5979 	int webdisp = 0;			/* NZ for web display, == port number */
5980 	int ccdisp = 0;			 	/* NZ for ChromeCast, == list index */
5981 #ifdef NT
5982 	int madvrdisp = 0;			/* NZ for MadVR display */
5983 #endif
5984 	disppath *disp = NULL;		/* Display being used */
5985 	double hpatscale = 1.0, vpatscale = 1.0;	/* scale factor for test patch size */
5986 	double ho = 0.0, vo = 0.0;	/* Test window offsets, -1.0 to 1.0 */
5987 	int out_tvenc = 0;			/* 1 to use RGB Video Level encoding */
5988 	int fullscreen = 0;       		/* NZ if whole screen should be filled with black */
5989 	int nowin = 0;				/* Don't create test window */
5990 	int ramd = 0;				/* Just test ramdac */
5991 	int fade = 0;				/* Test greyramp fade */
5992 	int native = 0;				/* X0 = use current per channel calibration curve */
5993 								/* X1 = set native linear output and use ramdac high precn. */
5994 								/* 0X = use current color management cLut (MadVR) */
5995 								/* 1X = disable color management cLUT (MadVR) */
5996 	int noramdac = 0;			/* nz if no ramdac access. native is set to X0 */
5997 	int nocm = 0;				/* nz if no CM cLUT access. native is set to 0X */
5998 	int inf = 0;				/* Infnite/manual patches flag */
5999 	char pcname[MAXNAMEL+1] = "\000";	/* CGATS patch color name */
6000 	int nmrgb = 0;				/* Number of manual RGB values */
6001 	double mrgb[10][3];			/* Manual RGB values */
6002 	int clear = 0;				/* Clear any display calibration (any calname is ignored) */
6003 	char sname[MAXNAMEL+1] = "\000";	/* Current cal save name */
6004 	int verify = 0;				/* Verify that calname is currently loaded */
6005 	int installprofile = 0;		/* Install (1) or uninstall (2) a profile for display */
6006 	int loadprofile = 0;		/* Load displays profile calibration into LUT */
6007 	int loadfile = 0;			/* Load given profile into LUT */
6008 	p_scope scope = p_scope_user;	/* Scope of profile instalation/un-instalation */
6009 	int daemonmode = 0;			/* X11 daemin loader mode */
6010 	char calname[MAXNAMEL+1] = "\000";	/* Calibration file name */
6011 	dispwin *dw;
6012 	unsigned int seed = 0x56781234;
6013 	int i, j;
6014 //	ramdac *r = NULL;
6015 	int is_ok_icc = 0;			/* The profile is OK */
6016 
6017 	error_program = "Dispwin";
6018 	check_if_not_interactive();
6019 
6020 	/* Process the arguments */
6021 	mfa = 0;        /* Minimum final arguments */
6022 	for(fa = 1;fa < argc;fa++) {
6023 
6024 		nfa = fa;					/* skip to nfa if next argument is used */
6025 		if (argv[fa][0] == '-') {	/* Look for any flags */
6026 			char *na = NULL;		/* next argument after flag, null if none */
6027 
6028 			if (argv[fa][2] != '\000')
6029 				na = &argv[fa][2];		/* next is directly after flag */
6030 			else {
6031 				if ((fa+1+mfa) < argc) {
6032 					if (argv[fa+1][0] != '-') {
6033 						nfa = fa + 1;
6034 						na = argv[nfa];		/* next is seperate non-flag argument */
6035 					}
6036 				}
6037 			}
6038 
6039 			if (argv[fa][1] == '?')
6040 				usage(0,"Usage requested");
6041 
6042 			else if (argv[fa][1] == 'v')
6043 				verb = 1;
6044 
6045 			/* Debug */
6046 			else if (argv[fa][1] == 'D') {
6047 				ddebug = 1;
6048 				if (na != NULL && na[0] >= '0' && na[0] <= '9') {
6049 					ddebug = atoi(na);
6050 					fa = nfa;
6051 				}
6052 				g_log->debug = ddebug;
6053 				callback_ddebug = ddebug;	/* dispwin global */
6054 			}
6055 
6056 			/* Display number */
6057 			else if (argv[fa][1] == 'd') {
6058 				if(na == NULL)  usage(0,"-d parameter missing");
6059 				if (strncmp(na,"web",3) == 0
6060 				 || strncmp(na,"WEB",3) == 0) {
6061 					webdisp = 8080;
6062 					if (na[3] == ':') {
6063 						webdisp = atoi(na+4);
6064 						if (webdisp == 0 || webdisp > 65535)
6065 							usage(0,"Web port number must be in range 1..65535");
6066 					}
6067 					fa = nfa;
6068 				} else if (strncmp(na,"cc",2) == 0
6069 				 || strncmp(na,"CC",2) == 0) {
6070 					ccdisp = 1;
6071 					if (na[2] == ':') {
6072 						if (na[3] < '0' || na[3] > '9')
6073 							usage(0x0001,"Available ChromeCasts");
6074 
6075 						ccdisp = atoi(na+3);
6076 						if (ccdisp <= 0)
6077 							usage(0,"ChromCast number must be in range 1..N");
6078 					}
6079 					fa = nfa;
6080 #ifdef NT
6081 				} else if (strncmp(na,"madvr",5) == 0
6082 				 || strncmp(na,"MADVR",5) == 0) {
6083 					madvrdisp = 1;
6084 					fa = nfa;
6085 #endif
6086 				} else {
6087 #if defined(UNIX_X11)
6088 					int ix, iv;
6089 
6090 					/* X11 type display name. */
6091 					if (strcmp(&argv[fa][2], "isplay") == 0 || strcmp(&argv[fa][2], "ISPLAY") == 0) {
6092 						if (++fa >= argc || argv[fa][0] == '-') usage(0,"-DISPLAY parameter missing");
6093 						setenv("DISPLAY", argv[fa], 1);
6094 					} else {
6095 						if (na == NULL) usage(0,"-d parameter missing");
6096 						fa = nfa;
6097 						if (sscanf(na, "%d,%d",&ix,&iv) != 2) {
6098 							ix = atoi(na);
6099 							iv = 0;
6100 						}
6101 						if (disp != NULL)
6102 							free_a_disppath(disp);
6103 						if ((disp = get_a_display(ix-1)) == NULL)
6104 							usage(0,"-d parameter '%s' is out of range",na);
6105 						if (iv > 0)
6106 							disp->rscreen = iv-1;
6107 					}
6108 #else
6109 					int ix;
6110 					if (na == NULL) usage(0,"-d parameter is missing");
6111 					fa = nfa;
6112 					ix = atoi(na);
6113 					if (disp != NULL)
6114 						free_a_disppath(disp);
6115 					if ((disp = get_a_display(ix-1)) == NULL)
6116 						usage(0,"-d parameter '%s' is out of range",na);
6117 #endif
6118 				}
6119 			}
6120 
6121 			/* Test patch offset and size */
6122 			else if (argv[fa][1] == 'P') {
6123 				fa = nfa;
6124 				if (na == NULL) usage(0,"-P parameters are missing");
6125 				if (sscanf(na, " %lf,%lf,%lf,%lf ", &ho, &vo, &hpatscale, &vpatscale) == 4) {
6126 					;
6127 				} else if (sscanf(na, " %lf,%lf,%lf ", &ho, &vo, &hpatscale) == 3) {
6128 					vpatscale = hpatscale;
6129 				} else {
6130 					usage(0,"-P parameters '%s' is badly formatted",na);
6131 				}
6132 				if (ho < 0.0 || ho > 1.0
6133 				 || vo < 0.0 || vo > 1.0
6134 				 || hpatscale <= 0.0 || hpatscale > 50.0
6135 				 || vpatscale <= 0.0 || vpatscale > 50.0)
6136 					usage(0,"-P parameters '%s' is out of range",na);
6137 				ho = 2.0 * ho - 1.0;
6138 				vo = 2.0 * vo - 1.0;
6139 
6140 			/* Black background */
6141 			} else if (argv[fa][1] == 'F') {
6142 				fullscreen = 1;
6143 
6144 			/* Video mode encoding */
6145 			} else if (argv[fa][1] == 'E') {
6146 				out_tvenc = 1;
6147 
6148 			} else if (argv[fa][1] == 'i')
6149 				inf = 1;
6150 
6151 			else if (argv[fa][1] == 'm')
6152 				inf = 2;
6153 
6154 			/* CGATS patch color file */
6155 			else if (argv[fa][1] == 'G') {
6156 				fa = nfa;
6157 				if (na == NULL) usage(0,"-G parameter is missing");
6158 				strncpy(pcname,na,MAXNAMEL); pcname[MAXNAMEL] = '\000';
6159 			}
6160 			/* Manual color */
6161 			else if (argv[fa][1] == 'C') {
6162 				fa = nfa;
6163 				if (nmrgb >= 10)
6164 					usage(0,"Can only be up to 10 -C values");
6165 				if (na == NULL) usage(0,"-C parameters are missing");
6166 				if (sscanf(na, "%lf,%lf,%lf ",&mrgb[nmrgb][0],&mrgb[nmrgb][1],&mrgb[nmrgb][2]) != 3)
6167 					usage(0,"-C parameters '%s' are badly formatted",na);
6168 				if (mrgb[nmrgb][0] < 0.0 || mrgb[nmrgb][0] > 1.0
6169 				 || mrgb[nmrgb][1] < 0.0 || mrgb[nmrgb][1] > 1.0
6170 				 || mrgb[nmrgb][2] < 0.0 || mrgb[nmrgb][2] > 1.0)
6171 					usage(0,"-C parameters %f %f %f are out of range 0.0 - 1.0",mrgb[nmrgb][0],mrgb[nmrgb][1],mrgb[nmrgb][2]);
6172 				nmrgb++;
6173 			}
6174 			else if (argv[fa][1] == 'f')
6175 				fade = 1;
6176 
6177 			else if (argv[fa][1] == 'r')
6178 				ramd = 1;
6179 
6180 			else if (argv[fa][1] == 'n') {
6181 				native = 3;			/* Disable cal & any CM */
6182 			}
6183 
6184 			else if (argv[fa][1] == 's') {
6185 				fa = nfa;
6186 				if (na == NULL) usage(0,"-s parameter is missing");
6187 				strncpy(sname,na,MAXNAMEL); sname[MAXNAMEL] = '\000';
6188 			}
6189 
6190 			else if (argv[fa][1] == 'c' || argv[fa][1] == 'C')
6191 				clear = 1;
6192 
6193 			else if (argv[fa][1] == 'V')
6194 				verify = 1;
6195 
6196 			else if (argv[fa][1] == 'I')
6197 				installprofile = 1;
6198 
6199 			else if (argv[fa][1] == 'U')
6200 				installprofile = 2;
6201 
6202 			else if (argv[fa][1] == 'L')
6203 				loadprofile = 1;
6204 
6205 			else if (argv[fa][1] == 'X')
6206 				daemonmode = 1;
6207 
6208 			else if (argv[fa][1] == 'S') {
6209 				fa = nfa;
6210 				if (na == NULL) usage(0,"-S parameter is missing");
6211 					if (na[0] == 'n' || na[0] == 'N')
6212 						scope = p_scope_network;
6213 					else if (na[0] == 'l' || na[0] == 'L')
6214 						scope = p_scope_local;
6215 					else if (na[0] == 'u' || na[0] == 'U')
6216 						scope = p_scope_user;
6217 			}
6218 			else
6219 				usage(0,"Unknown flag '%s'",argv[fa]);
6220 		}
6221 		else
6222 			break;
6223 	}
6224 
6225 	/* No explicit display has been set */
6226 	if (disp == NULL
6227 #ifdef NT
6228 	 && madvrdisp == 0
6229 #endif
6230 	 && webdisp == 0
6231 	 && ccdisp == 0) {
6232 		int ix = 0;
6233 #if defined(UNIX_X11)
6234 		char *dn, *pp;
6235 
6236 		if ((dn = getenv("DISPLAY")) != NULL) {
6237 			if ((pp = strrchr(dn, ':')) != NULL) {
6238 				if ((pp = strchr(pp, '.')) != NULL) {
6239 					if (pp[1] != '\000')
6240 						ix = atoi(pp+1);
6241 				}
6242 			}
6243 		}
6244 #endif
6245 		if ((disp = get_a_display(ix)) == NULL)
6246 			error("Unable to open the default display");
6247 	}
6248 
6249 	/* See if there's a calibration file */
6250 	if (fa < argc) {
6251 		strncpy(calname,argv[fa++],MAXNAMEL); calname[MAXNAMEL] = '\000';
6252 		if (installprofile == 0 && loadprofile == 0 && verify == 0)
6253 			loadfile = 1;		/* Load the given profile into the VideoLUT */
6254 	}
6255 
6256 #if defined(UNIX_X11)
6257 	if (webdisp == 0 && ccdisp == 0 && daemonmode) {
6258 		return x11_daemon_mode(disp, verb, ddebug);
6259 	}
6260 #endif
6261 
6262 	/* Bomb on bad combinations (not all are being detected) */
6263 	if (installprofile && calname[0] == '\000')
6264 		error("Can't install or uninstall a displays profile without profile argument");
6265 
6266 	if (verify && calname[0] == '\000' && loadprofile == 0)
6267 		error("No calibration/profile provided to verify against");
6268 
6269 	if (verify && installprofile == 1)
6270 		error("Can't verify and install a displays profile at the same time");
6271 
6272 	if (verify && installprofile == 2)
6273 		error("Can't verify and uninstall a displays profile at the same time");
6274 
6275 	/* Don't create a window if it won't be used */
6276 	if (ramd != 0 || sname[0] != '\000' || clear != 0 || verify != 0 || loadfile != 0 || installprofile != 0 || loadprofile != 0)
6277 		nowin = 1;
6278 
6279 	if (webdisp != 0) {
6280 		if ((dw = new_webwin(webdisp, 100.0 * hpatscale, 100.0 * vpatscale, ho, vo, nowin, native,
6281 			                        &noramdac, &nocm, out_tvenc, fullscreen, verb, ddebug)) == NULL) {
6282 			printf("Error - new_webwin failed!\n");
6283 			return -1;
6284 		}
6285 
6286 	} else if (ccdisp != 0) {
6287 		ccast_id **ids;
6288 		if ((ids = get_ccids()) == NULL) {
6289 			printf("Error - discovering ChromCasts failed\n");
6290 			return -1;
6291 		}
6292 		if (ids[0] == NULL) {
6293 			printf("Error - there are no ChromCasts to use\n");
6294 			return -1;
6295 		}
6296 		for (i = 0; ids[i] != NULL; i++)
6297 			;
6298 		if (ccdisp < 1 || ccdisp > i) {
6299 			printf("Error - chosen ChromCasts (%d) is outside list (1..%d)\n",ccdisp,i);
6300 			return -1;
6301 		}
6302 
6303 		if ((dw = new_ccwin(ids[ccdisp-1], 100.0 * hpatscale, 100.0 * vpatscale,
6304 			                   ho, vo, nowin, native, &noramdac, &nocm, out_tvenc,
6305 			                   fullscreen, 0, verb, ddebug)) == NULL) {
6306 			printf("Error - new_ccwin failed!\n");
6307 			free_ccids(ids);
6308 			return -1;
6309 		}
6310 		free_ccids(ids);
6311 
6312 #ifdef NT
6313 	} else if (madvrdisp != 0) {
6314 		if (out_tvenc) {
6315 			printf("Error - Set TV encodfing in MadVR\n");
6316 			return -1;
6317 		}
6318 
6319 		if ((dw = new_madvrwin(100.0 * hpatscale, 100.0 * vpatscale, ho, vo, nowin, native,
6320 			                   &noramdac, &nocm, out_tvenc, fullscreen, verb, ddebug)) == NULL) {
6321 			printf("Error - new_madvrwin failed! (Is it running and up to date?)\n");
6322 			return -1;
6323 		}
6324 #endif
6325 	} else {
6326 		if (verb) printf("About to open dispwin object on the display\n");
6327 		if ((dw = new_dispwin(disp, 100.0 * hpatscale, 100.0 * vpatscale, ho, vo, nowin, native,
6328 			                         &noramdac, &nocm, out_tvenc, fullscreen, 1, ddebug)) == NULL) {
6329 			printf("Error - new_dispwin failed!\n");
6330 			return -1;
6331 		}
6332 	}
6333 
6334 	if ((native & 1) != 0 && noramdac) {
6335 		warning("Unable to access to VideoLUTs so can't be sure colors are native");
6336 	}
6337 
6338 	/* Save the current Video LUT to the calfile */
6339 	if (sname[0] != '\000') {
6340 		cgats *ocg;			/* output cgats structure */
6341 		time_t clk = time(0);
6342 		struct tm *tsp = localtime(&clk);
6343 		char *atm = asctime(tsp);	/* Ascii time */
6344 		cgats_set_elem *setel;		/* Array of set value elements */
6345 		int nsetel = 0;
6346 
6347 		if (verb)
6348 			printf("About to save current loaded calibration to file '%s'\n",sname);
6349 
6350 		if (dw->oor == NULL) {
6351 			error("We don't have access to the VideoLUT");
6352 		}
6353 
6354 		ocg = new_cgats();				/* Create a CGATS structure */
6355 		ocg->add_other(ocg, "CAL"); 	/* our special type is Calibration file */
6356 
6357 		ocg->add_table(ocg, tt_other, 0);	/* Add a table for RAMDAC values */
6358 		ocg->add_kword(ocg, 0, "DESCRIPTOR", "Argyll Device Calibration Curves",NULL);
6359 		ocg->add_kword(ocg, 0, "ORIGINATOR", "Argyll synthcal", NULL);
6360 		atm[strlen(atm)-1] = '\000';	/* Remove \n from end */
6361 		ocg->add_kword(ocg, 0, "CREATED",atm, NULL);
6362 
6363 		ocg->add_kword(ocg, 0, "DEVICE_CLASS","DISPLAY", NULL);
6364 		ocg->add_kword(ocg, 0, "COLOR_REP", "RGB", NULL);
6365 
6366 		ocg->add_field(ocg, 0, "RGB_I", r_t);
6367 		ocg->add_field(ocg, 0, "RGB_R", r_t);
6368 		ocg->add_field(ocg, 0, "RGB_G", r_t);
6369 		ocg->add_field(ocg, 0, "RGB_B", r_t);
6370 
6371 		if ((setel = (cgats_set_elem *)malloc(sizeof(cgats_set_elem) * 4)) == NULL)
6372 			error("Malloc failed!");
6373 
6374 		/* Write the video lut curve values */
6375 		for (i = 0; i < dw->oor->nent; i++) {
6376 			double iv = i/(dw->oor->nent-1.0);
6377 
6378 #if defined(__APPLE__) && defined(__POWERPC__)
6379 			gcc_bug_fix(i);
6380 #endif
6381 			setel[0].d = iv;
6382 			setel[1].d = dw->oor->v[0][i];
6383 			setel[2].d = dw->oor->v[1][i];
6384 			setel[3].d = dw->oor->v[2][i];
6385 
6386 			ocg->add_setarr(ocg, 0, setel);
6387 		}
6388 
6389 		free(setel);
6390 
6391 		if (ocg->write_name(ocg, sname))
6392 			error("Write error to '%s' : %s",sname,ocg->err);
6393 
6394 		ocg->del(ocg);		/* Clean up */
6395 
6396 		/* Fall through, as we may want to do other stuff too */
6397 	}
6398 
6399 	/* Clear the display calibration curve */
6400 	if (clear != 0) {
6401 		int rv;
6402 
6403 		if (dw->or == NULL)
6404 			error("We don't have access to the VideoLUT for clearing");
6405 
6406 		for (i = 0; i < dw->or->nent; i++) {
6407 			double iv = i/(dw->or->nent-1.0);
6408 			dw->or->v[0][i] = iv;
6409 			dw->or->v[1][i] = iv;
6410 			dw->or->v[2][i] = iv;
6411 		}
6412 		if (verb)
6413 			printf("About to clear the calibration\n");
6414 		if ((rv = dw->set_ramdac(dw,dw->or,1)) != 0) {
6415 			if (rv == 2)
6416 				warning("Failed to set VideoLUTs persistently because current System Profile can't be renamed");
6417 			else
6418 				error("Failed to set VideoLUTs");
6419 		}
6420 
6421 		/* Fall through, as we may want to do other stuff too */
6422 	}
6423 
6424 	/* Un-Install the profile from the display */
6425 	if (installprofile == 2) {
6426 		int rv;
6427 		if ((rv = dw->uninstall_profile(dw, calname, scope))) {
6428 			if (rv == 2)
6429 				warning("Profile '%s' not found to uninstall!",calname);
6430 			else
6431 				error("Error trying to uninstall profile '%s'!",calname);
6432 		}
6433 		if (verb) {
6434 			printf("Un-Installed '%s'\n",calname);
6435 		}
6436 	}
6437 
6438 	/* Get any calibration from the provided .cal file or .profile, */
6439 	/* or calibration from the current default display profile, */
6440 	/* and put it in dw->or */
6441 	if (loadfile != 0 || verify != 0 || loadprofile != 0 || installprofile == 1) {
6442 		icmFile *rd_fp = NULL;
6443 		icc *icco = NULL;
6444 		cgats *ccg = NULL;			/* calibration cgats structure */
6445 
6446 		if (dw->r == NULL)
6447 			error("We don't have access to the VideoLUT for loading");
6448 
6449 		/* Should we load calfile instead of installed profile if it's present ??? */
6450 		if (loadprofile) {
6451 			if (calname[0] != '\000')
6452 				warning("Profile '%s' provided as argument is being ignored!",calname);
6453 
6454 			/* Get the current displays profile */
6455 			debug2((errout,"Loading calibration from display profile '%s'\n",dw->name));
6456 			if ((rd_fp = dw->get_profile(dw, calname, MAXNAMEL)) == NULL)
6457 				error("Failed to get the displays current ICC profile\n");
6458 
6459 		} else {
6460 			/* Open up the profile for reading */
6461 			debug2((errout,"Loading calibration from file '%s'\n",calname));
6462 			if ((rd_fp = new_icmFileStd_name(calname,"r")) == NULL)
6463 				error("Can't open file '%s'",calname);
6464 		}
6465 
6466 		if ((icco = new_icc()) == NULL)
6467 			error("Creation of ICC object failed");
6468 
6469 		/* Read header etc. */
6470 		if (icco->read(icco, rd_fp,0) == 0) {		/* Read ICC OK */
6471 			icmVideoCardGamma *wo;
6472 			double iv;
6473 
6474 			is_ok_icc = 1;			/* The profile is OK */
6475 
6476 			if ((wo = (icmVideoCardGamma *)icco->read_tag(icco, icSigVideoCardGammaTag)) == NULL) {
6477 				warning("No vcgt tag found in profile - assuming linear");
6478 				for (i = 0; i < dw->r->nent; i++) {
6479 					iv = i/(dw->r->nent-1.0);
6480 					dw->r->v[0][i] = iv;
6481 					dw->r->v[1][i] = iv;
6482 					dw->r->v[2][i] = iv;
6483 				}
6484 			} else {
6485 
6486 				if (wo->u.table.channels == 3) {
6487 					for (i = 0; i < dw->r->nent; i++) {
6488 						iv = i/(dw->r->nent-1.0);
6489 						dw->r->v[0][i] = wo->lookup(wo, 0, iv);
6490 						dw->r->v[1][i] = wo->lookup(wo, 1, iv);
6491 						dw->r->v[2][i] = wo->lookup(wo, 2, iv);
6492 //printf("~1 entry %d = %f %f %f\n",i,dw->r->v[0][i],dw->r->v[1][i],dw->r->v[2][i]);
6493 					}
6494 					debug("Got color vcgt calibration\n");
6495 				} else if (wo->u.table.channels == 1) {
6496 					for (i = 0; i < dw->r->nent; i++) {
6497 						iv = i/(dw->r->nent-1.0);
6498 						dw->r->v[0][i] =
6499 						dw->r->v[1][i] =
6500 						dw->r->v[2][i] = wo->lookup(wo, 0, iv);
6501 					}
6502 					debug("Got monochrom vcgt calibration\n");
6503 				}
6504 				/* ~~~ Ideally the vcgt should have been tagged if it is TV encoded, so */
6505 				/* that the scaling can be adjusted here if the RAMDAC depth differs from */
6506 				/* the vcgt depth. ~~~ */
6507 			}
6508 		} else {	/* See if it's a .cal file */
6509 			int ncal;
6510 			int ii, fi, ri, gi, bi;
6511 			double cal[3][MAX_CAL_ENT];
6512 			int out_tvenc = 0;			/* nz to use (16-235)/255 video encoding */
6513 
6514 			icco->del(icco);			/* Don't need these now */
6515 			icco = NULL;
6516 			rd_fp->del(rd_fp);
6517 			rd_fp = NULL;
6518 
6519 			ccg = new_cgats();			/* Create a CGATS structure */
6520 			ccg->add_other(ccg, "CAL"); /* our special calibration type */
6521 
6522 			if (ccg->read_name(ccg, calname)) {
6523 				ccg->del(ccg);
6524 				error("File '%s' is not a valid ICC profile or Argyll .cal file",calname);
6525 			}
6526 
6527 			if (ccg->ntables == 0 || ccg->t[0].tt != tt_other || ccg->t[0].oi != 0)
6528 				error("Calibration file isn't a CAL format file");
6529 			if (ccg->ntables < 1)
6530 				error("Calibration file '%s' doesn't contain at least one table",calname);
6531 
6532 			if ((ncal = ccg->t[0].nsets) <= 0)
6533 				error("No data in set of file '%s'",calname);
6534 
6535 			if (ncal < 2 || ncal > MAX_CAL_ENT)
6536 				error("Data set size %d is out of range for '%s'",ncal,calname);
6537 
6538 			if ((fi = ccg->find_kword(ccg, 0, "DEVICE_CLASS")) < 0)
6539 				error("Calibration file '%s' doesn't contain keyword COLOR_REP",calname);
6540 			if (strcmp(ccg->t[0].kdata[fi],"DISPLAY") != 0)
6541 				error("Calibration file '%s' doesn't have DEVICE_CLASS of DISPLAY",calname);
6542 
6543 			if ((fi = ccg->find_kword(ccg, 0, "TV_OUTPUT_ENCODING")) >= 0) {
6544 				if (strcmp(ccg->t[0].kdata[fi], "YES") == 0
6545 				 || strcmp(ccg->t[0].kdata[fi], "yes") == 0)
6546 					out_tvenc = 1;
6547 			}
6548 
6549 			if ((ii = ccg->find_field(ccg, 0, "RGB_I")) < 0)
6550 				error("Calibration file '%s' doesn't contain field RGB_I",calname);
6551 			if (ccg->t[0].ftype[ii] != r_t)
6552 				error("Field RGB_R in file '%s' is wrong type",calname);
6553 			if ((ri = ccg->find_field(ccg, 0, "RGB_R")) < 0)
6554 				error("Calibration file '%s' doesn't contain field RGB_R",calname);
6555 			if (ccg->t[0].ftype[ri] != r_t)
6556 				error("Field RGB_R in file '%s' is wrong type",calname);
6557 			if ((gi = ccg->find_field(ccg, 0, "RGB_G")) < 0)
6558 				error("Calibration file '%s' doesn't contain field RGB_G",calname);
6559 			if (ccg->t[0].ftype[gi] != r_t)
6560 				error("Field RGB_G in file '%s' is wrong type",calname);
6561 			if ((bi = ccg->find_field(ccg, 0, "RGB_B")) < 0)
6562 				error("Calibration file '%s' doesn't contain field RGB_B",calname);
6563 			if (ccg->t[0].ftype[bi] != r_t)
6564 				error("Field RGB_B in file '%s' is wrong type",calname);
6565 			for (i = 0; i < ncal; i++) {
6566 				cal[0][i] = *((double *)ccg->t[0].fdata[i][ri]);
6567 				cal[1][i] = *((double *)ccg->t[0].fdata[i][gi]);
6568 				cal[2][i] = *((double *)ccg->t[0].fdata[i][bi]);
6569 			}
6570 
6571 			/* Interpolate from cal value to RAMDAC entries */
6572 			for (i = 0; i < dw->r->nent; i++) {
6573 				double val, w;
6574 				unsigned int ix;
6575 
6576 				val = (ncal-1.0) * i/(dw->r->nent-1.0);
6577 				ix = (unsigned int)floor(val);		/* Coordinate */
6578 				if (ix > (ncal-2))
6579 					ix = (ncal-2);
6580 				w = val - (double)ix;		/* weight */
6581 				for (j = 0; j < 3; j++) {
6582 					val = cal[j][ix];
6583 					dw->r->v[j][i] = val + w * (cal[j][ix+1] - val);
6584 				}
6585 			}
6586 			/* If the calibration was created with a restricted range video encoding, */
6587 			/* ensure that the installed calibration applies this encoding. */
6588 			if (out_tvenc) {
6589 				for (i = 0; i < dw->r->nent; i++) {
6590 					for (j = 0; j < 3; j++) {
6591 						dw->r->v[j][i] = (dw->r->v[j][i] * (235.0-16.0) + 16.0)/255.0;
6592 
6593 						/* For video encoding the extra bits of precision are created by bit */
6594 						/* shifting rather than scaling, so we need to scale the fp value to */
6595 						/* account for this. */
6596 						if (dw->edepth > 8)
6597 							dw->r->v[j][i] = (dw->r->v[j][i] * 255 * (1 << (dw->edepth - 8)))
6598 				                       /((1 << dw->edepth) - 1.0);
6599 					}
6600 				}
6601 			}
6602 
6603 			debug("Got cal file calibration\n");
6604 		}
6605 		if (ccg != NULL)
6606 			ccg->del(ccg);
6607 		if (icco != NULL)
6608 			icco->del(icco);
6609 		if (rd_fp != NULL)
6610 			rd_fp->del(rd_fp);
6611 	}
6612 
6613 	/* Install the profile into the display and set as the default */
6614 	if (installprofile == 1) {
6615 		if (is_ok_icc == 0)
6616 			error("File '%s' doesn't seem to be an ICC profile!",calname);
6617 
6618 		if (dw->r== NULL) {
6619 			warning("Unable to access VideoLUT so can't install calibration");
6620 		} else {
6621 			if (verb)
6622 				printf("About to install '%s' as display's default profile\n",calname);
6623 			if (dw->or)
6624 				dw->or->del(dw->or);
6625 			if ((dw->or = dw->r->clone(dw->r)) == NULL)
6626 				error("Failed to clone VideoLUT - memory ?");
6627 			if (dw->install_profile(dw, calname, dw->or, scope))
6628 				error("Failed to install profile '%s'!",calname);
6629 			if (verb)
6630 				printf("Installed '%s' and made it the default\n",calname);
6631 		}
6632 
6633 	/* load the LUT with the calibration from the given file or the current display profile. */
6634 	} else if (loadfile != 0 || loadprofile != 0) {
6635 		int rv;
6636 
6637 		if (dw->or == NULL) {
6638 			warning("Calibration not loaded because there is no access to the VideoLUT");
6639 		} else {
6640 			if (verb)
6641 				printf("About to set display to given calibration\n");
6642 			if (dw->or)
6643 				dw->or->del(dw->or);
6644 			if ((dw->or = dw->r->clone(dw->r)) == NULL)
6645 				error("Failed to clone VideoLUT - memory ?");
6646 			if ((rv = dw->set_ramdac(dw,dw->or,1)) != 0) {
6647 				if (rv == 2)
6648 					error("Failed to set VideoLUTs persistently because current System Profile can't be renamed");
6649 				else
6650 					error("Failed to set VideoLUTs");
6651 			}
6652 			if (verb)
6653 				printf("Calibration set\n");
6654 		}
6655 	}
6656 
6657 	if (verify != 0) {
6658 		int ver = 1;
6659 		double berr = 0.0;
6660 
6661 		if (dw->oor == NULL)
6662 			error("Unable to get original VideoLUT for verify");
6663 
6664 		if (dw->r == NULL)
6665 			error("No calibration to verify against");
6666 
6667 		if (dw->r->nent != dw->oor->nent)
6668 			error("VideoLUTs have different size");
6669 
6670 		for (j = 0; j < 3; j++) {
6671 			for (i = 0; i < dw->oor->nent; i++) {
6672 				double err;
6673 				err = fabs(dw->oor->v[j][i] - dw->r->v[j][i]);
6674 				if (err > berr)
6675 					berr = err;
6676 				if (err > VERIFY_TOL) {
6677 					ver = 0;
6678 				}
6679 			}
6680 		}
6681 		if (ver)
6682 			printf("Verify: '%s' IS loaded (discrepancy %.1f%%)\n", calname, berr * 100);
6683 		else
6684 			printf("Verify: '%s' is NOT loaded (discrepancy %.1f%%)\n", calname, berr * 100);
6685 	}
6686 
6687 
6688 	/* If no other command selected, do a Window or VideoLUT test */
6689 	if (sname[0] == '\000' && clear == 0 && installprofile == 0 && loadfile == 0 && verify == 0 && loadprofile == 0) {
6690 
6691 		if (ramd == 0) {
6692 
6693 			if (fade) {
6694 				int i;
6695 				int steps = 256;
6696 				for (i = 0; i < steps; i++) {
6697 					double tt;
6698 					tt = i/(steps - 1.0);
6699 					dw->set_color(dw, tt, tt, tt);
6700 					msec_sleep(20);
6701 					printf("Val = %f\n",tt);
6702 				}
6703 
6704 			/* Patch colors from a CGATS file */
6705 			} else if (pcname[0] != '\000') {
6706 				cgats *icg;
6707 				int i, npat;
6708 				int ri, gi, bi;
6709 				int si = -1;
6710 
6711 				if ((icg = new_cgats()) == NULL)
6712 					error("new_cgats() failed\n");
6713 				icg->add_other(icg, "");		/* Allow any signature file */
6714 
6715 				if (icg->read_name(icg, pcname))
6716 					error("File '%s' read error : %s",pcname, icg->err);
6717 
6718 				if (icg->ntables < 1)		/* We don't use second table at the moment */
6719 					error ("Input file '%s' doesn't contain at least one table",pcname);
6720 
6721 				if ((npat = icg->t[0].nsets) <= 0)
6722 					error ("File '%s has no sets of data in the first table",pcname);
6723 
6724 				si = icg->find_field(icg, 0, "SAMPLE_ID");
6725 				if (si >= 0 && icg->t[0].ftype[si] != nqcs_t)
6726 					error("In file '%s' field SAMPLE_ID is wrong type",pcname);
6727 
6728 				if ((ri = icg->find_field(icg, 0, "RGB_R")) < 0)
6729 					error ("Input file '%s' doesn't contain field RGB_R",pcname);
6730 				if (icg->t[0].ftype[ri] != r_t)
6731 					error ("In file '%s' field RGB_R is wrong type",pcname);
6732 				if ((gi = icg->find_field(icg, 0, "RGB_G")) < 0)
6733 					error ("Input file '%s' doesn't contain field RGB_G",pcname);
6734 				if (icg->t[0].ftype[gi] != r_t)
6735 					error ("In file '%s' field RGB_G is wrong type",pcname);
6736 				if ((bi = icg->find_field(icg, 0, "RGB_B")) < 0)
6737 					error ("Input file '%s' doesn't contain field RGB_B",pcname);
6738 				if (icg->t[0].ftype[bi] != r_t)
6739 					error ("In file '%s' field RGB_B is wrong type",pcname);
6740 
6741 				if (inf == 2)
6742 					printf("\nHit return to advance each color\n");
6743 
6744 					if (inf == 2) {
6745 						printf("\nHit return to start\n");
6746 						getchar();
6747 					}
6748 				for (i = 0; i < npat; i++) {
6749 					double r, g, b;
6750 					r = *((double *)icg->t[0].fdata[i][ri]) / 100.0;
6751 					g = *((double *)icg->t[0].fdata[i][gi]) / 100.0;
6752 					b = *((double *)icg->t[0].fdata[i][bi]) / 100.0;
6753 
6754 					if (si >= 0)
6755 						printf("Patch id '%s'",((char *)icg->t[0].fdata[i][si]));
6756 					else
6757 						printf("Patch no %d",i+1);
6758 					printf(" color %f %f %f\n",r,g,b);
6759 
6760 					if (dw->set_color(dw, r, g, b) != 0) {
6761 						error ("set_color failed");
6762 					}
6763 
6764 					if (inf == 2)
6765 						getchar();
6766 					else
6767 						sleep(2);
6768 				}
6769 				icg->del(icg);
6770 
6771 			/* Manually define patche colors */
6772 			} else if (nmrgb > 0) {
6773 				int i;
6774 				int ri, gi, bi;
6775 
6776 				if (inf == 2)
6777 					printf("\nHit return to advance each color\n");
6778 
6779 					if (inf == 2) {
6780 						printf("\nHit return to start\n");
6781 						getchar();
6782 					}
6783 				for (i = 0; i < nmrgb; i++) {
6784 					double r, g, b;
6785 					r = mrgb[i][0];
6786 					g = mrgb[i][1];
6787 					b = mrgb[i][2];
6788 
6789 					printf("Patch no %d",i+1);
6790 					printf(" color %f %f %f\n",r,g,b);
6791 
6792 					if (dw->set_color(dw, r, g, b) != 0) {
6793 						error ("set_color failed");
6794 					}
6795 
6796 					if (inf == 2)
6797 						getchar();
6798 					else
6799 						sleep(2);
6800 				}
6801 
6802 			/* Preset and random patch colors */
6803 			} else {
6804 
6805 				if (inf == 2)
6806 					printf("\nHit return to advance each color\n");
6807 
6808 				printf("Setting White\n");
6809 				dw->set_color(dw, 1.0, 1.0, 1.0);	/* White */
6810 
6811 				if (inf == 2)
6812 					getchar();
6813 				else
6814 					sleep(2);
6815 
6816 				printf("Setting 75%% Grey\n");
6817 				dw->set_color(dw, 0.75, 0.75, 0.75);	/* Grey */
6818 
6819 				if (inf == 2)
6820 					getchar();
6821 				else
6822 					sleep(2);
6823 
6824 				printf("Setting 50%% Grey\n");
6825 				dw->set_color(dw, 0.5, 0.5, 0.5);	/* Grey */
6826 
6827 				if (inf == 2)
6828 					getchar();
6829 				else
6830 					sleep(2);
6831 
6832 				printf("Setting 25%% Grey\n");
6833 				dw->set_color(dw, 0.25, 0.25, 0.25);	/* Grey */
6834 
6835 				if (inf == 2)
6836 					getchar();
6837 				else
6838 					sleep(2);
6839 
6840 				printf("Setting 12.5%% Grey\n");
6841 				dw->set_color(dw, 0.125, 0.125, 0.125);	/* Grey */
6842 
6843 				if (inf == 2)
6844 					getchar();
6845 				else
6846 					sleep(2);
6847 
6848 				printf("Setting Black\n");
6849 				dw->set_color(dw, 0.0, 0.0, 0.0);
6850 
6851 				if (inf == 2)
6852 					getchar();
6853 				else
6854 					sleep(2);
6855 
6856 				printf("Setting Red\n");
6857 				dw->set_color(dw, 1.0, 0.0, 0.0);	/* Red */
6858 
6859 				if (inf == 2)
6860 					getchar();
6861 				else
6862 					sleep(2);
6863 
6864 				printf("Setting Green\n");
6865 				dw->set_color(dw, 0.0, 1.0,  0.0);	/* Green */
6866 
6867 				if (inf == 2)
6868 					getchar();
6869 				else
6870 					sleep(2);
6871 
6872 				printf("Setting Blue\n");
6873 				dw->set_color(dw, 0.0, 0.0, 1.0);	/* Blue */
6874 
6875 				if (inf == 2)
6876 					getchar();
6877 				else
6878 					sleep(2);
6879 
6880 				printf("Setting Cyan\n");
6881 				dw->set_color(dw, 0.0, 1.0, 1.0);	/* Cyan */
6882 
6883 				if (inf == 2)
6884 					getchar();
6885 				else
6886 					sleep(2);
6887 
6888 				printf("Setting Magenta\n");
6889 				dw->set_color(dw, 1.0, 0.0,  1.0);	/* Magenta */
6890 
6891 				if (inf == 2)
6892 					getchar();
6893 				else
6894 					sleep(2);
6895 
6896 				printf("Setting Yellow\n");
6897 				dw->set_color(dw, 1.0, 1.0, 0.0);	/* Yellow */
6898 
6899 				if (inf == 2)
6900 					getchar();
6901 				else
6902 					sleep(2);
6903 
6904 				printf("Setting 50%% Red\n");
6905 				dw->set_color(dw, 0.5, 0.0, 0.0);	/* Red */
6906 
6907 				if (inf == 2)
6908 					getchar();
6909 				else
6910 					sleep(2);
6911 
6912 				printf("Setting 50%% Green\n");
6913 				dw->set_color(dw, 0.0, 0.5,  0.0);	/* Green */
6914 
6915 				if (inf == 2)
6916 					getchar();
6917 				else
6918 					sleep(2);
6919 
6920 				printf("Setting 50%% Blue\n");
6921 				dw->set_color(dw, 0.0, 0.0, 0.5);	/* Blue */
6922 
6923 				if (inf == 2)
6924 					getchar();
6925 				else
6926 					sleep(2);
6927 
6928 				if (inf == 1) {
6929 					for (;inf != 0;) {
6930 						double col[3];
6931 
6932 						for (i = 0; i < 3; i++) {
6933 							seed = PSRAND32(seed);
6934 							col[i] = seed/4294967295.0;
6935 						}
6936 
6937 						printf("Setting %f %f %f\n",col[0],col[1],col[2]);
6938 						dw->set_color(dw, col[0],col[1],col[2]);
6939 
6940 						if (inf == 2)
6941 							getchar();
6942 						else
6943 							sleep(2);
6944 
6945 					}
6946 				}
6947 			}
6948 		}
6949 
6950 		if (inf != 2) {
6951 			/* Test out the VideoLUT access */
6952 			if (dw->r != NULL) {	/* Working ramdac to use */
6953 
6954 				/* Try darkening it */
6955 				for (j = 0; j < 3; j++) {
6956 					for (i = 0; i < dw->r->nent; i++) {
6957 						dw->r->v[j][i] = pow(dw->or->v[j][i], 1.6);
6958 					}
6959 				}
6960 				printf("Darkening screen\n");
6961 				if (dw->set_ramdac(dw, dw->r, 0)) {
6962 					dw->set_ramdac(dw, dw->or, 0);		/* is this needed ? */
6963 					error("Failed to set VideoLUTs");
6964 				}
6965 				sleep(1);
6966 
6967 				/* Try lightening it */
6968 				for (j = 0; j < 3; j++) {
6969 					for (i = 0; i < dw->r->nent; i++) {
6970 						dw->r->v[j][i] = pow(dw->or->v[j][i], 0.625);
6971 					}
6972 				}
6973 				printf("Lightening screen\n");
6974 				if (dw->set_ramdac(dw,dw->r,0)) {
6975 					dw->set_ramdac(dw,dw->or,0);		/* is this needed ? */
6976 					error("Failed to set VideoLUTs");
6977 				}
6978 				sleep(1);
6979 
6980 				/* restor it */
6981 				printf("Restoring screen\n");
6982 				if (dw->set_ramdac(dw,dw->or,0)) {
6983 					error("Failed to set VideoLUTs");
6984 				}
6985 
6986 			} else {
6987 				printf("We don't have access to the VideoLUT\n");
6988 			}
6989 
6990 			/* Test out the beeps */
6991 			printf("Normal beep\n");
6992 			normal_beep();
6993 
6994 			sleep(1);
6995 
6996 			printf("Good beep\n");
6997 			good_beep();
6998 
6999 			sleep(1);
7000 
7001 			printf("Bad double beep\n");
7002 			bad_beep();
7003 
7004 			sleep(2);		/* Allow beep to complete */
7005 		}
7006 	}
7007 
7008 	if (disp != NULL)
7009 		free_a_disppath(disp);
7010 
7011 	if (verb)
7012 		printf("About to destroy dispwin object\n");
7013 
7014 	dw->del(dw);
7015 
7016 	return 0;
7017 }
7018 
7019 #endif /* STANDALONE_TEST */
7020 
7021 /* ================================================================ */
7022 /* Possible ThinkPad MSWin code to keep the main screen on */
7023 
7024 #ifdef NEVER
7025 
7026 Public Class Form1
7027     Declare Sub XRCalibrationLidTurnOnNotification Lib "C:\Program Files (x86)\X-Rite\PANTONE Color Calibrator\XRLaptopIFSDK.dll" ()
7028     Declare Sub XRCalibrationLidTurnOffNotification Lib "C:\Program Files (x86)\X-Rite\PANTONE Color Calibrator\XRLaptopIFSDK.dll" ()
7029 
7030     Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
7031 
7032     End Sub
7033 
7034     Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
7035         Call XRCalibrationLidTurnOnNotification()
7036     End Sub
7037 
7038     Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
7039         Call XRCalibrationLidTurnOffNotification()
7040     End Sub
7041 End Class
7042 
7043 #endif
7044 
7045 /* ================================================================ */
7046 /* Unused Apple code */
7047 
7048 #ifdef NEVER
7049 	/* Got displays, now have a look through them */
7050 	for (i = 0; i < dcount; i++) {
7051 		GDHandle gdh;
7052 		GDPtr gdp;
7053 
7054 		/* Dump display mode dictionary */
7055 		CFIndex nde, j;
7056 		CFDictionaryRef dr;
7057 		void **keys, **values;
7058 
7059 		dr = CGDisplayCurrentMode(dids[i]);
7060 		nde = CFDictionaryGetCount(dr);
7061 
7062 		printf("Dict contains %d entries \n", nde);
7063 		if ((keys = (void **)malloc(nde * sizeof(void *))) == NULL) {
7064 			debug("malloc failed for disp mode keys\n");
7065 			free_disppaths(disps);
7066 			free(dids);
7067 			return NULL;
7068 		}
7069 		if ((values = (void **)malloc(nde * sizeof(void *))) == NULL) {
7070 			debug("malloc failed for disp mode values\n");
7071 			free(keys);
7072 			free_disppaths(disps);
7073 			free(dids);
7074 			return NULL;
7075 		}
7076 		CFDictionaryGetKeysAndValues(dr, (const void **)keys, (const void **)values);
7077 		for (j = 0; j < nde; j++) {
7078 			printf("Entry %d key = %s\n", j, CFStringGetCStringPtr(keys[j], kCFStringEncodingMacRoman));
7079 		}
7080 		free(values);
7081 		free(keys);
7082 	}
7083 #endif
7084 
7085 
7086 #ifdef NEVER
7087 /* How to install profiles for devices. */
7088 
7089 /* Callback to locate a profile ID. */
7090 struct idp_rec {
7091 	CMDeviceID ddid;			/* Device ID */
7092 //	char *fname;				/* Profile we're trying to find */
7093 	CMDeviceProfileID id;		/* Corresponding ID */
7094 	CMDeviceScope dsc;			/* Matching device scope */
7095 	int found;					/* Flag indicating it's been found */
7096 };
7097 
ItDevProfProc(const CMDeviceInfo * di,const NCMDeviceProfileInfo * pi,void * refCon)7098 OSErr ItDevProfProc (
7099 const CMDeviceInfo *di,
7100 const NCMDeviceProfileInfo *pi,
7101 void *refCon)
7102 {
7103 	CMError ev;
7104 	struct idp_rec *r = (struct idp_rec *)refCon;
7105 	CMDeviceProfileID id;
7106 
7107 	if (di->deviceClass != cmDisplayDeviceClass
7108 	 || di->deviceID != r->ddid) {
7109 		return noErr;
7110 	}
7111 
7112 	/* We'd qualify on the device mode too (deviceState ??), */
7113 	/* if we wanted to replace a profile for a particular mode. */
7114 
7115 	/* Assume this is a display with only one mode, and return */
7116 	/* the profile id and device scope */
7117 	r->id = pi->profileID;
7118 	r->dsc = di->deviceScope;
7119 	r->found = 1;
7120 	return noErr;
7121 //printf("~1 got match\n");
7122 
7123 /* Callback to locate a profile ID. */
7124 struct idp_rec {
7125 	CMDeviceID ddid;			/* Device ID */
7126 	CMDeviceProfileID id;		/* Corresponding ID */
7127 	CMDeviceScope dsc;			/* Matching device scope */
7128 	int found;					/* Flag indicating it's been found */
7129 };
7130 
7131 OSErr ItDevProfProc (
7132 const CMDeviceInfo *di,
7133 const NCMDeviceProfileInfo *pi,
7134 void *refCon)
7135 {
7136 	CMError ev;
7137 	struct idp_rec *r = (struct idp_rec *)refCon;
7138 	CMDeviceProfileID id;
7139 
7140 	if (di->deviceClass != cmDisplayDeviceClass
7141 	 || di->deviceID != r->ddid) {
7142 		return noErr;
7143 	}
7144 
7145 	/* Assume this is a display with only one mode, and return */
7146 	/* the profile id and device scope */
7147 	r->id = pi->profileID;
7148 	r->dsc = di->deviceScope;
7149 	r->found = 1;
7150 	return noErr;
7151 }
7152 
7153 #ifndef NEVER
7154 
7155 /* Given a location, return a string for it's path */
7156 static char *plocpath(CMProfileLocation *ploc) {
7157 
7158 	if (ploc->locType == cmFileBasedProfile) {
7159 		FSRef newRef;
7160   		static UInt8 path[256] = "";
7161 //printf("~1 converted spec file location\n");
7162 
7163 		if (FSpMakeFSRef(&ploc->u.fileLoc.spec, &newRef) == noErr) {
7164 			OSStatus stus;
7165 			if ((stus = FSRefMakePath(&newRef, path, 256)) == 0 || stus == fnfErr) {
7166 				return path;
7167 			}
7168 		}
7169 		return strdup(path);
7170 	} else if (ploc->locType == cmPathBasedProfile) {
7171 		return strdup(ploc->u.pathLoc.path);
7172 	}
7173 	return NULL;
7174 }
7175 
7176 #else
7177 
7178 /* fss2path takes the FSSpec of a file, folder or volume and returns it's POSIX (?) path. */
7179 /* Return NULL on error. Free returned string */
7180 /* NOTE FSSpec is deprecated in 10.5. Replace with FSRef ?? */
7181 static char *fss2path(FSSpec *fss) {
7182 	int i, l;						 /* fss->name contains name of last item in path */
7183 	char *path;
7184 
7185 	l = fss->name[0];
7186 	if ((path = malloc(l + 1)) == NULL)
7187 		return NULL;
7188 	for (i = 0; i < l; i++) {
7189 		if (fss->name[i+1] == '/')
7190 			path[i] = ':';
7191 		else
7192 			path[i] = fss->name[i+1];
7193 	}
7194 	path[i] = '\000';
7195 //printf("~1 path = '%s', l = %d\n",path,l);
7196 
7197 	if(fss->parID != fsRtParID) { /* path is more than just a volume name */
7198 		FSSpec tfss;
7199 //		CInfoPBRec pb;
7200 		FSRefParam pb;
7201 		int tl;
7202 		char *tpath;
7203 
7204 		memmove(&tfss, fss, sizeof(FSSpec));		/* Copy so we can modify */
7205 		memset(&pb, 0, sizeof(FSRefParam));
7206 		pb.ioNamePtr = tfss.name;
7207 		pb.ioVRefNum = tfss.vRefNum;
7208 		pb.ioDrParID = tfss.parID;
7209 		do {
7210 			pb.ioFDirIndex = -1;	/* get parent directory name */
7211 			pb.ioDrDirID = pb.dirInfo.ioDrParID;
7212 			if(PBGetCatlogInfoSync(&pb) != noErr) {
7213 				free(path);
7214 				return NULL;
7215 			}
7216 
7217 			/* Pre-pend the directory name separated by '/' */
7218 			if (pb.dirInfo.ioDrDirID == fsRtDirID) {
7219 				tl = 0;			/* Don't pre-pend volume name */
7220 			} else {
7221 				tl = tfss.name[0];
7222 			}
7223 			if ((tpath = malloc(tl + l + 1)) == NULL) {
7224 				free(path);
7225 			 	return NULL;
7226 			}
7227 			for (i = 0; i < tl; i++) {
7228 				if (tfss.name[i+1] == '/')
7229 					tpath[i] = ':';
7230 				else
7231 					tpath[i] = tfss.name[i+1];
7232 			}
7233 			tpath[i] = '/';
7234 			for (i = 0; i < l; i++)
7235 				tpath[tl+1+i] = path[i];
7236 			tpath[tl+1+i] = '\000';
7237 			free(path);
7238 			path = tpath;
7239 			l = tl + 1 + l;
7240 //printf("~1 path = '%s', l = %d\n",path,l);
7241 		} while(pb.dirInfo.ioDrDirID != fsRtDirID); /* while more directory levels */
7242 	}
7243 
7244 	return path;
7245 }
7246 
7247 /* Return a string containing the profiles path. */
7248 /* Return NULL on error. Free the string after use. */
7249 static char *plocpath(CMProfileLocation *ploc) {
7250 	if (ploc->locType == cmFileBasedProfile) {
7251 		return fss2path(&ploc->u.fileLoc.spec);
7252 	} else if (ploc->locType == cmPathBasedProfile) {
7253 		return strdup(ploc->u.pathLoc.path);
7254 	}
7255 	return NULL;
7256 }
7257 
7258 #endif
7259 
7260 /* Test code that checks what the current display default profile is, three ways */
7261 static void pcurpath(dispwin *p) {
7262 		CMProfileRef xprof;			/* Current AVID profile */
7263 		CMProfileLocation xploc;		/* Current profile location */
7264 		UInt32 xplocsz = sizeof(CMProfileLocation);
7265 		struct idp_rec cb;
7266 		CMError ev;
7267 
7268 		/* Get the current installed profile */
7269 		if ((ev = CMGetProfileByAVID((CMDisplayIDType)p->ddid, &xprof)) != noErr) {
7270 			debug2((errout,"CMGetProfileByAVID() failed with error %d\n",ev));
7271 			goto skip;
7272 		}
7273 
7274 		/* Get the current installed  profile's location */
7275 		if ((ev = NCMGetProfileLocation(xprof, &xploc, &xplocsz)) != noErr) {
7276 			debug2((errout,"NCMGetProfileLocation() failed with error %d\n",ev));
7277 			goto skip;
7278 		}
7279 
7280 //printf("~1 Current profile by AVID = '%s'\n",plocpath(&xploc));
7281 
7282 		/* Get the current CMDeviceProfileID and device scope */
7283 		cb.ddid = (CMDeviceID)p->ddid;			/* Display Device ID */
7284 		cb.found = 0;
7285 
7286 		if ((ev = CMIterateDeviceProfiles(ItDevProfProc, NULL, NULL, cmIterateAllDeviceProfiles, (void *)&cb)) != noErr) {
7287 			debug2((errout,"CMIterateDeviceProfiles() failed with error %d\n",ev));
7288 			goto skip;
7289 		}
7290 		if (cb.found == 0) {
7291 			debug2((errout,"Failed to find exsiting profiles ID\n"));
7292 			goto skip;
7293 		}
7294 		/* Got cb.id */
7295 
7296 		if ((ev = CMGetDeviceProfile(cmDisplayDeviceClass, (CMDeviceID)p->ddid, cb.id, &xploc)) != noErr) {
7297 			debug2((errout,"Failed to GetDeviceProfile\n"));
7298 			goto skip;
7299 		}
7300 //printf("~1 Current profile by Itterate = '%s'\n",plocpath(&xploc));
7301 
7302 		/* Get the default ID for the display */
7303 		if ((ev = CMGetDeviceDefaultProfileID(cmDisplayDeviceClass, (CMDeviceID)p->ddid, &cb.id)) != noErr) {
7304 			debug2((errout,"CMGetDeviceDefaultProfileID() failed with error %d\n",ev));
7305 			goto skip;
7306 		}
7307 
7308 		/* Get the displays default profile */
7309 		if ((ev = CMGetDeviceProfile(cmDisplayDeviceClass, (CMDeviceID)p->ddid, cb.id, &xploc)) != noErr) {
7310 			debug2((errout,"CMGetDeviceDefaultProfileID() failed with error %d\n",ev));
7311 			goto skip;
7312 		}
7313 //printf("~1 Current profile by get default = '%s'\n",plocpath(&xploc));
7314 
7315 	skip:;
7316 }
7317 	/* If we want the path to the profile, we'd do this: */
7318 
7319 	printf("id = 0x%x\n",pi->profileID);
7320 	if (pi->profileLoc.locType == cmFileBasedProfile) {
7321 		FSRef newRef;
7322   		UInt8 path[256] = "";
7323 
7324 		if (FSpMakeFSRef(&pi->profileLoc.u.fileLoc.spec, &newRef) == noErr) {
7325 			OSStatus stus;
7326 			if ((stus = FSRefMakePath(&newRef, path, 256)) == 0 || stus == fnfErr) {
7327 				printf("file = '%s'\n",path);
7328 				if (strcmp(r->fname, (char *)path) == 0) {
7329 					r->id = pi->profileID;
7330 					r->found = 1;
7331 					printf("got match\n");
7332 				}
7333 			}
7334 		}
7335 	} else if (pi->profileLoc.locType == cmPathBasedProfile) {
7336 		if (strcmp(r->fname, pi->profileLoc.u.pathLoc.path) == 0) {
7337 			r->id = pi->profileID;
7338 			r->dsc = di->deviceScope;
7339 			r->found = 1;
7340 			printf("got match\n");
7341 		}
7342 	}
7343 
7344 
7345 	{
7346 		struct idp_rec cb;
7347 
7348 		/* The CMDeviceProfileID wll always be 1 for a display, because displays have only one mode, */
7349 		/* so the Iterate could be avoided for it. */
7350 
7351 		cb.ddid = (CMDeviceID)p->ddid;			/* Display Device ID */
7352 //		cb.fname = dpath;
7353 		cb.found = 0;
7354 
7355 		if ((ev = CMIterateDeviceProfiles(ItDevProfProc, NULL, NULL, cmIterateAllDeviceProfiles, (void *)&cb)) != noErr) {
7356 			debug2((errout,"CMIterateDeviceProfiles() failed with error %d\n",dpath,ev));
7357 			return 1;
7358 		}
7359 		if (cb.found == 0) {
7360 			debug2((errout,"Failed to find exsiting  profiles ID, so ca't set it as default\n"));
7361 			return 1;
7362 		}
7363 		if ((ev = CMSetDeviceProfile(cmDisplayDeviceClass, (CMDeviceID)p->ddid, &cb.dsc, cb.id, &dploc)) != noErr) {
7364 			debug2((errout,"CMSetDeviceProfile() failed for file '%s' with error %d\n",dpath,ev));
7365 			return 1;
7366 		}
7367 		/* There is no point in doing the following, because displays only have one mode. */
7368 		/* Make it the default for the display */
7369 		if ((ev = CMSetDeviceDefaultProfileID(cmDisplayDeviceClass, (CMDeviceID)p->ddid, cb.id)) != noErr) {
7370 			debug2((errout,"CMSetDeviceDefaultProfileID() failed for file '%s' with error %d\n",dpath,ev));
7371 			return 1;
7372 		}
7373 
7374 #endif /* NEVER */
7375 
7376 #ifdef NEVER
7377 	/* Unused 10.6+ code */
7378 	CFUUIDRef dispuuid;
7379 	CFDictionaryRef dict, sdict, pdict;
7380 	CFStringRef id, urlstr;
7381 	CFURLRef url;
7382 	CFIndex bufSize;
7383 	char *dpath = NULL;			/* return value */
7384 
7385 	if ((dispuuid = CGDisplayCreateUUIDFromDisplayID(p->ddid)) == NULL) {
7386 		debugr2((errout,"CGDisplayCreateUUIDFromDisplayID() failed\n"));
7387 		return NULL;
7388 	}
7389 
7390 	if ((dict = ColorSyncDeviceCopyDeviceInfo(kColorSyncDisplayDeviceClass, dispuuid)) == NULL) {
7391 		debugr2((errout,"ColorSyncDeviceCopyDeviceInfo() failed\n"));
7392 		CFRelease(dispuuid);
7393 		return NULL;
7394 	}
7395 
7396 	/* Get the factory profile dictionary */
7397 	if ((sdict = CFDictionaryGetValue(dict, kColorSyncFactoryProfiles)) == NULL) {
7398 		debugrr("Failed to get kColorSyncFactoryProfiles\n");
7399 		CFRelease(dict);
7400 		CFRelease(dispuuid);
7401 		return NULL;
7402 	}
7403 	/* Lookup the default profile ID */
7404 	if ((id = CFDictionaryGetValue(sdict, kColorSyncDeviceDefaultProfileID)) == NULL) {
7405 		debugrr("Failed to get kColorSyncDeviceDefaultProfileID\n");
7406 		CFRelease(dict);
7407 		CFRelease(dispuuid);
7408 		return NULL;
7409 	}
7410 
7411 //	printf("~1 got ProfileID '%s'\n",CFStringGetCStringPtr(id, kCFStringEncodingMacRoman));
7412 
7413 	/* See if this ProfileID is in the factory profile dictionary */
7414 	if ((pdict = CFDictionaryGetValue(sdict, id)) != NULL) {
7415 	} else {
7416 		debugrr("Failed to get factory profile with id \n");
7417 
7418 		/* Get the custom profile dictionary */
7419 		if ((sdict = CFDictionaryGetValue(dict, kColorSyncCustomProfiles)) == NULL) {
7420 			debugrr("Failed to get kColorSyncCustomProfiles\n");
7421 			CFRelease(dict);
7422 			CFRelease(dispuuid);
7423 			return NULL;
7424 		}
7425 		/* See if the default id is in the custom profile dictionary */
7426 		if ((pdict = CFDictionaryGetValue(sdict, id)) == NULL) {
7427 			debugrr("Failed to locate default profile in factory or custom profile list\n");
7428 			CFRelease(dict);
7429 			CFRelease(dispuuid);
7430 			return NULL;
7431 		}
7432 	}
7433 
7434 	/* get the URL */
7435 	if ((url = CFDictionaryGetValue(pdict, kColorSyncDeviceProfileURL)) == NULL) {
7436 		debugrr("Failed to get default profile URL\n");
7437 		CFRelease(dict);
7438 		CFRelease(dispuuid);
7439 		return NULL;
7440 	}
7441 
7442 //	urlstr = CFURLGetString(url);
7443 //	printf("~1 got URL '%s'\n",CFStringGetCStringPtr(urlstr, kCFStringEncodingMacRoman));
7444 
7445 	urlstr = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
7446 	bufSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(urlstr),
7447 	                                              kCFStringEncodingUTF8) + 1;
7448 	if ((dpath = malloc(bufSize)) == NULL) {
7449 		debugrr("cur_profile malloc failed\n");
7450 		CFRelease(dict);
7451 		CFRelease(dispuuid);
7452 		return NULL;
7453 	}
7454 	if (!CFStringGetCString(urlstr, dpath, bufSize, kCFStringEncodingUTF8)) {
7455 		debugrr("cur_profile CFStringGetCString failed\n");
7456 		CFRelease(dict);
7457 		CFRelease(dispuuid);
7458 		return NULL;
7459 	}
7460 	printf("~1 got path '%s'\n",dpath);
7461 
7462 	CFRelease(dict);
7463 	CFRelease(dispuuid);
7464 
7465 //	return dpath;
7466 	return NULL;
7467 #endif
7468 
7469