#!/usr/bin/env python import commands, sys, os, os.path, random, socket, subprocess import cPickle, datetime, time from optparse import OptionParser, Values VERSION = "1.1" CACHE_LOCATION = os.path.expanduser('~/.randombg2_filelist_cache') try: import GregDebug from SigHandler import HUPInterrupt from GregDebug import debug, setDebugLevel, DEBUG_LEVEL_DEBUG, DEBUG_LEVEL_LOW, DEBUG_LEVEL_MEDIUM, DEBUG_LEVEL_HIGH, DEBUG_INCREMENT from FileLists import * except ImportError: print >>sys.stderr, "Missing libraries!\nExiting..." sys.exit(1) try: from collections import defaultdict def magicdict(): return defaultdict(dict) except ImportError: class magicdict(dict): def __getitem__(self, key): if not self.has_key(key): self[key] = {} return dict.__getitem__(self, key) class RandomBG(object): KDE_CONFIG = os.path.expanduser('~/.kde/share/config/kdesktoprc') def __init__(self, filelist, backgroundColour='black', permanent=False): windowManager = self._determineWindowManager() debug('Determined the window manager is "%s"' % windowManager, DEBUG_LEVEL_MEDIUM) self.backgroundColour = backgroundColour self.permanent = permanent self.filelist = filelist if windowManager == "WMAKER": self._randombg = self._randombgWMAKER elif windowManager == "KDE": self._randombg = self._randombgKDE elif windowManager == "OSX": self._randombg = self._randombgOSX else: raise Exception("Unknown window manager") def _determineWindowManager(self): """Searches for a some specified windows within the current X session to see what window manager we are running under""" debug("Testing for OSX (NonX)", DEBUG_LEVEL_LOW) if commands.getstatusoutput("ps ax -o command -c|grep -q WindowServer")[0] == 0: return "OSX" debug("Testing for KDE", DEBUG_LEVEL_LOW) if commands.getstatusoutput("xwininfo -name 'KDE Desktop'")[0] == 0: return "KDE" debug("Testing for WMaker", DEBUG_LEVEL_LOW) if commands.getstatusoutput("xlsclients | grep -qi wmaker")[0] == 0: return "WMAKER" return None def _randombgWMAKER(self, file): cmd = ["wmsetbg", "-b", self.backgroundColour, # Sets the background colour to be what the user specified "-S", # 'Smooth' (WTF?) "-e", # Center the image on the screen (only affects when the image in no the in the correct aspect ratio "-d", # dither "-a"] # scale the image, keeping the aspect ratio if self.permanent: cmd += ["-u"] # update the wmaker database cmd += [file] return subprocess.Popen(cmd, stdout=sys.stdout, stderr=sys.stderr, stdin=open('/dev/null', 'r')).wait() def _randombgOSX(self, file): cmd = """osascript -e 'tell application "finder" to set desktop picture to posix file "%s"'""" % file debug(cmd, DEBUG_LEVEL_DEBUG) return commands.getstatusoutput(cmd)[0] def _parseKDEConfig(self, filename = KDE_CONFIG): fd = open(filename, 'r') result = magicdict() section = None for line in fd: line = line.strip() if not line or line.startswith('#'): continue if line.startswith('[') and line.endswith(']'): section = line[1:-1] result[section] = {} continue elif not section: raise Exception('Invalid kdesktoprc file') unpack = line.split('=', 1) if len(unpack) == 2: key, val = unpack else: key, val = unpack[0], None result[section][key] = val fd.close() return result def _writeKDEConfig(self, config, filename = KDE_CONFIG): fd = open(filename, 'w') for section, values in config.items(): print >>fd, '[%s]' % section for k, v in values.items(): if v != None: print >>fd, '%s=%s' % (k,v) else: print >>fd, k print >>fd fd.close() def _randombgKDE(self, file): kdeconfig = self._parseKDEConfig() #kdeconfig['Background Common']['DrawBackgroundPerScreen_0']='true' for section in ('Desktop0', 'Desktop0Screen0'): kdeconfig[section]['Wallpaper'] = file kdeconfig[section]['UseSHM'] = 'true' kdeconfig[section]['WallpaperMode'] = 'ScaleAndCrop' # Ensure that random mode is disabled... if 'MultiWallpaperMode' in kdeconfig[section]: del kdeconfig[section]['MultiWallpaperMode'] self._writeKDEConfig(kdeconfig) return subprocess.Popen(['dcop', 'kdesktop', 'KBackgroundIface', 'configure'], stdout=sys.stdout, stderr=sys.stderr, stdin=open('/dev/null', 'r')).wait() def __call__(self): self.cycleNext() def cycleNext(self): file = self.filelist.getNextRandomImage() return self._randombg(file) def cyclePrev(self): file = self.filelist.getPrevRandomImage() return self._randombg(file) def buildparser(): def addfilestolist(optclass, opt, value, parser, fileList): fo = open(value) for line in fo: fileList.list.append(line.strip()) fo.close() fileList.allowAllRandom = False parser = OptionParser(version="%prog " + VERSION, description = "Picks a random background image", usage = "%prog [options] dir [dir2 ...]") parser.add_option("-p", "--permanent", action="store_true", dest="permanent", default=False, help="Make the background permanent. Note: This will cause all machines logged in with this account to simultaneously change background [Default: %default]") parser.add_option("-q", "--quiet", "--silent", action="count", dest="quiet", default=0, help="Make the script quiet (good for running from a shell script)") parser.add_option("-v", '-d', "--verbose", "--debug", action="count", dest="verbose", default=0, help="Make the louder (good for debugging, or those who are curious)") parser.add_option("-b", "--background-colour", action="store", type="string", dest="background_colour", default="black", help="Change the default background colour that is displayed if the image is not in the correct aspect ratio [Default: %default]") parser.add_option("--all-random", action="store_true", dest="all_random", default=False, help="Make sure that all images have been displayed before repeating an image") parser.add_option("--folder-random", action="store_true", dest="folder_random", default=False, help="Give each folder an equal chance of having an image selected from it") #parser.add_option("--file-list", # action="callback", callback=addfilestolist, type="string", callback_args=(fileList,), # help="Adds the list of images from the external file") parser.add_option("--cycle", action="store", type="int", default=0, dest="cycle_time", help="Cause the image to cycle every X seconds") return parser def main(): parser = buildparser() useroptions, paths = parser.parse_args(sys.argv[1:]) setDebugLevel(DEBUG_INCREMENT * (useroptions.quiet - useroptions.verbose)) debug("Just set GregDebug.DEBUG_LEVEL to %d" % GregDebug.DEBUG_LEVEL, DEBUG_LEVEL_LOW) if useroptions.all_random: filelist = AllRandomFileList() elif useroptions.folder_random: filelist = FolderRandomFileList() else: filelist = RandomFileList() for path in paths: filelist.doAddPath(path) if filelist.attemptCacheLoad(CACHE_LOCATION): debug("Loaded cache successfully", DEBUG_LEVEL_LOW) else: debug("Could not load cache") filelist.doScanPaths() try: if not filelist.hasImages(): print >>sys.stderr, "No files!" parser.print_help() sys.exit(1) ret = None debug("Initilizing RandomBG", DEBUG_LEVEL_DEBUG) randombg = RandomBG(filelist, useroptions.background_colour, useroptions.permanent) if useroptions.cycle_time > 0: while True: try: debug("Cycling wallpaper", DEBUG_LEVEL_LOW) ret = randombg() if ret: debug('Could not set wallpaper. Returned "%s" % ret') break debug('About to sleep for "%d" seconds' % useroptions.cycle_time, DEBUG_LEVEL_LOW) time.sleep(useroptions.cycle_time) except KeyboardInterrupt, e: break debug("Caught KeyboardInterrupt", DEBUG_LEVEL_LOW) except HUPInterrupt, e: # Force a new image to be displayed before the timeout debug("Caught SIGHUP: Loading new image") else: ret = randombg() finally: filelist.doStoreCache(CACHE_LOCATION) sys.exit(ret) if __name__ == "__main__": main()