]> code.delx.au - bg-scripts/blob - lib/WallChanger.py
d9f80dd8702d7cf58606511ec2fea99754786516
[bg-scripts] / lib / WallChanger.py
1 #! python
2
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
5 import FileLists
6
7 import python24_adapter # NB: Must be imported before collections
8 import collections
9
10 """This is a cross platform/cross window manager way to change your current
11 desktop image."""
12
13 __all__ = ('RandomBG')
14
15 def RandomBG(*args, **kwargs):
16 """Desktop Changer factory"""
17
18 ret = None
19
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)
23
24 if 'DISPLAY' not in os.environ or os.environ['DISPLAY'].startswith('/tmp/launch'):
25 # X11 is not running
26 return ret
27 else:
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
32 return ret
33
34 debug("Testing for KDE", DEBUG_LEVEL_LOW)
35 if commands.getstatusoutput("xwininfo -name 'KDE Desktop'")[0] == 0:
36 if ret is not None:
37 ret.nextChanger = __KDEChanger(*args, **kwargs)
38 else:
39 ret = __KDEChanger(*args, **kwargs)
40
41 debug("Testing for Gnome", DEBUG_LEVEL_LOW)
42 if commands.getstatusoutput("xwininfo -name 'gnome-session'")[0] == 0:
43 if ret is not None:
44 ret.nextChanger = __GnomeChanger(*args, **kwargs)
45 else:
46 ret = __GnomeChanger(*args, **kwargs)
47
48 debug("Testing for WMaker", DEBUG_LEVEL_LOW)
49 if commands.getstatusoutput("xlsclients | grep -qi wmaker")[0] == 0:
50 if ret is not None:
51 ret.nextChanger = __WMakerChanger(*args, **kwargs)
52 else:
53 ret = __WMakerChanger(*args, **kwargs)
54
55 if ret is None:
56 raise Exception("Unknown window manager")
57 else:
58 return ret
59
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
68
69 def callChained(self, filename):
70 if self.nextChanger is None:
71 return True
72 else:
73 return self.nextChanger.changeTo(filename)
74
75 def cycleNext(self):
76 file = self.filelist.getNextRandomImage()
77 return self.changeTo(file) and self.callChained(file)
78
79 def cyclePrev(self):
80 file = self.filelist.getPrevRandomImage()
81 return self.changeTo(file) and self.callChained(file)
82
83 def cycleReload(self):
84 try:
85 file = self.filelist.getCurrentImage()
86 return self.changeTo(file) and self.callChained(file)
87 except FileLists.FileListNotImplemented:
88 return self.cycleNext()
89
90
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))
102
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)
112 if convert_status:
113 debug('Convert failed')
114 cmd = ["wmsetbg",
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
120 "-d" # dither
121 ]
122 if self.permanent:
123 cmd += ["-u"] # update the wmaker database
124 cmd += [file]
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()
127
128 class __OSXChanger(__BaseChanger):
129 _ConvertedWallpaperLocation = '/tmp/wallpapers/'
130 _DesktopPlistLocation = os.path.expanduser('~/Library/Preferences/com.apple.desktop.plist')
131
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))
141
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())
146 cmd = ["convert", file, output_name]
147 debug("""Convert command: '"%s"'""" % '" "'.join(cmd), DEBUG_LEVEL_DEBUG)
148 return output_name, subprocess.Popen(cmd, stdout=sys.stdout, stderr=sys.stderr, stdin=None).wait()
149
150 def _fixDesktopPList(self):
151 """Removes the entry in the desktop plist file that specifies the wallpaper for each monitor"""
152 try:
153 import Foundation
154 desktopPList = Foundation.NSMutableDictionary.dictionaryWithContentsOfFile_(self._DesktopPlistLocation)
155 # Remove all but the 'default' entry
156 for k in desktopPList['Background'].keys():
157 if k == 'default':
158 continue
159 desktopPList['Background'].removeObjectForKey_(k)
160 # Store the plist again (Make sure we write it out atomically -- Don't want to break finder)
161 desktopPList.writeToFile_atomically_(self._DesktopPlistLocation, True)
162 except ImportError:
163 debug('Could not import the Foundation module, you may have problems with dual screens', DEBUG_LEVEL_MEDIUM)
164
165 def changeTo(self, file):
166 output_name, ret = self._convertImageFormat(file)
167 if ret: # Since 0 indicates success
168 debug("Convert failed %s" % ret)
169 return False
170 self._fixDesktopPList()
171 cmd = """osascript -e 'tell application "finder" to set desktop picture to posix file "%s"'""" % output_name
172 debug(cmd, DEBUG_LEVEL_DEBUG)
173 return not commands.getstatusoutput(cmd)[0]
174
175 class __GnomeChanger(__BaseChanger):
176 def changeTo(self, file):
177 cmd = ['gconftool-2', '--type', 'string', '--set', '/desktop/gnome/background/picture_filename', file]
178 debug(cmd, DEBUG_LEVEL_DEBUG)
179 return subprocess.Popen(cmd, stdout=sys.stdout, stderr=sys.stderr, stdin=None).wait()
180
181 class __KDEChanger(__BaseChanger):
182 def changeTo(self, file):
183 cmds = []
184 for group in ('Desktop0', 'Desktop0Screen0'):
185 base = ['kwriteconfig', '--file', 'kdesktoprc', '--group', group, '--key']
186 cmds.append(base + ['Wallpaper', file])
187 cmds.append(base + ['UseSHM', '--type', 'bool', 'true'])
188 cmds.append(base + ['WallpaperMode', 'ScaleAndCrop'])
189 cmds.append(base + ['MultiWallpaperMode', 'NoMulti'])
190
191 cmds.append(['dcop', 'kdesktop', 'KBackgroundIface', 'configure'])
192 for cmd in cmds:
193 debug(cmd, DEBUG_LEVEL_DEBUG)
194 if subprocess.Popen(cmd, stdout=sys.stdout, stderr=sys.stderr, stdin=None).wait() != 0:
195 return 1 # Fail
196
197 return 0 # Success
198