]> git.phdru.name Git - xsetbg.git/blob - xsetbg.py
Version 5.0.2
[xsetbg.git] / xsetbg.py
1 """Set a random background image (XWin)
2
3 Select a random image from a (list of) directory(s)
4 and set it as the desktop wallpaper (display it in the root window)
5 using xli program.
6
7 """
8
9 __author__ = "Oleg Broytman <phd@phdru.name>"
10 __copyright__ = "Copyright (C) 2000-2015 PhiloSoft Design"
11 __license__ = "GNU GPL"
12
13 __all__ = ['change']
14
15 from fcntl import flock, LOCK_EX, LOCK_UN, LOCK_NB
16 from datetime import timedelta
17 import os
18 import random
19 import re
20 import subprocess
21 import sys
22 from time import time
23
24 from xsetbg_conf import xsetbg_dir, xsetbg_conf
25 from xsetbg_db import xsetbg_db
26
27
28 def error(error_str):
29     sys.exit("%s: Error: %s\n" % (sys.argv[0], error_str))
30
31
32 if not xsetbg_db:
33     error("No database found. Run rescan_fs.py. Abort.")
34
35 if xsetbg_db.select().count() == 0:
36     error("No images found. Run rescan_fs.py. Abort.")
37
38
39 # minimum time in seconds between background image changes
40 if xsetbg_conf.has_option("xsetbg", "min_pause"):
41     min_pause = xsetbg_conf.getint("xsetbg", "min_pause")
42 else:
43     min_pause = 60
44
45 borders = xsetbg_conf.get("xsetbg", "borders").split(',')
46 if xsetbg_conf.has_option("xsetbg", "borders"):
47     borders = [border.strip() for border in
48                xsetbg_conf.get("xsetbg", "borders").split(',')]
49 else:
50     borders = ["darkcyan", "steelblue", "midnightblue"]
51
52 # minimum time in seconds between occurences of the same image
53 if xsetbg_conf.has_option("xsetbg", "min_delay"):
54     min_delay = xsetbg_conf.get("xsetbg", "min_delay")
55
56     # Borrowed from http://stackoverflow.com/a/2765366
57     td_re = re.compile('(?:(?P<years>\d+)y)?'
58                        '(?:(?P<months>\d+)m)?'
59                        '(?:(?P<days>\d+)d)?'
60                        '(?:T(?:(?P<hours>\d+)h)?'
61                        '(?:(?P<minutes>\d+)m)?(?:(?P<seconds>\d+)s)?)?')
62     td_dict = td_re.match(min_delay).groupdict(0)
63     delta = timedelta(days=int(td_dict['days']) +
64                       (int(td_dict['months']) * 30) +
65                       (int(td_dict['years']) * 365),
66                       hours=int(td_dict['hours']),
67                       minutes=int(td_dict['minutes']),
68                       seconds=int(td_dict['seconds']))
69
70     if delta:
71         min_delay = delta.days * 24*3600 + delta.seconds
72     else:
73         min_delay = int(min_delay)
74
75 else:
76     min_delay = 3600*24  # 24 hours
77
78
79 if xsetbg_db.select('last_shown IS NULL OR last_shown < %d' %
80                     (time() - min_delay)).count() == 0:
81     error("No unshown images found. Run rescan_fs.py "
82           "or decrease min_delay. Abort.")
83
84 fs_encoding = xsetbg_conf.get("images", "fs_encoding")
85
86 def change(force=False):
87     # Use the program's file as the lock file:
88     # lock it to prevent two processes run in parallel.
89     lock_file = open(os.path.join(xsetbg_dir, 'xsetbg.py'), 'r')
90
91     try:
92         flock(lock_file, LOCK_EX | LOCK_NB)
93     except IOError:  # already locked
94         lock_file.close()
95         return
96
97     try:
98         timestamp = xsetbg_db.select('last_shown IS NOT NULL',
99                                      orderBy='-last_shown')[0].last_shown
100         current_time = time()
101
102         if not force and timestamp is not None and \
103                 current_time - timestamp < min_pause:
104             # Too early to change background
105             return
106
107         # Select a random image that has never been shown
108         not_shown_select = xsetbg_db.select('last_shown IS NULL')
109         not_shown_count = not_shown_select.count()
110         if not_shown_count:
111             row = not_shown_select[random.randint(0, not_shown_count - 1)]
112         else:
113             old_shown_select = xsetbg_db.select(
114                 'last_shown IS NOT NULL AND last_shown < %d' %
115                 current_time - min_delay)
116             old_shown_count = old_shown_select.count()
117             if old_shown_count:
118                 row = old_shown_select[random.randint(0, old_shown_count - 1)]
119             else:
120                 error("No images to show found. Run rescan_fs.py "
121                       "or decrease min_delay. Abort.")
122
123         program_options = ["xli", "-border", random.choice(borders),
124                            "-center", "-onroot", "-quiet", "-zoom", "auto",
125                            row.full_name.encode(fs_encoding)]
126
127         rc = subprocess.call(program_options)
128         if rc:
129             error("cannot execute xli!")
130         else:
131             row.last_shown = current_time
132
133     finally:
134         # Unlock and close the lock file
135         flock(lock_file, LOCK_UN)
136         lock_file.close()