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