2 This module handles detection and clean ejection of removable storage. (Mainly SD cards)
4 On windows it looks for removable storage drives.
5 On MacOS it specificly looks for SD cards.
6 On Linux it looks for anything mounted in /media/
8 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
18 from xml.etree import cElementTree as ElementTree
20 from xml.etree import ElementTree
22 from Cura.util import profile
24 _removableCacheUpdateThread = None
27 def _parseStupidPListXML(e):
29 return _parseStupidPListXML(list(e)[0])
33 ret.append(_parseStupidPListXML(c))
42 ret[key] = _parseStupidPListXML(c)
51 def _findInTree(t, n):
54 if '_name' in t and t['_name'] == n:
56 for k, v in t.items():
57 ret += _findInTree(v, n)
60 ret += _findInTree(v, n)
63 def getPossibleSDcardDrives():
64 global _removableCache, _removableCacheUpdateThread
66 if profile.getPreference('auto_detect_sd') == 'False':
69 if _removableCacheUpdateThread is None:
70 _removableCacheUpdateThread = threading.Thread(target=_updateCache)
71 _removableCacheUpdateThread.daemon = True
72 _removableCacheUpdateThread.start()
73 return _removableCache
76 global _removableCache
80 if platform.system() == "Windows":
81 from ctypes import windll
83 bitmask = windll.kernel32.GetLogicalDrives()
84 for letter in string.uppercase:
85 if letter != 'A' and letter != 'B' and bitmask & 1 and windll.kernel32.GetDriveTypeA(letter + ':/') == 2:
87 nameBuffer = ctypes.create_unicode_buffer(1024)
88 if windll.kernel32.GetVolumeInformationW(ctypes.c_wchar_p(letter + ':/'), nameBuffer, ctypes.sizeof(nameBuffer), None, None, None, None, 0) == 0:
89 volumeName = nameBuffer.value
91 volumeName = 'NO NAME'
93 freeBytes = ctypes.c_longlong(0)
94 if windll.kernel32.GetDiskFreeSpaceExA(letter + ':/', ctypes.byref(freeBytes), None, None) == 0:
96 if freeBytes.value < 1:
98 drives.append(('%s (%s:)' % (volumeName, letter), letter + ':/', volumeName))
100 elif platform.system() == "Darwin":
101 p = subprocess.Popen(['system_profiler', 'SPUSBDataType', '-xml'], stdout=subprocess.PIPE)
102 xml = ElementTree.fromstring(p.communicate()[0])
105 xml = _parseStupidPListXML(xml)
106 for dev in _findInTree(xml, 'Mass Storage Device'):
107 if 'removable_media' in dev and dev['removable_media'] == 'yes' and 'volumes' in dev and len(dev['volumes']) > 0:
108 for vol in dev['volumes']:
109 if 'mount_point' in vol:
110 volume = vol['mount_point']
111 drives.append((os.path.basename(volume), volume + '/', os.path.basename(volume)))
113 p = subprocess.Popen(['system_profiler', 'SPCardReaderDataType', '-xml'], stdout=subprocess.PIPE)
114 xml = ElementTree.fromstring(p.communicate()[0])
117 xml = _parseStupidPListXML(xml)
119 if '_items' in entry:
120 for item in entry['_items']:
121 for dev in item['_items']:
122 if 'removable_media' in dev and dev['removable_media'] == 'yes' and 'volumes' in dev and len(dev['volumes']) > 0:
123 for vol in dev['volumes']:
124 if 'mount_point' in vol:
125 volume = vol['mount_point']
126 drives.append((os.path.basename(volume), volume + '/', os.path.basename(volume)))
128 for volume in glob.glob('/media/*'):
129 if os.path.ismount(volume):
130 drives.append((os.path.basename(volume), volume + '/', os.path.basename(volume)))
131 elif volume == '/media/'+os.getenv('USER'):
132 for volume in glob.glob('/media/'+os.getenv('USER')+'/*'):
133 if os.path.ismount(volume):
134 drives.append((os.path.basename(volume), volume + '/', os.path.basename(volume)))
136 _removableCache = drives
139 def ejectDrive(driveName):
140 if platform.system() == "Windows":
141 cmd = [os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'EjectMedia.exe')), driveName]
142 elif platform.system() == "Darwin":
143 cmd = ["diskutil", "eject", driveName]
145 cmd = ["umount", driveName]
148 if subprocess.mswindows:
149 su = subprocess.STARTUPINFO()
150 su.dwFlags |= subprocess.STARTF_USESHOWWINDOW
151 su.wShowWindow = subprocess.SW_HIDE
152 kwargs['startupinfo'] = su
153 p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)
154 output = p.communicate()
163 if __name__ == '__main__':
164 print getPossibleSDcardDrives()