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 _removableCache = None
73 _removableCacheUpdateThread.start()
74 while _removableCache is None:
76 return _removableCache
79 global _removableCache
83 if platform.system() == "Windows":
84 from ctypes import windll
86 bitmask = windll.kernel32.GetLogicalDrives()
87 for letter in string.uppercase:
88 if letter != 'A' and letter != 'B' and bitmask & 1 and windll.kernel32.GetDriveTypeA(letter + ':/') == 2:
90 nameBuffer = ctypes.create_unicode_buffer(1024)
91 if windll.kernel32.GetVolumeInformationW(ctypes.c_wchar_p(letter + ':/'), nameBuffer, ctypes.sizeof(nameBuffer), None, None, None, None, 0) == 0:
92 volumeName = nameBuffer.value
94 volumeName = 'NO NAME'
96 freeBytes = ctypes.c_longlong(0)
97 if windll.kernel32.GetDiskFreeSpaceExA(letter + ':/', ctypes.byref(freeBytes), None, None) == 0:
99 if freeBytes.value < 1:
101 drives.append(('%s (%s:)' % (volumeName, letter), letter + ':/', volumeName))
103 elif platform.system() == "Darwin":
104 p = subprocess.Popen(['system_profiler', 'SPUSBDataType', '-xml'], stdout=subprocess.PIPE)
105 xml = ElementTree.fromstring(p.communicate()[0])
108 xml = _parseStupidPListXML(xml)
109 for dev in _findInTree(xml, 'Mass Storage Device'):
110 if 'removable_media' in dev and dev['removable_media'] == 'yes' and 'volumes' in dev and len(dev['volumes']) > 0:
111 for vol in dev['volumes']:
112 if 'mount_point' in vol:
113 volume = vol['mount_point']
114 drives.append((os.path.basename(volume), volume + '/', os.path.basename(volume)))
116 p = subprocess.Popen(['system_profiler', 'SPCardReaderDataType', '-xml'], stdout=subprocess.PIPE)
117 xml = ElementTree.fromstring(p.communicate()[0])
120 xml = _parseStupidPListXML(xml)
122 if '_items' in entry:
123 for item in entry['_items']:
124 for dev in item['_items']:
125 if 'removable_media' in dev and dev['removable_media'] == 'yes' and 'volumes' in dev and len(dev['volumes']) > 0:
126 for vol in dev['volumes']:
127 if 'mount_point' in vol:
128 volume = vol['mount_point']
129 drives.append((os.path.basename(volume), volume + '/', os.path.basename(volume)))
131 for volume in glob.glob('/media/*'):
132 if os.path.ismount(volume):
133 drives.append((os.path.basename(volume), volume + '/', os.path.basename(volume)))
134 elif volume == '/media/'+os.getenv('USER'):
135 for volume in glob.glob('/media/'+os.getenv('USER')+'/*'):
136 if os.path.ismount(volume):
137 drives.append((os.path.basename(volume), volume + '/', os.path.basename(volume)))
139 _removableCache = drives
142 def ejectDrive(driveName):
143 if platform.system() == "Windows":
144 cmd = [os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'EjectMedia.exe')), driveName]
145 elif platform.system() == "Darwin":
146 cmd = ["diskutil", "eject", driveName]
148 cmd = ["umount", driveName]
151 if subprocess.mswindows:
152 su = subprocess.STARTUPINFO()
153 su.dwFlags |= subprocess.STARTF_USESHOWWINDOW
154 su.wShowWindow = subprocess.SW_HIDE
155 kwargs['startupinfo'] = su
156 p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)
157 output = p.communicate()
166 if __name__ == '__main__':
167 print getPossibleSDcardDrives()