3 import commands
, sys
, os
, os
.path
, subprocess
, time
4 from GregDebug
import debug
, setDebugLevel
, DEBUG_LEVEL_DEBUG
, DEBUG_LEVEL_LOW
, DEBUG_LEVEL_MEDIUM
, DEBUG_LEVEL_HIGH
, DEBUG_INCREMENT
7 import python24_adapter
# NB: Must be imported before collections
10 """This is a cross platform/cross window manager way to change your current
13 __all__
= ('RandomBG')
15 def RandomBG(*args
, **kwargs
):
16 """Desktop Changer factory"""
20 debug("Testing for OSX (NonX11)", DEBUG_LEVEL_LOW
)
21 if commands
.getstatusoutput("ps ax -o command -c|grep -q WindowServer")[0] == 0:
22 ret
= __OSXChanger(*args
, **kwargs
)
24 if 'DISPLAY' not in os
.environ
or os
.environ
['DISPLAY'].startswith('/tmp/launch'):
28 if os
.uname()[0] == 'Darwin':
29 # Try to detect if the X11 server is running on OSX
30 if commands
.getstatusoutput("ps ax -o command|grep -q '/.*X11 .* %s'" % os
.environ
['DISPLAY'])[0] != 0:
31 # X11 is not running for this display
34 debug("Testing for KDE", DEBUG_LEVEL_LOW
)
35 if commands
.getstatusoutput("xwininfo -name 'KDE Desktop'")[0] == 0:
37 ret
.nextChanger
= __KDEChanger(*args
, **kwargs
)
39 ret
= __KDEChanger(*args
, **kwargs
)
41 debug("Testing for Gnome", DEBUG_LEVEL_LOW
)
42 if commands
.getstatusoutput("xwininfo -name 'gnome-session'")[0] == 0:
44 ret
.nextChanger
= __GnomeChanger(*args
, **kwargs
)
46 ret
= __GnomeChanger(*args
, **kwargs
)
48 debug("Testing for WMaker", DEBUG_LEVEL_LOW
)
49 if commands
.getstatusoutput("xlsclients | grep -qi wmaker")[0] == 0:
51 ret
.nextChanger
= __WMakerChanger(*args
, **kwargs
)
53 ret
= __WMakerChanger(*args
, **kwargs
)
56 raise Exception("Unknown window manager")
60 class __BaseChanger(object):
61 def __init__(self
, filelist
, backgroundColour
='black', permanent
=False):
62 debug('Determined the window manager is "%s"' % self
.__class
__.__name
__, DEBUG_LEVEL_MEDIUM
)
63 self
.backgroundColour
= backgroundColour
64 self
.permanent
= permanent
65 self
.filelist
= filelist
66 # Used to 'chain' background changers
67 self
.nextChanger
= None
69 def callChained(self
, filename
):
70 if self
.nextChanger
is None:
73 return self
.nextChanger
.changeTo(filename
)
76 file = self
.filelist
.getNextRandomImage()
77 return self
.changeTo(file) and self
.callChained(file)
80 file = self
.filelist
.getPrevRandomImage()
81 return self
.changeTo(file) and self
.callChained(file)
83 def cycleReload(self
):
85 file = self
.filelist
.getCurrentImage()
86 return self
.changeTo(file) and self
.callChained(file)
87 except FileLists
.FileListNotImplemented
:
88 return self
.cycleNext()
91 class __WMakerChanger(__BaseChanger
):
92 _ConvertedWallpaperLocation
= '/tmp/wallpapers_wmaker/'
93 def _removeOldImageCache(self
):
94 """Cleans up any old temp images"""
95 if not os
.path
.isdir(self
._ConvertedWallpaperLocation
):
96 os
.mkdir(self
._ConvertedWallpaperLocation
)
97 for fullpath
, filenames
, dirnames
in os
.walk(self
._ConvertedWallpaperLocation
, topdown
=False):
98 for filename
in filenames
:
99 os
.unlink(os
.path
.join(fullpath
, filename
))
100 for dirname
in dirnames
:
101 os
.unlink(os
.path
.join(fullpath
, dirname
))
103 def _convertImageFormat(self
, file):
104 """Convert the image to a png, and store it in a local place"""
105 self
._removeOldImageCache
()
106 output_name
= os
.path
.join(self
._ConvertedWallpaperLocation
, '%s.png' % time
.time())
107 cmd
= ["convert", '-resize', '1280', '-gravity', 'Center', '-crop', '1280x800+0+0', file, output_name
]
108 debug("""Convert command: '"%s"'""" % '" "'.join(cmd
), DEBUG_LEVEL_DEBUG
)
109 return output_name
, subprocess
.Popen(cmd
, stdout
=sys
.stdout
, stderr
=sys
.stderr
, stdin
=None).wait()
110 def changeTo(self
, file):
111 file, convert_status
= self
._convertImageFormat
(file)
113 debug('Convert failed')
115 "-b", self
.backgroundColour
, # Sets the background colour to be what the user specified
116 "-S", # 'Smooth' (WTF?)
117 "-e", # Center the image on the screen (only affects when the image in no the in the correct aspect ratio
118 ### "-a", # scale the image, keeping the aspect ratio
119 "-u", # Force this to be the default background
123 cmd
+= ["-u"] # update the wmaker database
125 debug('''WMaker bgset command: "'%s'"''' % "' '".join(cmd
), DEBUG_LEVEL_DEBUG
)
126 return not subprocess
.Popen(cmd
, stdout
=sys
.stdout
, stderr
=sys
.stderr
, stdin
=None).wait()
128 class __OSXChanger(__BaseChanger
):
129 _ConvertedWallpaperLocation
= '/tmp/wallpapers/'
130 _DesktopPlistLocation
= os
.path
.expanduser('~/Library/Preferences/com.apple.desktop.plist')
132 def _removeOldImageCache(self
):
133 """Cleans up any old temp images"""
134 if not os
.path
.isdir(self
._ConvertedWallpaperLocation
):
135 os
.mkdir(self
._ConvertedWallpaperLocation
)
136 for fullpath
, filenames
, dirnames
in os
.walk(self
._ConvertedWallpaperLocation
, topdown
=False):
137 for filename
in filenames
:
138 os
.unlink(os
.path
.join(fullpath
, filename
))
139 for dirname
in dirnames
:
140 os
.unlink(os
.path
.join(fullpath
, dirname
))
142 def _convertImageFormat(self
, file):
143 """Convert the image to a png, and store it in a local place"""
144 self
._removeOldImageCache
()
145 output_name
= os
.path
.join(self
._ConvertedWallpaperLocation
, '%s.png' % time
.time())
147 import PIL
, PIL
.Image
148 img
= PIL
.Image
.open(file)
149 img
.save(output_name
, "PNG")
150 return output_name
, True
152 debug('Could not load PIL, going to try just copying the image')
154 output_name
= os
.path
.join(self
._ConvertedWallpaperLocation
, os
.path
.basename(file))
155 shutil
.copyfile(file, output_name
)
156 return output_name
, True
158 def _fixDesktopPList(self
):
159 """Removes the entry in the desktop plist file that specifies the wallpaper for each monitor"""
162 desktopPList
= Foundation
.NSMutableDictionary
.dictionaryWithContentsOfFile_(self
._DesktopPlistLocation
)
163 # Remove all but the 'default' entry
164 for k
in desktopPList
['Background'].keys():
167 desktopPList
['Background'].removeObjectForKey_(k
)
168 # Store the plist again (Make sure we write it out atomically -- Don't want to break finder)
169 desktopPList
.writeToFile_atomically_(self
._DesktopPlistLocation
, True)
171 debug('Could not import the Foundation module, you may have problems with dual screens', DEBUG_LEVEL_MEDIUM
)
173 def changeTo(self
, file):
174 output_name
, ret
= self
._convertImageFormat
(file)
176 debug("Convert failed")
178 self
._fixDesktopPList
()
179 cmd
= """osascript -e 'tell application "finder" to set desktop picture to posix file "%s"'""" % output_name
180 debug(cmd
, DEBUG_LEVEL_DEBUG
)
181 return not commands
.getstatusoutput(cmd
)[0]
183 class __GnomeChanger(__BaseChanger
):
184 def changeTo(self
, file):
185 cmd
= ['gconftool-2', '--type', 'string', '--set', '/desktop/gnome/background/picture_filename', file]
186 debug(cmd
, DEBUG_LEVEL_DEBUG
)
187 return subprocess
.Popen(cmd
, stdout
=sys
.stdout
, stderr
=sys
.stderr
, stdin
=None).wait()
189 class __KDEChanger(__BaseChanger
):
190 def changeTo(self
, file):
192 for group
in ('Desktop0', 'Desktop0Screen0'):
193 base
= ['kwriteconfig', '--file', 'kdesktoprc', '--group', group
, '--key']
194 cmds
.append(base
+ ['Wallpaper', file])
195 cmds
.append(base
+ ['UseSHM', '--type', 'bool', 'true'])
196 cmds
.append(base
+ ['WallpaperMode', 'ScaleAndCrop'])
197 cmds
.append(base
+ ['MultiWallpaperMode', 'NoMulti'])
199 cmds
.append(['dcop', 'kdesktop', 'KBackgroundIface', 'configure'])
201 debug(cmd
, DEBUG_LEVEL_DEBUG
)
202 if subprocess
.Popen(cmd
, stdout
=sys
.stdout
, stderr
=sys
.stderr
, stdin
=None).wait() != 0: