root/wine-doors/trunk/src/wine.py

Revision 1585, 27.2 kB (checked in by astormont, 5 days ago)

Fixed wine process viewer again, now works on fedora 10, probably broken on solaris

Line 
1 import os, string, re, time, sys
2 import urllib, tempfile
3
4 # CountInstances
5 import commands
6
7 # WaitForExit
8 import select, thread
9
10 # WineDoors global instances
11 from preferences import preferences
12 from log import log
13 from const import *
14 from utils import GetCDMountPoint, getwinecoloursfromgtk
15 import urllib
16
17 class Wine:
18
19     [ WIN32S_PLATFORM, WIN32_WINDOWS_PLATFORM, WIN32_NT_PLATFORM ] = range( 3 )
20
21     def __init__( self ):
22         self.gui = True
23
24     def ListProcesses( self ):
25         """ Returns a list of all running wine processes """
26         output = []
27         for proc in os.popen( r"pgrep -fl '\.exe'" ).read().split("\n"):
28             proc_split = proc.split(" ")
29             try:
30                 if proc_split[0] == "":
31                     continue
32                 if proc_split[1] == "mono":
33                     continue
34                 output.append( ( proc_split[0], proc_split[1] ) )
35             except:
36                 continue
37         return output
38
39     def SetColoursFromGtk( self ):
40         for name, data in getwinecoloursfromgtk().iteritems():
41             self.setRegistry( "HKCU\\Control Panel\\Colors\\%s" % name, data )
42
43     def SetDefaultColours( self ):
44         """ Removes custom colours from WINE registry """
45         self.unsetRegistry( "HKCU\\Control Panel\\Colors", "Background" )
46         self.unsetRegistry( "HKCU\\Control Panel\\Colors", "ButtonDkShadow" )
47         self.unsetRegistry( "HKCU\\Control Panel\\Colors", "ButtonFace" )
48         self.unsetRegistry( "HKCU\\Control Panel\\Colors", "ButtonHilight" )
49         self.unsetRegistry( "HKCU\\Control Panel\\Colors", "ButtonLight" )
50         self.unsetRegistry( "HKCU\\Control Panel\\Colors", "ButtonShadow" )
51         self.unsetRegistry( "HKCU\\Control Panel\\Colors", "ButtonText" )
52         self.unsetRegistry( "HKCU\\Control Panel\\Colors", "GrayText" )
53         self.unsetRegistry( "HKCU\\Control Panel\\Colors", "Hilight" )
54         self.unsetRegistry( "HKCU\\Control Panel\\Colors", "HilightText" )
55         self.unsetRegistry( "HKCU\\Control Panel\\Colors", "Window" )
56         self.unsetRegistry( "HKCU\\Control Panel\\Colors", "WindowText" )
57
58     def WineVersion( self ):
59         """ Returns wine version """
60         path = os.popen(preferences['winebinary'] + " --version" ).readlines()
61         for line in path:
62             if line.find("pulseaudio") == -1:
63                 return line.rstrip().split("-")[1]
64  
65     def GetAudioSystem( self ):
66         """ Returns backend currently set in reg """
67         return self.getRegistry( "HKCU\\\\Software\\\\Wine\\\\Drivers", "Audio" )
68
69     def GetAudioSystems( self ):
70         """ Returns a list of supported audio systems """
71         audio_sys = []
72         if os.path.isfile( "/usr/lib/wine/winealsa.drv.so" ):
73             audio_sys.append( "alsa" )
74         if os.path.isfile( "/usr/lib/wine/winejack.drv.so" ):
75             audio_sys.append( "jack" )
76         if os.path.isfile( "/usr/lib/wine/wineoss.drv.so" ):
77             audio_sys.append( "oss" )
78         if os.path.isfile( "/usr/lib/wine/wineesd.drv.so" ):
79             audio_sys.append( "esd" )
80         return audio_sys
81
82     def SetAudioSystem( self, backend ):
83         if backend:
84             self.setRegistry( "HKCU\\Software\\Wine\\Drivers\\Audio", backend )
85             log.Debug( "Audio backend changed to : " + backend )
86         else:
87             self.SetAudioSystem( "alsa" )
88             log.Debug( "No audio backend selected in winereg, alsa chosen as safe default." )
89
90     def GetWindowsVersion( self ):
91         win32_windows = self.getRegistry( "HKLM\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion", "VersionNumber" )
92         if win32_windows != "":
93             output = [ win32_windows,
94                        "0",
95                        self.WIN32_WINDOWS_PLATFORM,
96                        self.getRegistry( "HKLM\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion", "SubVersionNumber" ) ]
97         else:
98             output = [ self.getRegistry( "HKLM\\\\Software\\\\Microsoft\\\\Windows NT\\\\CurrentVersion", "CurrentVersion" ),
99                        self.getRegistry( "HKLM\\\\Software\\\\Microsoft\\\\Windows NT\\\\CurrentVersion", "CurrentBuildNumber" ),
100                        self.WIN32_NT_PLATFORM,
101                        self.getRegistry( "HKLM\\\\Software\\\\Microsoft\\\\Windows NT\\\\CurrentVersion", "CSDVersion" ) ]
102
103         for win_version in self.GetWindowsVersions():
104             if [ win_version[1], win_version[2], win_version[3], win_version[4] ] == output:
105                 return [ win_version[0] ] + output
106
107     def SetWindowsVersion( self, version_list ):
108         ( displayname, version, build, platform, service_pack ) = version_list
109         # If we are switching from NT to 9X (or vice versa), we need to remove some keys
110         old_platform = self.GetWindowsVersion()[3]
111         if platform == self.WIN32_WINDOWS_PLATFORM:
112             if old_platform == self.WIN32_NT_PLATFORM:
113                 self.unsetRegistry( "HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion", "CurrentVersion" )
114                 self.unsetRegistry( "HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion", "CSDVersion" )
115                 self.unsetRegistry( "HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion", "CurrentBuildNumber" )
116             self.setRegistry( "HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\VersionNumber", version )
117             self.setRegistry( "HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\SubVersionNumber", service_pack )
118         elif platform == self.WIN32_NT_PLATFORM:
119             if old_platform == self.WIN32_WINDOWS_PLATFORM:
120                 self.unsetRegistry( "HKLM\\Software\\Microsoft\\Windows\\CurrentVersion", "VersionNumber" )
121                 self.unsetRegistry( "HKLM\\Software\\Microsoft\\Windows\\CurrentVersion", "SubVersionNumber" )
122             self.setRegistry( "HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion\\CurrentVersion", version )
123             self.setRegistry( "HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion\\CSDVersion", service_pack )
124             self.setRegistry( "HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion\\CurrentBuildNumber", build )
125         else:
126             return False
127
128     def GetWindowsVersions( self ):
129         """ Returns list of windows names and versions """
130         #          Display Name,  CurrentVersion, Build,  Platform,                    Service Pack
131         return [ [ "Windows 2008",   "6.0",       "6001", self.WIN32_NT_PLATFORM,      "Service Pack 1" ],
132                  [ "Windows Vista",  "6.0",       "6000", self.WIN32_NT_PLATFORM,      " " ],
133                  [ "Windows 2003",   "5.2",       "3790",  self.WIN32_NT_PLATFORM,      "Service Pack 1" ],
134                  [ "Windows XP",     "5.1",       "2600",  self.WIN32_NT_PLATFORM,      "Service Pack 2" ],
135                  [ "Windows 2000",   "5.0",       "2195",  self.WIN32_NT_PLATFORM,      "Service Pack 4" ],
136                  [ "Windows ME",     "4.90.3000", "0",      self.WIN32_WINDOWS_PLATFORM, " " ],
137                  [ "Windows 98",     "4.10.2222", "0",      self.WIN32_WINDOWS_PLATFORM, " A " ],
138                  [ "Windows 95",     "4.0.950",   "0",      self.WIN32_WINDOWS_PLATFORM, "" ],
139                  [ "Windows NT 4.0", "4.0",       "1381",  self.WIN32_NT_PLATFORM,      "Service Pack 6a" ],
140                  [ "Windows NT 3.5", "3.51",      "1057",  self.WIN32_NT_PLATFORM,      "Service Pack 2" ],
141                  #[ "Windows 3.1",    "win31",     0,      self.WIN32S_PLATFORM,        "Win32s 1.3" ],
142                  #[ "Windows 3.0",    "win30",     0,      self.WIN32S_PLATFORM,        "Win32s 1.3" ],
143                  #[ "Windows 2.0",    "win20",     0,      self.WIN32S_PLATFORM,        "Win32s 1.3" ]
144                ]
145
146     def SetRegOwner( self, name ):
147         self.setRegistry("HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\RegisteredOwner", name )
148         self.setRegistry("HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion\\RegisteredOwner", name )
149
150     def SetRegCompany( self, name ):
151         self.setRegistry("HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\RegisteredOrganization", name )
152         self.setRegistry("HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion\\RegisteredOrganization", name )
153
154     def CountInstances( self, process_name="wine" ):
155         log.Debug( "Counting instances of " + process_name )
156
157         pgrep = commands.getoutput( "pgrep -f %s" % process_name )
158         processes = 0
159         for item in pgrep.split("\n"):
160             if not item.find( "python" ):
161                 processes += 1
162
163         log.Debug( "Found %i %s processes active" % ( processes, process_name ) )
164
165         return processes
166    
167     def CheckDrive( self, drivepath=None ):
168         self.drive = False
169         if not drivepath:
170             drivepath = preferences['wineroot']
171         log.Log( "Checking wine drive: " + drivepath )
172         if not os.path.isdir( drivepath ):
173             log.Debug( "wine.py: CheckDrive: Didn't find folder " + drivepath )
174         else:
175             log.Debug( "wine.py: CheckDrive: Found folder " + drivepath )
176        
177         if not os.path.isdir( os.path.expanduser( "~/.wine-doors/" ) ):
178             log.Debug( "wine.py: CheckDrive: Didn't find folder ~/.wine-doors/" )
179         else:
180             log.Debug( "wine.py: CheckDrive: Found folder ~/.wine-doors/" )
181        
182         confpath = os.path.join( preferences['wineroot'], "wine-doors/configured" )
183        
184         if not os.path.isfile( confpath ):
185             log.Debug( "wine.py: CheckDrive: Didn't find file " + confpath)
186         else:
187             self.drive = True
188             log.Debug( "wine.py: CheckDrive: Found file " + confpath)
189
190         if not self.drive:
191             log.Log( "wine.py: CheckDrive: No wine-drive defined in specified wineroot" )
192            
193         return self.drive
194        
195     def CreateDrive( self ):
196         """
197         Create a bare bones wine drive, we apply registry hacks and create
198         the necessary files for this drive to behave like a wine-doors bottle.
199         """
200         if os.path.isdir( preferences['wineroot'] + "drive_c/" ):
201             log.Log( "wine.py: CreateDrive: Wine drive already exists." )
202             log.Debug( "         "+preferences['wineroot'] )
203         else:
204             log.Debug( "wine.py: CreateDrive: Wine Prefix Create "+preferences['wineroot'] )
205             os.system( "wineprefixcreate --prefix %s -w > /dev/null 2>&1" % preferences['wineroot'] )
206        
207         if not os.path.isdir( preferences['wineroot'] ):
208             log.Error( "wine.py: CreateDrive: Failed to create wine drive" )
209             return
210        
211         log.Debug( "wine.py: CreateDrive: Creating wine-doors paths" )
212        
213         if not os.path.exists( os.path.expanduser( "~/.wine-doors" ) ):
214             os.mkdir( os.path.expanduser( "~/.wine-doors" ) )
215         if not os.path.exists( os.path.expanduser( "~/.wine-doors/installer-cache" ) ):
216             os.mkdir( os.path.expanduser( "~/.wine-doors/installer-cache" ) )
217         if not os.path.exists( os.path.expanduser( "~/.wine-doors/packlists" ) ):
218             os.mkdir( os.path.expanduser( "~/.wine-doors/packlists" ) )
219         if not os.path.exists( os.path.expanduser( "~/.wine-doors/apppacks" ) ):
220             os.mkdir( os.path.expanduser( "~/.wine-doors/apppacks" ) )
221         if not os.path.exists( os.path.expanduser( "~/.wine-doors/icons" ) ):
222             os.mkdir( os.path.expanduser( "~/.wine-doors/icons" ) )
223         # This is a hack for the home drive path, sometimes Y is expected
224         if not os.path.exists(preferences['wineroot']+"dosdevices/y:"):
225             os.system("ln -s "+os.path.expanduser("~")+" \""+preferences['wineroot']+"/dosdevices/y:\"")
226         log.Debug( "wine.py: CreateDrive: Configuring drives" )
227         os.system( "winecfg -D" ) # autoconfigure drives
228         self.WaitForExit()
229
230         log.Debug( "wine.py: CreateDrive: Registry changes" )
231
232         self.SetRegOwner( preferences['name'] )
233         self.SetRegCompany( preferences['company'] )
234        
235         # Add any registry hacks that may be needed for normal operation
236         self.Execute("regedit", preferences['prefix'] + "registry/audio-alsa.reg")
237         self.WaitForExit()
238         log.Log("wine.py: CreateDrive: Finished")
239         if not os.path.isdir(preferences['wineroot']+"/drive_c/Program Files"):
240             os.system("ln -s \""+self.ConvertPath(self.getProgramFilesDir())+"\" \""+
241                       preferences['wineroot']+"/drive_c/Program Files\"")   
242         os.system("touch "+ os.path.expanduser(os.path.join(preferences['wineroot'], "wine-doors/configured") ) )
243        
244     def WaitForExit( self, timeout=300 ):
245         reader, writer = os.pipe()
246         def wait_for_wineserver(timeout):
247             done = False
248             log.Debug( "Waiting for wineservers to exit" )
249             for x in range( 1, timeout+1 ):
250                 if self.CountInstances( "wineserver" ) == 0:
251                     done = True
252                     break
253                 else:
254                     time.sleep( 1 )
255             if not done and self.CountInstances( "wineserver" ) > 0:
256                 log.Error( "Wineservers still running after %d seconds" % timeout )
257             else:
258                 os.write( writer, "done" )
259         thread.start_new_thread( wait_for_wineserver, ( timeout, ) )
260         readable, dummy, dummy = select.select( ( reader, ), (), (), timeout )
261         if readable:
262             return True
263         else:
264             return False
265    
266     def Kill( self, appname, pid=None ):
267         """ Takes app name and optional pid as arguments """
268         log.Debug( "Killing %s" )
269         if pid:
270             os.system( "/usr/bin/kill -9 %s" % pid )
271         else:
272             os.system( "/usr/bin/killall -9 %s" % appname )
273
274     def KillAll( self ):
275         log.Debug( "Killing wine" )
276         for pname in ( "wine", "wine-pthread", "wineserver", "winedbg" ):
277             while self.CountInstances( pname ):
278                 self.Kill( pname )
279         self.Reboot()
280    
281     def Reboot( self ):
282         log.Debug( "Rebooting wine" )
283         output = file(os.path.expanduser( "~/.wine.stout.log" ), "wt")
284         errors = file(os.path.expanduser( "~/.wine.stout.log" ), "wt")
285         os.popen( preferences['winebinary'] + " wineboot -r >> ~/.wine.stdout.log 2>>~/.wine.stderr.log")
286        
287     def isShellScript( self, script ):
288         if script.endswith( ".sh" ):
289             return True
290         else:
291             return False
292            
293         filetype = os.popen( "head -n 1 "+script ).read()
294         if re.search ("^#!/", filetype):
295             return True
296         return False
297        
298     def ConvertPath( self, path ):
299         """
300         Convert a path from wine notation to linux absolute path
301         """
302         path = os.popen("winepath \""+ path +"\"" ).readlines()
303         for line in path:
304             if line.find("pulseaudio") == -1:
305                 return line.rstrip()
306    
307     def Execute( self, executable, argstring=None ):
308         """
309         Execute the installer, this will run regedit, msiexec, sh or wine
310         depending on the type of file that's to be executed
311         """
312         ahk = False
313         run = None
314         oldcwd = False
315         filefound = False
316
317         if not argstring:
318             argstring = ""
319         else:
320             argstring = " "+argstring
321
322         # hard coded list of things to "just let through"
323         allowed = [ "regedit", "winver", "msiexec" ]
324
325         # fix file:// URI's
326         executable = urllib.unquote( executable ).replace( "file://", "", 1 )
327         exec_orig = executable
328
329         # Avoid locking the CD drive, when ?:\\ is passed
330         if re.search("^.:\\\\", executable):
331             executable = self.ConvertPath( executable )
332             oldcwd = os.getcwd()
333             os.chdir( os.path.expanduser( "~/" ) )
334
335         # Various tests to locate the file, if we still fail return with an error
336         if not os.path.isfile(executable) and not executable.lower().split(" ")[0] in allowed:
337             log.Warn("Can't find executable %s, Assuming CD drive path" % executable)
338             cd = GetCDMountPoint()
339             exec_orig = re.sub("\\\\\\\\", "/", exec_orig)
340             exec_orig = re.sub("\\\\", "/", exec_orig)
341             for thiscd in cd:
342                 log.Warn(thiscd)
343                 executable = re.sub("^.*:", str(thiscd), exec_orig)
344                 if os.path.isfile(executable):
345                     filefound = True
346                     break
347             if not filefound:
348                 log.Warn("Executable file not found: " + executable)
349                 return FILE_NOT_FOUND
350
351         # Determine the executable, and construct an executable command               
352         if executable == "winver":
353             executable = "C:\\windows\\system32\\winver.exe"
354         elif executable.lower().split(" ")[0] in allowed:
355             run = executable
356         elif re.search("\.msi", executable):
357             run = "msiexec /i " + executable
358         elif re.search(".ahk", executable):
359             run = preferences['winebinary'] + " \"" + \
360                   self.getProgramFilesDir() + \
361                   "\\AutoHotKey\\AutoHotKey.exe\" " + executable
362             ahk = True
363         elif self.isShellScript( executable ):
364             run = "/bin/sh " + executable
365             ahk = True
366
367         if not run:
368             run = "%s \"%s\"" % (preferences['winebinary'], executable)
369
370         # Set useful env vars to pass to scripts etc...
371         run = "PFWIN=\"" + self.getProgramFilesDir() + "\" " + \
372               "PFUNIX=\"" + self.ConvertPath(self.getProgramFilesDir()) + "\" " + \
373               "WDPREFIX=\"" + preferences['prefix'] + "\" " + \
374               "WINEPREFIX=\"" + preferences['wineroot'] + "\" " + \
375               "WINE=\"" + preferences['winebinary'] + "\" " + \
376               run + \
377               argstring + \
378               " >> ~/.wine.stdout.log 2>>~/.wine.stderr.log"
379
380         if ahk:
381             log.Debug ( "Setting the keyboard layout to \"us\" for AutoHotKey" )
382             os.system( "setxkbmap us" )
383
384         log.Debug( "Executing: " + run )
385         retval = os.system( run