From ca1afbb8768209a5a7926e54061475737a4e79a4 Mon Sep 17 00:00:00 2001 From: James Bunton Date: Wed, 2 Jul 2008 02:17:38 +1000 Subject: [PATCH] WallChanger improvements * Moved lib/WallChanger.py to bin/wallchanger.py * wallchanger.py now runs standalone to set the desktop background. * Much better chaining, untested at the moment though. --- bin/randombg.py | 20 +-- lib/WallChanger.py => bin/wallchanger.py | 162 ++++++++++++----------- 2 files changed, 97 insertions(+), 85 deletions(-) rename lib/WallChanger.py => bin/wallchanger.py (60%) mode change 100644 => 100755 diff --git a/bin/randombg.py b/bin/randombg.py index f31cbb6..8b2edd1 100755 --- a/bin/randombg.py +++ b/bin/randombg.py @@ -17,7 +17,7 @@ except ImportError: try: # Required libraries import asyncsched - import WallChanger + import wallchanger except ImportError, e: critical("Missing libraries! Exiting...") sys.exit(1) @@ -193,8 +193,8 @@ class Cycler(object): error("No images were found. Exiting...") sys.exit(1) - debug("Initialising RandomBG") - self.randombg = WallChanger.RandomBG(options.background_colour, options.permanent) + debug("Initialising wallchanger") + wallchanger.init(options.background_colour, options.permanent) self.cycle_time = options.cycle_time self.task = None @@ -221,7 +221,7 @@ class Cycler(object): def cmd_reset(self): def next(): image = self.filelist.get_next_image() - self.randombg.setImage(image) + wallchanger.set_image(image) self.task = None self.cmd_reset() @@ -232,17 +232,17 @@ class Cycler(object): def cmd_reload(self): image = self.filelist.get_current_image() - self.randombg.setImage(image) + wallchanger.set_image(image) self.cmd_reset() def cmd_next(self): image = self.filelist.get_next_image() - self.randombg.setImage(image) + wallchanger.set_image(image) self.cmd_reset() def cmd_prev(self): image = self.filelist.get_prev_image() - self.randombg.setImage(image) + wallchanger.set_image(image) self.cmd_reset() def cmd_rescan(self): @@ -280,16 +280,16 @@ class Server(asynchat.async_chat): class Listener(asyncore.dispatcher): - def __init__(self, socket_filename, randombg): + def __init__(self, socket_filename, cycler): asyncore.dispatcher.__init__(self) - self.randombg = randombg + self.cycler = cycler self.create_socket(socket.AF_UNIX, socket.SOCK_STREAM) self.bind(socket_filename) self.listen(2) # Backlog = 2 def handle_accept(self): conn, addr = self.accept() - Server(self.randombg, conn, addr) + Server(self.cycler, conn, addr) def do_server(options, paths): diff --git a/lib/WallChanger.py b/bin/wallchanger.py old mode 100644 new mode 100755 similarity index 60% rename from lib/WallChanger.py rename to bin/wallchanger.py index efebd50..307d7d0 --- a/lib/WallChanger.py +++ b/bin/wallchanger.py @@ -1,77 +1,69 @@ -#! python +#!/usr/bin/env python +# Copyright 2008 Greg Darke +# Copyright 2008 James Bunton +# Licensed for distribution under the GPL version 2, check COPYING for details +# This is a cross platform/cross window manager way to change your wallpaper import commands, sys, os, os.path, subprocess, time -from GregDebug import debug, setDebugLevel, DEBUG_LEVEL_DEBUG, DEBUG_LEVEL_LOW, DEBUG_LEVEL_MEDIUM, DEBUG_LEVEL_HIGH, DEBUG_INCREMENT +from logging import debug, info, warning -import python24_adapter # NB: Must be imported before collections -import collections +__all__ = ("init", "set_image") -"""This is a cross platform/cross window manager way to change your current -desktop image.""" -__all__ = ('RandomBG') +changers = [] -def RandomBG(*args, **kwargs): - """Desktop Changer factory""" +def set_image(filename): + for changer in changers: + if not changer.set_image(filename): + warning("Failed to set background: wallchanger.set_image(%s), changer=%s" % (filename, changer)) - ret = None +def init(*args, **kwargs): + """Desktop Changer factory""" - debug("Testing for OSX (NonX11)", DEBUG_LEVEL_LOW) + debug("Testing for OSX (NonX11)") if commands.getstatusoutput("ps ax -o command -c|grep -q WindowServer")[0] == 0: - ret = __OSXChanger(*args, **kwargs) + changers.append(OSXChanger(*args, **kwargs)) if 'DISPLAY' not in os.environ or os.environ['DISPLAY'].startswith('/tmp/launch'): # X11 is not running - return ret + return else: if os.uname()[0] == 'Darwin': # Try to detect if the X11 server is running on OSX if commands.getstatusoutput("ps ax -o command|grep -q '/.*X11 .* %s'" % os.environ['DISPLAY'])[0] != 0: # X11 is not running for this display - return ret + return - debug("Testing for KDE", DEBUG_LEVEL_LOW) + debug("Testing for KDE") if commands.getstatusoutput("xwininfo -name 'KDE Desktop'")[0] == 0: - if ret is not None: - ret.nextChanger = __KDEChanger(*args, **kwargs) - else: - ret = __KDEChanger(*args, **kwargs) + changers.append(KDEChanger(*args, **kwargs)) - debug("Testing for Gnome", DEBUG_LEVEL_LOW) + debug("Testing for Gnome") if commands.getstatusoutput("xwininfo -name 'gnome-session'")[0] == 0: - if ret is not None: - ret.nextChanger = __GnomeChanger(*args, **kwargs) - else: - ret = __GnomeChanger(*args, **kwargs) + changers.append(GnomeChanger(*args, **kwargs)) - debug("Testing for WMaker", DEBUG_LEVEL_LOW) + debug("Testing for WMaker") if commands.getstatusoutput("xlsclients | grep -qi wmaker")[0] == 0: - if ret is not None: - ret.nextChanger = __WMakerChanger(*args, **kwargs) - else: - ret = __WMakerChanger(*args, **kwargs) + changers.append(WMakerChanger(*args, **kwargs)) - if ret is None: + if len(changers) == 0: raise Exception("Unknown window manager") - else: - return ret -class __BaseChanger(object): - def __init__(self, backgroundColour='black', permanent=False): - debug('Determined the window manager is "%s"' % self.__class__.__name__, DEBUG_LEVEL_MEDIUM) - self.backgroundColour = backgroundColour + +class BaseChanger(object): + name = "undefined" + def __init__(self, background_color='black', permanent=False): + info('Determined the window manager is "%s"' % self.name) + self.background_color = background_color self.permanent = permanent - # Used to 'chain' background changers - self.nextChanger = None - - def setImage(self, filename): - self._setImage(filename) - if self.nextChanger is not None: - self.nextChange.changeTo(filename) -class __WMakerChanger(__BaseChanger): + def set_image(self, filename): + raise NotImplementedError() + +class WMakerChanger(BaseChanger): + name = "WindowMaker" _ConvertedWallpaperLocation = '/tmp/wallpapers_wmaker/' - def _removeOldImageCache(self): + def remove_old_image_cache(self): """Cleans up any old temp images""" if not os.path.isdir(self._ConvertedWallpaperLocation): os.mkdir(self._ConvertedWallpaperLocation) @@ -81,20 +73,20 @@ class __WMakerChanger(__BaseChanger): for dirname in dirnames: os.unlink(os.path.join(fullpath, dirname)) - def _convertImageFormat(self, file): + def convert_image_format(self, file): """Convert the image to a png, and store it in a local place""" - self._removeOldImageCache() + self.remove_old_image_cache() output_name = os.path.join(self._ConvertedWallpaperLocation, '%s.png' % time.time()) cmd = ["convert", '-resize', '1280', '-gravity', 'Center', '-crop', '1280x800+0+0', file, output_name] - debug("""Convert command: '"%s"'""" % '" "'.join(cmd), DEBUG_LEVEL_DEBUG) + debug("""Convert command: '"%s"'""" % '" "'.join(cmd)) return output_name, subprocess.Popen(cmd, stdout=sys.stdout, stderr=sys.stderr, stdin=None).wait() - def _setImage(self, file): - file, convert_status = self._convertImageFormat(file) + def set_image(self, file): + file, convert_status = self.convert_image_format(file) if convert_status: debug('Convert failed') cmd = ["wmsetbg", - "-b", self.backgroundColour, # Sets the background colour to be what the user specified + "-b", self.background_color, # 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 ### "-a", # scale the image, keeping the aspect ratio @@ -104,14 +96,15 @@ class __WMakerChanger(__BaseChanger): if self.permanent: cmd += ["-u"] # update the wmaker database cmd += [file] - debug('''WMaker bgset command: "'%s'"''' % "' '".join(cmd), DEBUG_LEVEL_DEBUG) + debug('''WMaker bgset command: "'%s'"''' % "' '".join(cmd)) return not subprocess.Popen(cmd, stdout=sys.stdout, stderr=sys.stderr, stdin=None).wait() -class __OSXChanger(__BaseChanger): +class OSXChanger(BaseChanger): + name = "Mac OS X" _ConvertedWallpaperLocation = '/tmp/wallpapers/' _DesktopPlistLocation = os.path.expanduser('~/Library/Preferences/com.apple.desktop.plist') - def _removeOldImageCache(self): + def remove_old_image_cache(self): """Cleans up any old temp images""" if not os.path.isdir(self._ConvertedWallpaperLocation): os.mkdir(self._ConvertedWallpaperLocation) @@ -121,9 +114,9 @@ class __OSXChanger(__BaseChanger): for dirname in dirnames: os.unlink(os.path.join(fullpath, dirname)) - def _convertImageFormat(self, file): + def convert_image_format(self, file): """Convert the image to a png, and store it in a local place""" - self._removeOldImageCache() + self.remove_old_image_cache() output_name = os.path.join(self._ConvertedWallpaperLocation, '%s.png' % time.time()) try: import PIL, PIL.Image @@ -137,39 +130,41 @@ class __OSXChanger(__BaseChanger): shutil.copyfile(file, output_name) return output_name, True - def _fixDesktopPList(self): + def fix_desktop_plist(self): """Removes the entry in the desktop plist file that specifies the wallpaper for each monitor""" try: import Foundation - desktopPList = Foundation.NSMutableDictionary.dictionaryWithContentsOfFile_(self._DesktopPlistLocation) + desktop_plist = Foundation.NSMutableDictionary.dictionaryWithContentsOfFile_(self._DesktopPlistLocation) # Remove all but the 'default' entry - for k in desktopPList['Background'].keys(): + for k in desktop_plist['Background'].keys(): if k == 'default': continue - desktopPList['Background'].removeObjectForKey_(k) + desktop_plist['Background'].removeObjectForKey_(k) # Store the plist again (Make sure we write it out atomically -- Don't want to break finder) - desktopPList.writeToFile_atomically_(self._DesktopPlistLocation, True) + desktop_plist.writeToFile_atomically_(self._DesktopPlistLocation, True) except ImportError: - debug('Could not import the Foundation module, you may have problems with dual screens', DEBUG_LEVEL_MEDIUM) + debug('Could not import the Foundation module, you may have problems with dual screens') - def _setImage(self, file): - output_name, ret = self._convertImageFormat(file) + def set_image(self, file): + output_name, ret = self.convert_image_format(file) if not ret: debug("Convert failed") return False - self._fixDesktopPList() + self.fix_desktop_plist() cmd = """osascript -e 'tell application "finder" to set desktop picture to posix file "%s"'""" % output_name - debug(cmd, DEBUG_LEVEL_DEBUG) + debug(cmd) return not commands.getstatusoutput(cmd)[0] -class __GnomeChanger(__BaseChanger): - def _setImage(self, file): +class GnomeChanger(BaseChanger): + name = "Gnome" + def set_image(self, file): cmd = ['gconftool-2', '--type', 'string', '--set', '/desktop/gnome/background/picture_filename', file] - debug(cmd, DEBUG_LEVEL_DEBUG) - return subprocess.Popen(cmd, stdout=sys.stdout, stderr=sys.stderr, stdin=None).wait() + debug(cmd) + return not subprocess.Popen(cmd, stdout=sys.stdout, stderr=sys.stderr, stdin=None).wait() -class __KDEChanger(__BaseChanger): - def _setImage(self, file): +class KDEChanger(BaseChanger): + name = "KDE" + def set_image(self, file): cmds = [] for group in ('Desktop0', 'Desktop0Screen0'): base = ['kwriteconfig', '--file', 'kdesktoprc', '--group', group, '--key'] @@ -180,9 +175,26 @@ class __KDEChanger(__BaseChanger): cmds.append(['dcop', 'kdesktop', 'KBackgroundIface', 'configure']) for cmd in cmds: - debug(cmd, DEBUG_LEVEL_DEBUG) + debug(cmd) if subprocess.Popen(cmd, stdout=sys.stdout, stderr=sys.stderr, stdin=None).wait() != 0: - return 1 # Fail + return False + + return True + + +def main(filename): + import logging + logging.basicConfig(level=logging.DEBUG, format="%(levelname)s: %(message)s") + init() + set_image(filename) + +if __name__ == "__main__": + try: + filename = sys.argv[1] + except: + print >>sys.stderr, "Usage: %s filename" % sys.argv[0] + sys.exit(1) + + main(filename) - return 0 # Success -- 2.39.2