Merge branch 'master' of git+ssh://github.com/adefaria/clearscm
[clearscm.git] / bin / setbg
1 #!/usr/bin/perl
2
3 =pod
4
5 =head1 NAME $RCSfile: setbg,v $
6
7 Set background
8
9 =head1 VERSION
10
11 =over
12
13 =item Author
14
15 Andrew DeFaria <Andrew@ClearSCM.com>
16
17 =item Revision:
18
19 $Revision: 1.10 $
20
21 =item Created:
22
23 Fri Mar 18 01:14:38 PST 2005
24
25 =item Modified:
26
27 $Date: 2012/11/09 15:31:30 $
28
29 =back
30
31 =head1 SYNOPSIS
32
33  Usage: setbg [-u|sage] [-h|elp] [-ve|rbose] [-d|ebug] [-s|leep <n>]
34               [-bgdirs <bgdir> -bgdirs ...]
35
36  Where:
37
38  -u|sage:      Displays this usage
39  -h|elp:       Display full help
40  -ve|rbose:    Be verbose
41  -d|ebug:      Output debug messages
42
43  -s|leep:      Number of minutes to sleep between setting the background
44                (Default: 1 hour)
45  -l|ockscreen: Change lockscreen backround (Default: False)
46  -b|gdirs:     Directories to scan for images
47
48 =head1 DESCRIPTION
49
50 This script sets the background image randomly based on images found in bgdirs.
51 Note if this script is run again it senses that it was previously run and sends
52 the previous script a SIGUSR2 which the script intrprets as "Change the
53 background now", then exits.
54
55 Data is written to the following files:
56
57  ~/.setbg:      Contains the filename of the current background image
58  ~/.setbg.hist  Contains a history of all images displayed for this run
59  ~/.setbg.stats Contains statistical information for the current run
60
61 Also note that this script will process a SIGUSR1 to mean "re-evaluate the
62 contents of the bgdirs incase it has changed and display a new image". This is
63 useful for script to be able to alert setbg that something has changed. For
64 example, a script named rmbg might look at ~/.setbg to get the name of the
65 current background image file and remove it then signal setbg with SIGUSR1 to
66 have it re-evaluate the state of bgdirs.
67
68 Finally setbg will perform the this re-evaluation at midnight everyday. This is
69 useful because we point setbg to look at -bgdirs from Dropbox where Camera
70 Uploads is included and new pictures can arrive everyday.
71
72 =cut
73
74 use strict;
75 use warnings;
76 use experimental qw(signatures);
77
78 use FindBin;
79 use Getopt::Long;
80 use Proc::ProcessTable;
81 use File::Spec;
82 use CGI qw/:standard/;
83
84 use lib "$FindBin::Bin/../lib";
85
86 use Pod::Usage;
87
88 use DateUtils;
89 use Display;
90 use Logger;
91 use Utils;
92
93 my $VERSION  = '$Revision: 1.12 $';
94   ($VERSION) = ($VERSION =~ /\$Revision: (.*) /);
95
96 my $processes = Proc::ProcessTable->new;
97 my %opts = (
98   sleep      => 60,
99   lockscreen => 0,
100   usage      => sub { pod2usage },
101   help       => sub { pod2usage (-verbose => 2)},
102   verbose    => sub { set_verbose },
103   debug      => sub { set_debug },
104 );
105
106 my %totals;
107
108 sub displayStats() {
109   my $statsFile = Logger->new(
110     name      => ".$FindBin::Script.stats",
111     path      => $ENV{HOME},
112     extension => '',
113   );
114
115   $statsFile->log('At ' . localtime());
116   $statsFile->log('Sleep: ' . $opts{sleep});
117   $statsFile->log('Image directories:');
118
119   for (my $i = 0; $i < scalar @{$opts{bgdirs}}; $i++) {
120     $statsFile->log("\t$opts{bgdirs}[$i]: $opts{bgdircnt}[$i]")
121   } # for
122
123   Stats \%totals, $statsFile;
124
125   return;
126 } # displayStats
127
128 sub fillPictures($signame = undef) {
129   my @images;
130
131   $totals{bgdirs} = 0;
132
133   for (@{$opts{bgdirs}}) {
134     my ($status, @pics) = Execute "find \"$_/\" -type f";
135
136     chomp @pics;
137
138     push @images, grep(/jpg$|png$|gif$/i, @pics);
139
140     @pics = grep(/jpg$|png$|gif$/i, @pics);
141
142     push @{$opts{bgdircnt}}, scalar @pics;
143
144     $totals{bgdirs}++;
145   } # for
146
147   $totals{images} = scalar @images;
148
149   displayStats;
150
151   return @images;
152 } # fillPictures
153
154 sub writeHistory($msg) {
155   open my $hist, '>>', "$ENV{HOME}/.$FindBin::Script.hist"
156     or error "Unable to open $ENV{HOME}/.$FindBin::Script.hist for append - $!", 1;
157
158   $msg = localtime() . $msg;
159
160   print $hist "$msg\n";
161
162   close $hist;
163 } # writeHistory
164
165 sub writeSetBG($filename, $image) {
166   open my $file, '>', $filename
167     or error "Unable to open $filename for writing - $!", 1;
168
169   print $file "$image\n";
170
171   close $file;
172 } # writeSetBG
173
174 sub updateSetBG($bgimage, $lockimage) {
175   writeSetBG "$ENV{HOME}/.$FindBin::Script", $bgimage;
176   writeSetBG "$ENV{HOME}/.$FindBin::Script.lock", $lockimage;
177
178   my $msg  = ":$bgimage";
179      $msg .= " lock:$lockimage" if $opts{lockscreen};
180
181   writeHistory $msg;
182
183   return;
184 } # updateSetBG
185
186 sub SwitchWallPaper($saigname) {
187   # We don't need to do anything here, just handle the interrupt and
188   # let the while loop continue.
189   debug 'SwitchWallPaper: Interrupt received';
190   displayStats;
191
192   return;
193 } # SwitchWallPaper
194
195 ## Main
196 verbose "$FindBin::Script v$VERSION";
197
198 my @argvCopy = @ARGV;
199
200 GetOptions (
201   \%opts,
202   'usage',
203   'help',
204   'verbose',
205   'debug',
206   'sleep=i',
207   'lockscreen',
208   'bgdirs=s@',
209   'mate',
210 ) || Usage;
211
212 local $0 = "$FindBin::Script " . join ' ', @argvCopy;
213
214 for my $process (@{$processes->table}) {
215   if ($process->cmndline =~ /setbg/ and
216       $process->pid != $$) { 
217     kill 12, $process->pid;
218
219     exit 0;
220   } # if
221 } # for
222
223 for (my $i = 0; $i < scalar @{$opts{bgdirs}}; $i++) {
224   error "$opts{bgdirs}[$i] is not a directory", 1 unless -d $opts{bgdirs}[$i];
225
226   $opts{bgdirs}[$i] = File::Spec->rel2abs ($opts{bgdirs}[$i]);
227 } # for
228
229 # Using gsettings
230 my $setbg       = "gsettings";
231
232 my ($setbgOpts, $setLockOpts);
233
234 if ($opts{mate}) {
235   $setbgOpts   = 'set org.mate.background picture-filename ';
236   $setLockOpts = 'set org.mate.screensaver picture-filename ';
237 } else {
238   $setbgOpts   = "set org.gnome.desktop.background picture-uri \"file://";
239   $setLockOpts = "set org.gnome.desktop.screensaver picture-uri \"file://";
240 } # if
241
242 my @images = fillPictures;
243
244 Usage "No images to display. Must specify -bgdirs" unless @images;
245
246 $SIG{USR2} = \&SwitchWallPaper;
247 $SIG{USR1} = \&fillPictures;
248
249 my $debugger = $DB::OUT;
250 my $today;
251
252 truncate "$ENV{HOME}/.$FindBin::Script.hist", 0;
253
254 EnterDaemonMode unless defined $DB::OUT;
255
256 my $pickNewImages = 1;
257 my ($bgimage, $lockimage);
258
259 while () {
260   if ($pickNewImages) {
261     $bgimage   = escapeHTML ($images[int (rand $#images)]);
262     $lockimage = escapeHTML ($images[int (rand $#images)]);
263   } # if
264
265   my $monitorIsOn;
266
267   my ($status, @output) = Execute("xset q | grep Monitor | awk '{print \$3}'");
268
269   if ($status or $output[0] eq 'Off') {
270     writeHistory ":Monitor off, not setting background to $bgimage - will keep trying";
271
272     $pickNewImages = 0;
273   } else {
274     $pickNewImages = 1;
275
276     my $cmd = $opts{mate} ? "$setbg $setbgOpts\"$bgimage\" 2> /dev/null"
277                           : "$setbg $setbgOpts$bgimage\" 2> /dev/null";
278
279     ($status, @output) = Execute $cmd;
280
281     if ($status) {
282       error "Trying to set background - command used \"$cmd\"\n\nOutput\n\n" . 
283         join "\n", @output;
284       $totals{errors}++;
285     } else {
286       $totals{'Images displayed'}++;
287     } # if
288
289     if ($opts{lockscreen}) {
290       $cmd = $opts{mate} ? "$setbg $setLockOpts\"$lockimage\" 2> /dev/null"
291                          : "$setbg $setLockOpts$lockimage\" 2> /dev/null";
292
293       ($status, @output) = Execute $cmd;
294
295       if ($status != 0) {
296         error "Trying to set lock screen - command used \"$cmd\"\n\nOutput\n\n" .
297           join "\n", @output;
298         $totals{errors}++;
299       } else {
300         $totals{'Lock screens displayed'}++;
301       } # if
302     } # if
303
304     updateSetBG $bgimage, $lockimage;
305   } # if
306
307   displayStats;
308
309   $today = YMD;
310
311   sleep $opts{sleep} * 60;
312
313   if ($today ne YMD){
314     @images = fillPictures;
315
316     displayStats;
317   } # if
318 } # while