From 22f703cab05b7cd368f4de9e03991b7664dc5022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Mon, 1 Sep 2014 13:56:46 +0200 Subject: Initial import of argyll version 1.5.1-8 --- spectro/dispwin.c | 6321 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 6321 insertions(+) create mode 100644 spectro/dispwin.c (limited to 'spectro/dispwin.c') 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 +#include +#include +#include +#include +#include +#include +#ifndef NT +#include +#endif +#include +#include +#include +#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 + +#include + +# if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 +# include +# endif + +#ifndef CGFLOAT_DEFINED +#ifdef __LP64__ +typedef double CGFloat; +#else +typedef float CGFloat; +#endif /* defined(__LP64__) */ +#endif /* !CGFLOAT_DEFINED */ + +#include + +#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 + -- cgit v1.2.3