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