├── .gitignore ├── 1_2_basic_types.py ├── 1_3_lists_and_iteration.py ├── 1_4_dictionaries.py ├── 1_5_conditionals.py ├── 1_6_functions.py ├── 2_1_os_path.py ├── 2_2_subprocess.py ├── 2_3_plistlib.py ├── 2_4_system_information.py ├── 2_5_more_system_information.py ├── 3_1_Foundation.py ├── 3_2_CFPreferences.py ├── 3_3_1_Plists_again.py ├── 3_3_2_Foundation_and_plists.py ├── 4_1_python_script.py ├── 4_2_system_info.py ├── 4_3_0_machine_info.py ├── 4_3_1_machine_info.py ├── 4_3_2_machine_info.py ├── 4_3_3_machine_info.py ├── 4_3_4_machine_info.py ├── LICENSE.txt ├── README.md └── extras ├── 01_set_desktop_picture.py ├── 02_mirrortool.py └── FoundationPlist.py /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.pyc 3 | -------------------------------------------------------------------------------- /1_2_basic_types.py: -------------------------------------------------------------------------------- 1 | greeting = "Hello World" 2 | x = 12345 3 | print x 4 | 5 | y = x 6 | print y 7 | 8 | print greeting 9 | 10 | print x, y, greeting 11 | 12 | x = x + 3 13 | print x 14 | 15 | x = 1 16 | x += 2 17 | 18 | x 19 | 20 | print x 21 | 22 | print type(x) -------------------------------------------------------------------------------- /1_3_lists_and_iteration.py: -------------------------------------------------------------------------------- 1 | my_list = [1, 2, 3, 4, 56] 2 | my_list[0] 3 | 4 | my_list[-1] 5 | 6 | my_list[1:3] 7 | 8 | 9 | my_list = [1, 2, 3, 4, 56] 10 | for item in my_list: 11 | print item 12 | 13 | 14 | stuff = [4, "birds", 3.14, True] 15 | for thing in stuff: 16 | print thing, type(thing) -------------------------------------------------------------------------------- /1_4_dictionaries.py: -------------------------------------------------------------------------------- 1 | student_ids = {"Bob": 12334546, "Sally": 23666, "Jane": 45678899} 2 | student_ids["Jane"] 3 | 4 | for key in student_ids: 5 | print student_ids[key] 6 | 7 | for key in student_ids: 8 | print key, student_ids[key] 9 | 10 | 11 | prefs = {"UseColor": False, "NumberOfCopies": 2, "HeaderText": "Property of me"} 12 | print prefs["HeaderText"] -------------------------------------------------------------------------------- /1_5_conditionals.py: -------------------------------------------------------------------------------- 1 | my_grade = "A" 2 | if my_grade == "A": 3 | print "Party More" 4 | else: 5 | print "You partied enough" 6 | 7 | my_grade = "B" 8 | if my_grade == "A": 9 | print "Party More" 10 | elif my_grade == "B": 11 | print "You partied enough" 12 | else: 13 | print "You partied too much!" -------------------------------------------------------------------------------- /1_6_functions.py: -------------------------------------------------------------------------------- 1 | def question(question_number): 2 | if question_number == 1: 3 | return "What is your name?" 4 | elif question_number == 2: 5 | return "What is your quest?" 6 | elif question_number == 3: 7 | return "What is your favorite color?" 8 | else: 9 | return "Ahhhhhhh" 10 | 11 | print question(1) 12 | 13 | print question(3) 14 | 15 | 16 | def header(text, level): 17 | open_tag = "" 18 | close_tag = "" 19 | return open_tag + text + close_tag 20 | 21 | print header("My Great Title", 1) 22 | 23 | print header("My Subtitle", 2) 24 | 25 | 26 | def header(text, level=1): 27 | open_tag = "" 28 | close_tag = "" 29 | return open_tag + text + close_tag 30 | 31 | print header("My Great Title") 32 | 33 | -------------------------------------------------------------------------------- /2_1_os_path.py: -------------------------------------------------------------------------------- 1 | import os 2 | PATH = "/Library/Preferences/com.apple.SoftwareUpdate.plist" 3 | os.path.exists(PATH) 4 | 5 | os.path.isdir(PATH) 6 | 7 | os.path.dirname(PATH) 8 | 9 | os.path.basename(PATH) 10 | 11 | 12 | home = os.path.expanduser("~") 13 | print home 14 | 15 | prefs_dir = os.path.join(home, "Library/Preferences") 16 | print prefs_dir 17 | 18 | for filename in os.listdir(prefs_dir): 19 | print filename 20 | -------------------------------------------------------------------------------- /2_2_subprocess.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | cmd = ["/usr/bin/open", "http://disneyanimation.com"] 3 | subprocess.call(cmd) 4 | 5 | 6 | cmd = ['/usr/sbin/pkgutil', '--pkgs'] 7 | result = subprocess.call(cmd) 8 | print result 9 | 10 | 11 | cmd = ['/usr/sbin/pkgutil', '--pkgs'] 12 | proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 13 | (output, error_output) = proc.communicate() 14 | 15 | print "Output:", output 16 | 17 | print "Error output:", error_output 18 | 19 | print "Return code:", proc.returncode 20 | 21 | 22 | # don't run this one! 23 | subprocess.call(['/sbin/shutdown', '-r', 'now']) 24 | -------------------------------------------------------------------------------- /2_3_plistlib.py: -------------------------------------------------------------------------------- 1 | import plistlib 2 | filename = "/Applications/Safari.app/Contents/Info.plist" 3 | info = plistlib.readPlist(filename) 4 | info["CFBundleGetInfoString"] 5 | 6 | version = info["CFBundleShortVersionString"] 7 | print version 8 | 9 | print info["CFBundleURLTypes"] 10 | 11 | print info["CFBundleURLTypes"][0] 12 | 13 | print info["CFBundleURLTypes"][0]["CFBundleURLSchemes"] 14 | 15 | print info["CFBundleURLTypes"][0]["CFBundleURLSchemes"][0] 16 | 17 | 18 | filename = "/Library/Preferences/com.apple.loginwindow.plist" 19 | plistinfo = plistlib.readPlist(filename) 20 | -------------------------------------------------------------------------------- /2_4_system_information.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.platform 3 | 4 | 5 | import os 6 | os.uname() 7 | os.uname()[2] 8 | os.uname()[4] 9 | 10 | 11 | import platform 12 | platform.mac_ver() 13 | platform.mac_ver()[0] 14 | 15 | 16 | import subprocess 17 | cmd = ['/usr/bin/sw_vers', '-productVersion'] 18 | subprocess.check_output(cmd) 19 | 20 | subprocess.check_output(cmd).strip() 21 | -------------------------------------------------------------------------------- /2_5_more_system_information.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | cmd = ['/usr/sbin/system_profiler', 'SPHardwareDataType', '-xml'] 3 | proc = subprocess.call(cmd) 4 | 5 | 6 | proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 7 | (output, error) = proc.communicate() 8 | print output 9 | 10 | 11 | import plistlib 12 | info = plistlib.readPlistFromString(output) 13 | info 14 | 15 | 16 | hardware_info = info[0]['_items'][0] 17 | hardware_info.keys() 18 | 19 | 20 | hardware_info['cpu_type'] 21 | hardware_info['machine_model'] 22 | hardware_info['serial_number'] 23 | 24 | 25 | for key, value in hardware_info.items(): 26 | print str(key) + ": " + str(value) 27 | -------------------------------------------------------------------------------- /3_1_Foundation.py: -------------------------------------------------------------------------------- 1 | # https://developer.apple.com/library/mac/documentation/cocoa/reference/foundation/Miscellaneous/Foundation_Functions/Reference/reference.html 2 | 3 | import Foundation 4 | Foundation.NSUserName() 5 | Foundation.NSFullUserName() 6 | Foundation.NSHomeDirectory() -------------------------------------------------------------------------------- /3_2_CFPreferences.py: -------------------------------------------------------------------------------- 1 | # https://developer.apple.com/library/mac/documentation/CoreFoundation/Reference/CFPreferencesUtils/Reference/reference.html 2 | 3 | import CoreFoundation 4 | print CoreFoundation.CFPreferencesCopyAppValue("HomePage", "com.apple.Safari") 5 | 6 | new_home = "http://disneyanimation.com" 7 | CoreFoundation.CFPreferencesSetAppValue("HomePage", new_home, "com.apple.Safari") 8 | 9 | print CoreFoundation.CFPreferencesCopyAppValue("HomePage", "com.apple.Safari") 10 | 11 | 12 | my_policy = { 13 | "com.macromedia.Flash Player.plugin": { 14 | "PlugInDisallowPromptBeforeUseDialog": True, 15 | "PlugInFirstVisitPolicy": "PlugInPolicyAllowWithSecurityRestrictions", 16 | }, 17 | } 18 | 19 | CoreFoundation.CFPreferencesSetAppValue("ManagedPlugInPolicies", my_policy, "com.apple.Safari") -------------------------------------------------------------------------------- /3_3_1_Plists_again.py: -------------------------------------------------------------------------------- 1 | import plistlib 2 | filename = "/Library/Preferences/com.apple.loginwindow.plist" 3 | info = plistlib.readPlist(filename) 4 | 5 | -------------------------------------------------------------------------------- /3_3_2_Foundation_and_plists.py: -------------------------------------------------------------------------------- 1 | # https://developer.apple.com/library/mac/documentation/cocoa/conceptual/PropertyLists/Introduction/Introduction.html 2 | 3 | from Foundation import NSData 4 | from Foundation import NSPropertyListSerialization 5 | from Foundation import NSPropertyListMutableContainersAndLeaves 6 | 7 | filename = "/Library/Preferences/com.apple.loginwindow.plist" 8 | plist_data = NSData.dataWithContentsOfFile_(filename) 9 | (dataObject, plistFormat, error) = ( 10 | NSPropertyListSerialization.propertyListWithData_options_format_error_( 11 | plist_data, NSPropertyListMutableContainersAndLeaves, None, None)) 12 | 13 | print dataObject 14 | 15 | -------------------------------------------------------------------------------- /4_1_python_script.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | """Calls system_profiler and prints hardware info about 4 | the current machine""" 5 | 6 | import plistlib 7 | import subprocess 8 | 9 | cmd = ['/usr/sbin/system_profiler', 'SPHardwareDataType', '-xml'] 10 | output = subprocess.check_output(cmd) 11 | 12 | info = plistlib.readPlistFromString(output) 13 | 14 | hardware_info = info[0]['_items'][0] 15 | for key, value in hardware_info.items(): 16 | print str(key) + ": " + str(value) 17 | -------------------------------------------------------------------------------- /4_2_system_info.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | """Calls system_profiler and prints hardware info about 4 | the current machine""" 5 | 6 | import plistlib 7 | import subprocess 8 | 9 | def main(): 10 | cmd = ['/usr/sbin/system_profiler', 'SPHardwareDataType', '-xml'] 11 | output = subprocess.check_output(cmd) 12 | 13 | info = plistlib.readPlistFromString(output) 14 | 15 | hardware_info = info[0]['_items'][0] 16 | for key, value in hardware_info.items(): 17 | print str(key) + ": " + str(value) 18 | 19 | 20 | if __name__ == "__main__": 21 | main() -------------------------------------------------------------------------------- /4_3_0_machine_info.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | """Gets a bunch of info about the current machine""" 4 | 5 | import plistlib 6 | import subprocess 7 | 8 | 9 | def hardware_info(): 10 | '''Returns system_profiler hardware info as a dictionary''' 11 | cmd = ['/usr/sbin/system_profiler', 'SPHardwareDataType', '-xml'] 12 | output = subprocess.check_output(cmd) 13 | 14 | info = plistlib.readPlistFromString(output) 15 | 16 | hardware_info = info[0]['_items'][0] 17 | return hardware_info 18 | 19 | 20 | def main(): 21 | hw_info = hardware_info() 22 | processor = hw_info['cpu_type'] + ' ' + hw_info['current_processor_speed'] 23 | info = {} 24 | info['Host name'] = 'Not yet implemented' 25 | info['Serial number'] = hw_info['serial_number'] 26 | info['Machine model'] = hw_info['machine_model'] 27 | info['Processor'] = processor 28 | info['Memory'] = hw_info['physical_memory'] 29 | info['Users'] = 'Not yet implemented' 30 | info['Disk size'] = 'Not yet implemented' 31 | info['Disk free space'] = 'Not yet implemented' 32 | info['OS version'] = 'Not yet implemented' 33 | 34 | for key, value in info.items(): 35 | print '%s: %s' % (key, value) 36 | 37 | 38 | if __name__ == "__main__": 39 | main() -------------------------------------------------------------------------------- /4_3_1_machine_info.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | """Gets a bunch of info about the current machine""" 4 | 5 | import os 6 | import plistlib 7 | import subprocess 8 | 9 | 10 | def hardware_info(): 11 | '''Returns system_profiler hardware info as a dictionary''' 12 | cmd = ['/usr/sbin/system_profiler', 'SPHardwareDataType', '-xml'] 13 | output = subprocess.check_output(cmd) 14 | 15 | info = plistlib.readPlistFromString(output) 16 | 17 | hardware_info = info[0]['_items'][0] 18 | return hardware_info 19 | 20 | 21 | def main(): 22 | hw_info = hardware_info() 23 | processor = hw_info['cpu_type'] + ' ' + hw_info['current_processor_speed'] 24 | info = {} 25 | info['Host name'] = os.uname()[1] 26 | info['Serial number'] = hw_info['serial_number'] 27 | info['Machine model'] = hw_info['machine_model'] 28 | info['Processor'] = processor 29 | info['Memory'] = hw_info['physical_memory'] 30 | info['Users'] = 'Not yet implemented' 31 | info['Disk size'] = 'Not yet implemented' 32 | info['Disk free space'] = 'Not yet implemented' 33 | info['OS version'] = 'Not yet implemented' 34 | 35 | for key, value in info.items(): 36 | print '%s: %s' % (key, value) 37 | 38 | 39 | if __name__ == "__main__": 40 | main() -------------------------------------------------------------------------------- /4_3_2_machine_info.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | """Gets a bunch of info about the current machine""" 4 | 5 | import os 6 | import plistlib 7 | import subprocess 8 | 9 | 10 | def hardware_info(): 11 | '''Returns system_profiler hardware info as a dictionary''' 12 | cmd = ['/usr/sbin/system_profiler', 'SPHardwareDataType', '-xml'] 13 | output = subprocess.check_output(cmd) 14 | 15 | info = plistlib.readPlistFromString(output) 16 | 17 | hardware_info = info[0]['_items'][0] 18 | return hardware_info 19 | 20 | 21 | def list_users(): 22 | '''Returns home directories in /Users''' 23 | users_dir = os.listdir('/Users') 24 | home_dirs = [item for item in users_dir 25 | if not item.startswith('.') and item != 'Shared'] 26 | return home_dirs 27 | 28 | 29 | def main(): 30 | hw_info = hardware_info() 31 | processor = hw_info['cpu_type'] + ' ' + hw_info['current_processor_speed'] 32 | info = {} 33 | info['Host name'] = os.uname()[1] 34 | info['Serial number'] = hw_info['serial_number'] 35 | info['Machine model'] = hw_info['machine_model'] 36 | info['Processor'] = processor 37 | info['Memory'] = hw_info['physical_memory'] 38 | info['Users'] = list_users() 39 | info['Disk size'] = 'Not yet implemented' 40 | info['Disk free space'] = 'Not yet implemented' 41 | info['OS version'] = 'Not yet implemented' 42 | 43 | for key, value in info.items(): 44 | print '%s: %s' % (key, value) 45 | 46 | 47 | if __name__ == "__main__": 48 | main() -------------------------------------------------------------------------------- /4_3_3_machine_info.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | """Gets a bunch of info about the current machine""" 4 | 5 | import os 6 | import platform 7 | import plistlib 8 | import subprocess 9 | 10 | 11 | def hardware_info(): 12 | '''Returns system_profiler hardware info as a dictionary''' 13 | cmd = ['/usr/sbin/system_profiler', 'SPHardwareDataType', '-xml'] 14 | output = subprocess.check_output(cmd) 15 | 16 | info = plistlib.readPlistFromString(output) 17 | 18 | hardware_info = info[0]['_items'][0] 19 | return hardware_info 20 | 21 | 22 | def list_users(): 23 | '''Returns home directories in /Users''' 24 | users_dir = os.listdir('/Users') 25 | home_dirs = [item for item in users_dir 26 | if not item.startswith('.') and item != 'Shared'] 27 | return home_dirs 28 | 29 | 30 | def main(): 31 | hw_info = hardware_info() 32 | processor = hw_info['cpu_type'] + ' ' + hw_info['current_processor_speed'] 33 | info = {} 34 | info['Host name'] = os.uname()[1] 35 | info['Serial number'] = hw_info['serial_number'] 36 | info['Machine model'] = hw_info['machine_model'] 37 | info['Processor'] = processor 38 | info['Memory'] = hw_info['physical_memory'] 39 | info['Users'] = list_users() 40 | info['Disk size'] = 'Not yet implemented' 41 | info['Disk free space'] = 'Not yet implemented' 42 | info['OS version'] = platform.mac_ver()[0] 43 | 44 | for key, value in info.items(): 45 | print '%s: %s' % (key, value) 46 | 47 | 48 | if __name__ == "__main__": 49 | main() -------------------------------------------------------------------------------- /4_3_4_machine_info.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | """Gets a bunch of info about the current machine""" 4 | 5 | import os 6 | import platform 7 | import plistlib 8 | import subprocess 9 | 10 | 11 | def hardware_info(): 12 | '''Returns system_profiler hardware info as a dictionary''' 13 | cmd = ['/usr/sbin/system_profiler', 'SPHardwareDataType', '-xml'] 14 | output = subprocess.check_output(cmd) 15 | 16 | info = plistlib.readPlistFromString(output) 17 | 18 | hardware_info = info[0]['_items'][0] 19 | return hardware_info 20 | 21 | 22 | def list_users(): 23 | '''Returns home directories in /Users''' 24 | users_dir = os.listdir('/Users') 25 | home_dirs = [item for item in users_dir 26 | if not item.startswith('.') and item != 'Shared'] 27 | return home_dirs 28 | 29 | 30 | def disk_info(): 31 | '''Returns a tuple with size of startup disk and free space in bytes''' 32 | cmd = ['/usr/sbin/diskutil', 'info', '-plist', '/'] 33 | output = subprocess.check_output(cmd) 34 | 35 | info = plistlib.readPlistFromString(output) 36 | total_size = info['TotalSize'] 37 | free_space = info['FreeSpace'] 38 | return (total_size, free_space) 39 | 40 | 41 | def main(): 42 | hw_info = hardware_info() 43 | processor = hw_info['cpu_type'] + ' ' + hw_info['current_processor_speed'] 44 | (disk_size, free_space) = disk_info() 45 | info = {} 46 | info['Host name'] = os.uname()[1] 47 | info['Serial number'] = hw_info['serial_number'] 48 | info['Machine model'] = hw_info['machine_model'] 49 | info['Processor'] = processor 50 | info['Memory'] = hw_info['physical_memory'] 51 | info['Users'] = list_users() 52 | info['Disk size'] = disk_size 53 | info['Disk free space'] = free_space 54 | info['OS version'] = platform.mac_ver()[0] 55 | 56 | for key, value in info.items(): 57 | print '%s: %s' % (key, value) 58 | 59 | 60 | if __name__ == "__main__": 61 | main() -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright Disney Enterprises, Inc. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License 5 | and the following modification to it: Section 6 Trademarks. 6 | deleted and replaced with: 7 | 8 | 6. Trademarks. This License does not grant permission to use the 9 | trade names, trademarks, service marks, or product names of the 10 | Licensor and its affiliates, except as required for reproducing 11 | the content of the NOTICE file. 12 | 13 | You may obtain a copy of the License at 14 | http://www.apache.org/licenses/LICENSE-2.0 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repository contains code snippets and code examples for use with the PSU Mac Admins Conference 2014 workshop "Python for Systems Administrators" 2 | 3 | http://macadmins.psu.edu 4 | 5 | Download a zip archive [here](https://github.com/gregneagle/psumac2014_python/archive/master.zip). 6 | 7 | Or git clone the repo:
8 | `git clone https://github.com/gregneagle/psumac2014_python.git` 9 | 10 | We recommend you use a text editor designed for coding use during the workshop; TextWrangler is free. Download it [here](http://www.barebones.com/products/textwrangler/download.html). 11 | 12 | Other text editors that work well for this are [BBEdit](http://www.barebones.com/products/bbedit/), [TextMate](http://macromates.com), and [Sublime Text](http://www.sublimetext.com). 13 | 14 | -------------------------------------------------------------------------------- /extras/01_set_desktop_picture.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | '''Uses Cocoa classes via PyObjC to set a random desktop picture on all screens. 4 | Tested on Mountain Lion and Mavericks. 5 | 6 | See: 7 | https://developer.apple.com/library/mac/documentation/cocoa/reference/applicationkit/classes/NSWorkspace_Class/Reference/Reference.html 8 | 9 | https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSURL_Class/Reference/Reference.html 10 | 11 | https://developer.apple.com/library/mac/documentation/cocoa/reference/applicationkit/classes/NSScreen_Class/Reference/Reference.html 12 | ''' 13 | import glob 14 | import random 15 | 16 | from AppKit import NSWorkspace, NSScreen 17 | from Foundation import NSURL 18 | 19 | pictures_glob = glob.glob("/Library/Desktop Pictures/*.jpg") 20 | picture_path = random.choice(pictures_glob) 21 | 22 | # generate a fileURL for the desktop picture 23 | file_url = NSURL.fileURLWithPath_(picture_path) 24 | 25 | # make image options dictionary 26 | # we just make an empty one because the defaults are fine 27 | options = {} 28 | 29 | # get shared workspace 30 | ws = NSWorkspace.sharedWorkspace() 31 | 32 | # iterate over all screens 33 | for screen in NSScreen.screens(): 34 | # tell the workspace to set the desktop picture 35 | (result, error) = ws.setDesktopImageURL_forScreen_options_error_( 36 | file_url, screen, options, None) -------------------------------------------------------------------------------- /extras/02_mirrortool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # encoding: utf-8 3 | """ 4 | mirrortool.py 5 | 6 | Created by Greg Neagle on 2013-06-05. 7 | 8 | A PyObjC port of Fabian Canas's command-line mirror tool from 9 | http://mirror-displays.googlecode.com/ 10 | 11 | See: 12 | 13 | https://developer.apple.com/library/mac/#documentation/graphicsimaging/reference/Quartz_Services_Ref/Reference/reference.html 14 | 15 | https://developer.apple.com/library/mac/#documentation/graphicsimaging/Conceptual/QuartzDisplayServicesConceptual/Introduction/Introduction.html#//apple_ref/doc/uid/TP40004316 16 | """ 17 | 18 | import sys 19 | import os 20 | 21 | import Quartz 22 | 23 | 24 | def main(): 25 | # default mode if none specified 26 | mode = 'query' 27 | 28 | # get our own name 29 | toolname = os.path.basename(sys.argv[0]) 30 | 31 | # get any mode that may have been passed at the command line 32 | if len(sys.argv) > 1: 33 | mode = sys.argv[1].lower() 34 | 35 | # make sure the mode is one we know about 36 | if not mode in ['toggle', 'on', 'off', 'query', 'help']: 37 | mode = 'help' 38 | 39 | if mode == 'help': 40 | print """Usage: %s [toggle|on|off|query|help] 41 | toggle: Toggle mirroring mode 42 | on: Turn mirroring on 43 | off: Turn mirroring off 44 | query: Return mirroring state to stdout (default) 45 | help: Print this message and exit.""" % toolname 46 | exit(0) 47 | 48 | # we currently know only how to handle two attached displays 49 | max_displays = 2 50 | 51 | # get active display list 52 | # CGGetActiveDisplayList: 53 | # Provides a list of displays that are active (or drawable). 54 | (err, active_displays, 55 | number_of_active_displays) = Quartz.CGGetActiveDisplayList( 56 | max_displays, None, None) 57 | if err: 58 | print >> sys.stderr, "Error in obtaining active display list: %s" % err 59 | exit(-1) 60 | 61 | # get online display list 62 | # CGGetOnlineDisplayList: 63 | # Provides a list of displays that are online 64 | # (active, mirrored, or sleeping). 65 | (err, online_displays, 66 | number_of_online_displays) = Quartz.CGGetOnlineDisplayList( 67 | max_displays, None, None) 68 | if err: 69 | print >> sys.stderr, "Error in obtaining online display list: %s" % err 70 | exit(-1) 71 | 72 | # make sure we have the right number of displays. 73 | # currently that number is exactly 2. 74 | if number_of_online_displays > 2: 75 | print >> sys.stderr, (("Cannot handle more than two displays. " 76 | "%s displays detected.") 77 | % number_of_online_displays) 78 | exit(-1) 79 | 80 | if number_of_online_displays < 2: 81 | print >> sys.stderr, (("Can't mirror fewer than two displays. " 82 | "%s displays detected.") 83 | % number_of_online_displays) 84 | exit(-1) 85 | 86 | if number_of_online_displays == 2: 87 | if online_displays[0] == Quartz.CGMainDisplayID(): 88 | secondary_display = online_displays[1] 89 | else: 90 | secondary_display = online_displays[0] 91 | 92 | if mode == 'query': 93 | if number_of_active_displays == 2: 94 | print 'Display mirroring is off' 95 | else: 96 | print 'Display mirroring is on' 97 | exit(0) 98 | 99 | # begin display configuration 100 | (err, config_ref) = Quartz.CGBeginDisplayConfiguration(None) 101 | if err: 102 | print >> sys.stderr, "Error with CGBeginDisplayConfiguration: %s" % err 103 | exit(-1) 104 | 105 | if mode == 'toggle': 106 | if number_of_active_displays == 2: 107 | # currently unmirrored 108 | err = Quartz.CGConfigureDisplayMirrorOfDisplay( 109 | config_ref, secondary_display, Quartz.CGMainDisplayID()) 110 | else: 111 | # currently mirrored 112 | err = Quartz.CGConfigureDisplayMirrorOfDisplay( 113 | config_ref, secondary_display, Quartz.kCGNullDirectDisplay) 114 | 115 | if mode == 'on': 116 | if number_of_active_displays == 2: 117 | # currently unmirrored 118 | err = Quartz.CGConfigureDisplayMirrorOfDisplay( 119 | config_ref, secondary_display, Quartz.CGMainDisplayID()) 120 | 121 | if mode == 'off': 122 | if number_of_active_displays != 2: 123 | # currently mirrored 124 | err = Quartz.CGConfigureDisplayMirrorOfDisplay( 125 | config_ref, secondary_display, Quartz.kCGNullDirectDisplay) 126 | 127 | if err: 128 | print >> sys.stderr, "Error configuring displays: %s" % err 129 | exit(-1) 130 | 131 | # apply the changes 132 | err = Quartz.CGCompleteDisplayConfiguration( 133 | config_ref, Quartz.kCGConfigurePermanently) 134 | if err: 135 | print >> sys.stderr, ("Error with CGCompleteDisplayConfiguration: %s" 136 | % err) 137 | exit(-1) 138 | 139 | 140 | if __name__ == '__main__': 141 | main() -------------------------------------------------------------------------------- /extras/FoundationPlist.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # encoding: utf-8 3 | # 4 | # Copyright 2009-2013 Greg Neagle. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | """FoundationPlist.py -- a tool to generate and parse MacOSX .plist files. 18 | 19 | This is intended as a drop-in replacement for Python's included plistlib, 20 | with a few caveats: 21 | - readPlist() and writePlist() operate only on a filepath, 22 | not a file object. 23 | - there is no support for the deprecated functions: 24 | readPlistFromResource() 25 | writePlistToResource() 26 | - there is no support for the deprecated Plist class. 27 | 28 | The Property List (.plist) file format is a simple XML pickle supporting 29 | basic object types, like dictionaries, lists, numbers and strings. 30 | Usually the top level object is a dictionary. 31 | 32 | To write out a plist file, use the writePlist(rootObject, filepath) 33 | function. 'rootObject' is the top level object, 'filepath' is a 34 | filename. 35 | 36 | To parse a plist from a file, use the readPlist(filepath) function, 37 | with a file name. It returns the top level object (again, usually a 38 | dictionary). 39 | 40 | To work with plist data in strings, you can use readPlistFromString() 41 | and writePlistToString(). 42 | 43 | This version works only with OS X 10.6+., as we are now using the newer 44 | dataWithPropertyList:format:options:error: and 45 | propertyListWithData:options:format:error: NSPropertyListSerialization 46 | class methods. 47 | """ 48 | 49 | from Foundation import NSData, \ 50 | NSPropertyListSerialization, \ 51 | NSPropertyListMutableContainersAndLeaves, \ 52 | NSPropertyListXMLFormat_v1_0 53 | 54 | class FoundationPlistException(Exception): 55 | '''Base error for this module''' 56 | pass 57 | 58 | class NSPropertyListSerializationException(FoundationPlistException): 59 | '''Read error for this module''' 60 | pass 61 | 62 | class NSPropertyListWriteException(FoundationPlistException): 63 | '''Write error for this module''' 64 | pass 65 | 66 | def readPlist(filepath): 67 | '''Read a .plist file from filepath. Return the unpacked root object 68 | (which is usually a dictionary).''' 69 | plistData = NSData.dataWithContentsOfFile_(filepath) 70 | (dataObject, plistFormat, error) = ( 71 | NSPropertyListSerialization.propertyListWithData_options_format_error_( 72 | plistData, NSPropertyListMutableContainersAndLeaves, None, None)) 73 | if error: 74 | errmsg = u"%s in file %s" % (error, filepath) 75 | raise NSPropertyListSerializationException(errmsg) 76 | else: 77 | return dataObject 78 | 79 | 80 | def readPlistFromString(data): 81 | '''Read a plist data from a string. Return the root object.''' 82 | plistData = buffer(data) 83 | (dataObject, plistFormat, error) = ( 84 | NSPropertyListSerialization.propertyListWithData_options_format_error_( 85 | plistData, NSPropertyListMutableContainersAndLeaves, None, None)) 86 | if error: 87 | raise NSPropertyListSerializationException(error) 88 | else: 89 | return dataObject 90 | 91 | 92 | def writePlist(dataObject, filepath): 93 | '''Write 'rootObject' as a plist to filepath.''' 94 | (plistData, error) = ( 95 | NSPropertyListSerialization.dataWithPropertyList_format_options_error_( 96 | dataObject, NSPropertyListXMLFormat_v1_0, 0, None)) 97 | if error: 98 | raise NSPropertyListSerializationException(error) 99 | else: 100 | if plistData.writeToFile_atomically_(filepath, True): 101 | return 102 | else: 103 | raise NSPropertyListWriteException( 104 | u"Failed to write plist data to %s" % filepath) 105 | 106 | 107 | def writePlistToString(rootObject): 108 | '''Return 'rootObject' as a plist-formatted string.''' 109 | (plistData, error) = ( 110 | NSPropertyListSerialization.dataWithPropertyList_format_options_error_( 111 | rootObject, NSPropertyListXMLFormat_v1_0, 0, None)) 112 | if error: 113 | raise NSPropertyListSerializationException(error) 114 | else: 115 | return str(plistData) 116 | --------------------------------------------------------------------------------