chiark / gitweb /
Add option to disable SD auto detect. Log output if SD card remove fails.
[cura.git] / Cura / util / removableStorage.py
1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
2 import platform
3 import string
4 import glob
5 import os
6 import stat
7 import time
8 import subprocess
9 try:
10         from xml.etree import cElementTree as ElementTree
11 except:
12         from xml.etree import ElementTree
13
14 from Cura.util import profile
15
16 _removeableCache = None
17 _removeableCacheTime = None
18
19 def _parseStupidPListXML(e):
20         if e.tag == 'plist':
21                 return _parseStupidPListXML(list(e)[0])
22         if e.tag == 'array':
23                 ret = []
24                 for c in list(e):
25                         ret.append(_parseStupidPListXML(c))
26                 return ret
27         if e.tag == 'dict':
28                 ret = {}
29                 key = None
30                 for c in list(e):
31                         if c.tag == 'key':
32                                 key = c.text
33                         elif key is not None:
34                                 ret[key] = _parseStupidPListXML(c)
35                                 key = None
36                 return ret
37         if e.tag == 'true':
38                 return True
39         if e.tag == 'false':
40                 return False
41         return e.text
42
43 def _findInTree(t, n):
44         ret = []
45         if type(t) is dict:
46                 if '_name' in t and t['_name'] == n:
47                         ret.append(t)
48                 for k, v in t.items():
49                         ret += _findInTree(v, n)
50         if type(t) is list:
51                 for v in t:
52                         ret += _findInTree(v, n)
53         return ret
54
55 def getPossibleSDcardDrives():
56         global _removeableCache, _removeableCacheTime
57         if _removeableCache is not None and time.time() - _removeableCacheTime < 5.0:
58                 return _removeableCache
59
60         if profile.getPreference('auto_detect_sd') == 'False':
61                 return []
62
63         drives = []
64         if platform.system() == "Windows":
65                 from ctypes import windll
66                 import ctypes
67                 bitmask = windll.kernel32.GetLogicalDrives()
68                 for letter in string.uppercase:
69                         if bitmask & 1 and windll.kernel32.GetDriveTypeA(letter + ':/') == 2:
70                                 volumeName = ''
71                                 nameBuffer = ctypes.create_unicode_buffer(1024)
72                                 if windll.kernel32.GetVolumeInformationW(ctypes.c_wchar_p(letter + ':/'), nameBuffer, ctypes.sizeof(nameBuffer), None, None, None, None, 0) == 0:
73                                         volumeName = nameBuffer.value
74                                 if volumeName == '':
75                                         volumeName = 'NO NAME'
76
77                                 freeBytes = ctypes.c_longlong(0)
78                                 if windll.kernel32.GetDiskFreeSpaceExA(letter + ':/', ctypes.byref(freeBytes), None, None) == 0:
79                                         continue
80                                 if freeBytes.value < 1:
81                                         continue
82                                 drives.append(('%s (%s:)' % (volumeName, letter), letter + ':/', volumeName))
83                         bitmask >>= 1
84         elif platform.system() == "Darwin":
85                 p = subprocess.Popen(['system_profiler', 'SPUSBDataType', '-xml'], stdout=subprocess.PIPE)
86                 xml = ElementTree.fromstring(p.communicate()[0])
87                 p.wait()
88
89                 xml = _parseStupidPListXML(xml)
90                 for dev in _findInTree(xml, 'Mass Storage Device'):
91                         if 'removable_media' in dev and dev['removable_media'] == 'yes' and 'volumes' in dev and len(dev['volumes']) > 0:
92                                 for vol in dev['volumes']:
93                                         if 'mount_point' in vol:
94                                                 volume = vol['mount_point']
95                                                 drives.append((os.path.basename(volume), volume + '/', os.path.basename(volume)))
96
97                 p = subprocess.Popen(['system_profiler', 'SPCardReaderDataType', '-xml'], stdout=subprocess.PIPE)
98                 xml = ElementTree.fromstring(p.communicate()[0])
99                 p.wait()
100
101                 xml = _parseStupidPListXML(xml)
102                 for entry in xml:
103                         if '_items' in entry:
104                                 for item in entry['_items']:
105                                         for dev in item['_items']:
106                                                 if 'removable_media' in dev and dev['removable_media'] == 'yes' and 'volumes' in dev and len(dev['volumes']) > 0:
107                                                         for vol in dev['volumes']:
108                                                                 if 'mount_point' in vol:
109                                                                         volume = vol['mount_point']
110                                                                         drives.append((os.path.basename(volume), volume + '/', os.path.basename(volume)))
111         else:
112                 for volume in glob.glob('/media/*'):
113                         drives.append((os.path.basename(volume), volume + '/', os.path.basename(volume)))
114
115         _removeableCache = drives
116         _removeableCacheTime = time.time()
117         return drives
118
119 def ejectDrive(driveName):
120         if platform.system() == "Windows":
121                 cmd = [os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'EjectMedia.exe')), driveName]
122         elif platform.system() == "Darwin":
123                 cmd = ["diskutil", "eject", driveName]
124         else:
125                 cmd = ["umount", driveName]
126
127         kwargs = {}
128         if subprocess.mswindows:
129                 su = subprocess.STARTUPINFO()
130                 su.dwFlags |= subprocess.STARTF_USESHOWWINDOW
131                 su.wShowWindow = subprocess.SW_HIDE
132                 kwargs['startupinfo'] = su
133         p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)
134         output = p.communicate()
135
136         if p.wait():
137                 print output[0]
138                 print output[1]
139                 return False
140         else:
141                 return True
142
143 if __name__ == '__main__':
144         print getPossibleSDcardDrives()