blob: 2432b8708f37b8c3811df515c8284c190b2f03ab [file] [log] [blame]
# Copyright 2017 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Variables will be pulled into globals() from the ColorSync framework, and will
# trigger undefined-variables.
# pylint: disable=undefined-variable
# pytype: disable=name-error
import sys
if sys.platform.startswith('darwin'):
# pylint: disable=import-error
# pytype: disable=import-error
import Foundation
import Quartz
import objc
# pytype: enable=import-error
# pylint: enable=import-error
# There is no module for the ColorSync framework, so synthesize one using
# bridge # support.
color_sync_framework = '/System/Library/Frameworks/ApplicationServices.' \
'framework/Versions/A/Frameworks/ColorSync.framework'
# This string is the output of running gen_bridge_metadata on the ColorSync
# headers.
color_sync_bridge_string = """<?xml version='1.0'?>
<signatures version='1.0'>
<constant name='kColorSyncDeviceDefaultProfileID' type='^{__CFString=}'/>
<constant name='kColorSyncDisplayDeviceClass' type='^{__CFString=}'/>
<constant name='kColorSyncProfileUserScope' type='^{__CFString=}'/>
<function name='CGDisplayCreateUUIDFromDisplayID'>
<arg type='I'/>
<retval already_retained='true' type='^{__CFUUID=}'/>
</function>
<function name='ColorSyncDeviceCopyDeviceInfo'>
<arg type='^{__CFString=}'/>
<arg type='^{__CFUUID=}'/>
<retval already_retained='true' type='^{__CFDictionary=}'/>
</function>
<function name='ColorSyncDeviceSetCustomProfiles'>
<arg type='^{__CFString=}'/>
<arg type='^{__CFUUID=}'/>
<arg type='^{__CFDictionary=}'/>
<retval type='B'/>
</function>
</signatures>"""
objc.parseBridgeSupport(color_sync_bridge_string, globals(),
color_sync_framework)
# Set |display_id| to use the color profile specified in |profile_url|. If
# |profile_url| is None, then use the factor default.
def SetDisplayCustomProfile(device_id: int, profile_url: str) -> None:
if profile_url is None:
profile_url = Foundation.kCFNull
profile_info = {
kColorSyncDeviceDefaultProfileID: profile_url,
kColorSyncProfileUserScope: Foundation.kCFPreferencesCurrentUser
}
result = ColorSyncDeviceSetCustomProfiles(kColorSyncDisplayDeviceClass,
device_id, profile_info)
if not result:
raise Exception('Failed to set display custom profile')
# Returns the URL for the system's sRGB color profile.
def GetSRGBProfileURL() -> str:
srgb_profile_path = '/System/Library/ColorSync/Profiles/sRGB Profile.icc'
srgb_profile_url = Foundation.CFURLCreateFromFileSystemRepresentation(
None, srgb_profile_path.encode('utf-8'), len(srgb_profile_path), False)
return srgb_profile_url
# Return a map from display ID to custom color profiles set on the display or
# None if no custom color profile is set.
def GetDisplaysToProfileURLMap() -> dict:
display_profile_url_map = {}
online_display_list_result = Quartz.CGGetOnlineDisplayList(32, None, None)
error = online_display_list_result[0]
if error != Quartz.kCGErrorSuccess:
raise Exception('Failed to get online displays from Quartz')
online_displays = online_display_list_result[1]
for display_id in online_displays:
device_info = ColorSyncDeviceCopyDeviceInfo(
kColorSyncDisplayDeviceClass,
CGDisplayCreateUUIDFromDisplayID(display_id))
if not device_info:
raise Exception('KVM connection on bot is broken, please file a bug')
device_id = device_info['DeviceID']
custom_profile_url = None
if 'CustomProfiles' in device_info and '1' in device_info['CustomProfiles']:
custom_profile_url = device_info['CustomProfiles']['1']
display_profile_url_map[device_id] = custom_profile_url
return display_profile_url_map