#!/usr/bin/env python # Scrape colors from GTK and import them into Wine through the registry # # endolith@gmail.com 2009-02-06 # # Based on script by KillerKiwi [Ubuntu Forums] October 29th, 2007 # http://ubuntuforums.org/showthread.php?t=55286&page=3#23 # # which is based on patch by Johannes Roith [Mono-winforms-list] Wed, 27 Aug 2003 # http://lists.ximian.com/pipermail/mono-winforms-list/2003-August/000469.html import pygtk import gtk import os from tempfile import NamedTemporaryFile from gconf import Client import re # TODO: command line switch debug_mode = False def format_color_string(Color): """ Convert 48-bit gdk.Color to 24-bit 'RRR GGG BBB' triple. """ if type(Color) is gtk.gdk.Color: return '%s %s %s' % (Color.red/256, Color.green/256, Color.blue/256) else: print('Not a GdkColor: ' + str(Color)) return None def format_hex_color(color): """ Convert from hex color string to decimal 'RRR GGG BBB' triple. """ if re.match('^#[0-9a-fA-F]{12}$', color): r, g, b = color[1:3], color[5:7], color[9:11] #rrrrggggbbbb elif re.match('^#[0-9a-fA-F]{6}$', color): r, g, b = color[1:3], color[3:5], color[5:7] #RRGGBB elif re.match('^#[0-9a-fA-F]{3}$', color): r, g, b = color[1]*2, color[2]*2, color[3]*2 #09C becomes 0099CC. else: print('Cannot understand color string format: ' + str(color)) return None return '%s %s %s' % (int(r,16), int(g,16), int(b,16)) # TODO: fonts # Get some colors from GTK # gtk.Style object: # http://www.moeraki.com/pygtkreference/pygtk2reference/class-gtkstyle.html # Create a window to scrape colors from window = gtk.Window() button = gtk.Button() vbox = gtk.VBox() vbox.add(button) scrollbar = gtk.VScrollbar() vbox.add(scrollbar) menubar = gtk.MenuBar() menuitem = gtk.MenuItem() menubar.add(menuitem) vbox.add(menubar) window.add(vbox) # On show_all(), these are all converted from gtk.Style objects with wrong # colors into __main__.EngineStyle objects with correct colors. window.show_all() # Tooltips # http://www.daa.com.au/pipermail/pygtk/2005-November/011353.html tooltip = gtk.Window() tooltip.set_name('gtk-tooltips') tooltip.show_all() # Mappings for all the color values (described in readme.txt) # TODO: Need to mark which colors do not work with which themes and double-check EVERYTHING # * = Confirmed to work exactly for all themes (except Darklooks) # + = Not perfect, but close? # ? = Nothing to scrape? - Chose something similar gtk_colors = { # Title bars can't be accessed from PyGTK(?) so I just guessed to # make it work reasonably well with themes like Human. # Default Windows theme: Darker on left, light on right 'TitleText': window.style.white , #? White since we're guessing on dark selection color for titlebar 'InactiveTitleText': window.style.white , #? White since we're guessing on dark selection color for titlebar 'ActiveTitle': window.style.dark[gtk.STATE_SELECTED] , #? Guess on selection color like Clearlooks 'InactiveTitle': window.style.dark[gtk.STATE_NORMAL] , #? Darker color for gradient 'GradientActiveTitle': window.style.light[gtk.STATE_SELECTED], #? Lighter version for gradient 'GradientInactiveTitle': window.style.base[gtk.STATE_NORMAL] , #? Guess on base color 'ActiveBorder': button.style.bg[gtk.STATE_INSENSITIVE], #? Same as ButtonFace like Clearlooks 'InactiveBorder': button.style.bg[gtk.STATE_INSENSITIVE], #? Same as ButtonFace 'AppWorkSpace': window.style.base[gtk.STATE_NORMAL], #? MDI not used in GTK; use same color as Window, like Glade 'Background': None , #* Scraped from gconf below 'ButtonText': button.style.fg[gtk.STATE_NORMAL] , #* 'ButtonHilight': button.style.light[gtk.STATE_INSENSITIVE], #* 'ButtonLight': button.style.bg[gtk.STATE_INSENSITIVE] , #* Usually same as ButtonFace? 'ButtonFace': button.style.bg[gtk.STATE_INSENSITIVE] , #* 'ButtonAlternateFace': button.style.bg[gtk.STATE_INSENSITIVE] , #* Set to same as ButtonFace for now 'ButtonShadow': button.style.dark[gtk.STATE_INSENSITIVE] , #* 'ButtonDkShadow': button.style.black , #* 'GrayText': window.style.fg[gtk.STATE_INSENSITIVE], #* 'Hilight': window.style.base[gtk.STATE_SELECTED] , #* 'HilightText': window.style.fg[gtk.STATE_SELECTED] , #* 'HotTrackingColor': window.style.light[gtk.STATE_NORMAL] , #? Doesn't seem to exist in GTK; use lighter ButtonFace color, like CompizConfig Settings Manager 'InfoText': tooltip.style.fg[gtk.STATE_NORMAL], 'InfoWindow': tooltip.style.bg[gtk.STATE_NORMAL], 'Menu': menuitem.style.light[gtk.STATE_ACTIVE], 'MenuBar': menubar.style.bg[gtk.STATE_NORMAL] , 'MenuHilight': menuitem.style.bg[gtk.STATE_SELECTED] , 'MenuText': menuitem.style.fg[gtk.STATE_NORMAL] , 'Scrollbar': scrollbar.style.bg[gtk.STATE_ACTIVE], #+ Not right, even though gtk.RcStyle documentation says it is 'Window': window.style.base[gtk.STATE_NORMAL] , #* base or bg? 'WindowFrame': button.style.mid[gtk.STATE_SELECTED], #+ bg selected or light selected? 'WindowText': window.style.text[gtk.STATE_NORMAL] , #* } # As far as I know, there is no way to programmatically extract colors from GTK for the same elements in any engine, so we'll have to add engine-specific scrapers. # "Engines can and do whatever they want with the colors from the rc file ... A theme includes both the colors, and the code that paints using colors. So the code can use any of the colors it wants." # TODO: something like this: print "GTK engine:", if 'ClearlooksStyle' in repr(window.style): # kludgy, but works print "Clearlooks" # assign different colors to some elements elif 'MurrineStyle' in repr(window.style): print "Murrine" gtk_colors.update({'Window': window.style.base[gtk.STATE_NORMAL]}) elif 'CruxStyle' in repr(window.style): print "Crux" else: print "unknown" # QtPixbufStyle GlideStyle PixbufStyle HcStyle IndustrialStyle MistStyle ThiniceStyle darklooks = gtk.Style # Create list of formatted color value pair strings # Windows registry values are in the form "name"="data" with no spaces color_pairs = [] for name, data in gtk_colors.iteritems(): if data: color_pairs.append('"%s"="%s"' % (name, format_color_string(data))) # Get Desktop background color from Gnome's GConf, append to color pair list c = Client() desktop_color = format_hex_color(c.get_value('/desktop/gnome/background/primary_color')) if desktop_color: color_pairs.append('"Background"="%s"' % desktop_color) # TODO: Get Desktop background color from Xfce # import xfce4? # "i see, yes xfce does have something like that. you find it in /home/$USER/.config/xfce4/mcs_settings/desktop.xml" # Create a temporary Windows .reg registry file with the new values # I'm not sure of the meaning of S-1-5-4. This may not work on all systems? # I do know it is the same number under multiple accounts. try: wineprefix = os.environ['WINEPREFIX'] except KeyError: wineprefix = os.path.join(os.environ['HOME'],'.wine') winetemp = os.path.join(wineprefix, 'drive_c','windows','temp') f = NamedTemporaryFile(prefix = 'winecolors', suffix = '.reg', dir = winetemp, mode = 'w+') f.write("""REGEDIT4 [HKEY_USERS\S-1-5-4\Control Panel] [HKEY_USERS\S-1-5-4\Control Panel\Colors] """) # Alphabetize list (purely so that user.reg is easy to read; Wine doesn't care) color_pairs = sorted(color_pairs) # Append list to file, with newlines f.writelines(line + '\n' for line in color_pairs) f.flush() # Debugging if debug_mode: print '---- [' + f.name + '] ----' f.seek(0) for line in f: print line, print '--------\n' # Import values into Wine registry using regedit command print('Using regedit to import colors into registry...\n') os.system('regedit ' + f.name) # TODO: Check if this worked correctly. # Delete temporary file f.close() print('Done importing colors') # TODO: catch errors from regedit not working?