chiark / gitweb /
Look for CuraEngine in ..../CuraEngine/build/CuraEngine[.exe]
[cura.git] / Cura / util / removableStorage.py
1 """
2 This module handles detection and clean ejection of removable storage. (Mainly SD cards)
3 This is OS depended.
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/
7 """
8 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
9 import platform
10 import string
11 import glob
12 import os
13 import stat
14 import time
15 import subprocess
16 import threading
17 try:
18         from xml.etree import cElementTree as ElementTree
19 except:
20         from xml.etree import ElementTree
21
22 from Cura.util import profile
23
24 _removableCacheUpdateThread = None
25 _removableCache = []
26
27 def _parseStupidPListXML(e):
28         if e.tag == 'plist':
29                 return _parseStupidPListXML(list(e)[0])
30         if e.tag == 'array':
31                 ret = []
32                 for c in list(e):
33                         ret.append(_parseStupidPListXML(c))
34                 return ret
35         if e.tag == 'dict':
36                 ret = {}
37                 key = None
38                 for c in list(e):
39                         if c.tag == 'key':
40                                 key = c.text
41                         elif key is not None:
42                                 ret[key] = _parseStupidPListXML(c)
43                                 key = None
44                 return ret
45         if e.tag == 'true':
46                 return True
47         if e.tag == 'false':
48                 return False
49         return e.text
50
51 def _findInTree(t, n):
52         ret = []
53         if type(t) is dict:
54                 if '_name' in t and t['_name'] == n:
55                         ret.append(t)
56                 for k, v in t.items():
57                         ret += _findInTree(v, n)
58         if type(t) is list:
59                 for v in t:
60                         ret += _findInTree(v, n)
61         return ret
62
63 def getPossibleSDcardDrives():
64         global _removableCache, _removableCacheUpdateThread
65
66         if profile.getPreference('auto_detect_sd') == 'False':
67                 return []
68
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:
75                 time.sleep(0.01)
76         return _removableCache
77
78 def _updateCache():
79         global _removableCache
80
81         while True:
82                 drives = []
83                 if platform.system() == "Windows":
84                         from ctypes import windll
85                         import ctypes
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:
89                                         volumeName = ''
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
93                                         if volumeName == '':
94                                                 volumeName = 'NO NAME'
95
96                                         freeBytes = ctypes.c_longlong(0)
97                                         if windll.kernel32.GetDiskFreeSpaceExA(letter + ':/', ctypes.byref(freeBytes), None, None) == 0:
98                                                 continue
99                                         if freeBytes.value < 1:
100                                                 continue
101                                         drives.append(('%s (%s:)' % (volumeName, letter), letter + ':/', volumeName))
102                                 bitmask >>= 1
103                 elif platform.system() == "Darwin":
104                         p = subprocess.Popen(['system_profiler', 'SPUSBDataType', '-xml'], stdout=subprocess.PIPE)
105                         xml = ElementTree.fromstring(p.communicate()[0])
106                         p.wait()
107
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)))
115
116                         p = subprocess.Popen(['system_profiler', 'SPCardReaderDataType', '-xml'], stdout=subprocess.PIPE)
117                         xml = ElementTree.fromstring(p.communicate()[0])
118                         p.wait()
119
120                         xml = _parseStupidPListXML(xml)
121                         for entry in 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)))
130                 else:
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)))
138
139                 _removableCache = drives
140                 time.sleep(1)
141
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]
147         else:
148                 cmd = ["umount", driveName]
149
150         kwargs = {}
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()
158
159         if p.wait():
160                 print output[0]
161                 print output[1]
162                 return False
163         else:
164                 return True
165
166 if __name__ == '__main__':
167         print getPossibleSDcardDrives()