summaryrefslogtreecommitdiff
path: root/spectro/dispwin.c
diff options
context:
space:
mode:
Diffstat (limited to 'spectro/dispwin.c')
-rw-r--r--spectro/dispwin.c6321
1 files changed, 6321 insertions, 0 deletions
diff --git a/spectro/dispwin.c b/spectro/dispwin.c
new file mode 100644
index 0000000..4a6acd6
--- /dev/null
+++ b/spectro/dispwin.c
@@ -0,0 +1,6321 @@
+
+/*
+ * Argyll Color Correction System
+ * Display target patch window
+ *
+ * Author: Graeme W. Gill
+ * Date: 4/10/96
+ *
+ * Copyright 1998 - 2013, Graeme W. Gill
+ * All rights reserved.
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+/* This program displays test patches on a WinNT, MAC OSX or X11 windowing system. */
+
+/* TTBD
+ *
+ * Nice to have option to create non-square test window ?
+ *
+ * Should probably check the display attributes (like visual depth)
+ * and complain if we aren't using 24 bit color or better.
+ *
+ * Ideally should distinguish clearly between not having access to RAMDAC/VideoLuts
+ * (fail) vs. the display not having them at all.
+ *
+ * Is there a >8 bit way of setting frame buffer value on MSWin (see "Quantize")
+ * when using higher bit depth frame buffers ?
+ *
+ * Is there a >8 bit way of getting/setting RAMDAC indexes ?
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <math.h>
+#include <time.h>
+#include <signal.h>
+#ifndef NT
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include "copyright.h"
+#include "aconfig.h"
+#include "icc.h"
+#include "numsup.h"
+#include "cgats.h"
+#include "conv.h"
+#include "dispwin.h"
+#include "webwin.h"
+#if defined(UNIX_X11) && defined(USE_UCMM)
+#include "ucmm.h"
+#endif
+
+#ifdef __APPLE__
+
+#include <Foundation/Foundation.h>
+
+#include <AppKit/AppKit.h>
+
+# if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
+# include <copyfile.h>
+# endif
+
+#ifndef CGFLOAT_DEFINED
+#ifdef __LP64__
+typedef double CGFloat;
+#else
+typedef float CGFloat;
+#endif /* defined(__LP64__) */
+#endif /* !CGFLOAT_DEFINED */
+
+#include <IOKit/Graphics/IOGraphicsLib.h>
+
+#if __MAC_OS_X_VERSION_MAX_ALLOWED <= 1060
+/* This wasn't declared in 10.6, although it is needed */
+CFUUIDRef CGDisplayCreateUUIDFromDisplayID (uint32_t displayID);
+#endif /* < 10.6 */
+
+#endif /* __APPLE__ */
+
+#define VERIFY_TOL (1.0/255.0)
+#undef DISABLE_RANDR /* Disable XRandR code */
+
+#undef DEBUG
+//#define STANDALONE_TEST
+
+#ifdef DEBUG
+#define errout stderr
+# define debug(xx) fprintf(errout, xx )
+# define debug2(xx) fprintf xx
+# define debugr(xx) fprintf(errout, xx )
+# define debugr2(xx) fprintf xx
+# define debugrr(xx) fprintf(errout, xx )
+# define debugrr2(xx) fprintf xx
+# define debugrr2l(lev, xx) fprintf xx
+#else
+#define errout stderr
+# define debug(xx)
+# define debug2(xx)
+# define debugr(xx) if (p->ddebug) fprintf(errout, xx )
+# define debugr2(xx) if (p->ddebug) fprintf xx
+# define debugrr(xx) if (callback_ddebug) fprintf(errout, xx )
+# define debugrr2(xx) if (callback_ddebug) fprintf xx
+# define debugrr2l(lev, xx) if (callback_ddebug >= lev) fprintf xx
+#endif
+
+/* ----------------------------------------------- */
+/* Dealing with locating displays */
+
+int callback_ddebug = 0; /* Diagnostic global for get_displays() and get_a_display() */
+ /* and events */
+
+#ifdef NT
+
+#define sleep(secs) Sleep((secs) * 1000)
+
+static BOOL CALLBACK MonitorEnumProc(
+ HMONITOR hMonitor, /* handle to display monitor */
+ HDC hdcMonitor, /* NULL, because EnumDisplayMonitors hdc is NULL */
+ LPRECT lprcMonitor, /* Virtual screen coordinates of this monitor */
+ LPARAM dwData /* Context data */
+) {
+ disppath ***pdisps = (disppath ***)dwData;
+ disppath **disps = *pdisps;
+ MONITORINFOEX pmi;
+ int ndisps = 0;
+
+ debugrr2((errout, "MonitorEnumProc() called with hMonitor = 0x%x\n",hMonitor));
+
+ /* Get some more information */
+ pmi.cbSize = sizeof(MONITORINFOEX);
+ if (GetMonitorInfo(hMonitor, (MONITORINFO *)&pmi) == 0) {
+ debugrr("get_displays failed GetMonitorInfo - ignoring display\n");
+ return TRUE;
+ }
+
+ /* See if it seems to be a pseudo-display */
+ if (strncmp(pmi.szDevice, "\\\\.\\DISPLAYV", 12) == 0) {
+ debugrr("Seems to be invisible pseudo-display - ignoring it\n");
+ return TRUE;
+ }
+
+ /* Add the display to the list */
+ if (disps == NULL) {
+ if ((disps = (disppath **)calloc(sizeof(disppath *), 1 + 1)) == NULL) {
+ debugrr("get_displays failed on malloc\n");
+ return FALSE;
+ }
+ } else {
+ /* Count current number on list */
+ for (ndisps = 0; disps[ndisps] != NULL; ndisps++)
+ ;
+ if ((disps = (disppath **)realloc(disps,
+ sizeof(disppath *) * (ndisps + 2))) == NULL) {
+ debugrr("get_displays failed on malloc\n");
+ return FALSE;
+ }
+ disps[ndisps+1] = NULL; /* End marker */
+ }
+
+ if ((disps[ndisps] = calloc(sizeof(disppath),1)) == NULL) {
+ debugrr("get_displays failed on malloc\n");
+ return FALSE;
+ }
+
+ if ((disps[ndisps]->name = strdup(pmi.szDevice)) == NULL) {
+ debugrr("malloc failed\n");
+ return FALSE;
+ }
+ disps[ndisps]->prim = (pmi.dwFlags & MONITORINFOF_PRIMARY) ? 1 : 0;
+
+ disps[ndisps]->sx = lprcMonitor->left;
+ disps[ndisps]->sy = lprcMonitor->top;
+ disps[ndisps]->sw = lprcMonitor->right - lprcMonitor->left;
+ disps[ndisps]->sh = lprcMonitor->bottom - lprcMonitor->top;
+
+ disps[ndisps]->description = NULL;
+
+ 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));
+
+ *pdisps = disps;
+ return TRUE;
+}
+
+/* Dynamically linked function support */
+
+BOOL (WINAPI* pEnumDisplayDevices)(PVOID,DWORD,PVOID,DWORD) = NULL;
+
+#if !defined(NTDDI_LONGHORN) || NTDDI_VERSION < NTDDI_LONGHORN
+
+typedef enum {
+ WCS_PROFILE_MANAGEMENT_SCOPE_SYSTEM_WIDE,
+ WCS_PROFILE_MANAGEMENT_SCOPE_CURRENT_USER
+} WCS_PROFILE_MANAGEMENT_SCOPE;
+
+BOOL (WINAPI* pWcsAssociateColorProfileWithDevice)(WCS_PROFILE_MANAGEMENT_SCOPE,PCWSTR,PCWSTR) = NULL;
+BOOL (WINAPI* pWcsDisassociateColorProfileFromDevice)(WCS_PROFILE_MANAGEMENT_SCOPE,PCWSTR,PCWSTR) = NULL;
+
+#endif /* NTDDI_VERSION < NTDDI_LONGHORN */
+
+/* See if we can get the wanted function calls */
+/* return nz if OK */
+static int setup_dyn_calls() {
+ static int dyn_inited = 0;
+
+ if (dyn_inited == 0) {
+ dyn_inited = 1;
+
+ /* EnumDisplayDevicesA was left out of lib32.lib on earlier SDK's ... */
+ pEnumDisplayDevices = (BOOL (WINAPI*)(PVOID,DWORD,PVOID,DWORD)) GetProcAddress(LoadLibrary("USER32"), "EnumDisplayDevicesA");
+ if (pEnumDisplayDevices == NULL)
+ dyn_inited = 0;
+
+ /* Vista calls */
+#if !defined(NTDDI_LONGHORN) || NTDDI_VERSION < NTDDI_LONGHORN
+ pWcsAssociateColorProfileWithDevice = (BOOL (WINAPI*)(WCS_PROFILE_MANAGEMENT_SCOPE,PCWSTR,PCWSTR)) GetProcAddress(LoadLibrary("mscms"), "WcsAssociateColorProfileWithDevice");
+ pWcsDisassociateColorProfileFromDevice = (BOOL (WINAPI*)(WCS_PROFILE_MANAGEMENT_SCOPE,PCWSTR,PCWSTR)) GetProcAddress(LoadLibrary("mscms"), "WcsDisassociateColorProfileFromDevice");
+ /* These are checked individually */
+#endif /* NTDDI_VERSION < NTDDI_LONGHORN */
+ }
+
+ return dyn_inited;
+}
+
+/* Simple up conversion from char string to wchar string */
+/* Return NULL if malloc fails */
+/* ~~~ Note, should probably replace this with mbstowcs() ???? */
+static unsigned short *char2wchar(char *s) {
+ unsigned char *cp;
+ unsigned short *w, *wp;
+
+ if ((w = malloc(sizeof(unsigned short) * (strlen(s) + 1))) == NULL)
+ return w;
+
+ for (cp = (unsigned char *)s, wp = w; ; cp++, wp++) {
+ *wp = *cp; /* Zero extend */
+ if (*cp == 0)
+ break;
+ }
+
+ return w;
+}
+
+#endif /* NT */
+
+
+#if defined(UNIX_X11)
+/* Hack to notice if the error handler has been triggered */
+/* when a function doesn't return a value. */
+
+int g_error_handler_triggered = 0;
+
+/* A noop X11 error handler */
+int null_error_handler(Display *disp, XErrorEvent *ev) {
+ g_error_handler_triggered = 1;
+ return 0;
+}
+#endif /* X11 */
+
+/* Return pointer to list of disppath. Last will be NULL. */
+/* Return NULL on failure. Call free_disppaths() to free up allocation */
+disppath **get_displays() {
+ disppath **disps = NULL;
+
+#ifdef NT
+ DISPLAY_DEVICE dd;
+ char buf[200];
+ int i, j;
+
+ if (setup_dyn_calls() == 0) {
+ debugrr("Dynamic linking to EnumDisplayDevices or Vista AssociateColorProfile failed\n");
+ free_disppaths(disps);
+ return NULL;
+ }
+
+ /* Create an initial list of monitors */
+ /* (It might be better to call pEnumDisplayDevices(NULL, i ..) instead ??, */
+ /* then we can use the StateFlags to distingish monitors not attached to the desktop etc.) */
+ if (EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&disps) == 0) {
+ debugrr("EnumDisplayMonitors failed\n");
+ free_disppaths(disps);
+ return NULL;
+ }
+
+ /* Now locate detailed information about displays */
+ for (i = 0; ; i++) {
+ if (disps == NULL || disps[i] == NULL)
+ break;
+
+ dd.cb = sizeof(dd);
+
+ debugrr2((errout, "get_displays about to get monitor information for %d\n",i));
+ /* Get monitor information */
+ for (j = 0; ;j++) {
+ if ((*pEnumDisplayDevices)(disps[i]->name, j, &dd, 0) == 0) {
+ debugrr2((errout,"EnumDisplayDevices failed on '%s' Mon = %d\n",disps[i]->name,j));
+ if (j == 0) {
+ strcpy(disps[i]->monid, ""); /* We won't be able to set a profile */
+ }
+ break;
+ }
+ if (callback_ddebug) {
+ fprintf(errout,"Mon %d, name '%s'\n",j,dd.DeviceName);
+ fprintf(errout,"Mon %d, string '%s'\n",j,dd.DeviceString);
+ fprintf(errout,"Mon %d, flags 0x%x\n",j,dd.StateFlags);
+ fprintf(errout,"Mon %d, id '%s'\n",j,dd.DeviceID);
+ fprintf(errout,"Mon %d, key '%s'\n",j,dd.DeviceKey);
+ }
+ if (j == 0) {
+ strcpy(disps[i]->monid, dd.DeviceID);
+ }
+ }
+
+ sprintf(buf,"%s, at %d, %d, width %d, height %d%s",disps[i]->name+4,
+ disps[i]->sx, disps[i]->sy, disps[i]->sw, disps[i]->sh,
+ disps[i]->prim ? " (Primary Display)" : "");
+
+ if ((disps[i]->description = strdup(buf)) == NULL) {
+ debugrr("get_displays failed on malloc\n");
+ free_disppaths(disps);
+ return NULL;
+ }
+
+ debugrr2((errout, "get_displays added description '%s' to display %d\n",disps[i]->description,i));
+
+ /* Note that calling EnumDisplayDevices(NULL, j, ..) for the adapter can return other */
+ /* information, such as the graphics card name, and additional state flags. */
+ /* EnumDisplaySettings() can also be called to get information such as display depth etc. */
+ }
+
+#ifdef NEVER
+ /* Explore adapter information */
+ for (j = 0; ; j++) {
+ /* Get adapater information */
+ if ((*pEnumDisplayDevices)(NULL, j, &dd, 0) == 0)
+ break;
+ printf("Adapt %d, name '%s'\n",j,dd.DeviceName);
+ printf("Adapt %d, string '%s'\n",j,dd.DeviceString);
+ printf("Adapt %d, flags 0x%x\n",j,dd.StateFlags);
+ printf("Adapt %d, id '%s'\n",j,dd.DeviceID);
+ printf("Adapt %d, key '%s'\n",j,dd.DeviceKey);
+ }
+#endif /* NEVER */
+
+#endif /* NT */
+
+#ifdef __APPLE__
+ /* Note :- some recent releases of OS X have a feature which */
+ /* automatically adjusts the screen brigtness with ambient level. */
+ /* We may have to find a way of disabling this during calibration and profiling. */
+ /* See the "pset -g" command. */
+
+ /*
+ We could possibly use NSScreen instead of CG here,
+ but we'd need to have a an NSApp first, so perhaps not.
+
+ */
+
+ int i;
+ CGDisplayErr dstat;
+ CGDisplayCount dcount; /* Number of display IDs */
+ CGDirectDisplayID *dids; /* Array of display IDs */
+
+ if ((dstat = CGGetActiveDisplayList(0, NULL, &dcount)) != kCGErrorSuccess || dcount < 1) {
+ debugrr("CGGetActiveDisplayList #1 returned error\n");
+ return NULL;
+ }
+ if ((dids = (CGDirectDisplayID *)malloc(dcount * sizeof(CGDirectDisplayID))) == NULL) {
+ debugrr("malloc of CGDirectDisplayID's failed\n");
+ return NULL;
+ }
+ if ((dstat = CGGetActiveDisplayList(dcount, dids, &dcount)) != kCGErrorSuccess) {
+ debugrr("CGGetActiveDisplayList #2 returned error\n");
+ free(dids);
+ return NULL;
+ }
+
+ /* Found dcount displays */
+ debugrr2((errout,"Found %d screens\n",dcount));
+
+ /* Allocate our list */
+ if ((disps = (disppath **)calloc(sizeof(disppath *), dcount + 1)) == NULL) {
+ debugrr("get_displays failed on malloc\n");
+ free(dids);
+ return NULL;
+ }
+ for (i = 0; i < dcount; i++) {
+ if ((disps[i] = calloc(sizeof(disppath), 1)) == NULL) {
+ debugrr("get_displays failed on malloc\n");
+ free_disppaths(disps);
+ free(dids);
+ return NULL;
+ }
+ disps[i]->ddid = dids[i];
+ }
+
+ /* Got displays, now figure out a description for each one */
+ for (i = 0; i < dcount; i++) {
+ CGRect dbound; /* Bounding rectangle of chosen display */
+ io_service_t dport;
+ CFDictionaryRef ddr, pndr;
+ CFIndex dcount;
+ char *dp = NULL, desc[50];
+ char buf[200];
+
+ dbound = CGDisplayBounds(dids[i]);
+ disps[i]->sx = dbound.origin.x;
+ disps[i]->sy = dbound.origin.y;
+ disps[i]->sw = dbound.size.width;
+ disps[i]->sh = dbound.size.height;
+
+ /* Try and get some information about the display */
+ if ((dport = CGDisplayIOServicePort(dids[i])) == MACH_PORT_NULL) {
+ debugrr("CGDisplayIOServicePort returned error\n");
+ free_disppaths(disps);
+ free(dids);
+ return NULL;
+ }
+
+#ifdef NEVER
+ {
+ io_name_t name;
+ if (IORegistryEntryGetName(dport, name) != KERN_SUCCESS) {
+ debugrr("IORegistryEntryGetName returned error\n");
+ free_disppaths(disps);
+ free(dids);
+ return NULL;
+ }
+ printf("Driver %d name = '%s'\n",i,name);
+ }
+#endif
+ if ((ddr = IODisplayCreateInfoDictionary(dport, 0)) == NULL) {
+ debugrr("IODisplayCreateInfoDictionary returned NULL\n");
+ free_disppaths(disps);
+ free(dids);
+ return NULL;
+ }
+ if ((pndr = CFDictionaryGetValue(ddr, CFSTR(kDisplayProductName))) == NULL) {
+ debugrr("CFDictionaryGetValue returned NULL\n");
+ CFRelease(ddr);
+ free_disppaths(disps);
+ free(dids);
+ return NULL;
+ }
+ if ((dcount = CFDictionaryGetCount(pndr)) > 0) {
+ const void **keys;
+ const void **values;
+ int j;
+
+ keys = (const void **)calloc(sizeof(void *), dcount);
+ values = (const void **)calloc(sizeof(void *), dcount);
+ if (keys == NULL || values == NULL) {
+ if (keys != NULL)
+ free(keys);
+ if (values != NULL)
+ free(values);
+ debugrr("malloc failed\n");
+ CFRelease(ddr);
+ free_disppaths(disps);
+ free(dids);
+ return NULL;
+ }
+ CFDictionaryGetKeysAndValues(pndr, keys, values);
+ for (j = 0; j < dcount; j++) {
+ const char *k, *v;
+ char kbuf[50], vbuf[50];
+ k = CFStringGetCStringPtr(keys[j], kCFStringEncodingMacRoman);
+ if (k == NULL) {
+ if (CFStringGetCString(keys[j], kbuf, 50, kCFStringEncodingMacRoman))
+ k = kbuf;
+ }
+ v = CFStringGetCStringPtr(values[j], kCFStringEncodingMacRoman);
+ if (v == NULL) {
+ if (CFStringGetCString(values[j], vbuf, 50, kCFStringEncodingMacRoman))
+ v = vbuf;
+ }
+ /* We're only grabing the english description... */
+ if (k != NULL && v != NULL && strcmp(k, "en_US") == 0) {
+ strncpy(desc, v, 49);
+ desc[49] = '\000';
+ dp = desc;
+ }
+ }
+ free(keys);
+ free(values);
+ }
+ CFRelease(ddr);
+
+ if (dp == NULL) {
+ strcpy(desc, "(unknown)");
+ dp = desc;
+ }
+ sprintf(buf,"%s, at %d, %d, width %d, height %d%s",dp,
+ disps[i]->sx, disps[i]->sy, disps[i]->sw, disps[i]->sh,
+ CGDisplayIsMain(dids[i]) ? " (Primary Display)" : "");
+
+ if ((disps[i]->name = strdup(dp)) == NULL
+ || (disps[i]->description = strdup(buf)) == NULL) {
+ debugrr("get_displays failed on malloc\n");
+ free_disppaths(disps);
+ free(dids);
+ return NULL;
+ }
+ }
+
+ free(dids);
+#endif /* __APPLE__ */
+
+#if defined(UNIX_X11)
+ int i, j, k;
+ int defsix = 0; /* default screen index */
+ int dcount; /* Number of screens */
+ char *dname;
+ char dnbuf[100];
+ int evb = 0, erb = 0;
+ int majv, minv; /* Version */
+ Display *mydisplay;
+ int ndisps = 0;
+ XineramaScreenInfo *xai = NULL;
+ char desc1[100], desc2[200];
+
+ /* There seems to be no way of getting the available displays */
+ /* on an X11 system. Attempting to open them in sequence */
+ /* takes too long. We just rely on the user supplying the */
+ /* right display. We can enumerate screens though. */
+
+ /* Open the base display, and then enumerate all the screens */
+ if ((dname = getenv("DISPLAY")) != NULL) {
+ char *pp;
+ strncpy(dnbuf,dname,99); dnbuf[99] = '\000';
+ if ((pp = strrchr(dnbuf, ':')) != NULL) {
+ if ((pp = strchr(pp, '.')) == NULL)
+ strcat(dnbuf,".0");
+ else {
+ if (pp[1] == '\000')
+ strcat(dnbuf,"0");
+ else {
+ pp[1] = '0';
+ pp[2] = '\000';
+ }
+ }
+ }
+ } else
+ strcpy(dnbuf,":0.0");
+
+ if ((mydisplay = XOpenDisplay(dnbuf)) == NULL) {
+ debugrr2((errout, "failed to open display '%s'\n",dnbuf));
+ return NULL;
+ }
+
+#if RANDR_MAJOR == 1 && RANDR_MINOR >= 2 && !defined(DISABLE_RANDR)
+ /* Use Xrandr 1.2 if it's available, and if it's not disabled */
+ if (getenv("ARGYLL_IGNORE_XRANDR1_2") == NULL
+ && XRRQueryExtension(mydisplay, &evb, &erb) != 0
+ && XRRQueryVersion(mydisplay, &majv, &minv)
+ && majv == 1 && minv >= 2) {
+
+ if (XSetErrorHandler(null_error_handler) == 0) {
+ debugrr("get_displays failed on XSetErrorHandler\n");
+ XCloseDisplay(mydisplay);
+ free_disppaths(disps);
+ return NULL;
+ }
+
+ dcount = ScreenCount(mydisplay);
+
+ /* Go through all the screens */
+ for (i = 0; i < dcount; i++) {
+ XRRScreenResources *scrnres;
+ int jj; /* Screen index */
+
+ if ((scrnres = XRRGetScreenResources(mydisplay, RootWindow(mydisplay,i))) == NULL) {
+ debugrr("XRRGetScreenResources failed\n");
+ XCloseDisplay(mydisplay);
+ free_disppaths(disps);
+ return NULL;
+ }
+
+ /* Look at all the screens outputs */
+ for (jj = j = 0; j < scrnres->noutput; j++) {
+ XRROutputInfo *outi;
+ XRRCrtcInfo *crtci;
+
+ if ((outi = XRRGetOutputInfo(mydisplay, scrnres, scrnres->outputs[j])) == NULL) {
+ debugrr("XRRGetOutputInfo failed\n");
+ XRRFreeScreenResources(scrnres);
+ XCloseDisplay(mydisplay);
+ free_disppaths(disps);
+ return NULL;
+ }
+
+ if (outi->connection == RR_Disconnected ||
+ outi->crtc == None) {
+ continue;
+ }
+
+ /* Check that the VideoLUT's are accessible */
+ {
+ XRRCrtcGamma *crtcgam;
+
+ debugrr("Checking XRandR 1.2 VideoLUT access\n");
+ if ((crtcgam = XRRGetCrtcGamma(mydisplay, outi->crtc)) == NULL
+ || crtcgam->size == 0) {
+ fprintf(stderr,"XRandR 1.2 is faulty - falling back to older extensions\n");
+ if (crtcgam != NULL)
+ XRRFreeGamma(crtcgam);
+ free_disppaths(disps);
+ disps = NULL;
+ j = scrnres->noutput;
+ i = dcount;
+ continue; /* Abort XRandR 1.2 */
+ }
+ }
+#ifdef NEVER
+ {
+ Atom *oprops;
+ int noprop;
+
+ /* Get a list of the properties of the output */
+ oprops = XRRListOutputProperties(mydisplay, scrnres->outputs[j], &noprop);
+
+ printf("num props = %d\n", noprop);
+ for (k = 0; k < noprop; k++) {
+ printf("%d: atom 0x%x, name = '%s'\n", k, oprops[k], XGetAtomName(mydisplay, oprops[k]));
+ }
+ }
+#endif /* NEVER */
+
+ if ((crtci = XRRGetCrtcInfo(mydisplay, scrnres, outi->crtc)) != NULL) {
+ char *pp;
+
+ /* Add the output to the list */
+ if (disps == NULL) {
+ if ((disps = (disppath **)calloc(sizeof(disppath *), 1 + 1)) == NULL) {
+ debugrr("get_displays failed on malloc\n");
+ XRRFreeCrtcInfo(crtci);
+ XRRFreeScreenResources(scrnres);
+ XCloseDisplay(mydisplay);
+ return NULL;
+ }
+ } else {
+ if ((disps = (disppath **)realloc(disps,
+ sizeof(disppath *) * (ndisps + 2))) == NULL) {
+ debugrr("get_displays failed on malloc\n");
+ XRRFreeCrtcInfo(crtci);
+ XRRFreeScreenResources(scrnres);
+ XCloseDisplay(mydisplay);
+ return NULL;
+ }
+ disps[ndisps+1] = NULL; /* End marker */
+ }
+ /* ndisps is current display we're filling in */
+ if ((disps[ndisps] = calloc(sizeof(disppath),1)) == NULL) {
+ debugrr("get_displays failed on malloc\n");
+ XRRFreeCrtcInfo(crtci);
+ XRRFreeScreenResources(scrnres);
+ XCloseDisplay(mydisplay);
+ free_disppaths(disps);
+ return NULL;
+ }
+
+ disps[ndisps]->screen = i;
+ disps[ndisps]->uscreen = i;
+ disps[ndisps]->rscreen = i;
+ disps[ndisps]->sx = crtci->x;
+ disps[ndisps]->sy = crtci->y;
+ disps[ndisps]->sw = crtci->width;
+ disps[ndisps]->sh = crtci->height;
+ disps[ndisps]->crtc = outi->crtc; /* XID of crtc */
+ disps[ndisps]->output = scrnres->outputs[j]; /* XID of output */
+
+ sprintf(desc1,"Screen %d, Output %s",ndisps+1,outi->name);
+ sprintf(desc2,"%s at %d, %d, width %d, height %d",desc1,
+ disps[ndisps]->sx, disps[ndisps]->sy, disps[ndisps]->sw, disps[ndisps]->sh);
+
+ /* See if it is a clone */
+ for (k = 0; k < ndisps; k++) {
+ if (disps[k]->crtc == disps[ndisps]->crtc) {
+ sprintf(desc1, "[ Clone of %d ]",k+1);
+ strcat(desc2, desc1);
+ }
+ }
+ if ((disps[ndisps]->description = strdup(desc2)) == NULL) {
+ debugrr("get_displays failed on malloc\n");
+ XRRFreeCrtcInfo(crtci);
+ XRRFreeScreenResources(scrnres);
+ XCloseDisplay(mydisplay);
+ free_disppaths(disps);
+ return NULL;
+ }
+
+ /* Form the display name */
+ if ((pp = strrchr(dnbuf, ':')) != NULL) {
+ if ((pp = strchr(pp, '.')) != NULL) {
+ sprintf(pp,".%d",i);
+ }
+ }
+ if ((disps[ndisps]->name = strdup(dnbuf)) == NULL) {
+ debugrr("get_displays failed on malloc\n");
+ XRRFreeCrtcInfo(crtci);
+ XRRFreeScreenResources(scrnres);
+ XCloseDisplay(mydisplay);
+ free_disppaths(disps);
+ return NULL;
+ }
+ debugrr2((errout, "Display %d name = '%s'\n",ndisps,disps[ndisps]->name));
+
+ /* Create the X11 root atom of the default screen */
+ /* that may contain the associated ICC profile */
+ /* (The _%d variant will probably break with non-Xrandr */
+ /* aware software if Xrandr is configured to have more than */
+ /* a single virtual screen.) */
+ if (jj == 0)
+ strcpy(desc1, "_ICC_PROFILE");
+ else
+ sprintf(desc1, "_ICC_PROFILE_%d",jj);
+
+ if ((disps[ndisps]->icc_atom = XInternAtom(mydisplay, desc1, False)) == None)
+ error("Unable to intern atom '%s'",desc1);
+
+ /* Create the atom of the output that may contain the associated ICC profile */
+ if ((disps[ndisps]->icc_out_atom = XInternAtom(mydisplay, "_ICC_PROFILE", False)) == None)
+ error("Unable to intern atom '%s'","_ICC_PROFILE");
+
+ /* Grab the EDID from the output */
+ {
+ Atom edid_atom, ret_type;
+ int ret_format;
+ long ret_len = 0, ret_togo;
+ unsigned char *atomv = NULL;
+ int ii;
+ char *keys[] = { /* Possible keys that may be used */
+ "EDID_DATA",
+ "EDID",
+ ""
+ };
+
+ /* Try each key in turn */
+ for (ii = 0; keys[ii][0] != '\000'; ii++) {
+ /* Get the atom for the EDID data */
+ if ((edid_atom = XInternAtom(mydisplay, keys[ii], True)) == None) {
+ debugrr2((errout, "Unable to intern atom '%s'\n",keys[ii]));
+ /* Try the next key */
+ } else {
+
+ /* Get the EDID_DATA */
+ if (XRRGetOutputProperty(mydisplay, scrnres->outputs[j], edid_atom,
+ 0, 0x7ffffff, False, False, XA_INTEGER,
+ &ret_type, &ret_format, &ret_len, &ret_togo, &atomv) == Success
+ && (ret_len == 128 || ret_len == 256)) {
+ if ((disps[ndisps]->edid = malloc(sizeof(unsigned char) * ret_len)) == NULL) {
+ debugrr("get_displays failed on malloc\n");
+ XRRFreeCrtcInfo(crtci);
+ XRRFreeScreenResources(scrnres);
+ XCloseDisplay(mydisplay);
+ free_disppaths(disps);
+ return NULL;
+ }
+ memmove(disps[ndisps]->edid, atomv, ret_len);
+ disps[ndisps]->edid_len = ret_len;
+ XFree(atomv);
+ debugrr2((errout, "Got EDID for display\n"));
+ break;
+ }
+ /* Try the next key */
+ }
+ }
+ if (keys[ii][0] == '\000')
+ debugrr2((errout, "Failed to get EDID for display\n"));
+ }
+
+ jj++; /* Next enabled index */
+ ndisps++; /* Now it's number of displays */
+ XRRFreeCrtcInfo(crtci);
+ }
+ XRRFreeOutputInfo(outi);
+ }
+
+ XRRFreeScreenResources(scrnres);
+ }
+ XSetErrorHandler(NULL);
+ defsix = DefaultScreen(mydisplay);
+ }
+#endif /* randr >= V 1.2 */
+
+ if (disps == NULL) { /* Use Older style identification */
+ debugrr("get_displays checking for Xinerama\n");
+
+ if (XSetErrorHandler(null_error_handler) == 0) {
+ debugrr("get_displays failed on XSetErrorHandler\n");
+ XCloseDisplay(mydisplay);
+ return NULL;
+ }
+
+ if (XineramaQueryExtension(mydisplay, &evb, &erb) != 0
+ && XineramaIsActive(mydisplay)) {
+
+ xai = XineramaQueryScreens(mydisplay, &dcount);
+
+ if (xai == NULL || dcount == 0) {
+ debugrr("XineramaQueryScreens failed\n");
+ XCloseDisplay(mydisplay);
+ return NULL;
+ }
+ defsix = 0;
+ } else {
+ dcount = ScreenCount(mydisplay);
+ defsix = DefaultScreen(mydisplay);
+ }
+
+ /* Allocate our list */
+ if ((disps = (disppath **)calloc(sizeof(disppath *), dcount + 1)) == NULL) {
+ debugrr("get_displays failed on malloc\n");
+ XCloseDisplay(mydisplay);
+ return NULL;
+ }
+ for (i = 0; i < dcount; i++) {
+ if ((disps[i] = calloc(sizeof(disppath), 1)) == NULL) {
+ debugrr("get_displays failed on malloc\n");
+ free_disppaths(disps);
+ XCloseDisplay(mydisplay);
+ return NULL;
+ }
+ }
+
+ /* Create a description for each screen */
+ for (i = 0; i < dcount; i++) {
+ XF86VidModeMonitor monitor;
+ int evb = 0, erb = 0;
+ char *pp;
+
+ /* Form the display name */
+ if ((pp = strrchr(dnbuf, ':')) != NULL) {
+ if ((pp = strchr(pp, '.')) != NULL) {
+ sprintf(pp,".%d",i);
+ }
+ }
+ if ((disps[i]->name = strdup(dnbuf)) == NULL) {
+ debugrr("get_displays failed on malloc\n");
+ free_disppaths(disps);
+ XCloseDisplay(mydisplay);
+ return NULL;
+ }
+
+ debugrr2((errout, "Display %d name = '%s'\n",i,disps[i]->name));
+ if (xai != NULL) { /* Xinerama */
+ disps[i]->screen = 0; /* We are asuming Xinerame creates a single virtual screen */
+ disps[i]->uscreen = i; /* We are assuming xinerama lists screens in the same order */
+ disps[i]->rscreen = i;
+ disps[i]->sx = xai[i].x_org;
+ disps[i]->sy = xai[i].y_org;
+ disps[i]->sw = xai[i].width;
+ disps[i]->sh = xai[i].height;
+ } else {
+ disps[i]->screen = i;
+ disps[i]->uscreen = i;
+ disps[i]->rscreen = i;
+ disps[i]->sx = 0; /* Must be 0 */
+ disps[i]->sy = 0;
+ disps[i]->sw = DisplayWidth(mydisplay, disps[i]->screen);
+ disps[i]->sh = DisplayHeight(mydisplay, disps[i]->screen);
+ }
+
+ /* Create the X11 root atom of the default screen */
+ /* that may contain the associated ICC profile */
+ if (disps[i]->uscreen == 0)
+ strcpy(desc1, "_ICC_PROFILE");
+ else
+ sprintf(desc1, "_ICC_PROFILE_%d",disps[i]->uscreen);
+
+ if ((disps[i]->icc_atom = XInternAtom(mydisplay, desc1, False)) == None)
+ error("Unable to intern atom '%s'",desc1);
+
+ /* See if we can locate the EDID of the monitor for this screen */
+ for (j = 0; j < 2; j++) {
+ char edid_name[50];
+ Atom edid_atom, ret_type;
+ int ret_format = 8;
+ long ret_len, ret_togo;
+ unsigned char *atomv = NULL;
+
+ if (disps[i]->uscreen == 0) {
+ if (j == 0)
+ strcpy(edid_name,"XFree86_DDC_EDID1_RAWDATA");
+ else
+ strcpy(edid_name,"XFree86_DDC_EDID2_RAWDATA");
+ } else {
+ if (j == 0)
+ sprintf(edid_name,"XFree86_DDC_EDID1_RAWDATA_%d",disps[i]->uscreen);
+ else
+ sprintf(edid_name,"XFree86_DDC_EDID2_RAWDATA_%d",disps[i]->uscreen);
+ }
+
+ if ((edid_atom = XInternAtom(mydisplay, edid_name, True)) == None)
+ continue;
+ if (XGetWindowProperty(mydisplay, RootWindow(mydisplay, disps[i]->uscreen), edid_atom,
+ 0, 0x7ffffff, False, XA_INTEGER,
+ &ret_type, &ret_format, &ret_len, &ret_togo, &atomv) == Success
+ && (ret_len == 128 || ret_len == 256)) {
+ if ((disps[i]->edid = malloc(sizeof(unsigned char) * ret_len)) == NULL) {
+ debugrr("get_displays failed on malloc\n");
+ free_disppaths(disps);
+ XCloseDisplay(mydisplay);
+ return NULL;
+ }
+ memmove(disps[i]->edid, atomv, ret_len);
+ disps[i]->edid_len = ret_len;
+ XFree(atomv);
+ debugrr2((errout, "Got EDID for display\n"));
+ break;
+ } else {
+ debugrr2((errout, "Failed to get EDID for display\n"));
+ }
+ }
+
+ if (XF86VidModeQueryExtension(mydisplay, &evb, &erb) != 0) {
+ /* Some propietary multi-screen drivers (ie. TwinView & MergeFB) */
+ /* don't implement the XVidMode extension properly. */
+ monitor.model = NULL;
+ if (XF86VidModeGetMonitor(mydisplay, disps[i]->uscreen, &monitor) != 0
+ && monitor.model != NULL && monitor.model[0] != '\000')
+ sprintf(desc1, "%s",monitor.model);
+ else
+ sprintf(desc1,"Screen %d",i+1);
+ } else
+ sprintf(desc1,"Screen %d",i+1);
+
+ sprintf(desc2,"%s at %d, %d, width %d, height %d",desc1,
+ disps[i]->sx, disps[i]->sy, disps[i]->sw, disps[i]->sh);
+ if ((disps[i]->description = strdup(desc2)) == NULL) {
+ debugrr("get_displays failed on malloc\n");
+ free_disppaths(disps);
+ XCloseDisplay(mydisplay);
+ return NULL;
+ }
+ }
+ XSetErrorHandler(NULL);
+ }
+
+ /* Put the screen given by the display name at the top */
+ {
+ disppath *tdispp;
+ tdispp = disps[defsix];
+ disps[defsix] = disps[0];
+ disps[0] = tdispp;
+ }
+
+ if (xai != NULL)
+ XFree(xai);
+
+ XCloseDisplay(mydisplay);
+
+#endif /* UNIX X11 */
+
+ return disps;
+}
+
+/* Free a whole list of display paths */
+void free_disppaths(disppath **disps) {
+ if (disps != NULL) {
+ int i;
+ for (i = 0; ; i++) {
+ if (disps[i] == NULL)
+ break;
+
+ if (disps[i]->name != NULL)
+ free(disps[i]->name);
+ if (disps[i]->description != NULL)
+ free(disps[i]->description);
+#if defined(UNIX_X11)
+ if (disps[i]->edid != NULL)
+ free(disps[i]->edid);
+#endif
+ free(disps[i]);
+ }
+ free(disps);
+ }
+}
+
+/* Delete a single display from the list of display paths */
+void del_disppath(disppath **disps, int ix) {
+ if (disps != NULL) {
+ int i, j, k;
+ for (i = 0; ; i++) {
+ if (disps[i] == NULL)
+ break;
+
+ if (i == ix) { /* One to delete */
+ if (disps[i]->name != NULL)
+ free(disps[i]->name);
+ if (disps[i]->description != NULL)
+ free(disps[i]->description);
+#if defined(UNIX_X11)
+ if (disps[i]->edid != NULL)
+ free(disps[i]->edid);
+#endif
+ free(disps[i]);
+
+ /* Shuffle the rest down */
+ for (j = i, k = i + 1; ;j++, k++) {
+ disps[j] = disps[k];
+ if (disps[k] == NULL)
+ break;
+ }
+ return;
+ }
+ }
+ }
+}
+
+/* ----------------------------------------------- */
+/* Deal with selecting a display */
+
+/* Return the given display given its index 0..n-1 */
+disppath *get_a_display(int ix) {
+ disppath **paths, *rv = NULL;
+ int i;
+
+ if ((paths = get_displays()) == NULL)
+ return NULL;
+
+ for (i = 0; ;i++) {
+ if (paths[i] == NULL) {
+ free_disppaths(paths);
+ return NULL;
+ }
+ if (i == ix)
+ break;
+ }
+ if ((rv = malloc(sizeof(disppath))) == NULL) {
+ debugrr("get_a_display failed malloc\n");
+ free_disppaths(paths);
+ return NULL;
+ }
+ *rv = *paths[i]; /* Structure copy */
+ if ((rv->name = strdup(paths[i]->name)) == NULL) {
+ debugrr("get_displays failed on malloc\n");
+ free(rv->description);
+ free(rv);
+ free_disppaths(paths);
+ return NULL;
+ }
+ if ((rv->description = strdup(paths[i]->description)) == NULL) {
+ debugrr("get_displays failed on malloc\n");
+ free(rv);
+ free_disppaths(paths);
+ return NULL;
+ }
+#if defined(UNIX_X11)
+ if (paths[i]->edid != NULL) {
+ if ((rv->edid = malloc(sizeof(unsigned char) * paths[i]->edid_len)) == NULL) {
+ debugrr("get_displays failed on malloc\n");
+ free(rv);
+ free_disppaths(paths);
+ return NULL;
+ }
+ rv->edid_len = paths[i]->edid_len;
+ memmove(rv->edid, paths[i]->edid, rv->edid_len );
+ }
+#endif
+ free_disppaths(paths);
+ return rv;
+}
+
+void free_a_disppath(disppath *path) {
+ if (path != NULL) {
+ if (path->name != NULL)
+ free(path->name);
+ if (path->description != NULL)
+ free(path->description);
+#if defined(UNIX_X11)
+ if (path->edid != NULL)
+ free(path->edid);
+#endif
+ free(path);
+ }
+}
+
+/* ----------------------------------------------- */
+
+static ramdac *dispwin_clone_ramdac(ramdac *r);
+static void dispwin_setlin_ramdac(ramdac *r);
+static void dispwin_del_ramdac(ramdac *r);
+
+/* For VideoLUT/RAMDAC use, we assume that the number of entries in the RAMDAC */
+/* meshes perfectly with the display raster depth, so that we can */
+/* figure out how to apportion device values. We fail if they don't */
+/* seem to mesh. */
+
+/* !!! Would be nice to add an error message return to dispwin and */
+/* !!! pass errors back to it so that the detail can be reported */
+/* !!! to the user. */
+
+/* Get RAMDAC values. ->del() when finished. */
+/* Return NULL if not possible */
+static ramdac *dispwin_get_ramdac(dispwin *p) {
+ ramdac *r = NULL;
+ int i, j;
+
+#ifdef NT
+ WORD vals[3][256]; /* 256 x 16 bit elements (Quantize) */
+
+ debugr("dispwin_get_ramdac called\n");
+
+#ifdef NEVER /* Doesn't seem to return correct information on win2K systems */
+ if ((GetDeviceCaps(p->hdc, COLORMGMTCAPS) & CM_GAMMA_RAMP) == 0) {
+ debugr("dispwin_get_ramdac failed on GetDeviceCaps(CM_GAMMA_RAMP)\n");
+ return NULL;
+ }
+#endif
+
+ /* Allocate a ramdac */
+ if ((r = (ramdac *)calloc(sizeof(ramdac), 1)) == NULL) {
+ debugr("dispwin_get_ramdac failed on malloc()\n");
+ return NULL;
+ }
+ r->pdepth = p->pdepth;
+ r->nent = (1 << p->pdepth);
+ r->clone = dispwin_clone_ramdac;
+ r->setlin = dispwin_setlin_ramdac;
+ r->del = dispwin_del_ramdac;
+
+ for (j = 0; j < 3; j++) {
+
+ if ((r->v[j] = (double *)calloc(sizeof(double), r->nent)) == NULL) {
+ for (j--; j >= 0; j--)
+ free(r->v[j]);
+ free(r);
+ debugr("dispwin_get_ramdac failed on malloc()\n");
+ return NULL;
+ }
+ }
+
+ /* GetDeviceGammaRamp() is hard coded for 3 x 256 entries (Quantize) */
+ if (r->nent != 256) {
+ debugr2((errout,"GetDeviceGammaRamp() is hard coded for nent == 256, and we've got nent = %d!\n",r->nent));
+ return NULL;
+ }
+
+ if (GetDeviceGammaRamp(p->hdc, vals) == 0) {
+ debugr("dispwin_get_ramdac failed on GetDeviceGammaRamp()\n");
+ return NULL;
+ }
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < r->nent; i++) {
+ r->v[j][i] = vals[j][i]/65535.0;
+ }
+ }
+#endif /* NT */
+
+#ifdef __APPLE__
+ unsigned int nent;
+ CGGammaValue vals[3][16385];
+
+ debugr("dispwin_get_ramdac called\n");
+
+ if (CGGetDisplayTransferByTable(p->ddid, 163845, vals[0], vals[1], vals[2], &nent) != 0) {
+ debugr("CGGetDisplayTransferByTable failed\n");
+ return NULL;
+ }
+
+ if (nent == 16385) { /* oops - we didn't provide enought space! */
+ debugr("CGGetDisplayTransferByTable has more entries than we can handle\n");
+ return NULL;
+ }
+
+ if (nent != (1 << p->pdepth)) {
+ debugr("CGGetDisplayTransferByTable number of entries mismatches screen depth\n");
+ return NULL;
+ }
+
+ /* Allocate a ramdac */
+ if ((r = (ramdac *)calloc(sizeof(ramdac), 1)) == NULL) {
+ debugr("dispwin_get_ramdac failed on malloc()\n");
+ return NULL;
+ }
+
+ r->pdepth = p->pdepth;
+ r->nent = (1 << p->pdepth);
+ r->clone = dispwin_clone_ramdac;
+ r->setlin = dispwin_setlin_ramdac;
+ r->del = dispwin_del_ramdac;
+ for (j = 0; j < 3; j++) {
+
+ if ((r->v[j] = (double *)calloc(sizeof(double), r->nent)) == NULL) {
+ for (j--; j >= 0; j--)
+ free(r->v[j]);
+ free(r);
+ debugr("dispwin_get_ramdac failed on malloc()\n");
+ return NULL;
+ }
+ }
+
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < r->nent; i++) {
+ r->v[j][i] = vals[j][i];
+ }
+ }
+#endif /* __APPLE__ */
+
+#if defined(UNIX_X11)
+ unsigned short vals[3][16384];
+ int nent = 0;
+ int evb = 0, erb = 0;
+
+ debugr("dispwin_get_ramdac called\n");
+
+#if RANDR_MAJOR == 1 && RANDR_MINOR >= 2 && !defined(DISABLE_RANDR)
+ if (p->crtc != 0) { /* Using Xrandr 1.2 */
+ XRRCrtcGamma *crtcgam;
+ int nz = 0;
+
+ debugr("Getting gamma using Randr 1.2\n");
+
+ if ((crtcgam = XRRGetCrtcGamma(p->mydisplay, p->crtc)) == NULL) {
+ debugr("XRRGetCrtcGamma failed\n");
+ return NULL;
+ }
+
+ nent = crtcgam->size;
+
+ if (nent > 16384) {
+ debugr("XRRGetCrtcGammaSize has more entries than we can handle\n");
+ return NULL;
+ }
+
+ if (nent != (1 << p->pdepth)) {
+ debugr2((errout,"XRRGetCrtcGammaSize number of entries %d mismatches screen depth %d\n",nent,(1 << p->pdepth)));
+ return NULL;
+ }
+
+ /* Check for XRandR 1.2 startup bug */
+ for (i = 0; i < nent; i++) {
+ vals[0][i] = crtcgam->red[i];
+ vals[1][i] = crtcgam->green[i];
+ vals[2][i] = crtcgam->blue[i];
+ nz = vals[0][i] | vals[1][i] | vals[2][i];
+ }
+
+ /* Compensate for XRandR 1.2 startup bug */
+ if (nz == 0) {
+ debugr("Detected XRandR 1.2 bug ? Assuming linear ramp!\n");
+ for (i = 0; i < nent; i++) {
+ for (j = 0; j < 3; j++)
+ vals[j][i] = (int)(65535.0 * i/(nent-1.0) + 0.5);
+ }
+ }
+
+ XRRFreeGamma(crtcgam);
+
+ } else
+#endif /* randr >= V 1.2 */
+ {
+
+ if (XF86VidModeQueryExtension(p->mydisplay, &evb, &erb) == 0) {
+ debugr("XF86VidModeQueryExtension failed\n");
+ return NULL;
+ }
+ /* Some propietary multi-screen drivers (ie. TwinView & MergedFB) */
+ /* don't implement the XVidMode extenstion properly. */
+ if (XSetErrorHandler(null_error_handler) == 0) {
+ debugr("get_displays failed on XSetErrorHandler\n");
+ return NULL;
+ }
+ nent = -1;
+ if (XF86VidModeGetGammaRampSize(p->mydisplay, p->myrscreen, &nent) == 0
+ || nent == -1) {
+ XSetErrorHandler(NULL);
+ debugr("XF86VidModeGetGammaRampSize failed\n");
+ return NULL;
+ }
+ XSetErrorHandler(NULL); /* Restore handler */
+ if (nent == 0) {
+ debugr("XF86VidModeGetGammaRampSize returned 0 size\n");
+ return NULL;
+ }
+
+ if (nent > 16384) {
+ debugr("XF86VidModeGetGammaRampSize has more entries than we can handle\n");
+ return NULL;
+ }
+
+ if (XF86VidModeGetGammaRamp(p->mydisplay, p->myrscreen, nent, vals[0], vals[1], vals[2]) == 0) {
+ debugr("XF86VidModeGetGammaRamp failed\n");
+ return NULL;
+ }
+
+ if (nent != (1 << p->pdepth)) {
+ debugr2((errout,"CGGetDisplayTransferByTable number of entries %d mismatches screen depth %d\n",nent,(1 << p->pdepth)));
+ return NULL;
+ }
+ }
+
+ /* Allocate a ramdac */
+ if ((r = (ramdac *)calloc(sizeof(ramdac), 1)) == NULL) {
+ debugr("dispwin_get_ramdac failed on malloc()\n");
+ return NULL;
+ }
+
+ r->pdepth = p->pdepth;
+ r->nent = (1 << p->pdepth);
+ r->clone = dispwin_clone_ramdac;
+ r->setlin = dispwin_setlin_ramdac;
+ r->del = dispwin_del_ramdac;
+ for (j = 0; j < 3; j++) {
+
+ if ((r->v[j] = (double *)calloc(sizeof(double), r->nent)) == NULL) {
+ for (j--; j >= 0; j--)
+ free(r->v[j]);
+ free(r);
+ debugr("dispwin_get_ramdac failed on malloc()\n");
+ return NULL;
+ }
+ }
+
+ for (i = 0; i < r->nent; i++) {
+ for (j = 0; j < 3; j++) {
+ r->v[j][i] = vals[j][i]/65535.0;
+ }
+ }
+#endif /* UNXI X11 */
+ debugr("dispwin_get_ramdac returning OK\n");
+ return r;
+}
+
+#ifdef __APPLE__
+ /* Various support functions */
+
+#if __MAC_OS_X_VERSION_MAX_ALLOWED < 1060
+
+/* Given a location, return a string for it's path */
+static char *plocpath(CMProfileLocation *ploc) {
+
+ if (ploc->locType == cmFileBasedProfile) {
+ FSRef newRef;
+ UInt8 path[256] = "";
+
+ /* Note that there is no non-deprecated equivalent to this. */
+ /* Apple need to remove the cmFileBasedProfile type from the */
+ /* CMProfileLocation to do away with it. */
+ if (FSpMakeFSRef(&ploc->u.fileLoc.spec, &newRef) == noErr) {
+ OSStatus stus;
+ if ((stus = FSRefMakePath(&newRef, path, 256)) == 0 || stus == fnfErr)
+ return strdup((char *)path);
+ return NULL;
+ }
+ } else if (ploc->locType == cmPathBasedProfile) {
+ return strdup(ploc->u.pathLoc.path);
+ }
+ return NULL;
+}
+
+/* Ugh! ColorSync doesn't take care of endian issues !! */
+static void cs_w32(unsigned long *p, unsigned long val) {
+ ((char *)p)[0] = (char)(val >> 24);
+ ((char *)p)[1] = (char)(val >> 16);
+ ((char *)p)[2] = (char)(val >> 8);
+ ((char *)p)[3] = (char)(val);
+}
+
+static void cs_w16(unsigned short *p, unsigned short val) {
+ ((char *)p)[0] = (char)(val >> 8);
+ ((char *)p)[1] = (char)(val);
+}
+
+#endif /* < 10.6 */
+
+#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
+
+/* There doesn't seem to be any means of determining the locations */
+/* of profiles using current OS X API's, so we simply hard code them. */
+/* This makes the older code easier too. */
+
+#define COLORSYNC_DIR_NETWORK "/Network/Library/ColorSync/Profiles/"
+#define COLORSYNC_DIR_SYSTEM "/System/Library/ColorSync/Profiles/"
+#define COLORSYNC_DIR_LOCAL "/Library/ColorSync/Profiles/"
+#define COLORSYNC_DIR_USER "/Library/ColorSync/Profiles/"
+
+/* Given a profile name and a scope, return the path to the */
+/* installed profile. free the returned string when done. */
+/* Returns NULL on error */
+static char *iprof_path(p_scope scope, char *fname) {
+ char *home = "", *dirname, *basename, *rv = NULL;
+ int tlen = 0;
+
+ /* Locate the base filename in the fname */
+ for (basename = fname + strlen(fname); ; basename--) {
+ if (basename <= fname || basename[-1] == '/')
+ break;
+ }
+
+ /* NSFileManager's URLForDirectory: etc. doesn't have ColorSync, */
+ /* so we have no choice but to hard code the paths */
+ if (scope == p_scope_network)
+ dirname = COLORSYNC_DIR_NETWORK;
+ else if (scope == p_scope_system)
+ dirname = COLORSYNC_DIR_SYSTEM;
+ else if (scope == p_scope_local)
+ dirname = COLORSYNC_DIR_LOCAL;
+ else {
+ dirname = COLORSYNC_DIR_USER;
+ if ((home = getenv("HOME")) == NULL){
+ return NULL;
+ }
+ }
+
+ tlen = strlen(home) + strlen(dirname) + strlen(basename) + 1;
+ if ((rv = malloc(tlen)) == NULL) {
+ return NULL;
+ }
+
+ strcpy(rv, home);
+ strcat(rv, dirname);
+ strcat(rv, basename);
+
+ return rv;
+}
+
+/* Callback */
+typedef struct {
+ CFUUIDRef dispuuid; /* UUID to match */
+ CFStringRef id; /* ProfileId */
+ CFURLRef url; /* URL to return */
+} diter_cntx_t;
+
+bool diter_callback(CFDictionaryRef dict, void *cntx) {
+ diter_cntx_t *cx = (diter_cntx_t *)cntx;
+ CFStringRef str;
+ CFUUIDRef uuid;
+ CFBooleanRef iscur;
+
+ if ((str = CFDictionaryGetValue(dict, kColorSyncDeviceClass)) == NULL) {
+ debugrr("Failed to get kColorSyncDeviceClass\n");
+ return true;
+ }
+ if (!CFEqual(str, kColorSyncDisplayDeviceClass)) {
+ return true;
+ }
+ if ((uuid = CFDictionaryGetValue(dict, kColorSyncDeviceID)) == NULL) {
+ debugrr("Failed to get kColorSyncDeviceID\n");
+ return true;
+ }
+ if (!CFEqual(uuid, cx->dispuuid)) {
+ return true;
+ }
+ if ((iscur = CFDictionaryGetValue(dict, kColorSyncDeviceProfileIsCurrent)) == NULL) {
+ debugrr("Failed to get kColorSyncDeviceProfileIsCurrent\n");
+ return true;
+ }
+ if (!CFBooleanGetValue(iscur)) {
+ return true;
+ }
+
+ /* get the URL */
+ if ((cx->id = CFDictionaryGetValue(dict, kColorSyncDeviceProfileID)) == NULL) {
+ debugrr("Failed to get current profile ID\n");
+ return true;
+ }
+ if ((cx->url = CFDictionaryGetValue(dict, kColorSyncDeviceProfileURL)) == NULL) {
+ debugrr("Failed to get current profile URL\n");
+ return true;
+ }
+ CFRetain(cx->id);
+ CFRetain(cx->url);
+
+ return false;
+}
+
+/* Return the url to the given displays current profile. */
+/* Return NULL on error. CFRelease when done. */
+/* Optionally return the ProfileID string */
+CFURLRef cur_profile_url(CFStringRef *idp, dispwin *p) {
+ diter_cntx_t cx;
+
+ if ((cx.dispuuid = CGDisplayCreateUUIDFromDisplayID(p->ddid)) == NULL) {
+ debugr2((errout,"CGDisplayCreateUUIDFromDisplayID() failed\n"));
+ return NULL;
+ }
+ cx.id = NULL;
+ cx.url = NULL;
+
+ ColorSyncIterateDeviceProfiles(diter_callback, (void *)&cx);
+
+ CFRelease(cx.dispuuid);
+
+ if (idp != NULL)
+ *idp = cx.id;
+ else
+ CFRelease(cx.id);
+ return cx.url;
+}
+
+/* Convert a URL into a local POSIX path string */
+/* Return NULL on error. Free returned string when done. */
+char *url_to_path(CFURLRef url) {
+ CFStringRef urlstr;
+ CFIndex bufSize;
+ char *dpath = NULL; /* return value */
+
+ urlstr = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
+ bufSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(urlstr),
+ kCFStringEncodingUTF8) + 1;
+ if ((dpath = malloc(bufSize)) == NULL) {
+ debugrr("url_to_path: malloc failed\n");
+ CFRelease(urlstr);
+ return NULL;
+ }
+ if (!CFStringGetCString(urlstr, dpath, bufSize, kCFStringEncodingUTF8)) {
+ debugrr("url_to_path: CFStringGetCString failed\n");
+ CFRelease(urlstr);
+ return NULL;
+ }
+ CFRelease(urlstr);
+
+ return dpath;
+}
+
+/* Return information about the given displays current profile */
+/* Return NULL on error. Free returned string when done. */
+char *cur_profile(dispwin *p) {
+ CFURLRef url;
+ char *dpath = NULL; /* return value */
+
+ if ((url = cur_profile_url(NULL, p)) == NULL) {
+ debugr2((errout,"cur_profile failed to find current profile\n"));
+ return NULL;
+ }
+
+ dpath = url_to_path(url);
+
+ CFRelease(url);
+
+ return dpath;
+}
+
+#endif /* >= 10.6 */
+
+#endif /* __APPLE__ */
+
+/* Set the RAMDAC values. */
+/* Return nz if not possible */
+/* Return 2 for OS X when the current profile is a system profile */
+static int dispwin_set_ramdac(dispwin *p, ramdac *r, int persist) {
+ int i, j;
+
+#ifdef NT
+ WORD vals[3][256]; /* 16 bit elements */
+
+ debugr("dispwin_set_ramdac called\n");
+
+#ifdef NEVER /* Doesn't seem to return correct information on win2K systems */
+ if ((GetDeviceCaps(p->hdc, COLORMGMTCAPS) & CM_GAMMA_RAMP) == 0) {
+ debugr("dispwin_set_ramdac failed on GetDeviceCaps(CM_GAMMA_RAMP)\n");
+ return 1;
+ }
+#endif
+
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < r->nent; i++) {
+ double vv = r->v[j][i];
+ if (vv < 0.0)
+ vv = 0.0;
+ else if (vv > 1.0)
+ vv = 1.0;
+ vals[j][i] = (int)(65535.0 * vv + 0.5);
+ }
+ }
+
+ if (SetDeviceGammaRamp(p->hdc, vals) == 0) {
+ debugr2((errout,"dispwin_set_ramdac failed on SetDeviceGammaRamp() with error %d\n",GetLastError()));
+ return 1;
+ }
+#endif /* NT */
+
+#ifdef __APPLE__
+ { /* Transient first */
+ CGGammaValue vals[3][16384];
+
+ debugr("dispwin_set_ramdac called\n");
+
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < r->nent; i++) {
+ double vv = r->v[j][i];
+ if (vv < 0.0)
+ vv = 0.0;
+ else if (vv > 1.0)
+ vv = 1.0;
+ vals[j][i] = vv;
+ }
+ }
+
+ if (CGSetDisplayTransferByTable(p->ddid, r->nent, vals[0], vals[1], vals[2]) != 0) {
+ debugr("CGSetDisplayTransferByTable failed\n");
+ return 1;
+ }
+
+ }
+
+ /* In theory IOFBSetGamma() might work - but maybe it needs root, and won't */
+ /* sync with OS X's view of what's loaded. */
+
+ /* By default the OSX RAMDAC access is transient, lasting only as long */
+ /* as the process setting it. To set a temporary but persistent beyond this process */
+ /* calibration, we fake up a profile and install it in such a way that it will disappear, */
+ /* restoring the previous display profile whenever the current ColorSync display profile */
+ /* is restored to the screen. NOTE that this trick will fail if it is not possible */
+ /* to rename the currently selected profile file, ie. because it is a system profile. */
+ /* [ Would a workaround be to use a link, or copy of the system file ? ] */
+ if (persist)
+#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
+ if (persist) { /* Persistent */
+ int rv = 0;
+ CFStringRef id;
+ CFURLRef url;
+ char *tpath; /* Temporary profiles/original profiles path */
+ char *ppath; /* Current/renamed profiles path */
+ icmFile *rd_fp, *wr_fp;
+ icc *icco;
+ CFUUIDRef dispuuid;
+ CFStringRef keys[1];
+ CFURLRef values[1];
+ CFDictionaryRef dict;
+
+ debugr("Set_ramdac persist\n");
+
+ /* Get the current installed profile */
+ if ((url = cur_profile_url(&id, p)) == NULL) {
+ debugr2((errout,"cur_profile_url failed for current profile\n"));
+ return 1;
+ }
+ if ((tpath = url_to_path(url)) == NULL) {
+ debugr2((errout,"url_to_path failed for current profile\n"));
+ CFRelease(id);
+ CFRelease(url);
+ return 1;
+ }
+
+ debugr2((errout, "Current profile path = '%s'\n",tpath));
+
+ /* Create a patched version with our calibration: */
+ /* (Hmm. I think icclib will cope with V4 OK) */
+
+ /* Open up the profile for reading */
+ if ((rd_fp = new_icmFileStd_name(tpath,"r")) == NULL) {
+ debugr2((errout,"Failed to open profile '%s'\n",tpath));
+ free(tpath);
+ CFRelease(id);
+ CFRelease(url);
+ return 1;
+ }
+
+ if ((icco = new_icc()) == NULL) {
+ debugr2((errout,"Creation of ICC object failed\n"));
+ rd_fp->del(rd_fp);
+ free(tpath);
+ CFRelease(id);
+ CFRelease(url);
+ return 1;
+ }
+
+ /* Read header etc. */
+ if ((rv = icco->read(icco,rd_fp,0)) != 0) {
+ debugr2((errout,"%d, %s",rv,icco->err));
+ icco->del(icco);
+ rd_fp->del(rd_fp);
+ free(tpath);
+ CFRelease(id);
+ CFRelease(url);
+ return 1;
+ }
+ /* Read every tag */
+ if (icco->read_all_tags(icco) != 0) {
+ debugr2((errout,"Unable to read all tags: %d, %s",icco->errc,icco->err));
+ icco->del(icco);
+ rd_fp->del(rd_fp);
+ free(tpath);
+ CFRelease(id);
+ CFRelease(url);
+ return 1;
+ }
+
+ rd_fp->del(rd_fp); rd_fp = NULL;
+
+ /* Replace the description */
+ {
+ icmTextDescription *wo;
+ char *dst = "Dispwin Temp";
+
+ if (icco->find_tag(icco, icSigProfileDescriptionTag) == 0) {
+ if (icco->delete_tag(icco, icSigProfileDescriptionTag) != 0) {
+ debugr2((errout,"Unable to delete Description tag: %d, %s",icco->errc,icco->err));
+ icco->del(icco);
+ free(tpath);
+ CFRelease(id);
+ CFRelease(url);
+ return 1;
+ }
+ }
+
+ if ((wo = (icmTextDescription *)icco->add_tag(
+ icco, icSigProfileDescriptionTag, icSigTextDescriptionType)) == NULL) {
+ debugr2((errout,"Unable to add Description tag: %d, %s",icco->errc,icco->err));
+ icco->del(icco);
+ free(tpath);
+ CFRelease(id);
+ CFRelease(url);
+ return 1;
+ }
+
+ wo->size = strlen(dst)+1; /* Allocated and used size of desc, inc null */
+ wo->allocate((icmBase *)wo);/* Allocate space */
+ strcpy(wo->desc, dst); /* Copy the string in */
+ }
+ /* Replace the vcgt */
+ {
+ int c,i;
+ icmVideoCardGamma *wo;
+
+ if (icco->find_tag(icco, icSigVideoCardGammaTag) == 0) {
+ if (icco->delete_tag(icco, icSigVideoCardGammaTag) != 0) {
+ debugr2((errout,"Unable to delete VideoCardGamma tag: %d, %s",icco->errc,icco->err));
+ icco->del(icco);
+ free(tpath);
+ CFRelease(id);
+ CFRelease(url);
+ return 1;
+ }
+ }
+
+ if ((wo = (icmVideoCardGamma *)icco->add_tag(icco, icSigVideoCardGammaTag,
+ icSigVideoCardGammaType)) == NULL) {
+ debugr2((errout,"Unable to add VideoCardGamma tag: %d, %s",icco->errc,icco->err));
+ icco->del(icco);
+ free(tpath);
+ CFRelease(id);
+ CFRelease(url);
+ return 1;
+ }
+
+ wo->tagType = icmVideoCardGammaTableType;
+ wo->u.table.channels = 3; /* rgb */
+ wo->u.table.entryCount = r->nent; /* number of calibration entries */
+ wo->u.table.entrySize = 2; /* 16 bits */
+ wo->allocate((icmBase*)wo);
+
+ for (i = 0; i < r->nent; i++) {
+ for (j = 0; j < 3; j++) {
+ double vv = r->v[j][i];
+ int ivv;
+ if (vv < 0.0)
+ vv = 0.0;
+ else if (vv > 1.0)
+ vv = 1.0;
+ ((unsigned short*)wo->u.table.data)[r->nent * j + i] = (int)(vv * 65535.0 + 0.5);
+ }
+ }
+ }
+
+ if ((ppath = malloc(strlen(tpath) + 6)) == NULL) {
+ debugr2((errout,"malloc failed for display '%s'\n",p->name));
+ icco->del(icco);
+ free(tpath);
+ CFRelease(id);
+ CFRelease(url);
+ return 1;
+ }
+ strcpy(ppath, tpath);
+ strcat(ppath,".orig");
+
+ /* Rename the currently installed profile temporarily. */
+ /* This will fail if current profile is a system profile and not writable by the user. */
+ /* This could be worked around by cloning the system profile to the user */
+ /* area and installing it before modifiying it. */
+ if (rename(tpath, ppath) != 0) {
+ debugr2((errout,"Unable to rename '%s' to '%s'\n",tpath,ppath));
+ icco->del(icco);
+ free(ppath);
+ free(tpath);
+ CFRelease(id);
+ CFRelease(url);
+ return 2;
+ }
+
+ /* Rename worked */
+
+ debugr2((errout,"Renamed current profile '%s' to '%s'\n",tpath,ppath));
+
+ /* Save the modified profile to the original profile name */
+ if ((wr_fp = new_icmFileStd_name(tpath,"w")) == NULL) {
+ debugr2((errout,"Failed to open '%s' for writing\n",tpath));
+ free(ppath);
+ icco->del(icco);
+ free(tpath);
+ CFRelease(id);
+ CFRelease(url);
+ return 1;
+ }
+
+ if ((rv = icco->write(icco,wr_fp,0)) != 0) {
+ debugr2((errout,"Write file: %d, %s",rv,icco->err));
+ free(ppath);
+ wr_fp->del(wr_fp);
+ icco->del(icco);
+ free(tpath);
+ CFRelease(id);
+ CFRelease(url);
+ return 1;
+ }
+
+ icco->del(icco);
+ wr_fp->del(wr_fp);
+
+ /* Update to the "current" profile, which is actually the modified profile */
+ if ((dispuuid = CGDisplayCreateUUIDFromDisplayID(p->ddid)) == NULL) {
+ debugr2((errout,"CGDisplayCreateUUIDFromDisplayID() failed\n"));
+ free(ppath);
+ free(tpath);
+ CFRelease(id);
+ CFRelease(url);
+ return 1;
+ }
+
+ keys[0] = id;
+ values[0] = url;
+
+ if ((dict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&keys,
+ (const void **)&values, 1, NULL, NULL)) == NULL) {
+ debugr2((errout,"CFDictionaryCreate() failed\n"));
+ CFRelease(dispuuid);
+ free(ppath);
+ free(tpath);
+ CFRelease(id);
+ CFRelease(url);
+ return 1;
+ }
+ if (!ColorSyncDeviceSetCustomProfiles(kColorSyncDisplayDeviceClass, dispuuid, dict)) {
+ debugr2((errout,"ColorSyncDeviceSetCustomProfiles() failed\n"));
+ CFRelease(dict);
+ CFRelease(dispuuid);
+ free(ppath);
+ free(tpath);
+ CFRelease(id);
+ CFRelease(url);
+ return 1;
+ }
+ CFRelease(dict);
+ CFRelease(dispuuid);
+ CFRelease(id);
+ CFRelease(url);
+
+ /* Delete the temporary profile */
+ unlink(tpath);
+
+ /* Rename the current profile back to it's correct name */
+ if (rename(ppath, tpath) != 0) {
+ debugr2((errout,"Renaming existing profile '%s' failed\n",ppath));
+ return 1;
+ }
+ debugr2((errout,"Restored '%s' back to '%s'\n",ppath,tpath));
+ free(ppath);
+ free(tpath);
+ }
+#else /* < 10.6 */
+ if (persist) { /* Persistent */
+ CMError ev;
+ CMProfileRef prof; /* Current AVID profile */
+ CMProfileLocation ploc; /* Current profile location */
+ UInt32 plocsz = sizeof(CMProfileLocation);
+ char *ppath; /* Current/renamed profiles path */
+ char *tpath; /* Temporary profiles/original profiles path */
+ CMProfileRef tprof; /* Temporary profile */
+ CMProfileLocation tploc; /* Temporary profile */
+ CMVideoCardGammaType *vcgt = NULL; /* vcgt tag */
+ int size;
+ int i, j;
+
+ debugr("Set_ramdac persist\n");
+
+ /* Get the current installed profile */
+ if ((ev = CMGetProfileByAVID((CMDisplayIDType)p->ddid, &prof)) != noErr) {
+ debugr2((errout,"CMGetProfileByAVID() failed for display '%s' with error %d\n",p->name,ev));
+ return 1;
+ }
+
+ /* Get the current installed profile's location */
+ if ((ev = NCMGetProfileLocation(prof, &ploc, &plocsz)) != noErr) {
+ debugr2((errout,"NCMGetProfileLocation() failed for display '%s' with error %d\n",p->name,ev));
+ return 1;
+ }
+
+ debugr2((errout, "Current profile path = '%s'\n",plocpath(&ploc)));
+
+ if ((tpath = plocpath(&ploc)) == NULL) {
+ debugr2((errout,"plocpath failed for display '%s'\n",p->name));
+ return 1;
+ }
+
+ if (strlen(tpath) > 255) {
+ debugr2((errout,"current profile path is too long\n"));
+ return 1;
+ }
+ if ((ppath = malloc(strlen(tpath) + 6)) == NULL) {
+ debugr2((errout,"malloc failed for display '%s'\n",p->name));
+ free(tpath);
+ return 1;
+ }
+ strcpy(ppath,tpath);
+ strcat(ppath,".orig");
+
+ /* Rename the currently installed profile temporarily */
+ if (rename(tpath, ppath) != 0) {
+ debugr2((errout,"Renaming existing profile '%s' failed\n",ppath));
+ return 2;
+ }
+ debugr2((errout,"Renamed current profile '%s' to '%s'\n",tpath,ppath));
+
+ /* Make a copy of the renamed current profile back to it's true name */
+ tploc.locType = cmPathBasedProfile;
+ strncpy(tploc.u.pathLoc.path, tpath, 255);
+ tploc.u.pathLoc.path[255] = '\000';
+
+ /* Make the temporary copy */
+ if ((ev = CMCopyProfile(&tprof, &tploc, prof)) != noErr) {
+ debugr2((errout,"CMCopyProfile() failed for display '%s' with error %d\n",p->name,ev));
+ CMCloseProfile(prof);
+ unlink(tpath);
+ rename(ppath, tpath);
+ return 1;
+ }
+ CMCloseProfile(prof);
+
+ if ((ev = CMSetProfileDescriptions(tprof, "Dispwin Temp", 13, NULL, 0, NULL, 0)) != noErr) {
+ debugr2((errout,"cmVideoCardGammaTag`() failed for display '%s' with error %d\n",p->name,ev));
+ CMCloseProfile(tprof);
+ unlink(tpath);
+ rename(ppath, tpath);
+ return 1;
+ }
+
+ /* Change the description and set the vcgt tag to the calibration */
+ if ((vcgt = malloc(size = (sizeof(CMVideoCardGammaType) - 1 + 3 * 2 * r->nent))) == NULL) {
+ debugr2((errout,"malloc of vcgt tag failed for display '%s' with error %d\n",p->name,ev));
+ CMCloseProfile(tprof);
+ unlink(tpath);
+ rename(ppath, tpath);
+ return 1;
+ }
+ cs_w32(&vcgt->typeDescriptor, cmSigVideoCardGammaType);
+ cs_w32(&vcgt->gamma.tagType, cmVideoCardGammaTableType); /* Table, not formula */
+ cs_w16(&vcgt->gamma.u.table.channels, 3);
+ cs_w16(&vcgt->gamma.u.table.entryCount, r->nent);
+ cs_w16(&vcgt->gamma.u.table.entrySize, 2);
+
+ for (i = 0; i < r->nent; i++) {
+ for (j = 0; j < 3; j++) {
+ double vv = r->v[j][i];
+ int ivv;
+ if (vv < 0.0)
+ vv = 0.0;
+ else if (vv > 1.0)
+ vv = 1.0;
+ ivv = (int)(vv * 65535.0 + 0.5);
+ cs_w16(((unsigned short *)vcgt->gamma.u.table.data) + ((j * r->nent) + i), ivv);
+ }
+ }
+
+ /* Replace or add a vcgt tag */
+ if ((ev = CMSetProfileElement(tprof, cmVideoCardGammaTag, size, vcgt)) != noErr) {
+ debugr2((errout,"CMSetProfileElement vcgt tag failed with error %d\n",ev));
+ free(vcgt);
+ CMCloseProfile(tprof);
+ unlink(tpath);
+ rename(ppath, tpath);
+ return 1;
+ }
+ free(vcgt);
+
+ if ((ev = CMUpdateProfile(tprof)) != noErr) {
+ debugr2((errout,"CMUpdateProfile failed with error %d\n",ev));
+ CMCloseProfile(tprof);
+ unlink(tpath);
+ rename(ppath, tpath);
+ return 1;
+ }
+
+ /* Make temporary file the current profile - updates LUTs */
+ if ((ev = CMSetProfileByAVID((CMDisplayIDType)p->ddid, tprof)) != noErr) {
+ debugr2((errout,"CMSetProfileByAVID() failed for display '%s' with error %d\n",p->name,ev));
+ CMCloseProfile(tprof);
+ unlink(tpath);
+ rename(ppath, tpath);
+ return 1;
+ }
+ CMCloseProfile(tprof);
+ debugr2((errout,"Set display to use temporary profile '%s'\n",tpath));
+
+ /* Delete the temporary profile */
+ unlink(tpath);
+
+ /* Rename the current profile back to it's correct name */
+ if (rename(ppath, tpath) != 0) {
+ debugr2((errout,"Renaming existing profile '%s' failed\n",ppath));
+ return 1;
+ }
+ debugr2((errout,"Restored '%s' back to '%s'\n",ppath,tpath));
+ }
+#endif /* < 10.6 */
+
+#endif /* __APPLE__ */
+
+#if defined(UNIX_X11)
+ unsigned short vals[3][16384];
+
+ debugr("dispwin_set_ramdac called\n");
+
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < r->nent; i++) {
+ double vv = r->v[j][i];
+ if (vv < 0.0)
+ vv = 0.0;
+ else if (vv > 1.0)
+ vv = 1.0;
+ vals[j][i] = (int)(vv * 65535.0 + 0.5);
+ }
+ }
+
+#if RANDR_MAJOR == 1 && RANDR_MINOR >= 2 && !defined(DISABLE_RANDR)
+ if (p->crtc != 0) { /* Using Xrandr 1.2 */
+ XRRCrtcGamma *crtcgam;
+
+ debugr("Setting gamma using Randr 1.2\n");
+
+ if ((crtcgam = XRRAllocGamma(r->nent)) == NULL) {
+ debugr(" XRRAllocGamma failed\n");
+ return 1;
+ }
+
+ for (i = 0; i < r->nent; i++) {
+ crtcgam->red[i] = vals[0][i];
+ crtcgam->green[i] = vals[1][i];
+ crtcgam->blue[i] = vals[2][i];
+ }
+
+ XRRSetCrtcGamma(p->mydisplay, p->crtc, crtcgam);
+ XSync(p->mydisplay, False); /* Flush the change out */
+
+ XRRFreeGamma(crtcgam);
+
+ } else
+#endif /* randr >= V 1.2 */
+ {
+ /* Some propietary multi-screen drivers (ie. TwinView & MergedFB) */
+ /* don't implement the XVidMode extenstion properly. */
+ if (XSetErrorHandler(null_error_handler) == 0) {
+ debugr("get_displays failed on XSetErrorHandler\n");
+ return 1;
+ }
+ if (XF86VidModeSetGammaRamp(p->mydisplay, p->myrscreen, r->nent, vals[0], vals[1], vals[2]) == 0) {
+ XSetErrorHandler(NULL);
+ debugr("XF86VidModeSetGammaRamp failed\n");
+ return 1;
+ }
+ XSync(p->mydisplay, False); /* Flush the change out */
+ XSetErrorHandler(NULL);
+ }
+#endif /* UNXI X11 */
+
+ debugr("XF86VidModeSetGammaRamp returning OK\n");
+ return 0;
+}
+
+
+/* Clone ourselves */
+static ramdac *dispwin_clone_ramdac(ramdac *r) {
+ ramdac *nr;
+ int i, j;
+
+ debug("dispwin_clone_ramdac called\n");
+
+ /* Allocate a ramdac */
+ if ((nr = (ramdac *)calloc(sizeof(ramdac), 1)) == NULL) {
+ return NULL;
+ }
+
+ *nr = *r; /* Structrure copy */
+
+ for (j = 0; j < 3; j++) {
+
+ if ((nr->v[j] = (double *)calloc(sizeof(double), r->nent)) == NULL) {
+ for (j--; j >= 0; j--)
+ free(nr->v[j]);
+ free(nr);
+ return NULL;
+ }
+ }
+
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < r->nent; i++) {
+ nr->v[j][i] = r->v[j][i];
+ }
+ }
+
+ debug("clone is done\n");
+ return nr;
+}
+
+/* Set the ramdac values to linear */
+static void dispwin_setlin_ramdac(ramdac *r) {
+ int i, j;
+
+ debug("dispwin_setlin_ramdac called\n");
+
+ for (i = 0; i < r->nent; i++) {
+ double val = i/(r->nent - 1.0);
+ for (j = 0; j < 3; j++) {
+ r->v[j][i] = val;
+ }
+ }
+}
+
+/* We're done with a ramdac structure */
+static void dispwin_del_ramdac(ramdac *r) {
+ int j;
+
+ debug("dispwin_del_ramdac called\n");
+
+ for (j = 0; j < 3; j++) {
+ free(r->v[j]);
+ }
+
+ free(r);
+}
+
+/* ----------------------------------------------- */
+/* Useful function for X11 profie atom settings */
+
+#if defined(UNIX_X11)
+/* Return NZ on error */
+static int set_X11_atom(dispwin *p, char *fname) {
+ FILE *fp;
+ unsigned long psize, bread;
+ unsigned char *atomv;
+
+ debugr("Setting _ICC_PROFILE property\n");
+
+ /* Read in the ICC profile, then set the X11 atom value */
+#if !defined(O_CREAT) && !defined(_O_CREAT)
+# error "Need to #include fcntl.h!"
+#endif
+#if defined(O_BINARY) || defined(_O_BINARY)
+ if ((fp = fopen(fname,"rb")) == NULL)
+#else
+ if ((fp = fopen(fname,"r")) == NULL)
+#endif
+ {
+ debugr2((errout,"Can't open file '%s'\n",fname));
+ return 1;
+ }
+
+ /* Figure out how big it is */
+ if (fseek(fp, 0, SEEK_END)) {
+ debugr2((errout,"Seek '%s' to EOF failed\n",fname));
+ return 1;
+ }
+ psize = (unsigned long)ftell(fp);
+
+ if (fseek(fp, 0, SEEK_SET)) {
+ debugr2((errout,"Seek '%s' to SOF failed\n",fname));
+ return 1;
+ }
+
+ if ((atomv = (unsigned char *)malloc(psize)) == NULL) {
+ debugr2((errout,"Failed to allocate buffer for profile '%s'\n",fname));
+ return 1;
+ }
+
+ if ((bread = fread(atomv, 1, psize, fp)) != psize) {
+ debugr2((errout,"Failed to read profile '%s' into buffer\n",fname));
+ return 1;
+ }
+
+ fclose(fp);
+
+ XChangeProperty(p->mydisplay, RootWindow(p->mydisplay, 0), p->icc_atom,
+ XA_CARDINAL, 8, PropModeReplace, atomv, psize);
+
+#if RANDR_MAJOR == 1 && RANDR_MINOR >= 2 && !defined(DISABLE_RANDR)
+ if (p->icc_out_atom != 0) {
+ /* If Xrandr 1.2, set property on output */
+ /* This seems to fail on some servers. Ignore the error ? */
+ if (XSetErrorHandler(null_error_handler) == 0) {
+ debugr("get_displays failed on XSetErrorHandler\n");
+ return 1;
+ }
+ g_error_handler_triggered = 0;
+ XRRChangeOutputProperty(p->mydisplay, p->output, p->icc_out_atom,
+ XA_CARDINAL, 8, PropModeReplace, atomv, psize);
+ if (g_error_handler_triggered != 0) {
+ debugr("XRRChangeOutputProperty failed\n");
+ warning("Unable to set _ICC_PROFILE property on output");
+ }
+ XSync(p->mydisplay, False); /* Flush the change out */
+ XSetErrorHandler(NULL);
+ }
+#endif /* randr >= V 1.2 */
+ free(atomv);
+
+ return 0;
+}
+#endif /* UNXI X11 */
+
+/* ----------------------------------------------- */
+/* Install a display profile and make */
+/* it the default for this display. */
+/* Set the display to the calibration in the profile */
+/* (r == NULL if no calibration) */
+/* (We assume that the caller has checked that it's an ICC profile) */
+/* Return nz if failed */
+int dispwin_install_profile(dispwin *p, char *fname, ramdac *r, p_scope scope) {
+#ifdef NT
+ {
+ char *fullpath;
+ char *basename;
+ char colpath[MAX_PATH];
+ unsigned long colpathlen = MAX_PATH;
+ WCS_PROFILE_MANAGEMENT_SCOPE wcssc;
+ unsigned short *wpath, *wbname, *wmonid;
+
+ debugr2((errout,"dispwin_install_profile got '%s'\n",fname));
+
+ if (GetColorDirectory(NULL, colpath, &colpathlen) == 0) {
+ debugr2((errout,"Getting color directory failed\n"));
+ return 1;
+ }
+
+ if ((fullpath = _fullpath(NULL, fname, 0)) == NULL) {
+ debugr2((errout,"_fullpath() failed\n"));
+ return 1;
+ }
+
+ if ((basename = PathFindFileName(fullpath)) == NULL) {
+ debugr2((errout,"Locating base name in '%s' failed\n",fname));
+ free(fullpath);
+ return 1;
+ }
+
+ if ((strlen(colpath) + strlen(basename) + 2) > MAX_PATH) {
+ debugr2((errout,"Installed profile path too long\n"));
+ free(fullpath);
+ return 1;
+ }
+ strcat(colpath, "\\");
+ strcat(colpath, basename);
+
+ /* Setup in case we're on Vista */
+ if (scope == p_scope_user)
+ wcssc = WCS_PROFILE_MANAGEMENT_SCOPE_CURRENT_USER;
+ else
+ wcssc = WCS_PROFILE_MANAGEMENT_SCOPE_SYSTEM_WIDE;
+
+ if ((wpath = char2wchar(fullpath)) == NULL) {
+ debugr2((errout,"char2wchar failed\n"));
+ free(fullpath);
+ return 1;
+ }
+
+ if ((wbname = char2wchar(basename)) == NULL) {
+ debugr2((errout,"char2wchar failed\n"));
+ free(wpath);
+ free(fullpath);
+ return 1;
+ }
+
+ if ((wmonid = char2wchar(p->monid)) == NULL) {
+ debugr2((errout,"char2wchar failed\n"));
+ free(wbname);
+ free(wpath);
+ free(fullpath);
+ return 1;
+ }
+
+ debugr2((errout,"Installing '%s'\n",fname));
+
+ /* Install doesn't replace an existing installed profile, */
+ /* so we need to try and delete this profile first */
+ if (pWcsDisassociateColorProfileFromDevice != NULL) {
+ (*pWcsDisassociateColorProfileFromDevice)(wcssc, wbname, wmonid);
+ } else {
+ DisassociateColorProfileFromDevice(NULL, basename, p->monid);
+ }
+ if (UninstallColorProfile(NULL, basename, TRUE) == 0) {
+ /* UninstallColorProfile fails on Win2K */
+ _unlink(colpath);
+ }
+
+ if (InstallColorProfile(NULL, fullpath) == 0) {
+ debugr2((errout,"InstallColorProfile() failed for file '%s' with error %d\n",fname,GetLastError()));
+ free(wmonid);
+ free(wbname);
+ free(wpath);
+ free(fullpath);
+ return 1;
+ }
+
+ debugr2((errout,"Associating '%s' with '%s'\n",fullpath,p->monid));
+ if (pWcsAssociateColorProfileWithDevice != NULL) {
+ debugr("Using Vista Associate\n");
+ if ((*pWcsAssociateColorProfileWithDevice)(wcssc, wpath, wmonid) == 0) {
+ debugr2((errout,"WcsAssociateColorProfileWithDevice() failed for file '%s' with error %d\n",fullpath,GetLastError()));
+ free(wmonid);
+ free(wbname);
+ free(wpath);
+ free(fullpath);
+ return 1;
+ }
+ } else {
+ if (AssociateColorProfileWithDevice(NULL, fullpath, p->monid) == 0) {
+ debugr2((errout,"AssociateColorProfileWithDevice() failed for file '%s' with error %d\n",fullpath,GetLastError()));
+ free(wmonid);
+ free(wbname);
+ free(wpath);
+ free(fullpath);
+ return 1;
+ }
+ }
+
+ free(wmonid);
+ free(wbname);
+ free(wpath);
+ free(fullpath);
+ /* The default profile will be the last one associated */
+
+ /* MSWindows doesn't generally set the display to the current profile calibration, */
+ /* so we do it. */
+ if (p->set_ramdac(p,r,1))
+ error("Failed to set VideoLUT");
+
+ return 0;
+ }
+#endif /* NT */
+
+/* For Linux and OS X, make sure we don't create a file with the wrong owner */
+#if defined(UNIX)
+ /* If we're creating a user profile and running as root sudo */
+ if (scope == p_scope_user && geteuid() == 0) {
+ char *uids, *gids;
+ int uid, gid;
+
+ debugr("We're setting a user profile running as root - run as user\n");
+ if ((uids = getenv("SUDO_UID")) != NULL
+ && (gids = getenv("SUDO_GID")) != NULL) {
+ uid = atoi(uids);
+ gid = atoi(gids);
+ if (setegid(gid) || seteuid(uid)) {
+ debugr("seteuid or setegid failed\n");
+ }
+ debug2((errout,"Set euid %d and egid %d\n",uid,gid));
+ }
+ /* If setting local system profile and not effective root, but sudo */
+ } else if (scope != p_scope_user && getuid() == 0 && geteuid() != 0) {
+ if (getenv("SUDO_UID") != NULL
+ && getenv("SUDO_GID") != NULL) {
+
+ debugr("We're setting a system profile running as user - revert to root\n");
+ setegid(getgid());
+ seteuid(getuid());
+ }
+ }
+#endif /* OS X || Linux */
+
+#ifdef __APPLE__
+
+#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
+ {
+ char *dpath; /* Install file path */
+ CFUUIDRef dispuuid;
+ CFStringRef cfprofpath;
+ CFStringRef keys[1];
+ CFURLRef values[1];
+ CFDictionaryRef dict;
+
+ if ((dispuuid = CGDisplayCreateUUIDFromDisplayID(p->ddid)) == NULL) {
+ debugr2((errout,"CGDisplayCreateUUIDFromDisplayID() failed\n"));
+ return 1;
+ }
+
+ /* Determine the location the profile will be installed into */
+ if ((dpath = iprof_path(scope, fname)) == NULL) {
+ debugr2((errout,"iprof_path() failed\n"));
+ CFRelease(dispuuid);
+ return 1;
+ }
+
+ debugr2((errout,"Source profile '%s'\n",fname));
+ debugr2((errout,"Destination profile '%s'\n",dpath));
+
+ /* Copy the new profile to the destination */
+ if (copyfile(fname, dpath, NULL, COPYFILE_ALL) != 0) {
+ debugr2((errout,"copyfile failed\n"));
+ free(dpath);
+ CFRelease(dispuuid);
+ return 1;
+ }
+
+ /* Register it with the OS and make it the default */
+ if ((cfprofpath = CFStringCreateWithCString(kCFAllocatorDefault, dpath,
+ kCFStringEncodingUTF8)) == NULL) {
+ debugr2((errout,"CFStringCreateWithCString() failed\n"));
+ free(dpath);
+ CFRelease(dispuuid);
+ return 1;
+ }
+ free(dpath); dpath = NULL;
+
+ keys[0] = kColorSyncDeviceDefaultProfileID;
+ if ((values[0] = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, cfprofpath,
+ kCFURLPOSIXPathStyle, false)) == NULL) {
+ debugr2((errout,"CFURLCreateWithFileSystemPath() failed\n"));
+ CFRelease(cfprofpath);
+ CFRelease(dispuuid);
+ return 1;
+ }
+
+ if ((dict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&keys,
+ (const void **)&values, 1, NULL, NULL)) == NULL) {
+ debugr2((errout,"CFDictionaryCreate() failed\n"));
+ CFRelease(values[0]);
+ CFRelease(cfprofpath);
+ CFRelease(dispuuid);
+ return 1;
+ }
+ if (!ColorSyncDeviceSetCustomProfiles(kColorSyncDisplayDeviceClass, dispuuid, dict)) {
+ debugr2((errout,"ColorSyncDeviceSetCustomProfiles() failed\n"));
+ CFRelease(dict);
+ CFRelease(values[0]);
+ CFRelease(cfprofpath);
+ CFRelease(dispuuid);
+ return 1;
+ }
+ CFRelease(dict);
+ CFRelease(values[0]);
+ CFRelease(cfprofpath);
+ CFRelease(dispuuid);
+
+ return 0;
+ }
+#else /* 10.6 and prior */
+ // Switch to using iprof_path() to simplify ?
+ {
+ CMError ev;
+ short vref;
+ FSRef dirref;
+ char dpath[FILENAME_MAX];
+ char *basename;
+
+ CMProfileLocation ploc; /* Source profile location */
+ CMProfileRef prof; /* Source profile */
+ CMProfileLocation dploc; /* Destinaion profile location */
+ CMProfileRef dprof; /* Destinaion profile */
+
+ if (scope == p_scope_network)
+ vref = kNetworkDomain;
+ else if (scope == p_scope_system)
+ vref = kSystemDomain;
+ else if (scope == p_scope_local)
+ vref = kLocalDomain;
+ else
+ vref = kUserDomain;
+
+ /* Locate the appropriate ColorSync path */
+ if ((ev = FSFindFolder(vref, kColorSyncProfilesFolderType, kCreateFolder, &dirref)) != noErr) {
+ debugr2((errout,"FSFindFolder() failed with error %d\n",ev));
+ return 1;
+ }
+
+ /* Convert to POSIX path */
+ if ((ev = FSRefMakePath(&dirref, (unsigned char *)dpath, FILENAME_MAX)) != noErr) {
+ debugr2((errout,"FSRefMakePath failed with error %d\n",ev));
+ return 1;
+ }
+
+ /* Locate the base filename in the fname */
+ for (basename = fname + strlen(fname); ; basename--) {
+ if (basename <= fname || basename[-1] == '/')
+ break;
+ }
+
+ /* Append the basename to the ColorSync directory path */
+ if ((strlen(dpath) + strlen(basename) + 2) > FILENAME_MAX
+ || (strlen(dpath) + strlen(basename) + 2) > 256) {
+ debugr2((errout,"ColorSync dir + profile name too long\n"));
+ return 1;
+ }
+ strcat(dpath, "/");
+ strcat(dpath, basename);
+
+ debugr2((errout,"Source profile '%s'\n",fname));
+ debugr2((errout,"Destination profile '%s'\n",dpath));
+
+ /* Open the profile we want to install */
+ ploc.locType = cmPathBasedProfile;
+ strncpy(ploc.u.pathLoc.path, fname, 255);
+ ploc.u.pathLoc.path[255] = '\000';
+
+ if ((ev = CMOpenProfile(&prof, &ploc)) != noErr) {
+ debugr2((errout,"CMOpenProfile() failed for file '%s' with error %d\n",fname,ev));
+ return 1;
+ }
+
+ /* Delete any current profile */
+ unlink(dpath);
+
+ /* Make a copy of it to the ColorSync directory */
+ dploc.locType = cmPathBasedProfile;
+ strncpy(dploc.u.pathLoc.path, dpath, 255);
+ dploc.u.pathLoc.path[255] = '\000';
+
+ if ((ev = CMCopyProfile(&dprof, &dploc, prof)) != noErr) {
+ debugr2((errout,"CMCopyProfile() failed for file '%s' with error %d\n",dpath,ev));
+ return 1;
+ }
+
+ /* Make it the current profile - updates LUTs */
+ if ((ev = CMSetProfileByAVID((CMDisplayIDType)p->ddid, dprof)) != noErr) {
+ debugr2((errout,"CMSetProfileByAVID() failed for file '%s' with error %d\n",fname,ev));
+ return 1;
+ }
+ CMCloseProfile(prof);
+ CMCloseProfile(dprof);
+
+ return 0;
+ }
+#endif /* 10.6 and prior */
+#endif /* __APPLE__ */
+
+#if defined(UNIX_X11) && defined(USE_UCMM)
+ {
+ ucmm_error ev;
+ ucmm_scope sc;
+ FILE *fp;
+ unsigned long psize, bread;
+ unsigned char *atomv;
+ int rv;
+
+ if (scope == p_scope_network
+ || scope == p_scope_system
+ || scope == p_scope_local)
+ sc = ucmm_local_system;
+ else
+ sc = ucmm_user;
+
+ if ((ev = ucmm_install_monitor_profile(sc, p->edid, p->edid_len, p->name, fname)) != ucmm_ok) {
+ debugr2((errout,"Installing profile '%s' failed with error %d '%s'\n",fname,ev,ucmm_error_string(ev)));
+ return 1;
+ }
+
+ if ((rv = set_X11_atom(p, fname)) != 0) {
+ debugr2((errout,"Setting X11 atom failed"));
+ return 1;
+ }
+
+ /* X11 doesn't set the display to the current profile calibration, */
+ /* so we do it. */
+ if (p->set_ramdac(p,r,1)) {
+ debugr2((errout,"Failed to set VideoLUT"));
+ return 1;
+ }
+ return 0;
+ }
+#endif /* UNXI X11 */
+
+ return 1;
+}
+
+/* Un-Install a display profile */
+/* Return nz if failed, */
+/* 1 if not sucessfully deleted */
+/* 2 if profile not found */
+int dispwin_uninstall_profile(dispwin *p, char *fname, p_scope scope) {
+#ifdef NT
+ {
+ char *fullpath;
+ char *basename;
+ char colpath[MAX_PATH];
+ unsigned long colpathlen = MAX_PATH;
+ WCS_PROFILE_MANAGEMENT_SCOPE wcssc;
+ unsigned short *wbname, *wmonid;
+
+ debugr2((errout,"Uninstalling '%s'\n", fname));
+
+ if (GetColorDirectory(NULL, colpath, &colpathlen) == 0) {
+ debugr2((errout,"Getting color directory failed\n"));
+ return 1;
+ }
+
+ if ((fullpath = _fullpath(NULL, fname, 0)) == NULL) {
+ debugr2((errout,"_fullpath() failed\n"));
+ return 1;
+ }
+
+ if ((basename = PathFindFileName(fullpath)) == NULL) {
+ debugr2((errout,"Locating base name in '%s' failed\n",fname));
+ free(fullpath);
+ return 1;
+ }
+
+ if ((strlen(colpath) + strlen(basename) + 2) > MAX_PATH) {
+ debugr2((errout,"Installed profile path too long\n"));
+ free(fullpath);
+ return 1;
+ }
+ strcat(colpath, "\\");
+ strcat(colpath, basename);
+
+ /* Setup in case we're on Vista */
+ if (scope == p_scope_user)
+ wcssc = WCS_PROFILE_MANAGEMENT_SCOPE_CURRENT_USER;
+ else
+ wcssc = WCS_PROFILE_MANAGEMENT_SCOPE_SYSTEM_WIDE;
+
+ if ((wbname = char2wchar(basename)) == NULL) {
+ debugr2((errout,"char2wchar failed\n"));
+ free(fullpath);
+ return 1;
+ }
+
+ if ((wmonid = char2wchar(p->monid)) == NULL ) {
+ debugr2((errout,"char2wchar failed\n"));
+ free(wbname);
+ free(fullpath);
+ return 1;
+ }
+
+ debugr2((errout,"Disassociating '%s' from '%s'\n",basename,p->monid));
+
+ if (pWcsDisassociateColorProfileFromDevice != NULL) {
+ debugr("Using Vista Disassociate\n");
+ /* Ignore error if profile is already disasociated or doesn't exist */
+ if ((*pWcsDisassociateColorProfileFromDevice)(wcssc, wbname, wmonid) == 0
+ && GetLastError() != 2015 && GetLastError() != 2011) {
+ debugr2((errout,"WcsDisassociateColorProfileWithDevice() failed for file '%s' with error %d\n",basename,GetLastError()));
+ free(wmonid);
+ free(wbname);
+ free(fullpath);
+ return 1;
+ }
+ } else {
+ /* Ignore error if profile is already disasociated or doesn't exist */
+ if (DisassociateColorProfileFromDevice(NULL, basename, p->monid) == 0
+ && GetLastError() != 2015 && GetLastError() != 2011) {
+ debugr2((errout,"DisassociateColorProfileWithDevice() failed for file '%s' with error %d\n",basename,GetLastError()));
+ free(wmonid);
+ free(wbname);
+ free(fullpath);
+ return 1;
+ }
+ }
+
+ if (UninstallColorProfile(NULL, basename, TRUE) == 0) {
+ /* This can happen when some other program has the profile open */
+ int ev;
+ struct _stat sbuf;
+ debugr2((errout,"Warning, uninstallColorProfile() failed for file '%s' with error %d\n", basename,GetLastError()));
+ free(wmonid);
+ free(wbname);
+ free(fullpath);
+ return 2;
+ }
+
+ free(wmonid);
+ free(wbname);
+ free(fullpath);
+
+ return 0;
+ }
+#endif /* NT */
+
+/* For Linux and OS X, make sure we don't create a file with the wrong owner */
+#if defined(UNIX)
+ /* If we're creating a user profile and running as root sudo */
+ if (scope == p_scope_user && geteuid() == 0) {
+ char *uids, *gids;
+ int uid, gid;
+
+ debugr("We're setting a user profile running as root - run as user\n");
+ if ((uids = getenv("SUDO_UID")) != NULL
+ && (gids = getenv("SUDO_GID")) != NULL) {
+ uid = atoi(uids);
+ gid = atoi(gids);
+ if (setegid(gid) || seteuid(uid)) {
+ debugr("seteuid or setegid failed\n");
+ }
+ debug2((errout,"Set euid %d and egid %d\n",uid,gid));
+ }
+ /* If setting local system proile and not effective root, but sudo */
+ } else if (scope != p_scope_user && getuid() == 0 && geteuid() != 0) {
+ if (getenv("SUDO_UID") != NULL
+ && getenv("SUDO_GID") != NULL) {
+
+ debugr("We're setting a system profile running as user - revert to root\n");
+ setegid(getgid());
+ seteuid(getuid());
+ }
+ }
+#endif /* OS X || Linux */
+#ifdef __APPLE__
+#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
+ {
+ char *dpath; /* Un-install file path */
+ struct stat sbuf;
+ int ev;
+ CFStringRef keys[1];
+ CFURLRef values[1];
+ CFDictionaryRef dict;
+ CFUUIDRef dispuuid;
+
+ /* Determine the location the profile will be installed into */
+ if ((dpath = iprof_path(scope, fname)) == NULL) {
+ debugr2((errout,"iprof_path() failed\n"));
+ return 1;
+ }
+
+ debugr2((errout,"Profile to delete '%s'\n",dpath));
+
+ if (stat(dpath, &sbuf) != 0) {
+ debugr2((errout,"delete '%s' profile doesn't exist\n",dpath));
+ return 2;
+ }
+ if ((ev = unlink(dpath)) != 0) {
+ debugr2((errout,"delete '%s' failed with %d\n",dpath,ev));
+ return 1;
+ }
+
+ free(dpath); dpath = NULL;
+
+ /* Make ColorSync notice that it's gone */
+ /* (Works on 10.7, but not 10.6 ? */
+ if ((dispuuid = CGDisplayCreateUUIDFromDisplayID(p->ddid)) == NULL) {
+ debugr2((errout,"CGDisplayCreateUUIDFromDisplayID() failed\n"));
+ return 1;
+ }
+
+ keys[0] = kColorSyncDeviceDefaultProfileID;
+ values[0] = (CFURLRef)kCFNull;
+
+ if ((dict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&keys,
+ (const void **)&values, 1, NULL, NULL)) == NULL) {
+ debugr2((errout,"CFDictionaryCreate() failed\n"));
+ CFRelease(dispuuid);
+ return 1;
+ }
+
+ if (!ColorSyncDeviceSetCustomProfiles(kColorSyncDisplayDeviceClass, dispuuid, dict)) {
+ debugr2((errout,"ColorSyncDeviceSetCustomProfiles() failed\n"));
+ CFRelease(dict);
+ CFRelease(dispuuid);
+ return 1;
+ }
+ CFRelease(dict);
+ CFRelease(dispuuid);
+
+ return 0;
+ }
+#else /* 10.6 and prior */
+ // ~~~ can use above code
+ {
+ CMError cmev;
+ int ev;
+ short vref;
+ char dpath[FILENAME_MAX];
+ char *basename;
+ FSRef dirref;
+ struct stat sbuf;
+
+ if (scope == p_scope_network)
+ vref = kNetworkDomain;
+ else if (scope == p_scope_system)
+ vref = kSystemDomain;
+ else if (scope == p_scope_local)
+ vref = kLocalDomain;
+ else
+ vref = kUserDomain;
+
+ /* Locate the appropriate ColorSync path */
+ if ((cmev = FSFindFolder(vref, kColorSyncProfilesFolderType, kCreateFolder, &dirref)) != noErr) {
+ debugr2((errout,"FSFindFolder() failed with error %d\n",cmev));
+ return 1;
+ }
+
+ /* Convert to POSIX path */
+ if ((cmev = FSRefMakePath(&dirref, (unsigned char *)dpath, FILENAME_MAX)) != noErr) {
+ debugr2((errout,"FSRefMakePath failed with error %d\n",cmev));
+ return 1;
+ }
+
+ /* Locate the base filename in the fname */
+ for (basename = fname + strlen(fname); ; basename--) {
+ if (basename <= fname || basename[-1] == '/')
+ break;
+ }
+
+ /* Append the basename to the ColorSync directory path */
+ if ((strlen(dpath) + strlen(basename) + 2) > FILENAME_MAX
+ || (strlen(dpath) + strlen(basename) + 2) > 256) {
+ debugr2((errout,"ColorSync dir + profile name too long\n"));
+ return 1;
+ }
+ strcat(dpath, "/");
+ strcat(dpath, basename);
+ debugr2((errout,"Profile to delete '%s'\n",dpath));
+
+ if (stat(dpath, &sbuf) != 0) {
+ debugr2((errout,"delete '%s' profile doesn't exist\n",dpath));
+ return 2;
+ }
+ if ((ev = unlink(dpath)) != 0) {
+ debugr2((errout,"delete '%s' failed with %d\n",dpath,ev));
+ return 1;
+ }
+
+ return 0;
+ }
+#endif /* 10.6 and prior */
+#endif /* __APPLE__ */
+
+#if defined(UNIX_X11) && defined(USE_UCMM)
+ {
+ ucmm_error ev;
+ ucmm_scope sc;
+
+ if (scope == p_scope_network
+ || scope == p_scope_system
+ || scope == p_scope_local)
+ sc = ucmm_local_system;
+ else
+ sc = ucmm_user;
+
+ if ((ev = ucmm_uninstall_monitor_profile(sc, p->edid, p->edid_len, p->name, fname)) != ucmm_ok) {
+ debugr2((errout,"Installing profile '%s' failed with error %d '%s'\n",fname,ev,ucmm_error_string(ev)));
+ return 1;
+ }
+
+ XDeleteProperty(p->mydisplay, RootWindow(p->mydisplay, 0), p->icc_atom);
+
+#if RANDR_MAJOR == 1 && RANDR_MINOR >= 2 && !defined(DISABLE_RANDR)
+ /* If Xrandr 1.2, set property on output */
+ if (p->icc_out_atom != 0) {
+ XRRDeleteOutputProperty(p->mydisplay, p->output, p->icc_out_atom);
+ }
+#endif /* randr >= V 1.2 */
+ return 0;
+ }
+#endif /* UNXI X11 */
+
+ return 1;
+}
+
+/* Get the currently installed display profile and return it as an icmFile. */
+/* Return the name as well, up to mxlen chars, excluding nul. */
+/* Return NULL if failed. */
+icmFile *dispwin_get_profile(dispwin *p, char *name, int mxlen) {
+ icmFile *rd_fp = NULL;
+
+#ifdef NT
+ {
+ char buf[MAX_PATH];
+ DWORD blen = MAX_PATH;
+
+ if (GetICMProfile(p->hdc, &blen, buf) == 0) {
+ debugr2((errout, "GetICMProfile failed, lasterr = %d\n",GetLastError()));
+ return NULL;
+ }
+
+ debugr2((errout,"Loading default profile '%s'\n",buf));
+ if ((rd_fp = new_icmFileStd_name(buf,"r")) == NULL)
+ debugr2((errout,"Can't open file '%s'",buf));
+
+ return rd_fp;
+ }
+#endif /* NT */
+
+#ifdef __APPLE__
+#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
+ {
+ char *dpath; /* Read file path */
+ struct stat sbuf;
+ icmAlloc *al;
+ void *buf;
+ FILE *fp;
+
+ if ((dpath = cur_profile(p)) == NULL) {
+ debugr2((errout,"cur_profile() failed\n"));
+ return NULL;
+ }
+
+ /* Get the profile size */
+ if (stat(dpath, &sbuf) != 0) {
+ debugr2((errout,"Failed to open profile '%s'\n",dpath));
+ free(dpath);
+ return NULL;
+ }
+
+ if ((al = new_icmAllocStd()) == NULL) {
+ debugr("new_icmAllocStd failed\n");
+ free(dpath);
+ return NULL;
+ }
+ if ((buf = al->malloc(al, sbuf.st_size)) == NULL) {
+ debugr("malloc of profile buffer failed\n");
+ free(dpath);
+ return NULL;
+ }
+
+ if ((fp = fopen(dpath, "r")) == NULL) {
+ debugr2((errout,"opening '%s' failed\n",dpath));
+ al->free(al, buf);
+ free(dpath);
+ return NULL;
+ }
+ if (fread(buf, 1, sbuf.st_size, fp) != sbuf.st_size) {
+ debugr2((errout,"reading '%s' failed\n",dpath));
+ al->free(al, buf);
+ fclose(fp);
+ free(dpath);
+ return NULL;
+ }
+ fclose(fp);
+ free(dpath); dpath = NULL;
+
+ /* Memory File fp that will free the buffer when deleted: */
+ if ((rd_fp = new_icmFileMem_ad(buf, sbuf.st_size, al)) == NULL) {
+ debugr("Creating memory file profile failed");
+ al->free(al, buf);
+ al->del(al);
+ return NULL;
+ }
+
+ if (name != NULL) {
+ strncpy(name, "Display", mxlen);
+ name[mxlen] = '\000';
+ }
+
+ return rd_fp;
+ }
+
+#else /* 10.5 and prior */
+ {
+ CMError ev;
+ CMProfileRef prof, dprof; /* Source profile */
+ CMProfileLocation dploc; /* Destinaion profile location */
+ CMAppleProfileHeader hdr;
+ icmAlloc *al;
+
+#ifdef NEVER
+ /* Get the current display profile */
+ if ((ev = CMGetProfileByAVID((CMDisplayIDType)p->ddid, &prof)) != noErr) {
+ debugr2((errout,"CMGetProfileByAVID() failed with error %d\n",ev));
+ return NULL;
+ }
+#else
+ CMDeviceProfileID curID; /* Current Device Default profile ID */
+ CMProfileLocation cploc; /* Current profile location */
+
+ /* Get the default ID for the display */
+ if ((ev = CMGetDeviceDefaultProfileID(cmDisplayDeviceClass, (CMDeviceID)p->ddid, &curID)) != noErr) {
+ debugr2((errout,"CMGetDeviceDefaultProfileID() failed with error %d\n",ev));
+ return NULL;
+ }
+
+ /* Get the displays profile */
+ if ((ev = CMGetDeviceProfile(cmDisplayDeviceClass, (CMDeviceID)p->ddid, curID, &cploc)) != noErr) {
+ debugr2((errout,"CMGetDeviceDefaultProfileID() failed with error %d\n",ev));
+ return NULL;
+ }
+
+ if ((ev = CMOpenProfile(&prof, &cploc)) != noErr) {
+ debugr2((errout,"CMOpenProfile() failed with error %d\n",ev));
+ return NULL;
+ }
+#endif
+
+ /* Get the profile size */
+ if ((ev = CMGetProfileHeader(prof, &hdr)) != noErr) {
+ debugr2((errout,"CMGetProfileHeader() failed with error %d\n",ev));
+ return NULL;
+ }
+
+ /* Make a copy of the profile to a memory buffer */
+ dploc.locType = cmBufferBasedProfile;
+ dploc.u.bufferLoc.size = hdr.cm1.size;
+ if ((al = new_icmAllocStd()) == NULL) {
+ debugr("new_icmAllocStd failed\n");
+ return NULL;
+ }
+ if ((dploc.u.bufferLoc.buffer = al->malloc(al, dploc.u.bufferLoc.size)) == NULL) {
+ debugr("malloc of profile buffer failed\n");
+ return NULL;
+ }
+
+ if ((ev = CMCopyProfile(&dprof, &dploc, prof)) != noErr) {
+ debugr2((errout,"CMCopyProfile() failed for AVID to buffer with error %d\n",ev));
+ return NULL;
+ }
+
+ /* Memory File fp that will free the buffer when deleted: */
+ if ((rd_fp = new_icmFileMem_ad((void *)dploc.u.bufferLoc.buffer, dploc.u.bufferLoc.size, al)) == NULL) {
+ debugr("Creating memory file from CMProfileLocation failed");
+ al->free(al, dploc.u.bufferLoc.buffer);
+ al->del(al);
+ CMCloseProfile(prof);
+ CMCloseProfile(dprof);
+ return NULL;
+ }
+
+ if (name != NULL) {
+ strncpy(name, "Display", mxlen);
+ name[mxlen] = '\000';
+ }
+
+ CMCloseProfile(prof);
+ CMCloseProfile(dprof);
+
+ return rd_fp;
+ }
+#endif /* 10.5 and prior */
+#endif /* __APPLE__ */
+
+#if defined(UNIX_X11) && defined(USE_UCMM)
+ /* Try and get the currently installed profile from ucmm */
+ {
+ ucmm_error ev;
+ char *profile = NULL;
+
+ debugr2((errout,"dispwin_get_profile called\n"));
+
+ if ((ev = ucmm_get_monitor_profile(p->edid, p->edid_len, p->name, &profile)) == ucmm_ok) {
+
+ if (name != NULL) {
+ strncpy(name, profile, mxlen);
+ name[mxlen] = '\000';
+ }
+
+ debugr2((errout,"Loading current profile '%s'\n",profile));
+ if ((rd_fp = new_icmFileStd_name(profile,"r")) == NULL) {
+ debugr2((errout,"Can't open file '%s'",profile));
+ free(profile);
+ return NULL;
+ }
+
+ /* Implicitly we set the X11 atom to be the profile we just got */
+ debugr2((errout,"Setting X11 atom to current profile '%s'\n",profile));
+ if (set_X11_atom(p, profile) != 0) {
+ debugr2((errout,"Setting X11 atom to profile '%s' failed",profile));
+ /* Hmm. We ignore this error */
+ }
+ return rd_fp;
+ }
+ if (ev != ucmm_no_profile) {
+ debugr2((errout,"Got ucmm error %d '%s'\n",ev,ucmm_error_string(ev)));
+ return NULL;
+ }
+ debugr2((errout,"Failed to get configured profile, so use X11 atom\n"));
+ /* Drop through to using the X11 root window atom */
+ }
+ {
+ Atom ret_type;
+ int ret_format;
+ long ret_len, ret_togo;
+ char aname[30];
+ unsigned char *atomv = NULL; /* Profile loaded from/to atom */
+ unsigned char *buf;
+ icmAlloc *al;
+
+ atomv = NULL;
+
+ strcpy(aname, "_ICC_PROFILE");
+
+#if RANDR_MAJOR == 1 && RANDR_MINOR >= 2 && !defined(DISABLE_RANDR)
+ /* If Xrandr 1.2, get property on output */
+ if (p->icc_out_atom != 0) {
+
+ /* Get the ICC profile property */
+ if (XRRGetOutputProperty(p->mydisplay, p->output, p->icc_out_atom,
+ 0, 0x7ffffff, False, False, XA_CARDINAL,
+ &ret_type, &ret_format, &ret_len, &ret_togo, &atomv) != Success || ret_len == 0) {
+ debugr("Failed to read ICC_PROFILE property from Xranr output\n");
+ }
+
+ }
+#endif /* randr >= V 1.2 */
+
+ if (atomv == NULL) {
+ if (p->myuscreen != 0)
+ sprintf(aname, "_ICC_PROFILE_%d",p->myuscreen);
+
+ /* Get the ICC profile property */
+ if (XGetWindowProperty(p->mydisplay, RootWindow(p->mydisplay, 0), p->icc_atom,
+ 0, 0x7ffffff, False, XA_CARDINAL,
+ &ret_type, &ret_format, &ret_len, &ret_togo, &atomv) != Success || ret_len == 0) {
+ debugr2((errout,"Getting property '%s' from RootWindow\n", aname));
+ return NULL;
+ }
+ }
+
+ /* This is a bit of a fiddle to keep the memory allocations */
+ /* straight. (We can't assume that X11 and icc are using the */
+ /* same allocators) */
+ if ((al = new_icmAllocStd()) == NULL) {
+ debugr("new_icmAllocStd failed\n");
+ return NULL;
+ }
+ if ((buf = al->malloc(al, ret_len)) == NULL) {
+ debugr("malloc of profile buffer failed\n");
+ return NULL;
+ }
+ memmove(buf, atomv, ret_len);
+ XFree(atomv);
+
+ /* Memory File fp that will free the buffer when deleted: */
+ if ((rd_fp = new_icmFileMem_ad((void *)buf, ret_len, al)) == NULL) {
+ debugr("Creating memory file from X11 atom failed");
+ al->free(al, buf);
+ al->del(al);
+ return NULL;
+ }
+
+ if (name != NULL) {
+ strncpy(name, aname, mxlen);
+ name[mxlen] = '\000';
+ }
+ return rd_fp;
+ }
+#endif /* UNXI X11 */
+
+ return NULL;
+}
+
+/* ----------------------------------------------- */
+
+/* Restore the display state */
+static void restore_display(dispwin *p) {
+
+ /* Restore the ramdac */
+ if (p->or != NULL) {
+ p->set_ramdac(p, p->or, 0);
+ p->or->del(p->or);
+ p->or = NULL;
+ debugr("Restored original ramdac\n");
+ }
+ if (p->r != NULL) {
+ p->r->del(p->r);
+ p->r = NULL;
+ }
+
+#if defined(UNIX_X11)
+
+#if ScreenSaverMajorVersion >= 1 && ScreenSaverMinorVersion >= 1 /* X11R7.1 */
+ if (p->xsssuspend) {
+ XScreenSaverSuspend(p->mydisplay, False);
+ p->xsssuspend = 0;
+ }
+#endif
+ if (p->xssvalid) {
+ /* Restore the X11 screen saver state */
+ XSetScreenSaver(p->mydisplay, p->timeout, p->interval,
+ p->prefer_blanking, p->allow_exposures);
+ }
+
+ /* Restore the xscreensaver */
+ if (p->xssrunning) {
+ system("xscreensaver -nosplash 2>/dev/null >/dev/null&");
+ }
+
+ if (p->gnomessrunning && p->gnomepid != -1) {
+ kill(p->gnomepid, SIGKILL); /* Kill the process inhibiting the screen saver */
+ }
+
+ /* Restore the KDE screen saver state */
+ if (p->kdessrunning) {
+ system("dcop kdesktop KScreensaverIface enable true 2>&1 >/dev/null");
+ }
+
+ /* Restore DPMS */
+ if (p->dpmsenabled) {
+ DPMSEnable(p->mydisplay);
+ }
+
+ /* Flush any changes out */
+ XSync(p->mydisplay, False);
+#endif /* UNIX_X11 */
+}
+
+/* ----------------------------------------------- */
+/* On something killing our process, deal with Ramac & ScreenSaver cleanup */
+
+#ifdef NT
+void (__cdecl *dispwin_int)(int sig) = SIG_DFL;
+void (__cdecl *dispwin_term)(int sig) = SIG_DFL;
+#endif
+#ifdef UNIX
+void (*dispwin_hup)(int sig) = SIG_DFL;
+void (*dispwin_int)(int sig) = SIG_DFL;
+void (*dispwin_term)(int sig) = SIG_DFL;
+#endif
+
+/* List of dispwin's to clean up */
+static dispwin *signal_dispwin = NULL;
+
+static void dispwin_sighandler(int arg) {
+ dispwin *pp, *np;
+
+ /* Restore all dispwin's Ramdacs & screen savers */
+ for (pp = signal_dispwin; pp != NULL; pp = np) {
+ np = pp->next;
+ restore_display(pp);
+ }
+
+ /* Call through to previous handler */
+#ifdef UNIX
+ if (arg == SIGHUP && dispwin_hup != SIG_DFL && dispwin_hup != SIG_IGN)
+ dispwin_hup(arg);
+#endif
+ if (arg == SIGINT && dispwin_int != SIG_DFL && dispwin_int != SIG_IGN)
+ dispwin_int(arg);
+ if (arg == SIGTERM && dispwin_term != SIG_DFL && dispwin_term != SIG_IGN)
+ dispwin_term(arg);
+ exit(0);
+}
+
+static void dispwin_install_signal_handlers(dispwin *p) {
+
+ if (signal_dispwin == NULL) {
+ /* Install the signal handler to ensure cleanup */
+#ifdef UNIX
+ dispwin_hup = signal(SIGHUP, dispwin_sighandler);
+#endif /* UNIX */
+ dispwin_int = signal(SIGINT, dispwin_sighandler);
+ dispwin_term = signal(SIGTERM, dispwin_sighandler);
+ }
+
+ /* Add this one to the list */
+ p->next = signal_dispwin;
+ signal_dispwin = p;
+}
+
+static void dispwin_uninstall_signal_handlers(dispwin *p) {
+
+ /* Find it and delete it from our static cleanup list */
+ if (signal_dispwin != NULL) {
+ if (signal_dispwin == p) {
+ signal_dispwin = p->next;
+ if (signal_dispwin == NULL) {
+#if defined(UNIX)
+ signal(SIGHUP, dispwin_hup);
+#endif /* UNIX */
+ signal(SIGINT, dispwin_int);
+ signal(SIGTERM, dispwin_term);
+ }
+ } else {
+ dispwin *pp;
+ for (pp = signal_dispwin; pp != NULL; pp = pp->next) {
+ if (pp->next == p) {
+ pp->next = p->next;
+ break;
+ }
+ }
+ }
+ }
+ p->next = NULL;
+}
+
+/* ----------------------------------------------- */
+/* Test patch window specific declarations */
+
+#ifdef __APPLE__
+
+@class DWWin;
+@class DWView;
+
+/* Our OS X window specific information */
+typedef struct {
+ dispwin *p;
+ DWWin *window; /* Our NSWindow */
+ DWView *view; /* Our NSView */
+} osx_cntx_t;
+
+static void OSX_ProcessEvents(dispwin *p);
+
+// - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+@interface DWView : NSView {
+ osx_cntx_t *cntx;
+}
+- (void)setCntx:(osx_cntx_t *)cntx;
+@end
+
+@implementation DWView
+
+- (void)setCntx:(osx_cntx_t *)val {
+ cntx = val;
+}
+
+// A transparent 1x1 GIF: (43 bytes)
+unsigned char emptyCursor[43] = {
+ 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x21, 0xf9, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3b };
+
+/* Make cursor invisible over our window */
+/*
+ * This doesn't work very well. The only way to work it properly
+ * is to create a CGEventTap and do a hide/unkid cursor when
+ * the mouse enters the window. This needs the main thread
+ * to be dedicated to running the event loop.
+ */
+- (void)resetCursorRects {
+ [super resetCursorRects];
+// [self addCursorRect:[self bounds] cursor:[NSCursor crosshairCursor]];
+ NSData *idata = [NSData dataWithBytes:(void *)emptyCursor length:43];
+ NSImage *img = [NSImage alloc];
+ img = [img initWithData:idata];
+ NSCursor *curs = [NSCursor alloc];
+ curs = [curs initWithImage:img hotSpot:NSMakePoint(0,0)];
+ [self addCursorRect:[self bounds] cursor:curs];
+}
+
+- (void)drawRect:(NSRect)rect {
+ osx_cntx_t *cx = cntx;
+ dispwin *p = cx->p;
+ NSRect frect;
+ NSBezierPath* aPath = [NSBezierPath bezierPath];
+
+ frect = NSMakeRect(p->tx, p->ty, (1.0 + p->tw), (1.0 + p->th));
+
+ [[NSColor colorWithDeviceRed: p->r_rgb[0]
+ green: p->r_rgb[1]
+ blue: p->r_rgb[2]
+ alpha: 1.0] setFill];
+ [aPath appendBezierPathWithRect:frect];
+ [aPath fill];
+}
+
+@end
+
+// - - - - - - - - - - - - - - - - - - - - - - - - -
+
+@interface DWWin : NSWindow {
+ osx_cntx_t *cntx;
+}
+- (void)setCntx:(osx_cntx_t *)cntx;
+@end
+
+@implementation DWWin
+
+- (void)setCntx:(osx_cntx_t *)val {
+ cntx = val;
+}
+
+- (BOOL)canBecomeMainWindow {
+ return NO;
+}
+
+/* So that we can change the cursor on a borderless window: */
+- (BOOL)canBecomeKeyWindow {
+ return YES;
+}
+
+- (BOOL)isMoveable {
+ return NO;
+}
+
+- (BOOL)windowShouldClose:(id)sender {
+// printf("Got Window windowShouldClose\n");
+
+// [NSApp terminate: nil];
+ return YES;
+}
+
+@end
+
+/* Create our window */
+static void create_my_win(NSRect rect, osx_cntx_t *cx) {
+ dispwin *p = cx->p;
+ int i;
+
+ /* We need to locate the NSScreen that corresponds to the */
+ /* CGDirectDisplayID - look through them for a match. */
+ NSScreen *screen = nil;
+ NSArray *screenArray = [NSScreen screens];
+ for (i = 0; i < [screenArray count]; i++) {
+ NSDictionary *screenDescription = [[screenArray objectAtIndex:i] deviceDescription];
+
+ // CFShow(screenDescription); // Dump it out
+ /* Hmm. Also has "NSDeviceBitsPerSample" entry with value 8 in dict. */
+
+ NSNumber* screenID = [screenDescription objectForKey:@"NSScreenNumber"];
+ CGDirectDisplayID ddid = (CGDirectDisplayID)[screenID unsignedIntValue];
+ if (ddid == p->ddid) {
+ screen = [screenArray objectAtIndex:i];
+ break;
+ }
+ }
+
+ /* Create Window */
+ cx->window = [[DWWin alloc] initWithContentRect: rect
+ styleMask: NSBorderlessWindowMask
+ backing: NSBackingStoreBuffered
+ defer: YES
+ screen: screen];
+
+ [cx->window setLevel: NSScreenSaverWindowLevel];
+
+ [cx->window setBackgroundColor: [NSColor blackColor]];
+
+ [cx->window setTitle: @"Argyll Window"];
+
+ /* Use our view for the whole window to draw plot */
+ cx->view = [DWView new];
+ [cx->view setCntx:(void *)cx];
+ [cx->window setContentView: cx->view];
+
+ [cx->window makeKeyAndOrderFront: nil];
+}
+
+#endif /* __APPLE__ */
+
+/* ----------------------------------------------- */
+
+/* Change the window color. */
+/* Return 1 on error, 2 on window being closed */
+static int dispwin_set_color(
+dispwin *p,
+double r, double g, double b /* Color values 0.0 - 1.0 */
+) {
+ int j;
+
+ debugr("dispwin_set_color called\n");
+
+ if (p->nowin)
+ return 1;
+
+ p->rgb[0] = r;
+ p->rgb[1] = g;
+ p->rgb[2] = b;
+
+ for (j = 0; j < 3; j++) {
+ if (p->rgb[j] < 0.0)
+ p->rgb[j] = 0.0;
+ else if (p->rgb[j] > 1.0)
+ p->rgb[j] = 1.0;
+ p->r_rgb[j] = p->rgb[j];
+ }
+
+ /* Use ramdac for high precision native output. */
+ /* The ramdac is used to hold the lsb that the frame buffer */
+ /* doesn't hold. */
+ if (p->native == 1) {
+ double prange = p->r->nent - 1.0;
+
+ for (j = 0; j < 3; j++) {
+ int tt;
+
+//printf("~1 %d: in %f, ",j,p->rgb[j]);
+ tt = (int)(p->rgb[j] * prange);
+ p->r->v[j][tt] = p->rgb[j];
+ p->r_rgb[j] = (double)tt/prange; /* RAMDAC output Quantized value */
+//printf(" cell[%d], val %f, rast val %f\n",tt, p->rgb[j], p->r_rgb[j]);
+ }
+ if (p->set_ramdac(p,p->r, 0)) {
+ debugr("set_ramdac() failed\n");
+ return 1;
+ }
+ }
+
+ /* - - - - - - - - - - - - - - */
+#ifdef NT
+ {
+ MSG msg;
+ INPUT fip;
+
+ /* Stop the system going to sleep */
+
+ /* This used to work OK in reseting the screen saver, but not in Vista :-( */
+ SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
+
+ /* So we use a fake mouse non-movement reset the Vista screensaver. */
+ SystemParametersInfo(SPI_SETBLOCKSENDINPUTRESETS, FALSE, NULL, 0);
+ fip.type = INPUT_MOUSE;
+ fip.mi.dx = 0;
+ fip.mi.dy = 0;
+ fip.mi.dwFlags = MOUSEEVENTF_MOVE;
+ fip.mi.time = 0;
+ fip.mi.dwExtraInfo = 0;
+ SendInput(1, &fip, sizeof(INPUT));
+
+ p->colupd++;
+
+ /* Trigger a WM_PAINT */
+ if (!InvalidateRect(p->hwnd, NULL, FALSE)) {
+ debugr2((errout,"InvalidateRect failed, lasterr = %d\n",GetLastError()));
+ return 1;
+ }
+
+ /* Wait for WM_PAINT to be executed */
+ while (p->colupd != p->colupde) {
+ msec_sleep(20);
+ }
+ }
+#endif /* NT */
+
+ /* - - - - - - - - - - - - - - */
+
+#ifdef __APPLE__
+
+ if (p->winclose) {
+ return 2;
+ }
+
+ /* Stop the system going to sleep */
+ UpdateSystemActivity(OverallAct);
+
+ /* Make sure our window is brought to the front at least once, */
+ /* but not every time, in case the user wants to kill the application. */
+ if (p->btf == 0){
+ OSStatus stat;
+ ProcessSerialNumber cpsn;
+ if ((stat = GetCurrentProcess(&cpsn)) != noErr) {
+ debugr2((errout,"GetCurrentProcess returned error %d\n",stat));
+ } else {
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
+ if ((stat = TransformProcessType(&cpsn, kProcessTransformToForegroundApplication)) != noErr) {
+ debugr2((errout,"TransformProcessType returned error %d\n",stat));
+ }
+#endif /* OS X 10.3 */
+ if ((stat = SetFrontProcess(&cpsn)) != noErr) {
+ debugr2((errout,"SetFrontProcess returned error %d\n",stat));
+ }
+ }
+ p->btf = 1;
+ }
+
+ /* Trigger an update that fills window with r_rgb[] */
+ [((osx_cntx_t *)(p->osx_cntx))->view setNeedsDisplay: YES ];
+
+ /* Process events */
+ OSX_ProcessEvents(p);
+
+#endif /* __APPLE__ */
+
+ /* - - - - - - - - - - - - - - */
+
+#if defined(UNIX_X11)
+ {
+ Colormap mycmap;
+ XColor col;
+ int vali[3];
+
+ /* Indicate that we've got activity to the X11 Screensaver */
+ XResetScreenSaver(p->mydisplay);
+
+ /* Quantize to 16 bit color */
+ for (j = 0; j < 3; j++)
+ vali[j] = (int)(65535.0 * p->r_rgb[j] + 0.5);
+
+ mycmap = DefaultColormap(p->mydisplay, p->myscreen);
+ col.red = vali[0];
+ col.green = vali[1];
+ col.blue = vali[2];
+ XAllocColor(p->mydisplay, mycmap, &col);
+ XSetForeground(p->mydisplay, p->mygc, col.pixel);
+
+ XFillRectangle(p->mydisplay, p->mywindow, p->mygc,
+ p->tx, p->ty, p->tw, p->th);
+
+ XSync(p->mydisplay, False); /* Make sure it happens */
+ }
+#endif /* UNXI X11 */
+
+ if (p->callout != NULL) {
+ int rv;
+ char *cmd;
+
+ if ((cmd = malloc(strlen(p->callout) + 200)) == NULL)
+ error("Malloc of command string failed");
+
+ sprintf(cmd, "%s %d %d %d %f %f %f",p->callout,
+ (int)(r * 255.0 + 0.5),(int)(g * 255.0 + 0.5),(int)(b * 255.0 + 0.5), r, g, b);
+ if ((rv = system(cmd)) != 0)
+ warning("System command '%s' failed with %d",cmd,rv);
+ free(cmd);
+ }
+
+ /* Allow some time for the display to update before */
+ /* a measurement can take place. This allows for CRT */
+ /* refresh, or LCD processing/update time, + */
+ /* display settling time (quite long for smaller LCD changes). */
+ msec_sleep(p->update_delay);
+
+ return 0;
+}
+
+/* ----------------------------------------------- */
+/* Set an update delay, and return the previous value */
+/* Value can be set to zero, but othewise will be forced */
+/* to be >= min_update_delay */
+static int dispwin_set_update_delay(
+dispwin *p,
+int update_delay) {
+ int cval = p->update_delay;
+ p->update_delay = update_delay;
+ if (update_delay != 0 && p->update_delay < p->min_update_delay)
+ p->update_delay = p->min_update_delay;
+ return cval;
+}
+
+/* ----------------------------------------------- */
+/* Set the shell set color callout */
+void dispwin_set_callout(
+dispwin *p,
+char *callout
+) {
+ debugr2((errout,"dispwin_set_callout called with '%s'\n",callout));
+
+ p->callout = strdup(callout);
+}
+
+/* ----------------------------------------------- */
+/* Destroy ourselves */
+static void dispwin_del(
+dispwin *p
+) {
+
+ debugr("dispwin_del called\n");
+
+ if (p == NULL)
+ return;
+
+ /* Restore original RAMDAC if we were in native mode, */
+ /* and restore screensaver */
+ restore_display(p);
+ dispwin_uninstall_signal_handlers(p);
+
+ /* -------------------------------------------------- */
+#ifdef NT
+
+ if (p->hwnd != NULL) {
+
+ p->quit = 1;
+ if (PostMessage(p->hwnd, WM_CLOSE, (WPARAM)NULL, (LPARAM)NULL) != 0) {
+ while(p->hwnd != NULL)
+ msec_sleep(20);
+ } else {
+ debugr2((errout, "PostMessage(WM_GETICON failed, lasterr = %d\n",GetLastError()));
+ }
+// DestroyCursor(p->curs);
+
+ if (p->mth != NULL) { /* Message thread */
+ p->mth->del(p->mth);
+ }
+ }
+
+ if (p->hdc != NULL)
+ DeleteDC(p->hdc);
+
+#endif /* NT */
+ /* -------------------------------------------------- */
+
+ /* -------------------------------------------------- */
+#ifdef __APPLE__
+ if (p->nowin == 0) { /* We have a window up */
+ restore_display(p);
+ if (p->osx_cntx != NULL) { /* And we've allocated a context */
+ osx_cntx_t *cx = (osx_cntx_t *)p->osx_cntx;
+
+ p->winclose = 1;
+
+ [cx->window release];
+ free(p->osx_cntx);
+ p->osx_cntx = NULL;
+ }
+ }
+
+// ~~
+// CGDisplayShowCursor(p->ddid);
+
+#endif /* __APPLE__ */
+ /* -------------------------------------------------- */
+
+ /* -------------------------------------------------- */
+#if defined(UNIX_X11)
+ debugr("About to close display\n");
+
+ if (p->mydisplay != NULL) {
+ if (p->nowin == 0) { /* We have a window up */
+
+ XFreeGC(p->mydisplay, p->mygc);
+ XDestroyWindow(p->mydisplay, p->mywindow);
+ }
+ XCloseDisplay(p->mydisplay);
+ }
+ debugr("finished\n");
+
+#endif /* UNXI X11 */
+ /* -------------------------------------------------- */
+
+ if (p->name != NULL)
+ free(p->name);
+ if (p->description != NULL)
+ free(p->description);
+ if (p->callout != NULL)
+ free(p->callout);
+
+ free(p);
+}
+
+/* ----------------------------------------------- */
+/* Event handler callbacks */
+
+#ifdef NT
+
+/* Undocumented flag. Set when minimized/maximized etc. */
+#ifndef SWP_STATECHANGED
+#define SWP_STATECHANGED 0x8000
+#endif
+
+static LRESULT CALLBACK MainWndProc(
+ HWND hwnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam
+) {
+ debugrr2l(4, (stderr, "Handling message type 0x%x\n",message));
+
+ if (message >= WM_APP) {
+ debugrr2l(4, (stderr, "Message ignored\n"));
+ return 0;
+ }
+
+ switch(message) {
+ case WM_PAINT: {
+ dispwin *p = NULL;
+ int vali[3];
+ HDC hdc;
+ PAINTSTRUCT ps;
+ RECT rect;
+ HBRUSH hbr;
+ int j;
+
+#ifdef _WIN64
+ if ((p = (dispwin *)GetWindowLongPtr(hwnd, GWLP_USERDATA)) == NULL)
+#else
+ if ((p = (dispwin *)GetWindowLong(hwnd, GWL_USERDATA)) == NULL)
+#endif
+ {
+ debugrr2l(4,(stderr, "GetWindowLongPtr failed, lasterr = %d\n",GetLastError()));
+ hdc = BeginPaint(hwnd, &ps);
+ /* Don't know where to paint */
+ EndPaint(hwnd, &ps);
+ return 0;
+ }
+
+ /* Check that there is something to paint */
+ if (GetUpdateRect(hwnd, NULL, FALSE) == 0) {
+ debugrr2l(4, (stderr,"The update region was empty\n"));
+ }
+
+ /* Frame buffer Quantize 8 bit color */
+ for (j = 0; j < 3; j++) {
+ vali[j] = (int)(255.0 * p->r_rgb[j] + 0.5);
+ }
+
+ hdc = BeginPaint(hwnd, &ps);
+
+ SaveDC(hdc);
+
+ /* Try and turn ICM off */
+#ifdef ICM_DONE_OUTSIDEDC
+ if (!SetICMMode(hdc, ICM_DONE_OUTSIDEDC)) {
+ /* This seems to fail with "invalid handle" under NT4 */
+ /* Does it work under Win98 or Win2K ? */
+ printf("SetICMMode failed, lasterr = %d\n",GetLastError());
+ }
+#endif
+
+ hbr = CreateSolidBrush(RGB(vali[0],vali[1],vali[2]));
+ SelectObject(hdc,hbr);
+
+ SetRect(&rect, p->tx, p->ty, p->tx + p->tw, p->ty + p->th);
+ FillRect(hdc, &rect, hbr);
+
+ RestoreDC(hdc,-1);
+ DeleteDC(hdc);
+
+ EndPaint(hwnd, &ps);
+
+ p->colupde = p->colupd; /* We're updated to this color */
+
+ return 0;
+ }
+
+ /* Prevent any changes in position of the window */
+ case WM_WINDOWPOSCHANGING: {
+ WINDOWPOS *wpos = (WINDOWPOS *)lParam;
+ 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));
+ wpos->flags &= ~SWP_FRAMECHANGED & ~SWP_NOREDRAW;
+ wpos->flags |= SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER
+ | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_SHOWWINDOW;
+ debugrr2l(4,(stderr, "flags now = 0x%x\n",wpos->flags));
+ return DefWindowProc(hwnd, message, wParam, lParam);
+ }
+ case WM_WINDOWPOSCHANGED: {
+ WINDOWPOS *wpos = (WINDOWPOS *)lParam;
+ 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));
+ debugrr2l(4,(stderr, "It's a windowposchanged, flags = 0x%x\n",wpos->flags));
+ return 0;
+ }
+ case WM_CLOSE:
+ DestroyWindow(hwnd);
+ return 0;
+
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ return 0;
+ }
+
+ debugrr2l(4,(stderr, "Handle message using DefWindowProc()\n"));
+
+ return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+#endif /* NT */
+
+#ifdef __APPLE__
+
+/* Not the event handler (because events are handled by the Cocoa objects) */
+/* but a function to call to process events after an update */
+static void OSX_ProcessEvents(dispwin *p) {
+ NSEvent *event;
+ NSDate *to;
+
+ /* We're creating and draining a pool here to ensure that all the */
+ /* auto release objects get drained when we're finished (?) */
+ NSAutoreleasePool *tpool = [NSAutoreleasePool new];
+
+ /* Wait until the events are done */
+ to = [NSDate dateWithTimeIntervalSinceNow:0.01]; /* autorelease ? */
+ for (;;) {
+ /* Hmm. Assume event is autorelease */
+ if ((event = [NSApp nextEventMatchingMask:NSAnyEventMask
+ untilDate:to inMode:NSDefaultRunLoopMode dequeue:YES]) != nil) {
+ [NSApp sendEvent:event];
+ } else {
+ break;
+ }
+ }
+ [tpool release];
+}
+
+#endif /* __APPLE__ */
+
+#if defined(UNIX_X11)
+ /* None */
+#endif /* UNXI X11 */
+
+/* ----------------------------------------------- */
+#ifdef NT
+
+/* Thread to handle message processing, so that there is no delay */
+/* when the main thread is doing other things. */
+int win_message_thread(void *pp) {
+ dispwin *p = (dispwin *)pp;
+ MSG msg;
+ WNDCLASS wc;
+
+ debugrr2l(4, (stderr, "win_message_thread started\n"));
+
+ /* Fill in window class structure with parameters that describe the */
+ /* main window. */
+ wc.style = 0 ; /* Class style(s). */
+ wc.lpfnWndProc = MainWndProc; /* Function to retrieve messages for windows of this class. */
+ wc.cbClsExtra = 0; /* No per-class extra data. */
+ wc.cbWndExtra = 0; /* No per-window extra data. */
+ wc.hInstance = NULL; /* Application that owns the class. */
+ wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ wc.hCursor = LoadCursor(NULL, IDC_CROSS);
+ wc.hbrBackground = GetStockObject(BLACK_BRUSH);
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = p->AppName;
+
+ /* Make the cursor disapear over our window */
+ /* (How does it know our window ??) */
+ ShowCursor(FALSE);
+
+ if ((p->arv = RegisterClass(&wc)) == 0) {
+ debugr2((errout, "RegisterClass failed, lasterr = %d\n",GetLastError()));
+ p->inited = 2;
+ return 0;
+ }
+
+#ifndef WS_EX_NOACTIVATE
+# define WS_EX_NOACTIVATE 0x08000000L
+#endif
+ p->hwnd = CreateWindowEx(
+ WS_EX_NOACTIVATE | WS_EX_TOPMOST,
+// 0,
+ p->AppName,
+ "Argyll Display Calibration Window",
+ WS_VISIBLE | WS_DISABLED | WS_POPUP,
+// WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+// WS_POPUPWINDOW | WS_VISIBLE,
+ p->xo, p->yo, /* Location */
+ p->wi, p->he, /* Size */
+ NULL, /* Handle to parent or owner */
+ NULL, /* Handle to menu or child window */
+ NULL, /* hInstance Handle to appication instance */
+ NULL); /* pointer to window creation data */
+
+ if (!p->hwnd) {
+ debugr2((errout, "CreateWindow failed, lasterr = %d\n",GetLastError()));
+ p->inited = 2;
+ return 0;
+ }
+
+ /* Associate the dispwin object with the window, */
+ /* so that the event callback can access it */
+#ifdef _WIN64
+ SetWindowLongPtr(p->hwnd, GWLP_USERDATA, (LONG_PTR)p);
+#else
+ SetWindowLong(p->hwnd, GWL_USERDATA, (LONG)p);
+#endif
+
+ /*
+ Should we call BOOL SystemParametersInfo()
+ to disable high contrast, powertimout and screensaver timeout ?
+
+ */
+
+ debugrr2l(4, (stderr, "win_message_thread initialized - about to process messages\n"));
+ p->inited = 1;
+
+ for (;;) {
+ if (GetMessage(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+
+ if (p->quit != 0) {
+ /* Process any pending messages */
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ break;
+ }
+ }
+ }
+
+ if (UnregisterClass(p->AppName, NULL) == 0) {
+ warning("UnregisterClass failed, lasterr = %d\n",GetLastError());
+ }
+
+ p->hwnd = NULL; /* Signal it's been deleted */
+
+ return 0;
+}
+
+#endif /* NT */
+
+/* Create a RAMDAC access and display test window, default grey */
+dispwin *new_dispwin(
+disppath *disp, /* Display to calibrate. */
+double width, double height, /* Width and height in mm */
+double hoff, double voff, /* Offset from center in fraction of screen, range -1.0 .. 1.0 */
+int nowin, /* NZ if no window should be created - RAMDAC access only */
+int native, /* 0 = use current current or given calibration curve */
+ /* 1 = use native linear out & high precision */
+int *noramdac, /* Return nz if no ramdac access. native is set to 0 */
+int blackbg, /* NZ if whole screen should be filled with black */
+int override, /* NZ if override_redirect is to be used on X11 */
+int ddebug /* >0 to print debug statements to stderr */
+) {
+ dispwin *p = NULL;
+ char *cp;
+
+ debug("new_dispwin called\n");
+
+ if ((p = (dispwin *)calloc(sizeof(dispwin), 1)) == NULL) {
+ if (ddebug) fprintf(stderr,"new_dispwin failed because malloc failed\n");
+ return NULL;
+ }
+
+ /* !!!! Make changes in webwin.c as well !!!! */
+ p->nowin = nowin;
+ p->native = native;
+ p->blackbg = blackbg;
+ p->ddebug = ddebug;
+ p->get_ramdac = dispwin_get_ramdac;
+ p->set_ramdac = dispwin_set_ramdac;
+ p->install_profile = dispwin_install_profile;
+ p->uninstall_profile = dispwin_uninstall_profile;
+ p->get_profile = dispwin_get_profile;
+ p->set_color = dispwin_set_color;
+ p->set_update_delay = dispwin_set_update_delay;
+ p->set_callout = dispwin_set_callout;
+ p->del = dispwin_del;
+
+ p->rgb[0] = p->rgb[1] = p->rgb[2] = 0.5; /* Set Grey as the initial test color */
+
+ p->min_update_delay = 20;
+
+ if ((cp = getenv("ARGYLL_MIN_DISPLAY_UPDATE_DELAY_MS")) != NULL) {
+ p->min_update_delay = atoi(cp);
+ if (p->min_update_delay < 20)
+ p->min_update_delay = 20;
+ if (p->min_update_delay > 60000)
+ p->min_update_delay = 60000;
+ debugr2((errout, "new_dispwin: Minimum display update delay set to %d msec\n",p->min_update_delay));
+ }
+
+ p->update_delay = DISPLAY_UPDATE_DELAY; /* Default update delay */
+ if (p->update_delay < p->min_update_delay)
+ p->update_delay = p->min_update_delay;
+
+ /* Basic object is initialised, so create a window */
+
+ /* -------------------------------------------------- */
+#ifdef NT
+ {
+ WNDCLASS wc;
+ int disp_hsz, disp_vsz; /* Display horizontal/vertical size in mm */
+ int disp_hrz, disp_vrz; /* Display horizontal/vertical resolution in pixels */
+ int wi, he; /* Width and height of window in pixels */
+ int xo, yo; /* Window location in pixels */
+ int bpp;
+
+ p->AppName = "Argyll Test Window";
+
+ debugr2((errout, "new_dispwin: About to open display '%s'\n",disp->name));
+
+ /* Get device context to main display */
+ /* (This is the recommended way of doing this, and works on Vista) */
+ if ((p->hdc = CreateDC(disp->name, NULL, NULL, NULL)) == NULL) {
+ debugr2((errout, "new_dispwin: CreateDC failed, lasterr = %d\n",GetLastError()));
+ dispwin_del(p);
+ return NULL;
+ }
+
+ if ((p->name = strdup(disp->name)) == NULL) {
+ debugr2((errout, "new_dispwin: Malloc failed\n"));
+ dispwin_del(p);
+ return NULL;
+ }
+ if ((p->description = strdup(disp->description)) == NULL) {
+ debugr2((errout, "new_dispwin: Malloc failed\n"));
+ dispwin_del(p);
+ return NULL;
+ }
+ strcpy(p->monid, disp->monid);
+
+ disp_hsz = GetDeviceCaps(p->hdc, HORZSIZE); /* mm */
+ disp_vsz = GetDeviceCaps(p->hdc, VERTSIZE);
+ disp_hrz = GetDeviceCaps(p->hdc, HORZRES); /* pixels */
+ disp_vrz = GetDeviceCaps(p->hdc, VERTRES);
+
+ wi = (int)(width * (double)disp_hrz/(double)disp_hsz + 0.5);
+ if (wi > disp_hrz)
+ wi = disp_hrz;
+ he = (int)(height * (double)disp_vrz/(double)disp_vsz + 0.5);
+ if (he > disp_vrz)
+ he = disp_vrz;
+
+ if (p->blackbg) { /* Window fills the screen, test area is within it */
+ p->tx = (int)((hoff * 0.5 + 0.5) * (disp->sw - wi) + 0.5);
+ p->ty = (int)((voff * 0.5 + 0.5) * (disp->sh - he) + 0.5);
+ p->tw = wi;
+ p->th = he;
+ wi = disp->sw;
+ he = disp->sh;
+ xo = disp->sx;
+ yo = disp->sy;
+ } else { /* Test area completely fills the window */
+ p->tx = 0;
+ p->ty = 0;
+ p->tw = wi;
+ p->th = he;
+ xo = disp->sx + (int)((hoff * 0.5 + 0.5) * (disp->sw - wi) + 0.5);
+ yo = disp->sy + (int)((voff * 0.5 + 0.5) * (disp->sh - he) + 0.5);
+ }
+ p->ww = wi;
+ p->wh = he;
+
+ /* It's a bit difficult to know how windows defines the display */
+ /* depth. Microsofts doco is fuzzy, and typical values */
+ /* for BITSPIXEL and PLANES are confusing (What does "32" and "1" */
+ /* mean ?) NUMCOLORS seems to be -1 on my box, and perhaps */
+ /* is only applicable to up to 256 paletized colors. The doco */
+ /* for COLORRES is also fuzzy, but it returns a meaningful number */
+ /* on my box (24) */
+ if (p->ddebug) {
+ fprintf(errout,"Windows display RASTERCAPS 0x%x, BITSPIXEL %d, PLANES %d, NUMCOLORS %d, COLORRES %d\n",
+ GetDeviceCaps(p->hdc, RASTERCAPS),
+ GetDeviceCaps(p->hdc, BITSPIXEL),GetDeviceCaps(p->hdc, PLANES),
+ GetDeviceCaps(p->hdc, NUMCOLORS),GetDeviceCaps(p->hdc, COLORRES));
+ }
+ if (GetDeviceCaps(p->hdc, RASTERCAPS) & RC_PALETTE) {
+ debugr2((errout, "new_dispwin: can't calibrate palette based device!\n"));
+ dispwin_del(p);
+ return NULL;
+ }
+ bpp = GetDeviceCaps(p->hdc, COLORRES);
+ if (bpp <= 0)
+ p->pdepth = 8; /* Assume this is so */
+ else
+ p->pdepth = bpp/3;
+
+ if (nowin == 0) {
+
+ /* We use a thread to process the window messages, so that */
+ /* Task Manager doesn't think it's not responding. */
+
+ /* Because messages only get delivered to same thread that created window, */
+ /* the thread needs to create window. :-( */
+
+ p->xo = xo; /* Pass info to thread */
+ p->yo = yo;
+ p->wi = wi;
+ p->he = he;
+
+ if ((p->mth = new_athread(win_message_thread, (void *)p)) == NULL) {
+ debugr2((errout, "new_dispwin: new_athread failed\n"));
+ dispwin_del(p);
+ return NULL;
+ }
+
+ /* Wait for thread to run */
+ while (p->inited == 0) {
+ msec_sleep(20);
+ }
+ /* If thread errored */
+ if (p->inited != 1) { /* Error */
+ debugr2((errout, "new_dispwin: new_athread returned error\n"));
+ dispwin_del(p);
+ return NULL;
+ }
+ }
+
+ /* Install the signal handler to ensure cleanup */
+ dispwin_install_signal_handlers(p);
+ }
+
+#endif /* NT */
+ /* -------------------------------------------------- */
+
+ /* -------------------------------------------------- */
+#ifdef __APPLE__
+
+ if ((p->name = strdup(disp->name)) == NULL) {
+ debugr2((errout,"new_dispwin: Malloc failed\n"));
+ dispwin_del(p);
+ return NULL;
+ }
+ p->ddid = disp->ddid; /* Display we're working on */
+
+#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
+ {
+ CGDisplayModeRef dispmode;
+ CFStringRef pixenc;
+
+ p->pdepth = 0;
+
+ dispmode = CGDisplayCopyDisplayMode(p->ddid);
+ pixenc = CGDisplayModeCopyPixelEncoding(dispmode);
+
+ /* Hmm. Don't know what to do with kIO16BitFloatPixels or kIO32BitFloatPixels */
+ if (CFStringCompare(pixenc, CFSTR(kIO64BitDirectPixels), kCFCompareCaseInsensitive)
+ == kCFCompareEqualTo)
+ p->pdepth = 16;
+ else if (CFStringCompare(pixenc, CFSTR(kIO30BitDirectPixels), kCFCompareCaseInsensitive)
+ == kCFCompareEqualTo)
+ p->pdepth = 10;
+ else if (CFStringCompare(pixenc, CFSTR(IO32BitDirectPixels), kCFCompareCaseInsensitive)
+ == kCFCompareEqualTo)
+ p->pdepth = 8;
+ else if (CFStringCompare(pixenc, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive)
+ == kCFCompareEqualTo)
+ p->pdepth = 5;
+ CFRelease(pixenc);
+ CGDisplayModeRelease(dispmode);
+ }
+#else
+ p->pdepth = CGDisplayBitsPerSample(p->ddid);
+#endif
+
+ if (nowin == 0) { /* Create a window */
+ osx_cntx_t *cx;
+ CGSize sz; /* Display size in mm */
+ int wi, he; /* Width and height in pixels */
+ int xo, yo; /* Window location */
+ NSRect wrect;
+
+ debugr2((errout, "new_dispwin: About to open display '%s'\n",disp->name));
+
+ /* If we don't have an application object, create one. */
+ /* (This should go in a common library) */
+ /* Note that we don't actually clean this up on exit - */
+ /* possibly we can't. */
+ if (NSApp == nil) {
+ static NSAutoreleasePool *pool; /* Pool used for NSApp */
+ pool = [NSAutoreleasePool new];
+ NSApp = [NSApplication sharedApplication]; /* Creates NSApp */
+ [NSApp finishLaunching];
+ /* We seem to need this, because otherwise we don't get focus automatically */
+ [NSApp activateIgnoringOtherApps: YES];
+ }
+
+ if ((cx = (osx_cntx_t *)calloc(sizeof(osx_cntx_t), 1)) == NULL) {
+ debugr2((errout,"new_dispwin: Malloc failed (osx_cntx_t)\n"));
+ dispwin_del(p);
+ return NULL;
+ }
+ cx->p = p;
+ p->osx_cntx = cx;
+
+ sz = CGDisplayScreenSize(p->ddid);
+ debugr2((errout," Display size = %f x %f mm\n",sz.width,sz.height));
+
+ wi = (int)(width * disp->sw/sz.width + 0.5);
+ if (wi > disp->sw)
+ wi = disp->sw;
+ he = (int)(height * disp->sh/sz.height + 0.5);
+ if (he > disp->sh)
+ he = disp->sh;
+
+ /* (Because Cocoa origin is botton left, we flip voff) */
+ /* (Cocoa doesn't use disp->sx/sy either - each screen origin is at 0,0) */
+ if (p->blackbg) { /* Window fills the screen, test area is within it */
+ p->tx = (int)((hoff * 0.5 + 0.5) * (disp->sw - wi) + 0.5);
+ p->ty = (int)((-voff * 0.5 + 0.5) * (disp->sh - he) + 0.5);
+ p->tw = wi;
+ p->th = he;
+ wi = disp->sw;
+ he = disp->sh;
+ xo = 0;
+ yo = 0;
+ } else { /* Test area completely fills the window */
+ p->tx = 0;
+ p->ty = 0;
+ p->tw = wi;
+ p->th = he;
+ xo = (int)((hoff * 0.5 + 0.5) * (disp->sw - wi) + 0.5);
+ yo = (int)((-voff * 0.5 + 0.5) * (disp->sh - he) + 0.5);
+ }
+ p->ww = wi;
+ p->wh = he;
+
+ wrect.origin.x = xo;
+ wrect.origin.y = yo;
+ wrect.size.width = wi;
+ wrect.size.height = he;
+
+ create_my_win(wrect, cx);
+
+ OSX_ProcessEvents(p);
+
+ p->winclose = 0;
+ }
+
+ /* Install the signal handler to ensure cleanup */
+ dispwin_install_signal_handlers(p);
+#endif /* __APPLE__ */
+ /* -------------------------------------------------- */
+
+ /* -------------------------------------------------- */
+#if defined(UNIX_X11)
+ {
+ /* NOTE: That we're not doing much to detect if the display/window
+ we open is unsuitable for high quality color (ie. at least
+ 24 bit etc.
+ */
+
+#ifdef NEVER // ?? is this for a specific reason ??
+ if (signal_dispwin != NULL) {
+ debugr2((errout,"new_dispwin: Attempting to open more than one dispwin!\n"));
+ dispwin_del(p);
+ return NULL;
+ }
+#endif
+
+ /* stuff for X windows */
+ char *pp, *bname; /* base display name */
+ Window rootwindow;
+ char *appname = "TestWin";
+ Visual *myvisual;
+ XSetWindowAttributes myattr;
+ XEvent myevent;
+ XTextProperty myappname;
+ XSizeHints mysizehints;
+ XWMHints mywmhints;
+ int evb = 0, erb = 0; /* Extension version */
+ unsigned long myforeground,mybackground;
+ int disp_hsz, disp_vsz; /* Display horizontal/vertical size in mm */
+ int disp_hrz, disp_vrz; /* Display horizontal/vertical resolution in pixels (virtual screen) */
+ int wi, he; /* Width and height of window in pixels */
+ int xo, yo; /* Window location in pixels */
+
+ /* Create the base display name (in case of Xinerama, XRandR) */
+ if ((bname = strdup(disp->name)) == NULL) {
+ debugr2((errout,"new_dispwin: Malloc failed\n"));
+ dispwin_del(p);
+ return NULL;
+ }
+ if ((pp = strrchr(bname, ':')) != NULL) {
+ if ((pp = strchr(pp, '.')) != NULL) {
+ sprintf(pp,".%d",disp->screen);
+ }
+ }
+
+ /* open the display */
+ p->mydisplay = XOpenDisplay(bname);
+ if(!p->mydisplay) {
+ debugr2((errout,"new_dispwin: Unable to open display '%s'\n",bname));
+ dispwin_del(p);
+ free(bname);
+ return NULL;
+ }
+ free(bname);
+ debugr("new_dispwin: Opened display OK\n");
+
+ if ((p->name = strdup(disp->name)) == NULL) {
+ debugr2((errout,"new_dispwin: Malloc failed\n"));
+ dispwin_del(p);
+ return NULL;
+ }
+ p->myscreen = disp->screen;
+ p->myuscreen = disp->uscreen;
+ p->myrscreen = disp->rscreen;
+
+#if RANDR_MAJOR == 1 && RANDR_MINOR >= 2 && !defined(DISABLE_RANDR)
+ /* These will be NULL otherwise */
+ p->icc_atom = disp->icc_atom;
+ p->crtc = disp->crtc;
+ p->output = disp->output;
+ p->icc_out_atom = disp->icc_out_atom;
+#endif /* randr >= V 1.2 */
+
+ if (disp->edid != NULL) {
+ if ((p->edid = malloc(sizeof(unsigned char) * disp->edid_len)) == NULL) {
+ debugr2((errout,"new_dispwin: Malloc failed\n"));
+ dispwin_del(p);
+ return NULL;
+ }
+ p->edid_len = disp->edid_len;
+ memmove(p->edid, disp->edid, p->edid_len);
+ }
+
+ //p->pdepth = DefaultDepth(p->mydisplay, p->myscreen)/3;
+ myvisual = DefaultVisual(p->mydisplay, p->myscreen);
+ p->pdepth = myvisual->bits_per_rgb;
+
+ if (nowin == 0) { /* Create a window */
+ rootwindow = RootWindow(p->mydisplay, p->myscreen);
+
+ myforeground = BlackPixel(p->mydisplay, p->myscreen);
+ mybackground = BlackPixel(p->mydisplay, p->myscreen);
+
+ /* Get device context to main display */
+ disp_hsz = DisplayWidthMM(p->mydisplay, p->myscreen);
+ disp_vsz = DisplayHeightMM(p->mydisplay, p->myscreen);
+ disp_hrz = DisplayWidth(p->mydisplay, p->myscreen);
+ disp_vrz = DisplayHeight(p->mydisplay, p->myscreen);
+
+ /* Compute width and offset from overal display in case Xinerama is active */
+ wi = (int)(width * (double)disp_hrz/(double)disp_hsz + 0.5);
+ if (wi > disp_hrz)
+ wi = disp_hrz;
+ he = (int)(height * (double)disp_vrz/(double)disp_vsz + 0.5);
+ if (he > disp_vrz)
+ he = disp_vrz;
+
+ if (p->blackbg) { /* Window fills the screen, test area is within it */
+ p->tx = (int)((hoff * 0.5 + 0.5) * (disp->sw - wi) + 0.5);
+ p->ty = (int)((voff * 0.5 + 0.5) * (disp->sh - he) + 0.5);
+ p->tw = wi;
+ p->th = he;
+ wi = disp->sw;
+ he = disp->sh;
+ xo = disp->sx;
+ yo = disp->sy;
+ } else { /* Test area completely fills the window */
+ p->tx = 0;
+ p->ty = 0;
+ p->tw = wi;
+ p->th = he;
+ xo = disp->sx + (int)((hoff * 0.5 + 0.5) * (disp->sw - wi) + 0.5);
+ yo = disp->sy + (int)((voff * 0.5 + 0.5) * (disp->sh - he) + 0.5);
+ }
+ p->ww = wi;
+ p->wh = he;
+
+ /* Setup Size Hints */
+ mysizehints.flags = PPosition | USSize;
+ mysizehints.x = xo;
+ mysizehints.y = yo;
+ mysizehints.width = wi;
+ mysizehints.height = he;
+
+ /* Setup Window Manager Hints */
+ mywmhints.flags = InputHint | StateHint;
+ mywmhints.input = 0;
+ mywmhints.initial_state = NormalState;
+
+ /* Setup Window Attributes */
+ myattr.background_pixel = mybackground;
+ myattr.bit_gravity = CenterGravity;
+ myattr.win_gravity = CenterGravity;
+ myattr.backing_store = WhenMapped; /* Since we aren't listning to events */
+ if (override)
+ myattr.override_redirect = True; /* Takes the WM out of the picture */
+ else
+ myattr.override_redirect = False;
+
+ debugr("Opening window\n");
+ p->mywindow = XCreateWindow(
+ p->mydisplay, rootwindow,
+ mysizehints.x,mysizehints.y,mysizehints.width,mysizehints.height,
+ 0, /* Border width */
+ CopyFromParent, /* Depth */
+ InputOutput, /* Class */
+ CopyFromParent, /* Visual */
+ CWBackPixel | CWBitGravity /* Attributes Valumask */
+ | CWWinGravity | CWBackingStore | CWOverrideRedirect,
+ &myattr /* Attribute details */
+ );
+
+#ifdef NEVER
+ XWindowAttributes mywattributes;
+
+ /* Get the windows attributes */
+ if (XGetWindowAttributes(
+ p->mydisplay, p->mywindow,
+ &mywattributes) == 0) {
+ debugr("new_dispwin: XGetWindowAttributes failed\n");
+ dispwin_del(p);
+ return NULL;
+ }
+ p->pdepth = mywattributes.depth/3;
+#endif
+
+ /* Setup TextProperty */
+ XStringListToTextProperty(&appname, 1, &myappname);
+
+ XSetWMProperties(
+ p->mydisplay, p->mywindow,
+ &myappname, /* Window name */
+ &myappname, /* Icon name */
+ NULL, 0, /* argv, argc */
+ &mysizehints,
+ &mywmhints,
+ NULL); /* No class hints */
+
+ /* Set aditional properties */
+ {
+ Atom optat;
+ unsigned int opaque = 0xffffffff;
+ unsigned int xid = (unsigned int)rootwindow; /* Hope this is 32 bit */
+ XChangeProperty(
+ p->mydisplay, p->mywindow,
+ XA_WM_TRANSIENT_FOR, /* Property */
+ XA_WINDOW, /* Type */
+ 32, /* format = bits in type of unsigned int */
+ PropModeReplace, /* Change mode */
+ (char *)(&xid), /* Data is Root Window XID */
+ 1 /* Number of elements of data */
+ );
+
+ /* Set hint for compositing WMs that the window must be opaque */
+ if ((optat = XInternAtom(p->mydisplay, "_NET_WM_WINDOW_OPACITY", False)) != None) {
+ XChangeProperty(p->mydisplay, p->mywindow, optat,
+ XA_CARDINAL, 32, PropModeReplace, (char *)(&opaque), 1);
+ }
+ if ((optat = XInternAtom(p->mydisplay, "_NET_WM_WINDOW_OPACITY_LOCKED", False)) != None) {
+ XChangeProperty(p->mydisplay, p->mywindow, optat,
+ XA_CARDINAL, 32, PropModeReplace, (char *)(&opaque), 1);
+ }
+ }
+
+ p->mygc = XCreateGC(p->mydisplay,p->mywindow,0,0);
+ XSetBackground(p->mydisplay,p->mygc,mybackground);
+ XSetForeground(p->mydisplay,p->mygc,myforeground);
+
+ /* Create an invisible cursor over our window */
+ {
+ Cursor mycursor;
+ Pixmap mypixmap;
+ Colormap mycmap;
+ XColor col;
+ char pmdata[1] = { 0 };
+
+ col.red = col.green = col.blue = 0;
+
+ mycmap = DefaultColormap(p->mydisplay, p->myscreen);
+ XAllocColor(p->mydisplay, mycmap, &col);
+ mypixmap = XCreatePixmapFromBitmapData(p->mydisplay, p->mywindow, pmdata, 1, 1, 0, 0, 1);
+ mycursor = XCreatePixmapCursor(p->mydisplay, mypixmap, mypixmap, &col, &col, 0,0);
+ XDefineCursor(p->mydisplay, p->mywindow, mycursor);
+ }
+
+ XSelectInput(p->mydisplay,p->mywindow, ExposureMask);
+
+ XMapRaised(p->mydisplay,p->mywindow);
+ debug("Raised window\n");
+
+ /* ------------------------------------------------------- */
+ /* Suspend any screensavers if we can */
+
+ /* Install the signal handler to ensure cleanup */
+ dispwin_install_signal_handlers(p);
+
+#if ScreenSaverMajorVersion >= 1 && ScreenSaverMinorVersion >= 1 /* X11R7.1 ??? */
+
+ /* Disable any screensavers that work properly with XScreenSaverSuspend() */
+ if (XScreenSaverQueryExtension (p->mydisplay, &evb, &erb) != 0) {
+ int majv, minv;
+ XScreenSaverSuspend(p->mydisplay, True);
+ p->xsssuspend = 1;
+
+ /* Else we'd have to register as a screensaver to */
+ /* prevent another one activating ?? */
+ }
+#endif /* X11R7.1 screensaver extension */
+
+ /* Disable the native X11 screensaver */
+ if (p->xsssuspend == 0) {
+
+ /* Save the screensaver state, and then disable it */
+ XGetScreenSaver(p->mydisplay, &p->timeout, &p->interval,
+ &p->prefer_blanking, &p->allow_exposures);
+ XSetScreenSaver(p->mydisplay, 0, 0, DefaultBlanking, DefaultExposures);
+ p->xssvalid = 1;
+ }
+
+ /* Disable xscreensaver if it is running */
+ if (p->xssrunning == 0) {
+ p->xssrunning = (system("xscreensaver-command -version 2>/dev/null >/dev/null") == 0);
+ if (p->xssrunning)
+ system("xscreensaver-command -exit 2>/dev/null >/dev/null");
+ }
+
+ /* Disable gnomescreensaver if it is running */
+ if (p->gnomessrunning == 0) {
+ p->gnomessrunning = (system("gnome-screensaver-command -q "
+ "2>/dev/null >/dev/null") == 0);
+ if (p->gnomessrunning) {
+ sigset_t nsm, osm;
+ /* Ensure that other process doesn't get the signals we want to catch */
+ sigemptyset(&nsm);
+ sigaddset(&nsm,SIGHUP);
+ sigaddset(&nsm,SIGINT);
+ sigaddset(&nsm,SIGTERM);
+ sigprocmask(SIG_BLOCK, &nsm, &osm);
+
+ if ((p->gnomepid = fork()) == 0) {
+ freopen("/dev/null", "r", stdin);
+ freopen("/dev/null", "a", stdout); /* Hide output */
+ freopen("/dev/null", "a", stderr);
+ execlp("gnome-screensaver-command", "gnome-screensaver-command","-i","-n","argyll","-r","measuring screen",NULL);
+
+ _exit(0);
+ }
+ sigprocmask(SIG_SETMASK, &osm, NULL); /* restore the signals */
+ }
+ }
+
+ /* kscreensaver > 3.5.9 obeys XResetScreenSaver(), but earlier versions don't. */
+ /* Disable any KDE screen saver if it's active */
+ if (p->kdessrunning == 0) {
+ /* dcop is very slow if we're not actually running kde. */
+ /* Check that kde is running first */
+ if (system("ps -e 2>/dev/null | grep kdesktop 2>/dev/null >/dev/null") == 0) {
+ p->kdessrunning = (system("dcop kdesktop KScreensaverIface isEnabled "
+ "2>/dev/null | grep true 2>/dev/null >/dev/null") == 0);
+ }
+ if (p->kdessrunning) {
+ system("dcop kdesktop KScreensaverIface enable false 2>&1 >/dev/null");
+ }
+ }
+
+ /* If DPMS is enabled, disable it */
+ if (DPMSQueryExtension(p->mydisplay, &evb, &erb) != 0) {
+ CARD16 power_level;
+ BOOL state;
+
+ if (DPMSInfo(p->mydisplay, &power_level, &state)) {
+ if ((p->dpmsenabled = state) != 0)
+ DPMSDisable(p->mydisplay);
+ }
+ }
+
+ /* Deal with any pending events */
+ debug("About to enter main loop\n");
+ while(XPending(p->mydisplay) > 0) {
+ XNextEvent(p->mydisplay, &myevent);
+ switch(myevent.type) {
+ case Expose:
+ if(myevent.xexpose.count == 0) { /* Repare the exposed region */
+ debug("Servicing final expose\n");
+ XFillRectangle(p->mydisplay, p->mywindow, p->mygc,
+ p->tx, p->ty, p->tw, p->th);
+ debug("Finished expose\n");
+ }
+ break;
+ }
+ }
+ } else {
+ /* Install the signal handler to ensure cleanup */
+ dispwin_install_signal_handlers(p);
+ }
+ }
+#endif /* UNIX X11 */
+ /* -------------------------------------------------- */
+
+ if (!p->nowin) {
+ /* Setup for native mode */
+ if (p->native) {
+ debug("About to setup native mode\n");
+ if ((p->or = p->get_ramdac(p)) == NULL
+ || (p->r = p->or->clone(p->or)) == NULL) {
+ if (noramdac != NULL)
+ *noramdac = 1;
+ debugr("new_dispwin: Accessing VideoLUT failed, so no way to guarantee that calibration is turned off!!\n");
+ warning("new_dispwin: Accessing VideoLUT failed, so no way to guarantee that calibration is turned off!!");
+ p->native = 0;
+ } else {
+ p->r->setlin(p->r);
+ if (noramdac != NULL)
+ *noramdac = 0;
+ debug("Saved original VideoLUT\n");
+ }
+ } else {
+ if (p->get_ramdac(p) == NULL) {
+ if (noramdac != NULL)
+ *noramdac = 1;
+ }
+ }
+
+ /* Make sure initial test color is displayed */
+ dispwin_set_color(p, p->rgb[0], p->rgb[1], p->rgb[2]);
+ }
+
+ debugr("new_dispwin: return sucessfully\n");
+ return p;
+}
+
+/* ================================================================ */
+#if defined(UNIX_X11)
+/* Process to continuously monitor XRandR events, */
+/* and load the appropriate calibration and profiles */
+/* for each monitor. */
+int x11_daemon_mode(disppath *disp, int verb, int ddebug) {
+
+#if RANDR_MAJOR == 1 && RANDR_MINOR >= 2 && !defined(DISABLE_RANDR)
+ char *dname;
+ char *pp;
+ char dnbuf[100];
+ Display *mydisplay;
+ int majv, minv; /* Version */
+ int evb = 0, erb = 0;
+ int dopoll = 1; /* Until XRandR is fixed */
+ XEvent myevent;
+ int update_profiles = 1; /* Do it on entry */
+
+ /* Open the base display */
+ strncpy(dnbuf,disp->name,99); dnbuf[99] = '\000';
+ if ((pp = strrchr(dnbuf, ':')) != NULL) {
+ if ((pp = strchr(pp, '.')) == NULL)
+ strcat(dnbuf,".0");
+ else {
+ if (pp[1] == '\000')
+ strcat(dnbuf,"0");
+ else {
+ pp[1] = '0';
+ pp[2] = '\000';
+ }
+ }
+ }
+
+ if ((mydisplay = XOpenDisplay(dnbuf)) == NULL) {
+ debug2((errout, "x11_daemon_mode: failed to open display '%s'\n",dnbuf));
+ return -1;
+ }
+
+ if (verb) printf("Opened display '%s'\n",dnbuf);
+
+ /* !!!! we want to create a test here, to see if we have to poll, */
+ /* !!!! or whether we spontainously get events when the EDID changes. */
+
+ /* Use Xrandr 1.2 if it's available and not disabled */
+ if (getenv("ARGYLL_IGNORE_XRANDR1_2") == NULL
+ && XRRQueryExtension(mydisplay, &evb, &erb) != 0
+ && XRRQueryVersion(mydisplay, &majv, &minv)
+ && majv == 1 && minv >= 2) {
+ if (verb) printf("Found XRandR 1.2 or latter\n");
+
+ XRRSelectInput(mydisplay,RootWindow(mydisplay,0),
+ RRScreenChangeNotifyMask
+ | RRCrtcChangeNotifyMask
+ | RROutputChangeNotifyMask
+ | RROutputPropertyNotifyMask
+ );
+
+ /* Deal with any pending events */
+ if (verb) printf("About to enter main loop waiting for XRandR changes\n");
+ for(;;) {
+
+ if (update_profiles == 0) {
+ if (dopoll) {
+ for (;;) {
+ XRRGetScreenResources(mydisplay, RootWindow(mydisplay,0));
+ if(XPending(mydisplay) > 0)
+ break;
+ sleep(2);
+ }
+ } else {
+ /* Sleep until there is an event */
+ XPeekEvent(mydisplay, &myevent);
+ }
+ }
+
+ /* Get all our events until we run out */
+ while (XPending(mydisplay) > 0) {
+ XNextEvent(mydisplay, &myevent);
+ if (myevent.type == evb + RRScreenChangeNotify) {
+// printf("~1 Got RRScreenChangeNotify\n");
+ update_profiles = 1;
+ } else if (myevent.type == evb + RRNotify) {
+ update_profiles = 1;
+ XRRNotifyEvent *rrne = (XRRNotifyEvent *)(&myevent);
+ if (rrne->subtype == RRNotify_CrtcChange) {
+// printf("~1 Got RRCrtcChangeNotify\n");
+ }
+ else if (rrne->subtype == RRNotify_OutputChange) {
+// printf("~1 Got RROutputChangeNotify\n");
+ }
+ else if (rrne->subtype == RRNotify_OutputProperty) {
+// printf("~1 Got RROutputPropertyNotify\n");
+ }
+ }
+ }
+
+ if (update_profiles) {
+ disppath **dp;
+ ramdac *r = NULL;
+
+ if (verb) printf("Updating profiles for display '%s'\n",dnbuf);
+
+ dp = get_displays();
+ if (dp == NULL || dp[0] == NULL) {
+ if (verb) printf("Failed to enumerate all the screens for display '%s'\n",dnbuf);
+ continue;
+ } else {
+ int i, j;
+ dispwin *dw;
+ char calname[MAXNAMEL+1] = "\000"; /* Calibration file name */
+ icmFile *rd_fp = NULL;
+ icc *icco = NULL;
+ icmVideoCardGamma *wo;
+ double iv;
+
+ for (i = 0; ; i++) {
+ if (dp[i] == NULL)
+ break;
+ if (verb) printf("Updating display %d = '%s'\n",i+1,dp[i]->description);
+
+ if ((dw = new_dispwin(dp[i], 0.0, 0.0, 0.0, 0.0, 1, 0, NULL, 0, 0, ddebug)) == NULL) {
+ if (verb) printf("Failed to access screen %d of display '%s'\n",i+1,dnbuf);
+ continue;
+ }
+ if ((r = dw->get_ramdac(dw)) == NULL) {
+ if (verb) printf("Failed to access VideoLUT of screen %d for display '%s'\n",i+1,dnbuf);
+ dw->del(dw);
+ continue;
+ }
+
+ /* Grab the installed profile from the ucmm */
+ if ((rd_fp = dw->get_profile(dw, calname, MAXNAMEL)) == NULL) {
+ if (verb) printf("Failed to find profile of screen %d for display '%s'\n",i+1,dnbuf);
+ r->del(r);
+ dw->del(dw);
+ continue;
+ }
+
+ if ((icco = new_icc()) == NULL) {
+ if (verb) printf("Failed to create profile object for screen %d for display '%s'\n",i+1,dnbuf);
+ rd_fp->del(rd_fp);
+ r->del(r);
+ dw->del(dw);
+ continue;
+ }
+
+ /* Read header etc. */
+ if (icco->read(icco, rd_fp,0) != 0) { /* Read ICC OK */
+ if (verb) printf("Failed to read profile for screen %d for display '%s'\n",i+1,dnbuf);
+ icco->del(icco);
+ rd_fp->del(rd_fp);
+ r->del(r);
+ dw->del(dw);
+ continue;
+ }
+
+ if ((wo = (icmVideoCardGamma *)icco->read_tag(icco, icSigVideoCardGammaTag)) == NULL) {
+ if (verb) printf("Failed to fined vcgt tagd in profile for screen %d for display '%s' so setting linear\n",i+1,dnbuf);
+ for (j = 0; j < r->nent; j++) {
+ iv = j/(r->nent-1.0);
+ r->v[0][j] = iv;
+ r->v[1][j] = iv;
+ r->v[2][j] = iv;
+ }
+ } else {
+ if (wo->u.table.channels == 3) {
+ for (j = 0; j < r->nent; j++) {
+ iv = j/(r->nent-1.0);
+ r->v[0][j] = wo->lookup(wo, 0, iv);
+ r->v[1][j] = wo->lookup(wo, 1, iv);
+ r->v[2][j] = wo->lookup(wo, 2, iv);
+ }
+ } else if (wo->u.table.channels == 1) {
+ for (j = 0; j < r->nent; j++) {
+ iv = j/(r->nent-1.0);
+ r->v[0][j] =
+ r->v[1][j] =
+ r->v[2][j] = wo->lookup(wo, 0, iv);
+ }
+ debug("Got monochrom vcgt calibration\n");
+ } else {
+ if (verb) printf("vcgt tag is unrecognized in profile for screen %d for display '%s'\n",i+1,dnbuf);
+ icco->del(icco);
+ rd_fp->del(rd_fp);
+ r->del(r);
+ dw->del(dw);
+ continue;
+ }
+ }
+ if (dw->set_ramdac(dw,r,1) != 0) {
+ if (verb) printf("Unable to set vcgt tag for screen %d for display '%s'\n",i+1,dnbuf);
+ icco->del(icco);
+ rd_fp->del(rd_fp);
+ r->del(r);
+ dw->del(dw);
+ continue;
+ }
+ if (verb) printf("Loaded profile and calibration for screen %d for display '%s'\n",i+1,dnbuf);
+ icco->del(icco);
+ rd_fp->del(rd_fp);
+ r->del(r);
+ dw->del(dw);
+ }
+ }
+ free_disppaths(dp);
+ update_profiles = 0;
+ }
+ }
+ } else
+#endif /* randr >= V 1.2 */
+
+ if (verb) printf("XRandR 1.2 is not available - quitting\n");
+ return -1;
+}
+
+#endif
+
+/* ================================================================ */
+#ifdef STANDALONE_TEST
+/* test/utility code */
+
+#if defined(__APPLE__) && defined(__POWERPC__)
+
+/* Workaround for a ppc gcc 3.3 optimiser bug... */
+/* It seems to cause a segmentation fault instead of */
+/* converting an integer loop index into a float, */
+/* when there are sufficient variables in play. */
+static int gcc_bug_fix(int i) {
+ static int nn;
+ nn += i;
+ return nn;
+}
+#endif /* APPLE */
+
+#include "numlib.h"
+
+static void usage(char *diag, ...) {
+ disppath **dp;
+ fprintf(stderr,"Test display patch window, Set Video LUTs, Install profiles, Version %s\n",ARGYLL_VERSION_STR);
+ fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n");
+ if (diag != NULL) {
+ va_list args;
+ fprintf(stderr,"Diagnostic: ");
+ va_start(args, diag);
+ vfprintf(stderr, diag, args);
+ va_end(args);
+ fprintf(stderr,"\n");
+ }
+ fprintf(stderr,"usage: dispwin [options] [calfile] \n");
+ fprintf(stderr," -v Verbose mode\n");
+#if defined(UNIX_X11)
+ fprintf(stderr," -display displayname Choose X11 display name\n");
+ fprintf(stderr," -d n[,m] Choose the display n from the following list (default 1)\n");
+ fprintf(stderr," Optionally choose different display m for Video LUT access\n");
+#else
+ fprintf(stderr," -d n Choose the display from the following list (default 1)\n");
+#endif
+ dp = get_displays();
+ if (dp == NULL || dp[0] == NULL) {
+ fprintf(stderr," ** No displays found **\n");
+ } else {
+ int i;
+ for (i = 0; ; i++) {
+ if (dp[i] == NULL)
+ break;
+ fprintf(stderr," %d = '%s'\n",i+1,dp[i]->description);
+ }
+ }
+ free_disppaths(dp);
+ fprintf(stderr," -dweb[:port] Display via a web server at port (default 8080)\n");
+ fprintf(stderr," -P ho,vo,ss[,vs] Position test window and scale it\n");
+ fprintf(stderr," -F Fill whole screen with black background\n");
+ fprintf(stderr," -i Run forever with random values\n");
+ fprintf(stderr," -G filename Display RGB colors from CGATS file\n");
+ fprintf(stderr," -m Manually cycle through values\n");
+ fprintf(stderr," -f Test grey ramp fade\n");
+ fprintf(stderr," -r Test just Video LUT loading & Beeps\n");
+ fprintf(stderr," -n Test native output (rather than through Video LUT)\n");
+ fprintf(stderr," -s filename Save the currently loaded Video LUT to 'filename'\n");
+ fprintf(stderr," -c Load a linear display calibration\n");
+ fprintf(stderr," -V Verify that calfile/profile cal. is currently loaded in LUT\n");
+ fprintf(stderr," -I Install profile for display and use it's calibration\n");
+ fprintf(stderr," -U Un-install profile for display\n");
+ fprintf(stderr," -S d Specify the install/uninstall scope for OS X [nlu] or X11/Vista [lu]\n");
+ fprintf(stderr," d is one of: n = network, l = local system, u = user (default)\n");
+ fprintf(stderr," -L Load installed profiles cal. into Video LUT\n");
+#if defined(UNIX_X11)
+ fprintf(stderr," -E Run in daemon loader mode for given X11 server\n");
+#endif /* X11 */
+ fprintf(stderr," -D [level] Print debug diagnostics to stderr\n");
+ fprintf(stderr," calfile Load calibration (.cal or %s) into Video LUT\n",ICC_FILE_EXT);
+ exit(1);
+}
+
+/* 32 bit pseudo random sequencer based on XOR feedback */
+/* generates number between 1 and 4294967295 */
+#define PSRAND32(S) (((S) & 0x80000000) ? (((S) << 1) ^ 0xa398655d) : ((S) << 1))
+
+int
+main(int argc, char *argv[]) {
+ int fa, nfa, mfa; /* current argument we're looking at */
+ int verb = 0; /* Verbose flag */
+ int ddebug = 0; /* debug level */
+ int webdisp = 0; /* NZ for web display, == port number */
+ disppath *disp = NULL; /* Display being used */
+ double hpatscale = 1.0, vpatscale = 1.0; /* scale factor for test patch size */
+ double ho = 0.0, vo = 0.0; /* Test window offsets, -1.0 to 1.0 */
+ int blackbg = 0; /* NZ if whole screen should be filled with black */
+ int nowin = 0; /* Don't create test window */
+ int ramd = 0; /* Just test ramdac */
+ int fade = 0; /* Test greyramp fade */
+ int native = 0; /* 0 = use current current or given calibration curve */
+ /* 1 = set native linear output and use ramdac high prec'n */
+ /* 2 = set native linear output */
+ int noramdac = 0; /* Set to nz if there is no VideoLUT access */
+ int inf = 0; /* Infnite/manual patches flag */
+ char pcname[MAXNAMEL+1] = "\000"; /* CGATS patch color name */
+ int clear = 0; /* Clear any display calibration (any calname is ignored) */
+ char sname[MAXNAMEL+1] = "\000"; /* Current cal save name */
+ int verify = 0; /* Verify that calname is currently loaded */
+ int installprofile = 0; /* Install (1) or uninstall (2) a profile for display */
+ int loadprofile = 0; /* Load displays profile calibration into LUT */
+ int loadfile = 0; /* Load given profile into LUT */
+ p_scope scope = p_scope_user; /* Scope of profile instalation/un-instalation */
+ int daemonmode = 0; /* X11 daemin loader mode */
+ char calname[MAXNAMEL+1] = "\000"; /* Calibration file name */
+ dispwin *dw;
+ unsigned int seed = 0x56781234;
+ int i, j;
+ ramdac *or = NULL, *r = NULL;
+ int is_ok_icc = 0; /* The profile is OK */
+
+ error_program = "Dispwin";
+ check_if_not_interactive();
+
+ /* Process the arguments */
+ mfa = 0; /* Minimum final arguments */
+ for(fa = 1;fa < argc;fa++) {
+
+ nfa = fa; /* skip to nfa if next argument is used */
+ if (argv[fa][0] == '-') { /* Look for any flags */
+ char *na = NULL; /* next argument after flag, null if none */
+
+ if (argv[fa][2] != '\000')
+ na = &argv[fa][2]; /* next is directly after flag */
+ else {
+ if ((fa+1+mfa) < argc) {
+ if (argv[fa+1][0] != '-') {
+ nfa = fa + 1;
+ na = argv[nfa]; /* next is seperate non-flag argument */
+ }
+ }
+ }
+
+ if (argv[fa][1] == '?')
+ usage("Usage requested");
+
+ else if (argv[fa][1] == 'v')
+ verb = 1;
+
+ /* Debug */
+ else if (argv[fa][1] == 'D') {
+ ddebug = 1;
+ if (na != NULL && na[0] >= '0' && na[0] <= '9') {
+ ddebug = atoi(na);
+ fa = nfa;
+ }
+ callback_ddebug = ddebug; /* dispwin global */
+ }
+
+ /* Display number */
+ else if (argv[fa][1] == 'd') {
+ if (strncmp(na,"web",3) == 0
+ || strncmp(na,"WEB",3) == 0) {
+ webdisp = 8080;
+ if (na[3] == ':') {
+ webdisp = atoi(na+4);
+ if (webdisp == 0 || webdisp > 65535)
+ usage("Web port number must be in range 1..65535");
+ }
+ fa = nfa;
+ } else {
+#if defined(UNIX_X11)
+ int ix, iv;
+
+ /* X11 type display name. */
+ if (strcmp(&argv[fa][2], "isplay") == 0 || strcmp(&argv[fa][2], "ISPLAY") == 0) {
+ if (++fa >= argc || argv[fa][0] == '-') usage("-DISPLAY parameter missing");
+ setenv("DISPLAY", argv[fa], 1);
+ } else {
+ if (na == NULL) usage("-d parameter missing");
+ fa = nfa;
+ if (sscanf(na, "%d,%d",&ix,&iv) != 2) {
+ ix = atoi(na);
+ iv = 0;
+ }
+ if (disp != NULL)
+ free_a_disppath(disp);
+ if ((disp = get_a_display(ix-1)) == NULL)
+ usage("-d parameter '%s' is out of range",na);
+ if (iv > 0)
+ disp->rscreen = iv-1;
+ }
+#else
+ int ix;
+ if (na == NULL) usage("-d parameter is missing");
+ fa = nfa;
+ ix = atoi(na);
+ if (disp != NULL)
+ free_a_disppath(disp);
+ if ((disp = get_a_display(ix-1)) == NULL)
+ usage("-d parameter '%s' is out of range",na);
+#endif
+ }
+ }
+
+ /* Test patch offset and size */
+ else if (argv[fa][1] == 'P') {
+ fa = nfa;
+ if (na == NULL) usage("-p parameters are missing");
+ if (sscanf(na, " %lf,%lf,%lf,%lf ", &ho, &vo, &hpatscale, &vpatscale) == 4) {
+ ;
+ } else if (sscanf(na, " %lf,%lf,%lf ", &ho, &vo, &hpatscale) == 3) {
+ vpatscale = hpatscale;
+ } else {
+ usage("-p parameters '%s' is badly formatted",na);
+ }
+ if (ho < 0.0 || ho > 1.0
+ || vo < 0.0 || vo > 1.0
+ || hpatscale <= 0.0 || hpatscale > 50.0
+ || vpatscale <= 0.0 || vpatscale > 50.0)
+ usage("-p parameters '%s' is out of range",na);
+ ho = 2.0 * ho - 1.0;
+ vo = 2.0 * vo - 1.0;
+
+ /* Black background */
+ } else if (argv[fa][1] == 'F') {
+ blackbg = 1;
+
+ } else if (argv[fa][1] == 'i')
+ inf = 1;
+
+ else if (argv[fa][1] == 'm' || argv[fa][1] == 'M')
+ inf = 2;
+
+ /* CGATS patch color file */
+ else if (argv[fa][1] == 'G') {
+ fa = nfa;
+ if (na == NULL) usage("-G parameter is missing");
+ strncpy(pcname,na,MAXNAMEL); pcname[MAXNAMEL] = '\000';
+ }
+ else if (argv[fa][1] == 'f')
+ fade = 1;
+
+ else if (argv[fa][1] == 'r' || argv[fa][1] == 'R')
+ ramd = 1;
+
+ else if (argv[fa][1] == 'n' || argv[fa][1] == 'N') {
+ native = 1;
+ if (argv[fa][1] == 'N')
+ native = 2;
+ }
+
+ else if (argv[fa][1] == 's') {
+ fa = nfa;
+ if (na == NULL) usage("-s parameter is missing");
+ strncpy(sname,na,MAXNAMEL); sname[MAXNAMEL] = '\000';
+ }
+
+ else if (argv[fa][1] == 'c' || argv[fa][1] == 'C')
+ clear = 1;
+
+ else if (argv[fa][1] == 'V')
+ verify = 1;
+
+ else if (argv[fa][1] == 'I')
+ installprofile = 1;
+
+ else if (argv[fa][1] == 'U')
+ installprofile = 2;
+
+ else if (argv[fa][1] == 'L')
+ loadprofile = 1;
+
+ else if (argv[fa][1] == 'E')
+ daemonmode = 1;
+
+ else if (argv[fa][1] == 'S') {
+ fa = nfa;
+ if (na == NULL) usage("-S parameter is missing");
+ if (na[0] == 'n' || na[0] == 'N')
+ scope = p_scope_network;
+ else if (na[0] == 'l' || na[0] == 'L')
+ scope = p_scope_local;
+ else if (na[0] == 'u' || na[0] == 'U')
+ scope = p_scope_user;
+ }
+ else
+ usage("Unknown flag '%s'",argv[fa]);
+ }
+ else
+ break;
+ }
+
+ /* No explicit display has been set */
+ if (webdisp == 0 && disp == NULL) {
+ int ix = 0;
+#if defined(UNIX_X11)
+ char *dn, *pp;
+
+ if ((dn = getenv("DISPLAY")) != NULL) {
+ if ((pp = strrchr(dn, ':')) != NULL) {
+ if ((pp = strchr(pp, '.')) != NULL) {
+ if (pp[1] != '\000')
+ ix = atoi(pp+1);
+ }
+ }
+ }
+#endif
+ if ((disp = get_a_display(ix)) == NULL)
+ error("Unable to open the default display");
+ }
+
+ /* See if there's a calibration file */
+ if (fa < argc) {
+ strncpy(calname,argv[fa++],MAXNAMEL); calname[MAXNAMEL] = '\000';
+ if (installprofile == 0 && loadprofile == 0 && verify == 0)
+ loadfile = 1; /* Load the given profile into the videoLUT */
+ }
+
+#if defined(UNIX_X11)
+ if (webdisp == 0 && daemonmode) {
+ return x11_daemon_mode(disp, verb, ddebug);
+ }
+#endif
+
+ /* Bomb on bad combinations (not all are being detected) */
+ if (installprofile && calname[0] == '\000')
+ error("Can't install or uninstall a displays profile without profile argument");
+
+ if (verify && calname[0] == '\000' && loadprofile == 0)
+ error("No calibration/profile provided to verify against");
+
+ if (verify && installprofile == 1)
+ error("Can't verify and install a displays profile at the same time");
+
+ if (verify && installprofile == 2)
+ error("Can't verify and uninstall a displays profile at the same time");
+
+ /* Don't create a window if it won't be used */
+ if (ramd != 0 || sname[0] != '\000' || clear != 0 || verify != 0 || loadfile != 0 || installprofile != 0 || loadprofile != 0)
+ nowin = 1;
+
+
+ if (webdisp != 0) {
+ if ((dw = new_webwin(webdisp, 100.0 * hpatscale, 100.0 * vpatscale, ho, vo, nowin, blackbg, verb, ddebug)) == NULL) {
+ printf("Error - new_webpwin failed!\n");
+ return -1;
+ }
+ noramdac = 1;
+
+ } else {
+ if (verb) printf("About to open dispwin object on the display\n");
+ if ((dw = new_dispwin(disp, 100.0 * hpatscale, 100.0 * vpatscale, ho, vo, nowin, native, &noramdac, blackbg, 1, ddebug)) == NULL) {
+ printf("Error - new_dispwin failed!\n");
+ return -1;
+ }
+ }
+
+ if (native != 0 && noramdac) {
+ error("We don't have access to the VideoLUT so can't display native colors\n");
+ }
+
+ /* Save the current Video LUT to the calfile */
+ if (sname[0] != '\000') {
+ cgats *ocg; /* output cgats structure */
+ time_t clk = time(0);
+ struct tm *tsp = localtime(&clk);
+ char *atm = asctime(tsp); /* Ascii time */
+ cgats_set_elem *setel; /* Array of set value elements */
+ int nsetel = 0;
+
+ if (verb)
+ printf("About to save current loaded calibration to file '%s'\n",sname);
+
+ if ((r = dw->get_ramdac(dw)) == NULL) {
+ error("We don't have access to the VideoLUT");
+ }
+
+ ocg = new_cgats(); /* Create a CGATS structure */
+ ocg->add_other(ocg, "CAL"); /* our special type is Calibration file */
+
+ ocg->add_table(ocg, tt_other, 0); /* Add a table for RAMDAC values */
+ ocg->add_kword(ocg, 0, "DESCRIPTOR", "Argyll Device Calibration Curves",NULL);
+ ocg->add_kword(ocg, 0, "ORIGINATOR", "Argyll synthcal", NULL);
+ atm[strlen(atm)-1] = '\000'; /* Remove \n from end */
+ ocg->add_kword(ocg, 0, "CREATED",atm, NULL);
+
+ ocg->add_kword(ocg, 0, "DEVICE_CLASS","DISPLAY", NULL);
+ ocg->add_kword(ocg, 0, "COLOR_REP", "RGB", NULL);
+
+ ocg->add_field(ocg, 0, "RGB_I", r_t);
+ ocg->add_field(ocg, 0, "RGB_R", r_t);
+ ocg->add_field(ocg, 0, "RGB_G", r_t);
+ ocg->add_field(ocg, 0, "RGB_B", r_t);
+
+ if ((setel = (cgats_set_elem *)malloc(sizeof(cgats_set_elem) * 4)) == NULL)
+ error("Malloc failed!");
+
+ /* Write the video lut curve values */
+ for (i = 0; i < r->nent; i++) {
+ double iv = i/(r->nent-1.0);
+
+#if defined(__APPLE__) && defined(__POWERPC__)
+ gcc_bug_fix(i);
+#endif
+ setel[0].d = iv;
+ setel[1].d = r->v[0][i];
+ setel[2].d = r->v[1][i];
+ setel[3].d = r->v[2][i];
+
+ ocg->add_setarr(ocg, 0, setel);
+ }
+
+ free(setel);
+
+ if (ocg->write_name(ocg, sname))
+ error("Write error to '%s' : %s",sname,ocg->err);
+
+ ocg->del(ocg); /* Clean up */
+ r->del(r);
+ r = NULL;
+
+ /* Fall through, as we may want to do other stuff too */
+ }
+
+ /* Clear the display calibration curve */
+ if (clear != 0) {
+ int rv;
+
+ if ((r = dw->get_ramdac(dw)) == NULL) {
+ error("We don't have access to the VideoLUT");
+ }
+
+ for (i = 0; i < r->nent; i++) {
+ double iv = i/(r->nent-1.0);
+ r->v[0][i] = iv;
+ r->v[1][i] = iv;
+ r->v[2][i] = iv;
+ }
+ if (verb)
+ printf("About to clear the calibration\n");
+ if ((rv = dw->set_ramdac(dw,r,1)) != 0) {
+ if (rv == 2)
+ warning("Failed to set VideoLUTs persistently because current System Profile can't be renamed");
+ else
+ error("Failed to set VideoLUTs");
+ }
+
+ r->del(r);
+ r = NULL;
+
+ /* Fall through, as we may want to do other stuff too */
+ }
+
+ /* Un-Install the profile from the display */
+ if (installprofile == 2) {
+ int rv;
+ if ((rv = dw->uninstall_profile(dw, calname, scope))) {
+ if (rv == 2)
+ warning("Profile '%s' not found to uninstall!",calname);
+ else
+ error("Error trying to uninstall profile '%s'!",calname);
+ }
+ if (verb) {
+ printf("Un-Installed '%s'\n",calname);
+ }
+ }
+
+ /* Get any calibration from the provided .cal file or .profile, */
+ /* or calibration from the current default display profile, */
+ /* and put it in r */
+ if (loadfile != 0 || verify != 0 || loadprofile != 0 || installprofile == 1) {
+ icmFile *rd_fp = NULL;
+ icc *icco = NULL;
+ cgats *ccg = NULL; /* calibration cgats structure */
+
+ /* Get a calibration that's compatible with the display. */
+ /* This can fail and return NULL - error if we later need it */
+ r = dw->get_ramdac(dw);
+
+ /* Should we load calfile instead of installed profile if it's present ??? */
+ if (loadprofile) {
+ if (calname[0] != '\000')
+ warning("Profile '%s' provided as argument is being ignored!\n",calname);
+
+ /* Get the current displays profile */
+ debug2((errout,"Loading calibration from display profile '%s'\n",dw->name));
+ if ((rd_fp = dw->get_profile(dw, calname, MAXNAMEL)) == NULL)
+ error("Failed to get the displays current ICC profile\n");
+
+ } else {
+ /* Open up the profile for reading */
+ debug2((errout,"Loading calibration from file '%s'\n",calname));
+ if ((rd_fp = new_icmFileStd_name(calname,"r")) == NULL)
+ error("Can't open file '%s'",calname);
+ }
+
+ if ((icco = new_icc()) == NULL)
+ error("Creation of ICC object failed");
+
+ /* Read header etc. */
+ if (icco->read(icco, rd_fp,0) == 0) { /* Read ICC OK */
+ icmVideoCardGamma *wo;
+ double iv;
+
+ is_ok_icc = 1; /* The profile is OK */
+
+ if ((wo = (icmVideoCardGamma *)icco->read_tag(icco, icSigVideoCardGammaTag)) == NULL) {
+ warning("No vcgt tag found in profile - assuming linear\n");
+ if (r != NULL) {
+ for (i = 0; i < r->nent; i++) {
+ iv = i/(r->nent-1.0);
+ r->v[0][i] = iv;
+ r->v[1][i] = iv;
+ r->v[2][i] = iv;
+ }
+ }
+ } else {
+
+ /* Hmm. Perhaps we should ignore this if the vcgt is linear ?? */
+ if (r == NULL)
+ error("We don't have access to the VideoLUT");
+
+ if (wo->u.table.channels == 3) {
+ for (i = 0; i < r->nent; i++) {
+ iv = i/(r->nent-1.0);
+ r->v[0][i] = wo->lookup(wo, 0, iv);
+ r->v[1][i] = wo->lookup(wo, 1, iv);
+ r->v[2][i] = wo->lookup(wo, 2, iv);
+//printf("~1 entry %d = %f %f %f\n",i,r->v[0][i],r->v[1][i],r->v[2][i]);
+ }
+ debug("Got color vcgt calibration\n");
+ } else if (wo->u.table.channels == 1) {
+ for (i = 0; i < r->nent; i++) {
+ iv = i/(r->nent-1.0);
+ r->v[0][i] =
+ r->v[1][i] =
+ r->v[2][i] = wo->lookup(wo, 0, iv);
+ }
+ debug("Got monochrom vcgt calibration\n");
+ } else {
+ r->del(r);
+ r = NULL;
+ }
+ }
+ } else { /* See if it's a .cal file */
+ int ncal;
+ int ii, fi, ri, gi, bi;
+ double cal[3][256];
+
+ icco->del(icco); /* Don't need these now */
+ icco = NULL;
+ rd_fp->del(rd_fp);
+ rd_fp = NULL;
+
+ ccg = new_cgats(); /* Create a CGATS structure */
+ ccg->add_other(ccg, "CAL"); /* our special calibration type */
+
+ if (ccg->read_name(ccg, calname)) {
+ ccg->del(ccg);
+ error("File '%s' is not a valid ICC profile or Argyll .cal file",calname);
+ }
+
+ if (ccg->ntables == 0 || ccg->t[0].tt != tt_other || ccg->t[0].oi != 0)
+ error("Calibration file isn't a CAL format file");
+ if (ccg->ntables < 1)
+ error("Calibration file '%s' doesn't contain at least one table",calname);
+
+ if ((ncal = ccg->t[0].nsets) <= 0)
+ error("No data in set of file '%s'",calname);
+
+ if (ncal != 256)
+ error("Expect 256 data sets in file '%s'",calname);
+
+ if ((fi = ccg->find_kword(ccg, 0, "DEVICE_CLASS")) < 0)
+ error("Calibration file '%s' doesn't contain keyword COLOR_REP",calname);
+ if (strcmp(ccg->t[0].kdata[fi],"DISPLAY") != 0)
+ error("Calibration file '%s' doesn't have DEVICE_CLASS of DISPLAY",calname);
+
+ if ((ii = ccg->find_field(ccg, 0, "RGB_I")) < 0)
+ error("Calibration file '%s' doesn't contain field RGB_I",calname);
+ if (ccg->t[0].ftype[ii] != r_t)
+ error("Field RGB_R in file '%s' is wrong type",calname);
+ if ((ri = ccg->find_field(ccg, 0, "RGB_R")) < 0)
+ error("Calibration file '%s' doesn't contain field RGB_R",calname);
+ if (ccg->t[0].ftype[ri] != r_t)
+ error("Field RGB_R in file '%s' is wrong type",calname);
+ if ((gi = ccg->find_field(ccg, 0, "RGB_G")) < 0)
+ error("Calibration file '%s' doesn't contain field RGB_G",calname);
+ if (ccg->t[0].ftype[gi] != r_t)
+ error("Field RGB_G in file '%s' is wrong type",calname);
+ if ((bi = ccg->find_field(ccg, 0, "RGB_B")) < 0)
+ error("Calibration file '%s' doesn't contain field RGB_B",calname);
+ if (ccg->t[0].ftype[bi] != r_t)
+ error("Field RGB_B in file '%s' is wrong type",calname);
+ for (i = 0; i < ncal; i++) {
+ cal[0][i] = *((double *)ccg->t[0].fdata[i][ri]);
+ cal[1][i] = *((double *)ccg->t[0].fdata[i][gi]);
+ cal[2][i] = *((double *)ccg->t[0].fdata[i][bi]);
+ }
+
+ if (r == NULL)
+ error("We don't have access to the VideoLUT");
+
+ /* Interpolate from cal value to RAMDAC entries */
+ for (i = 0; i < r->nent; i++) {
+ double val, w;
+ unsigned int ix;
+
+ val = (ncal-1.0) * i/(r->nent-1.0);
+ ix = (unsigned int)floor(val); /* Coordinate */
+ if (ix > (ncal-2))
+ ix = (ncal-2);
+ w = val - (double)ix; /* weight */
+ for (j = 0; j < 3; j++) {
+ val = cal[j][ix];
+ r->v[j][i] = val + w * (cal[j][ix+1] - val);
+ }
+ }
+ debug("Got cal file calibration\n");
+ }
+ if (ccg != NULL)
+ ccg->del(ccg);
+ if (icco != NULL)
+ icco->del(icco);
+ if (rd_fp != NULL)
+ rd_fp->del(rd_fp);
+ }
+
+ /* Install the profile into the display and set as the default */
+ if (installprofile == 1) {
+ if (is_ok_icc == 0)
+ error("File '%s' doesn't seem to be an ICC profile!",calname);
+
+ if (verb)
+ printf("About to install '%s' as display's default profile\n",calname);
+ if (dw->install_profile(dw, calname, r, scope)) {
+ error("Failed to install profile '%s'!",calname);
+ }
+ if (verb) {
+ printf("Installed '%s' and made it the default\n",calname);
+ }
+
+ /* load the LUT with the calibration from the given file or the current display profile. */
+ /* (But don't load profile calibration if we're verifying against it) */
+ } else if (loadfile != 0 || (loadprofile != 0 && verify == 0)) {
+ int rv;
+
+ /* r == NULL if no VideoLUT access and ICC profile without vcgt */
+ if (r == NULL) {
+ warning("No linear calibration loaded because there is no access to the VideoLUT");
+ } else {
+ if (verb)
+ printf("About to set display to given calibration\n");
+ if ((rv = dw->set_ramdac(dw,r,1)) != 0) {
+ if (rv == 2)
+ error("Failed to set VideoLUTs persistently because current System Profile can't be renamed");
+ else
+ error("Failed to set VideoLUTs");
+ }
+ if (verb)
+ printf("Calibration set\n");
+ }
+ }
+
+ if (verify != 0) {
+ int ver = 1;
+ double berr = 0.0;
+ if ((or = dw->get_ramdac(dw)) == NULL)
+ error("Unable to get current VideoLUT for verify");
+
+ if (r == NULL)
+ error("No calibration to verify against");
+
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < r->nent; i++) {
+ double err;
+ err = fabs(r->v[j][i] - or->v[j][i]);
+ if (err > berr)
+ berr = err;
+ if (err > VERIFY_TOL) {
+ ver = 0;
+ }
+ }
+ }
+ if (ver)
+ printf("Verify: '%s' IS loaded (discrepancy %.1f%%)\n", calname, berr * 100);
+ else
+ printf("Verify: '%s' is NOT loaded (discrepancy %.1f%%)\n", calname, berr * 100);
+ or->del(or);
+ or = NULL;
+ }
+ if (r != NULL) {
+ r->del(r);
+ r = NULL;
+ }
+
+ /* If no other command selected, do a Window or VideoLUT test */
+ if (sname[0] == '\000' && clear == 0 && installprofile == 0 && loadfile == 0 && verify == 0 && loadprofile == 0) {
+
+ if (ramd == 0) {
+
+ if (fade) {
+ int i;
+ int steps = 256;
+ for (i = 0; i < steps; i++) {
+ double tt;
+ tt = i/(steps - 1.0);
+ dw->set_color(dw, tt, tt, tt);
+ msec_sleep(20);
+ printf("Val = %f\n",tt);
+ }
+
+ /* Patch colors from a CGATS file */
+ } else if (pcname[0] != '\000') {
+ cgats *icg;
+ int i, npat;
+ int ri, gi, bi;
+ int si = -1;
+
+ if ((icg = new_cgats()) == NULL)
+ error("new_cgats() failed\n");
+ icg->add_other(icg, ""); /* Allow any signature file */
+
+ if (icg->read_name(icg, pcname))
+ error("File '%s' read error : %s",pcname, icg->err);
+
+ if (icg->ntables < 1) /* We don't use second table at the moment */
+ error ("Input file '%s' doesn't contain at least one table",pcname);
+
+ if ((npat = icg->t[0].nsets) <= 0)
+ error ("File '%s has no sets of data in the first table",pcname);
+
+ si = icg->find_field(icg, 0, "SAMPLE_ID");
+ if (si >= 0 && icg->t[0].ftype[si] != nqcs_t)
+ error("In file '%s' field SAMPLE_ID is wrong type",pcname);
+
+ if ((ri = icg->find_field(icg, 0, "RGB_R")) < 0)
+ error ("Input file '%s' doesn't contain field RGB_R",pcname);
+ if (icg->t[0].ftype[ri] != r_t)
+ error ("In file '%s' field RGB_R is wrong type",pcname);
+ if ((gi = icg->find_field(icg, 0, "RGB_G")) < 0)
+ error ("Input file '%s' doesn't contain field RGB_G",pcname);
+ if (icg->t[0].ftype[gi] != r_t)
+ error ("In file '%s' field RGB_G is wrong type",pcname);
+ if ((bi = icg->find_field(icg, 0, "RGB_B")) < 0)
+ error ("Input file '%s' doesn't contain field RGB_B",pcname);
+ if (icg->t[0].ftype[bi] != r_t)
+ error ("In file '%s' field RGB_B is wrong type",pcname);
+
+ if (inf == 2)
+ printf("\nHit return to advance each color\n");
+
+ if (inf == 2) {
+ printf("\nHit return to start\n");
+ getchar();
+ }
+ for (i = 0; i < npat; i++) {
+ double r, g, b;
+ r = *((double *)icg->t[0].fdata[i][ri]) / 100.0;
+ g = *((double *)icg->t[0].fdata[i][gi]) / 100.0;
+ b = *((double *)icg->t[0].fdata[i][bi]) / 100.0;
+
+ if (si >= 0)
+ printf("Patch id '%s'",((char *)icg->t[0].fdata[i][si]));
+ else
+ printf("Patch no %d",i+1);
+ printf(" color %f %f %f\n",r,g,b);
+
+ dw->set_color(dw, r, g, b);
+
+ if (inf == 2)
+ getchar();
+ else
+ sleep(2);
+ }
+ icg->del(icg);
+
+ /* Preset and random patch colors */
+ } else {
+
+ if (inf == 2)
+ printf("\nHit return to advance each color\n");
+
+ printf("Setting White\n");
+ dw->set_color(dw, 1.0, 1.0, 1.0); /* White */
+
+ if (inf == 2)
+ getchar();
+ else
+ sleep(2);
+
+ printf("Setting 75%% Grey\n");
+ dw->set_color(dw, 0.75, 0.75, 0.75); /* Grey */
+
+ if (inf == 2)
+ getchar();
+ else
+ sleep(2);
+
+ printf("Setting 50%% Grey\n");
+ dw->set_color(dw, 0.5, 0.5, 0.5); /* Grey */
+
+ if (inf == 2)
+ getchar();
+ else
+ sleep(2);
+
+ printf("Setting 25%% Grey\n");
+ dw->set_color(dw, 0.25, 0.25, 0.25); /* Grey */
+
+ if (inf == 2)
+ getchar();
+ else
+ sleep(2);
+
+ printf("Setting 12.5%% Grey\n");
+ dw->set_color(dw, 0.125, 0.125, 0.125); /* Grey */
+
+ if (inf == 2)
+ getchar();
+ else
+ sleep(2);
+
+ printf("Setting Black\n");
+ dw->set_color(dw, 0.0, 0.0, 0.0);
+
+ if (inf == 2)
+ getchar();
+ else
+ sleep(2);
+
+ printf("Setting Red\n");
+ dw->set_color(dw, 1.0, 0.0, 0.0); /* Red */
+
+ if (inf == 2)
+ getchar();
+ else
+ sleep(2);
+
+ printf("Setting Green\n");
+ dw->set_color(dw, 0.0, 1.0, 0.0); /* Green */
+
+ if (inf == 2)
+ getchar();
+ else
+ sleep(2);
+
+ printf("Setting Blue\n");
+ dw->set_color(dw, 0.0, 0.0, 1.0); /* Blue */
+
+ if (inf == 2)
+ getchar();
+ else
+ sleep(2);
+
+ printf("Setting Cyan\n");
+ dw->set_color(dw, 0.0, 1.0, 1.0); /* Cyan */
+
+ if (inf == 2)
+ getchar();
+ else
+ sleep(2);
+
+ printf("Setting Magenta\n");
+ dw->set_color(dw, 1.0, 0.0, 1.0); /* Magenta */
+
+ if (inf == 2)
+ getchar();
+ else
+ sleep(2);
+
+ printf("Setting Yellow\n");
+ dw->set_color(dw, 1.0, 1.0, 0.0); /* Yellow */
+
+ if (inf == 2)
+ getchar();
+ else
+ sleep(2);
+
+ printf("Setting 50%% Red\n");
+ dw->set_color(dw, 0.5, 0.0, 0.0); /* Red */
+
+ if (inf == 2)
+ getchar();
+ else
+ sleep(2);
+
+ printf("Setting 50%% Green\n");
+ dw->set_color(dw, 0.0, 0.5, 0.0); /* Green */
+
+ if (inf == 2)
+ getchar();
+ else
+ sleep(2);
+
+ printf("Setting 50%% Blue\n");
+ dw->set_color(dw, 0.0, 0.0, 0.5); /* Blue */
+
+ if (inf == 2)
+ getchar();
+ else
+ sleep(2);
+
+ if (inf == 1) {
+ for (;inf != 0;) {
+ double col[3];
+
+ for (i = 0; i < 3; i++) {
+ seed = PSRAND32(seed);
+ col[i] = seed/4294967295.0;
+ }
+
+ printf("Setting %f %f %f\n",col[0],col[1],col[2]);
+ dw->set_color(dw, col[0],col[1],col[2]);
+
+ if (inf == 2)
+ getchar();
+ else
+ sleep(2);
+
+ }
+ }
+ }
+ }
+
+ if (inf != 2) {
+ /* Test out the VideoLUT access */
+ if ((dw->or = dw->get_ramdac(dw)) != NULL) { /* Use dw->or so signal will restore */
+
+ r = dw->or->clone(dw->or);
+
+ /* Try darkening it */
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < r->nent; i++) {
+ r->v[j][i] = pow(dw->or->v[j][i], 2.0);
+ }
+ }
+ printf("Darkening screen\n");
+ if (dw->set_ramdac(dw,r,0)) {
+ dw->set_ramdac(dw,or,0);
+ error("Failed to set VideoLUTs");
+ }
+ sleep(1);
+
+ /* Try lightening it */
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < r->nent; i++) {
+ r->v[j][i] = pow(dw->or->v[j][i], 0.5);
+ }
+ }
+ printf("Lightening screen\n");
+ if (dw->set_ramdac(dw,r,0)) {
+ dw->set_ramdac(dw,or,0);
+ error("Failed to set VideoLUTs");
+ }
+ sleep(1);
+
+ /* restor it */
+ printf("Restoring screen\n");
+ if (dw->set_ramdac(dw,dw->or,0)) {
+ error("Failed to set VideoLUTs");
+ }
+
+ r->del(r);
+ dw->or->del(dw->or);
+ dw->or = NULL;
+
+ } else {
+ printf("We don't have access to the VideoLUT\n");
+ }
+
+ /* Test out the beeps */
+ printf("Normal beep\n");
+ normal_beep();
+
+ sleep(1);
+
+ printf("Good beep\n");
+ good_beep();
+
+ sleep(1);
+
+ printf("Bad double beep\n");
+ bad_beep();
+
+ sleep(2); /* Allow beep to complete */
+ }
+ }
+
+ if (disp != NULL)
+ free_a_disppath(disp);
+
+ if (verb)
+ printf("About to destroy dispwin object\n");
+
+ dw->del(dw);
+
+ return 0;
+}
+
+#endif /* STANDALONE_TEST */
+
+/* ================================================================ */
+/* Possible ThinkPad MSWin code to keep the main screen on */
+
+#ifdef NEVER
+
+Public Class Form1
+ Declare Sub XRCalibrationLidTurnOnNotification Lib "C:\Program Files (x86)\X-Rite\PANTONE Color Calibrator\XRLaptopIFSDK.dll" ()
+ Declare Sub XRCalibrationLidTurnOffNotification Lib "C:\Program Files (x86)\X-Rite\PANTONE Color Calibrator\XRLaptopIFSDK.dll" ()
+
+ Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
+
+ End Sub
+
+ Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
+ Call XRCalibrationLidTurnOnNotification()
+ End Sub
+
+ Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
+ Call XRCalibrationLidTurnOffNotification()
+ End Sub
+End Class
+
+#endif
+
+/* ================================================================ */
+/* Unused Apple code */
+
+#ifdef NEVER
+ /* Got displays, now have a look through them */
+ for (i = 0; i < dcount; i++) {
+ GDHandle gdh;
+ GDPtr gdp;
+
+ /* Dump display mode dictionary */
+ CFIndex nde, j;
+ CFDictionaryRef dr;
+ void **keys, **values;
+
+ dr = CGDisplayCurrentMode(dids[i]);
+ nde = CFDictionaryGetCount(dr);
+
+ printf("Dict contains %d entries \n", nde);
+ if ((keys = (void **)malloc(nde * sizeof(void *))) == NULL) {
+ debug("malloc failed for disp mode keys\n");
+ free_disppaths(disps);
+ free(dids);
+ return NULL;
+ }
+ if ((values = (void **)malloc(nde * sizeof(void *))) == NULL) {
+ debug("malloc failed for disp mode values\n");
+ free(keys);
+ free_disppaths(disps);
+ free(dids);
+ return NULL;
+ }
+ CFDictionaryGetKeysAndValues(dr, (const void **)keys, (const void **)values);
+ for (j = 0; j < nde; j++) {
+ printf("Entry %d key = %s\n", j, CFStringGetCStringPtr(keys[j], kCFStringEncodingMacRoman));
+ }
+ free(values);
+ free(keys);
+ }
+#endif
+
+
+#ifdef NEVER
+/* How to install profiles for devices. */
+
+/* Callback to locate a profile ID. */
+struct idp_rec {
+ CMDeviceID ddid; /* Device ID */
+// char *fname; /* Profile we're trying to find */
+ CMDeviceProfileID id; /* Corresponding ID */
+ CMDeviceScope dsc; /* Matching device scope */
+ int found; /* Flag indicating it's been found */
+};
+
+OSErr ItDevProfProc (
+const CMDeviceInfo *di,
+const NCMDeviceProfileInfo *pi,
+void *refCon)
+{
+ CMError ev;
+ struct idp_rec *r = (struct idp_rec *)refCon;
+ CMDeviceProfileID id;
+
+ if (di->deviceClass != cmDisplayDeviceClass
+ || di->deviceID != r->ddid) {
+ return noErr;
+ }
+
+ /* We'd qualify on the device mode too (deviceState ??), */
+ /* if we wanted to replace a profile for a particular mode. */
+
+ /* Assume this is a display with only one mode, and return */
+ /* the profile id and device scope */
+ r->id = pi->profileID;
+ r->dsc = di->deviceScope;
+ r->found = 1;
+ return noErr;
+//printf("~1 got match\n");
+
+/* Callback to locate a profile ID. */
+struct idp_rec {
+ CMDeviceID ddid; /* Device ID */
+ CMDeviceProfileID id; /* Corresponding ID */
+ CMDeviceScope dsc; /* Matching device scope */
+ int found; /* Flag indicating it's been found */
+};
+
+OSErr ItDevProfProc (
+const CMDeviceInfo *di,
+const NCMDeviceProfileInfo *pi,
+void *refCon)
+{
+ CMError ev;
+ struct idp_rec *r = (struct idp_rec *)refCon;
+ CMDeviceProfileID id;
+
+ if (di->deviceClass != cmDisplayDeviceClass
+ || di->deviceID != r->ddid) {
+ return noErr;
+ }
+
+ /* Assume this is a display with only one mode, and return */
+ /* the profile id and device scope */
+ r->id = pi->profileID;
+ r->dsc = di->deviceScope;
+ r->found = 1;
+ return noErr;
+}
+
+#ifndef NEVER
+
+/* Given a location, return a string for it's path */
+static char *plocpath(CMProfileLocation *ploc) {
+
+ if (ploc->locType == cmFileBasedProfile) {
+ FSRef newRef;
+ static UInt8 path[256] = "";
+//printf("~1 converted spec file location\n");
+
+ if (FSpMakeFSRef(&ploc->u.fileLoc.spec, &newRef) == noErr) {
+ OSStatus stus;
+ if ((stus = FSRefMakePath(&newRef, path, 256)) == 0 || stus == fnfErr) {
+ return path;
+ }
+ }
+ return strdup(path);
+ } else if (ploc->locType == cmPathBasedProfile) {
+ return strdup(ploc->u.pathLoc.path);
+ }
+ return NULL;
+}
+
+#else
+
+/* fss2path takes the FSSpec of a file, folder or volume and returns it's POSIX (?) path. */
+/* Return NULL on error. Free returned string */
+/* NOTE FSSpec is deprecated in 10.5. Replace with FSRef ?? */
+static char *fss2path(FSSpec *fss) {
+ int i, l; /* fss->name contains name of last item in path */
+ char *path;
+
+ l = fss->name[0];
+ if ((path = malloc(l + 1)) == NULL)
+ return NULL;
+ for (i = 0; i < l; i++) {
+ if (fss->name[i+1] == '/')
+ path[i] = ':';
+ else
+ path[i] = fss->name[i+1];
+ }
+ path[i] = '\000';
+//printf("~1 path = '%s', l = %d\n",path,l);
+
+ if(fss->parID != fsRtParID) { /* path is more than just a volume name */
+ FSSpec tfss;
+// CInfoPBRec pb;
+ FSRefParam pb;
+ int tl;
+ char *tpath;
+
+ memmove(&tfss, fss, sizeof(FSSpec)); /* Copy so we can modify */
+ memset(&pb, 0, sizeof(FSRefParam));
+ pb.ioNamePtr = tfss.name;
+ pb.ioVRefNum = tfss.vRefNum;
+ pb.ioDrParID = tfss.parID;
+ do {
+ pb.ioFDirIndex = -1; /* get parent directory name */
+ pb.ioDrDirID = pb.dirInfo.ioDrParID;
+ if(PBGetCatlogInfoSync(&pb) != noErr) {
+ free(path);
+ return NULL;
+ }
+
+ /* Pre-pend the directory name separated by '/' */
+ if (pb.dirInfo.ioDrDirID == fsRtDirID) {
+ tl = 0; /* Don't pre-pend volume name */
+ } else {
+ tl = tfss.name[0];
+ }
+ if ((tpath = malloc(tl + l + 1)) == NULL) {
+ free(path);
+ return NULL;
+ }
+ for (i = 0; i < tl; i++) {
+ if (tfss.name[i+1] == '/')
+ tpath[i] = ':';
+ else
+ tpath[i] = tfss.name[i+1];
+ }
+ tpath[i] = '/';
+ for (i = 0; i < l; i++)
+ tpath[tl+1+i] = path[i];
+ tpath[tl+1+i] = '\000';
+ free(path);
+ path = tpath;
+ l = tl + 1 + l;
+//printf("~1 path = '%s', l = %d\n",path,l);
+ } while(pb.dirInfo.ioDrDirID != fsRtDirID); /* while more directory levels */
+ }
+
+ return path;
+}
+
+/* Return a string containing the profiles path. */
+/* Return NULL on error. Free the string after use. */
+static char *plocpath(CMProfileLocation *ploc) {
+ if (ploc->locType == cmFileBasedProfile) {
+ return fss2path(&ploc->u.fileLoc.spec);
+ } else if (ploc->locType == cmPathBasedProfile) {
+ return strdup(ploc->u.pathLoc.path);
+ }
+ return NULL;
+}
+
+#endif
+
+/* Test code that checks what the current display default profile is, three ways */
+static void pcurpath(dispwin *p) {
+ CMProfileRef xprof; /* Current AVID profile */
+ CMProfileLocation xploc; /* Current profile location */
+ UInt32 xplocsz = sizeof(CMProfileLocation);
+ struct idp_rec cb;
+ CMError ev;
+
+ /* Get the current installed profile */
+ if ((ev = CMGetProfileByAVID((CMDisplayIDType)p->ddid, &xprof)) != noErr) {
+ debug2((errout,"CMGetProfileByAVID() failed with error %d\n",ev));
+ goto skip;
+ }
+
+ /* Get the current installed profile's location */
+ if ((ev = NCMGetProfileLocation(xprof, &xploc, &xplocsz)) != noErr) {
+ debug2((errout,"NCMGetProfileLocation() failed with error %d\n",ev));
+ goto skip;
+ }
+
+//printf("~1 Current profile by AVID = '%s'\n",plocpath(&xploc));
+
+ /* Get the current CMDeviceProfileID and device scope */
+ cb.ddid = (CMDeviceID)p->ddid; /* Display Device ID */
+ cb.found = 0;
+
+ if ((ev = CMIterateDeviceProfiles(ItDevProfProc, NULL, NULL, cmIterateAllDeviceProfiles, (void *)&cb)) != noErr) {
+ debug2((errout,"CMIterateDeviceProfiles() failed with error %d\n",ev));
+ goto skip;
+ }
+ if (cb.found == 0) {
+ debug2((errout,"Failed to find exsiting profiles ID\n"));
+ goto skip;
+ }
+ /* Got cb.id */
+
+ if ((ev = CMGetDeviceProfile(cmDisplayDeviceClass, (CMDeviceID)p->ddid, cb.id, &xploc)) != noErr) {
+ debug2((errout,"Failed to GetDeviceProfile\n"));
+ goto skip;
+ }
+//printf("~1 Current profile by Itterate = '%s'\n",plocpath(&xploc));
+
+ /* Get the default ID for the display */
+ if ((ev = CMGetDeviceDefaultProfileID(cmDisplayDeviceClass, (CMDeviceID)p->ddid, &cb.id)) != noErr) {
+ debug2((errout,"CMGetDeviceDefaultProfileID() failed with error %d\n",ev));
+ goto skip;
+ }
+
+ /* Get the displays default profile */
+ if ((ev = CMGetDeviceProfile(cmDisplayDeviceClass, (CMDeviceID)p->ddid, cb.id, &xploc)) != noErr) {
+ debug2((errout,"CMGetDeviceDefaultProfileID() failed with error %d\n",ev));
+ goto skip;
+ }
+//printf("~1 Current profile by get default = '%s'\n",plocpath(&xploc));
+
+ skip:;
+}
+ /* If we want the path to the profile, we'd do this: */
+
+ printf("id = 0x%x\n",pi->profileID);
+ if (pi->profileLoc.locType == cmFileBasedProfile) {
+ FSRef newRef;
+ UInt8 path[256] = "";
+
+ if (FSpMakeFSRef(&pi->profileLoc.u.fileLoc.spec, &newRef) == noErr) {
+ OSStatus stus;
+ if ((stus = FSRefMakePath(&newRef, path, 256)) == 0 || stus == fnfErr) {
+ printf("file = '%s'\n",path);
+ if (strcmp(r->fname, (char *)path) == 0) {
+ r->id = pi->profileID;
+ r->found = 1;
+ printf("got match\n");
+ }
+ }
+ }
+ } else if (pi->profileLoc.locType == cmPathBasedProfile) {
+ if (strcmp(r->fname, pi->profileLoc.u.pathLoc.path) == 0) {
+ r->id = pi->profileID;
+ r->dsc = di->deviceScope;
+ r->found = 1;
+ printf("got match\n");
+ }
+ }
+
+
+ {
+ struct idp_rec cb;
+
+ /* The CMDeviceProfileID wll always be 1 for a display, because displays have only one mode, */
+ /* so the Iterate could be avoided for it. */
+
+ cb.ddid = (CMDeviceID)p->ddid; /* Display Device ID */
+// cb.fname = dpath;
+ cb.found = 0;
+
+ if ((ev = CMIterateDeviceProfiles(ItDevProfProc, NULL, NULL, cmIterateAllDeviceProfiles, (void *)&cb)) != noErr) {
+ debug2((errout,"CMIterateDeviceProfiles() failed with error %d\n",dpath,ev));
+ return 1;
+ }
+ if (cb.found == 0) {
+ debug2((errout,"Failed to find exsiting profiles ID, so ca't set it as default\n"));
+ return 1;
+ }
+ if ((ev = CMSetDeviceProfile(cmDisplayDeviceClass, (CMDeviceID)p->ddid, &cb.dsc, cb.id, &dploc)) != noErr) {
+ debug2((errout,"CMSetDeviceProfile() failed for file '%s' with error %d\n",dpath,ev));
+ return 1;
+ }
+ /* There is no point in doing the following, because displays only have one mode. */
+ /* Make it the default for the display */
+ if ((ev = CMSetDeviceDefaultProfileID(cmDisplayDeviceClass, (CMDeviceID)p->ddid, cb.id)) != noErr) {
+ debug2((errout,"CMSetDeviceDefaultProfileID() failed for file '%s' with error %d\n",dpath,ev));
+ return 1;
+ }
+
+#endif /* NEVER */
+
+#ifdef NEVER
+ /* Unused 10.6+ code */
+ CFUUIDRef dispuuid;
+ CFDictionaryRef dict, sdict, pdict;
+ CFStringRef id, urlstr;
+ CFURLRef url;
+ CFIndex bufSize;
+ char *dpath = NULL; /* return value */
+
+ if ((dispuuid = CGDisplayCreateUUIDFromDisplayID(p->ddid)) == NULL) {
+ debugr2((errout,"CGDisplayCreateUUIDFromDisplayID() failed\n"));
+ return NULL;
+ }
+
+ if ((dict = ColorSyncDeviceCopyDeviceInfo(kColorSyncDisplayDeviceClass, dispuuid)) == NULL) {
+ debugr2((errout,"ColorSyncDeviceCopyDeviceInfo() failed\n"));
+ CFRelease(dispuuid);
+ return NULL;
+ }
+
+ /* Get the factory profile dictionary */
+ if ((sdict = CFDictionaryGetValue(dict, kColorSyncFactoryProfiles)) == NULL) {
+ debugrr("Failed to get kColorSyncFactoryProfiles\n");
+ CFRelease(dict);
+ CFRelease(dispuuid);
+ return NULL;
+ }
+ /* Lookup the default profile ID */
+ if ((id = CFDictionaryGetValue(sdict, kColorSyncDeviceDefaultProfileID)) == NULL) {
+ debugrr("Failed to get kColorSyncDeviceDefaultProfileID\n");
+ CFRelease(dict);
+ CFRelease(dispuuid);
+ return NULL;
+ }
+
+// printf("~1 got ProfileID '%s'\n",CFStringGetCStringPtr(id, kCFStringEncodingMacRoman));
+
+ /* See if this ProfileID is in the factory profile dictionary */
+ if ((pdict = CFDictionaryGetValue(sdict, id)) != NULL) {
+ } else {
+ debugrr("Failed to get factory profile with id \n");
+
+ /* Get the custom profile dictionary */
+ if ((sdict = CFDictionaryGetValue(dict, kColorSyncCustomProfiles)) == NULL) {
+ debugrr("Failed to get kColorSyncCustomProfiles\n");
+ CFRelease(dict);
+ CFRelease(dispuuid);
+ return NULL;
+ }
+ /* See if the default id is in the custom profile dictionary */
+ if ((pdict = CFDictionaryGetValue(sdict, id)) == NULL) {
+ debugrr("Failed to locate default profile in factory or custom profile list\n");
+ CFRelease(dict);
+ CFRelease(dispuuid);
+ return NULL;
+ }
+ }
+
+ /* get the URL */
+ if ((url = CFDictionaryGetValue(pdict, kColorSyncDeviceProfileURL)) == NULL) {
+ debugrr("Failed to get default profile URL\n");
+ CFRelease(dict);
+ CFRelease(dispuuid);
+ return NULL;
+ }
+
+// urlstr = CFURLGetString(url);
+// printf("~1 got URL '%s'\n",CFStringGetCStringPtr(urlstr, kCFStringEncodingMacRoman));
+
+ urlstr = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
+ bufSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(urlstr),
+ kCFStringEncodingUTF8) + 1;
+ if ((dpath = malloc(bufSize)) == NULL) {
+ debugrr("cur_profile malloc failed\n");
+ CFRelease(dict);
+ CFRelease(dispuuid);
+ return NULL;
+ }
+ if (!CFStringGetCString(urlstr, dpath, bufSize, kCFStringEncodingUTF8)) {
+ debugrr("cur_profile CFStringGetCString failed\n");
+ CFRelease(dict);
+ CFRelease(dispuuid);
+ return NULL;
+ }
+ printf("~1 got path '%s'\n",dpath);
+
+ CFRelease(dict);
+ CFRelease(dispuuid);
+
+// return dpath;
+ return NULL;
+#endif
+