Home | History | Annotate | Download | only in helper
      1 /*
      2  * Copyright (C) 2010 Google Inc. All rights reserved.
      3  * Copyright (C) 2012 Apple Inc.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  *     * Redistributions of source code must retain the above copyright
     10  * notice, this list of conditions and the following disclaimer.
     11  *     * Redistributions in binary form must reproduce the above
     12  * copyright notice, this list of conditions and the following disclaimer
     13  * in the documentation and/or other materials provided with the
     14  * distribution.
     15  *     * Neither the name of Google Inc. nor the names of its
     16  * contributors may be used to endorse or promote products derived from
     17  * this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #import <AvailabilityMacros.h>
     33 #import <AppKit/AppKit.h>
     34 #include <signal.h>
     35 #include <stdio.h>
     36 #include <stdlib.h>
     37 
     38 // This is a simple helper app that changes the color sync profile to the
     39 // generic profile and back when done.  This program is managed by the layout
     40 // test script, so it can do the job for multiple DumpRenderTree while they are
     41 // running layout tests.
     42 
     43 namespace {
     44 
     45 #if defined(MAC_OS_X_VERSION_10_7) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
     46 
     47 CFURLRef sUserColorProfileURL;
     48 
     49 void installLayoutTestColorProfile()
     50 {
     51     // To make sure we get consistent colors (not dependent on the chosen color
     52     // space of the main display), we force the generic RGB color profile.
     53     // This causes a change the user can see.
     54 
     55     CFUUIDRef mainDisplayID = CGDisplayCreateUUIDFromDisplayID(CGMainDisplayID());
     56 
     57     if (!sUserColorProfileURL) {
     58         CFDictionaryRef deviceInfo = ColorSyncDeviceCopyDeviceInfo(kColorSyncDisplayDeviceClass, mainDisplayID);
     59 
     60         if (!deviceInfo) {
     61             NSLog(@"No display attached to system; not setting main display's color profile.");
     62             CFRelease(mainDisplayID);
     63             return;
     64         }
     65 
     66         CFDictionaryRef profileInfo = (CFDictionaryRef)CFDictionaryGetValue(deviceInfo, kColorSyncCustomProfiles);
     67         if (profileInfo) {
     68             sUserColorProfileURL = (CFURLRef)CFDictionaryGetValue(profileInfo, CFSTR("1"));
     69             CFRetain(sUserColorProfileURL);
     70         } else {
     71             profileInfo = (CFDictionaryRef)CFDictionaryGetValue(deviceInfo, kColorSyncFactoryProfiles);
     72             CFDictionaryRef factoryProfile = (CFDictionaryRef)CFDictionaryGetValue(profileInfo, CFSTR("1"));
     73             sUserColorProfileURL = (CFURLRef)CFDictionaryGetValue(factoryProfile, kColorSyncDeviceProfileURL);
     74             CFRetain(sUserColorProfileURL);
     75         }
     76 
     77         CFRelease(deviceInfo);
     78     }
     79 
     80     ColorSyncProfileRef genericRGBProfile = ColorSyncProfileCreateWithName(kColorSyncGenericRGBProfile);
     81     CFErrorRef error;
     82     CFURLRef profileURL = ColorSyncProfileGetURL(genericRGBProfile, &error);
     83     if (!profileURL) {
     84         NSLog(@"Failed to get URL of Generic RGB color profile! Many pixel tests may fail as a result. Error: %@", error);
     85 
     86         if (sUserColorProfileURL) {
     87             CFRelease(sUserColorProfileURL);
     88             sUserColorProfileURL = 0;
     89         }
     90 
     91         CFRelease(genericRGBProfile);
     92         CFRelease(mainDisplayID);
     93         return;
     94     }
     95 
     96     CFMutableDictionaryRef profileInfo = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
     97     CFDictionarySetValue(profileInfo, kColorSyncDeviceDefaultProfileID, profileURL);
     98 
     99     if (!ColorSyncDeviceSetCustomProfiles(kColorSyncDisplayDeviceClass, mainDisplayID, profileInfo)) {
    100         NSLog(@"Failed to set color profile for main display! Many pixel tests may fail as a result.");
    101 
    102         if (sUserColorProfileURL) {
    103             CFRelease(sUserColorProfileURL);
    104             sUserColorProfileURL = 0;
    105         }
    106     }
    107 
    108     CFRelease(profileInfo);
    109     CFRelease(genericRGBProfile);
    110     CFRelease(mainDisplayID);
    111 }
    112 
    113 void restoreUserColorProfile(void)
    114 {
    115     // This is used as a signal handler, and thus the calls into ColorSync are unsafe.
    116     // But we might as well try to restore the user's color profile, we're going down anyway...
    117 
    118     if (!sUserColorProfileURL)
    119         return;
    120 
    121     CFUUIDRef mainDisplayID = CGDisplayCreateUUIDFromDisplayID(CGMainDisplayID());
    122     CFMutableDictionaryRef profileInfo = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    123     CFDictionarySetValue(profileInfo, kColorSyncDeviceDefaultProfileID, sUserColorProfileURL);
    124     ColorSyncDeviceSetCustomProfiles(kColorSyncDisplayDeviceClass, mainDisplayID, profileInfo);
    125     CFRelease(mainDisplayID);
    126     CFRelease(profileInfo);
    127 }
    128 
    129 #else // For Snow Leopard and before, use older CM* API.
    130 
    131 const char colorProfilePath[] = "/System/Library/ColorSync/Profiles/Generic RGB Profile.icc";
    132 
    133 CMProfileLocation initialColorProfileLocation; // The locType field is initialized to 0 which is the same as cmNoProfileBase.
    134 
    135 void installLayoutTestColorProfile()
    136 {
    137     // To make sure we get consistent colors (not dependent on the Main display),
    138     // we force the generic rgb color profile.  This cases a change the user can
    139     // see.
    140     const CMDeviceScope scope = { kCFPreferencesCurrentUser, kCFPreferencesCurrentHost };
    141 
    142     CMProfileRef profile = 0;
    143     int error = CMGetProfileByAVID((CMDisplayIDType)kCGDirectMainDisplay, &profile);
    144     if (!error) {
    145         UInt32 size = sizeof(initialColorProfileLocation);
    146         error = NCMGetProfileLocation(profile, &initialColorProfileLocation, &size);
    147         CMCloseProfile(profile);
    148     }
    149     if (error) {
    150         NSLog(@"failed to get the current color profile, pixmaps won't match. Error: %d", (int)error);
    151         initialColorProfileLocation.locType = cmNoProfileBase;
    152         return;
    153     }
    154 
    155     CMProfileLocation location;
    156     location.locType = cmPathBasedProfile;
    157     strncpy(location.u.pathLoc.path, colorProfilePath, sizeof(location.u.pathLoc.path));
    158     error = CMSetDeviceProfile(cmDisplayDeviceClass, (CMDeviceID)kCGDirectMainDisplay, &scope, cmDefaultProfileID, &location);
    159     if (error) {
    160         NSLog(@"failed install the generic color profile, pixmaps won't match. Error: %d", (int)error);
    161         initialColorProfileLocation.locType = cmNoProfileBase;
    162     }
    163 }
    164 
    165 void restoreUserColorProfile(void)
    166 {
    167     // This is used as a signal handler, and thus the calls into ColorSync are unsafe.
    168     // But we might as well try to restore the user's color profile, we're going down anyway...
    169     if (initialColorProfileLocation.locType != cmNoProfileBase) {
    170         const CMDeviceScope scope = { kCFPreferencesCurrentUser, kCFPreferencesCurrentHost };
    171         int error = CMSetDeviceProfile(cmDisplayDeviceClass, (CMDeviceID)kCGDirectMainDisplay, &scope, cmDefaultProfileID, &initialColorProfileLocation);
    172         if (error) {
    173             NSLog(@"Failed to restore color profile, use System Preferences -> Displays -> Color to reset. Error: %d", (int)error);
    174         }
    175         initialColorProfileLocation.locType = cmNoProfileBase;
    176     }
    177 }
    178 
    179 #endif
    180 
    181 void simpleSignalHandler(int sig)
    182 {
    183     // Try to restore the color profile and try to go down cleanly
    184     restoreUserColorProfile();
    185     exit(128 + sig);
    186 }
    187 
    188 } // namespace
    189 
    190 int main(int argc, char* argv[])
    191 {
    192     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    193 
    194     // Hooks the ways we might get told to clean up...
    195     signal(SIGINT, simpleSignalHandler);
    196     signal(SIGHUP, simpleSignalHandler);
    197     signal(SIGTERM, simpleSignalHandler);
    198 
    199     // Save off the current profile, and then install the layout test profile.
    200     installLayoutTestColorProfile();
    201 
    202     // Let the script know we're ready
    203     printf("ready\n");
    204     fflush(stdout);
    205 
    206     // Wait for any key (or signal)
    207     getchar();
    208 
    209     // Restore the profile
    210     restoreUserColorProfile();
    211 
    212     [pool release];
    213     return 0;
    214 }
    215