summaryrefslogtreecommitdiff
path: root/bin/random_wallpaper
blob: 15d2da9fac416a2e7a8c7bf54f7773cc5dd9f688 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#!/usr/bin/python3
# Reads a given directory and set a random
# wallpaper using the images on that dir.

import subprocess
import os
import os.path
import re
import random
import sys
import tempfile

class Display:
    index           = None
    name            = None
    offset          = None
    size            = None

    wall_filename   = None

    def __lt__(self, other):
        if self.offset[1] != other.offset[1]:
            return self.offset[1] < other.offset[1]
        return self.offset[0] < other.offset[0]

def detect_screen_geometry():
    screen      = None
    displays    = []

    p = subprocess.Popen('xrandr', stdout = subprocess.PIPE, text = True)
    for line in p.stdout:
        if line.startswith('Screen'):
            if screen:
                sys.stderr.write('Multiple X screens, not supported by this script\n')
                raise NotImplementedError

            m = re.search(r'current (\d*) x (\d*),', line)
            try:
                screen = [int(m.group(1)), int(m.group(2))]
            except:
                sys.stderr.write('Unsupported screen specification line: %s' % line)
                raise
        elif ' connected ' in line:
            m = re.match(r'(\S*) connected (\S+ )*(\d*)x(\d*)([+-]\d*)([+-]\d*)', line)
            if m:
                d = Display()

                d.name = m.group(1)
                d.offset = (int(m.group(5)), int(m.group(6)))
                d.size   = (int(m.group(3)), int(m.group(4)))

                if d.size[0] <= 0 or d.size[1] <= 0:
                    sys.stderr.write('Invalid non-positive display dimensions\n')
                    raise ValueError
                if d.offset[0] < 0 or d.offset[1] < 0:
                    sys.stderr.write('Negative display geometry offset, this is not supported\n')
                    raise NotImplementedError

                displays.append(d)

    if screen is None:
        sys.stderr.write('Missing screen specification in xrandr output\n')
        raise ValueError

    # sort the displays in the render scan order
    displays = sorted(displays)
    for i, d in enumerate(displays):
        d.index = i

    return screen, displays


if len(sys.argv) < 3:
    sys.stderr.write('Usage: %s <wallpaper_dir> <state dir> <display number 0> [<display number 1> ... ]\n'%(sys.argv[0]))
    sys.exit(-1)

# parse the commandline
wall_dir    = os.path.expandvars(sys.argv[1])
state_dir   = os.path.expandvars(sys.argv[2])

sys.stderr.write('Trying directory %s.\n'%wall_dir)
if not os.path.isdir(wall_dir):
    sys.stderr.write('Path %s is not a directory, aborting.\n'%wall_dir)
    sys.exit(-1)
if not os.path.isdir(state_dir):
    sys.stderr.write('Invalid state dir supplied, won\'t store/use any state\n')
    state_dir = None

screen, displays = detect_screen_geometry()

sys.stderr.write('The X screen resolution is %dx%d.\n' % (screen[0], screen[1]))
sys.stderr.write('The connected displays are:\n')
for i, d in enumerate(displays):
    sys.stderr.write('    %d: %s - %d x %d at (%d, %d)\n' % (d.index, d.name, d.size[0], d.size[1], d.offset[0], d.offset[1]))

# pick a new wallpaper for each specified display
files = os.listdir(wall_dir)
random.seed()

if len(sys.argv) >= 4 and sys.argv[3] == 'all':
    disp_indices = range(len(displays))
else:
    disp_indices = map(int, sys.argv[3:])

for d_idx in disp_indices:
    if d_idx >= len(displays):
        sys.stderr.write('Display index %d is too large, there are only %d connected displays\n'
                         % (d_idx, len(displays)))
        raise ValueError
    d = displays[d_idx]

    wallfile = random.choice(files)
    sys.stderr.write('Picking wall %s for display %d (%s)\n' % (wallfile, d.index, d.name))

    if state_dir:
        fname = os.path.join(state_dir, '%d.png' % d.index)
    else:
        d.tfile = tempfile.NamedTemporaryFile()
        fname   = d.tfile.name

    subprocess.check_call(['convert', '-size', '%dx%d' %(d.size[0], d.size[1]), 'canvas:black',
                           os.path.join(wall_dir, wallfile), '-resize', '%dx%d' % (d.size[0], d.size[1]),
                           '-gravity', 'center', '-composite', fname])
    d.wall_filename = fname

# compose the wallpaper file
tf = tempfile.NamedTemporaryFile()
cmdline = ['convert', '-size', '%dx%d' % (screen[0], screen[1]), 'canvas:black']
for d in displays:
    fname = d.wall_filename

    if fname is None and state_dir:
        tmpname = os.path.join(state_dir, '%d.png' % d.index)
        if os.path.isfile(tmpname):
            fname = tmpname

    if fname is not None:
        cmdline.extend([fname, '-geometry', '+%d+%d' %(d.offset[0], d.offset[1]), '-composite'])
cmdline.append('png:' + tf.name)
subprocess.check_call(cmdline)

# set the background
try:
    subprocess.check_call(['feh', '--bg-center', '--no-xinerama', tf.name])
except (subprocess.CalledProcessError, OSError):
    sys.stderr.write('Error setting the wallpaper. Is feh installed?\n')
    sys.exit(-1)

sys.exit(0)