rename runProgram
[bg-scripts] / ChangeWallpaper.pyw
1 # Python program to change the background under windows (tested on windows XP)
2
3 from __future__ import division
4
5 import sys, os
6 from PIL import Image
7
8 import ctypes
9 # Kinda like 'from ctypes.windll import user32'
10 user32 = ctypes.windll.user32
11
12 ###sys.stdout = file(os.path.join(os.environ['USERPROFILE'], 'Desktop', 'Output.txt'), 'w')
13 ###sys.stderr = sys.stdout
14
15 def getUnicodeArgs():
16 LocalFree = ctypes.windll.kernel32.LocalFree
17
18 # Defining the GetCommandLineW function
19 GetCommandLineW = ctypes.windll.kernel32.GetCommandLineW
20 GetCommandLineW.restype = ctypes.c_wchar_p
21
22 # Defining the CommandLineToArgvW function
23 CommandLineToArgvW = ctypes.windll.shell32.CommandLineToArgvW
24 CommandLineToArgvW.argtypes = (ctypes.c_wchar_p, ctypes.c_void_p)
25 CommandLineToArgvW.restype = ctypes.c_void_p
26
27 args = GetCommandLineW()
28 # if args != NULL
29 if args:
30 try:
31 numArgs = ctypes.c_int()
32 argv_address = CommandLineToArgvW(args, ctypes.byref(numArgs))
33
34 # if argv_address != NULL
35 if argv_address:
36 try:
37 argvArrayType = ctypes.c_wchar_p * numArgs.value
38 argvArray = argvArrayType.from_address(argv_address)
39 argv = tuple(unicode(arg) for arg in argvArray)
40 finally:
41 # Free the memory in argv_address
42 LocalFree(argv_address)
43 finally:
44 # Free this memory since it isn't needed
45 LocalFree(args)
46
47
48 # Drop the first argument (since this the call to the python interperator
49 return argv[1:]
50
51 sys.unicode_argv = getUnicodeArgs()
52
53
54 # Taken from the Platform SDK
55 SPI_SETDESKWALLPAPER = 20
56 SPIF_SENDWININICHANGE = 2
57
58 # Resize an image (returns a new image)
59 def resize(image, (outputWidth, outputHeight),
60 fillBoundingBox = False,
61 cropImage = True,
62 method = Image.BICUBIC):
63
64 imgWidth, imgHeight = image.size
65
66 if (imgHeight == outputHeight) and (imgWidth == outputWidth):
67 return image
68
69 aspectRatio = imgHeight / imgWidth
70 outputAspectRatio = outputHeight / outputWidth
71
72 if fillBoundingBox:
73 if aspectRatio < outputAspectRatio:
74 newHeight = outputHeight
75 newWidth = int(outputHeight / aspectRatio)
76 # (left, upper, right, lower)
77 offset = (newWidth - outputWidth) // 2
78 outputBox = (offset, 0, offset + outputWidth, outputHeight)
79 else:
80 newWidth = outputWidth
81 newHeight = int(outputWidth * aspectRatio)
82 # (left, upper, right, lower)
83 offset = (newHeight - outputHeight) // 2
84 outputBox = (0, offset, newWidth, offset + outputHeight)
85
86 print >>sys.stderr, "Aspects: ", aspectRatio, outputAspectRatio
87 print >>sys.stderr, "Output: ", outputWidth, outputHeight
88 print >>sys.stderr, "Img: ", imgWidth, imgHeight
89 print >>sys.stderr, "new:", newWidth, newHeight
90 print >>sys.stderr, "CropBox: ", outputBox
91
92 image = image.resize( (newWidth, newHeight), method)
93 if cropImage and not\
94 ((newHeight == outputHeight) and (newWidth == outputWidth)):
95 image = image.crop( outputBox )
96 else:
97 raise NotImplementedError
98
99 return image
100
101 # Converts the input image to a bitmap (with optional resizing)
102 def convertImage(pathToImage, boundingBox = None):
103 TEMP_FILE = 'pywallpaper.bmp'
104 newPath = os.path.join(os.getcwd(), TEMP_FILE)
105
106 bmpImg = Image.open(pathToImage)
107
108 if boundingBox is not None:
109 bmpImg = resize(bmpImg, boundingBox, fillBoundingBox = True)
110
111 # Check the colour space it is in (we only want RGB)
112 if bmpImg.mode == 'RGBA':
113 bmpImg = bmpImg.convert('RGB')
114
115 # Save it as a bitmap
116 bmpImg.save(newPath, "BMP")
117
118 return newPath
119
120 # Actually sets the wallpaper
121 def updateWall(pathToBMP):
122 # Parameters for SystemParametersInfoA are:
123 # (UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni)
124 user32.SystemParametersInfoA(SPI_SETDESKWALLPAPER,
125 0,
126 pathToBMP,
127 SPIF_SENDWININICHANGE)
128
129 ###
130 # Code from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/460509
131 # Gets the size of all monitors attached to this computer)
132 ###
133 class RECT(ctypes.Structure):
134 _fields_ = [
135 ('left', ctypes.c_long),
136 ('top', ctypes.c_long),
137 ('right', ctypes.c_long),
138 ('bottom', ctypes.c_long)
139 ]
140 def dump(self):
141 return map(int, (self.left, self.top, self.right, self.bottom))
142
143 class MONITORINFO(ctypes.Structure):
144 _fields_ = [
145 ('cbSize', ctypes.c_ulong),
146 ('rcMonitor', RECT),
147 ('rcWork', RECT),
148 ('dwFlags', ctypes.c_ulong)
149 ]
150
151 def get_monitors():
152 retval = []
153 CBFUNC = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_ulong, ctypes.c_ulong, ctypes.POINTER(RECT), ctypes.c_double)
154 def cb(hMonitor, hdcMonitor, lprcMonitor, dwData):
155 r = lprcMonitor.contents
156 #print "cb: %s %s %s %s %s %s %s %s" % (hMonitor, type(hMonitor), hdcMonitor, type(hdcMonitor), lprcMonitor, type(lprcMonitor), dwData, type(dwData))
157 data = [hMonitor]
158 data.append(r.dump())
159 retval.append(data)
160 return 1
161 cbfunc = CBFUNC(cb)
162 temp = user32.EnumDisplayMonitors(0, 0, cbfunc, 0)
163 #print temp
164 return retval
165
166 def monitor_areas():
167 retval = []
168 monitors = get_monitors()
169 for hMonitor, extents in monitors:
170 data = [hMonitor]
171 mi = MONITORINFO()
172 mi.cbSize = ctypes.sizeof(MONITORINFO)
173 mi.rcMonitor = RECT()
174 mi.rcWork = RECT()
175 res = user32.GetMonitorInfoA(hMonitor, ctypes.byref(mi))
176 data.append(mi.rcMonitor.dump())
177 data.append(mi.rcWork.dump())
178 retval.append(data)
179 return retval
180
181 ###
182 # End code from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/460509
183 ###
184
185 def main():
186 def getPrimarySize():
187 monitors = monitor_areas()
188 monitorID, size, workingSize = monitors[0]
189 sizeLeft, sizeTop, sizeRight, sizeBottom = size
190 return (sizeRight - sizeLeft, sizeBottom - sizeTop)
191
192 pathToBMP = convertImage(sys.unicode_argv[1], getPrimarySize())
193 updateWall(pathToBMP)
194
195 if __name__ == '__main__':
196 main()