-
-
Save jdah/d3d7a4ec4b12bdf74bdd0de4bc12a2e2 to your computer and use it in GitHub Desktop.
# Run with python .\spotlight.py | |
from functools import reduce | |
import ctypes, os, winreg, glob, time | |
from PIL import Image | |
SPOTLIGHT_PATH = r"{0}\Local\Packages\Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy\LocalState\Assets" | |
# >>>> CONFIGURATION VARIABLES <<<< | |
CHANGE_TIME_MINUTES = 2.5 | |
MONITOR_COUNT = 2 | |
MONITOR_SIZE = [(1920, 1080), (1920, 1080)] | |
SPI_SETDESKWALLPAPER = 0x0014 | |
SPI_SETDESKPATTERN = 0x0015 | |
SPIF_UPDATEINIFILE = 0x01 | |
SPIF_SENDWININICHANGE = 0x02 | |
# Map of wallpaper filenames to the number of times that they have been used | |
wallpapers = {} | |
# Next wallpaper in the sequence to be changed | |
next_change = 0 | |
# Names of the current wallpapers | |
current_wallpapers = [None] * MONITOR_COUNT | |
def get_spotlight_path(): | |
return SPOTLIGHT_PATH.format(os.getenv("APPDATA") + "\\..") | |
def set_wallpapers(paths): | |
# Load all images | |
images = list() | |
for i, p in enumerate(paths): | |
images.append(Image.open(p).resize(MONITOR_SIZE[i], Image.BILINEAR)) | |
# Concatenate the images end-to-end | |
final_image = Image.new("RGB", (reduce(lambda x, y: x + y.width, images, 0), max([x.height for x in images]))) | |
width = 0 | |
for i, image in enumerate(images): | |
final_image.paste(image, (width, 0)) | |
width += image.width | |
final_image.save(".\\wp.jpg", "JPEG") | |
for k, v in {("TileWallpaper", "1"), ("WallpaperStyle", "0")}: | |
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Control Panel\\Desktop", 0, winreg.KEY_WOW64_32KEY | winreg.KEY_WRITE) | |
winreg.SetValueEx(key, k, 0, winreg.REG_SZ, v) | |
ctypes.windll.user32.SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, os.path.abspath(".\\wp.jpg"), SPIF_SENDWININICHANGE | SPIF_UPDATEINIFILE) | |
def change(): | |
global wallpapers, next_change, current_wallpapers | |
def setw(): | |
sp = get_spotlight_path() + "\\" | |
set_wallpapers([sp + p for p in current_wallpapers]) | |
lowest = lambda w: sorted(w.items(), key=lambda x: x[1])[0][0] | |
# If no wallpapers have been set yet, then set up the initial wallpapers | |
if current_wallpapers[0] is None: | |
for x in range(0, MONITOR_COUNT): | |
l = lowest(wallpapers) | |
wallpapers[l] += 1 | |
current_wallpapers[x] = l | |
setw() | |
return | |
# Set the current wallpaper for the next monitor to be changed to the | |
# wallpaper least used out of the list | |
l = lowest(wallpapers) | |
wallpapers[l] += 1 | |
current_wallpapers[next_change] = l | |
next_change += 1 | |
if next_change == MONITOR_COUNT: | |
next_change = 0 | |
setw() | |
# Poll for new wallpapers and update the wallpaper table | |
def update(): | |
size_max = (max([x[0] for x in MONITOR_SIZE]), max([x[1] for x in MONITOR_SIZE])) | |
for wp in glob.glob(get_spotlight_path() + "\\*"): | |
name = os.path.basename(wp) | |
if not name in wallpapers: | |
try: | |
with Image.open(wp) as image: | |
if image.width < size_max[0] or image.height < size_max[1]: | |
continue | |
except: | |
continue | |
wallpapers[name] = 0 | |
if __name__ == "__main__": | |
while True: | |
update() | |
change() | |
time.sleep(CHANGE_TIME_MINUTES * 60) |
@pcarver Windows 8 allowed setting unique wallpaper for each monitor. This was removed in Windows 10 and the workaround is to make a single image that covers the "virtual space" all of the monitors cover. "Tiling" the single image is required to make it work correctly, otherwise a mangled version will be used and duplicated on each monitor. It may take you a few tries with an image editor. "Tiling" the single image is required to make it work correctly, otherwise a mangled version will be used and duplicated on each monitor.
In my case I had a laptop screen for a primary monitor and a larger secondary monitor that wasn't always connected (though this fortunately didn't break anything). I made a composite image that was the combined width of my two screens and the maximum height between them. The primary screen wallpaper had to be top-aligned even though the screens were bottom-aligned in the display settings.
Hopefully that helps!
If I'm reading this correctly, you're creating a single wallpaper by combining two images into a single image that is the width of your two side-by-side identically sized monitors. Then you're "tiling" it, but because of the resizing it you end up with just one tile spanning across both monitors. Is this because of a shortcoming in the Windows API for assigning wallpapers to specific monitors?
I'm using four monitors with three different resolutions (including one of them in a portrait orientation) so my total desktop area is not rectangular. In order to adapt your code, would I need to construct a single wallpaper image that places individual sub-images within a larger rectangle that includes all of my monitors?
Some of the coordinates of my desktop are negative numbers since I have monitors above and to the left or (0, 0).