├── .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 |
--------------------------------------------------------------------------------