#!/usr/bin/env perl -w
#
# Copyright (c) 2003 Guido Berhoerster
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# FvwmRandomBack
#
# A FVWM module to display a random image in the root window.
#
# View the manpage with pod2man:
# pod2man -c "FVWM Module" FvwmRandomBack | nroff -man | less
#
# Note: This module requires PerlMagick (a Perl interface to ImageMagick which
#       is included in the official ImageMagick distribution) to be properly
#       installed.
#

use 5.004;
use strict;
use lib `fvwm-perllib dir`;
use FVWM::Module::Toolkit qw(POSIX File::Find Image::Magick);

my @image_list;

# initialize module, set Debug to 1 or 2 for debug output
my $module = new FVWM::Module(
	Name  => "FvwmRandomBack",
	Debug => 0,
);

# default configuration options
my $configTracker = $module->track(
	"ModuleConfig",
	DefaultConfig => { ImagePath    => undef,
			   Magnify      => undef,
			   RetainPixmap => undef }
);

# get configuration
my $config = $configTracker->data;

# create ImageMagick object
my $image = Image::Magick->new;

# find pictures, all files below the specified path are "pinged" by ImageMagick
# so that all images will be recognized independently of their filename
# extension
sub FindImages {
	# see ImageMagick documentation for filetype abbreviations
	my $file_types = "GIF|JPEG|PNG|XPM";
	my $image_format;
	my $filename = $File::Find::name;
	if ((-r $filename) && (-f $filename) && ($filename !~ /\.xvpics/)) {
		$image_format = $image->Ping($filename);
		push @image_list, $filename
			if (($image_format) && ($image_format =~
				/($file_types)/));
	}
}

# convert, modify and display a random image in the root window
sub SetRootWindow {
	my $fvwmroot = "fvwm-root";
	my $tmp_name;
	my $image_pos;
	my $image_width;
	my $image_height;

# get dimensions of current viewport
	my $pageTracker = $module->track("PageInfo");
	my $page = $pageTracker->data;
	my $vp_width = $page->{'vp_width'};
	my $vp_height = $page->{'vp_height'};

	$module->internalDie("The image path was not specified.")
		unless defined $config->{ImagePath};

	$module->internalDie("$config->{ImagePath} does not exist.")
		unless (-d $config->{ImagePath});

	$fvwmroot = "$fvwmroot -r"
		if defined $config->{RetainPixmap};

	srand;

# search for images
	$module->debug("Searching for images in $config->{ImagePath}.");
	find (\&FindImages, $config->{ImagePath});

	$module->internalDie("No images found in $config->{ImagePath}.")
		unless @image_list;

# select random image
	$image_pos = int(rand(@image_list));

	$image->Read($image_list[$image_pos]);

	($image_width, $image_height) =
		$image->Get('width', 'height');

# if the image is smaller than the current viewport it will be tiled unless
# Magnify is specified; otherwise it will be resized proportionally so that the
# width or height fits the viewport and superfluous parts on the top an bottom
# or on left and right will be cropped
	if ((($image_width >= $vp_width) && ($image_height >= $vp_height))
		|| defined $config->{Magnify}) {
		$module->debug("Picture is either bigger than the viewport size"
			. " or it is smaller and the Magnify option was"
			. " specified. Resizing image.");
		if ($image_width > $image_height) {
			$image->Resize(geometry=>"x" . $vp_height);
		}
		else {
			$image->Resize(geometry=>$vp_width . "x");
		}
	}

	$image->Set(gravity=>'center');

	$image->Crop(geometry=>"$vp_width" . "x" . "$vp_height");

# try to create a temporary file and retry if not successful
	do {
		$tmp_name = tmpnam()
	}
	until sysopen TMPFILE, $tmp_name, O_WRONLY | O_CREAT | O_EXCL;

# add a handler to delete the temporary file when exiting or dying
	END {
		unlink $tmp_name
			or
			$module->internalDie("Could not remove $tmp_name: $!");
	}

	$image->Write(file=>\*TMPFILE, magick=>"png");

	close TMPFILE;

	system "$fvwmroot $tmp_name";

	$module->showMessage("Background image: $image_list[$image_pos].");
}

# main routine
$module->debug("Starting " . $module->name . ".");
SetRootWindow();
# undef $image;
$module->debug("Exiting " . $module->name . ".");


__END__

=head1 NAME

B<FvwmRandomBack> - a FVWM module to display a random image in the root window

=head1 SYNOPSIS

C<Module FvwmRandomBack>

B<FvwmRandomBack> can only be invoked by fvwm. Command line invocation of the
module will not work.

=head1 DESCRIPTION

B<FvwmRandomBack> recursively searches a directory for all images in GIF, JPEG,
PNG, or XPM format, selects one randomly and displays it in the root window.

If the image is bigger than the current viewport it will be resized and cropped
accordingly. Otherwise it will either be tiled or optionally magnified.
B<FvwmRandomBack> uses ImageMagick's perl module in order to modify and convert
the image and B<fvwm-root> to set the root window.

=head1 INVOCATION

B<FvwmRandomBack> can be invoked by fvwm with the command 'C<Module
FvwmRandomBack>' at any time. After setting the root window to a random image
it will exit. It might be convenient to invoke B<FvwmRandomBack> during
initialization by adding

    AddToFunc StartFunction I Module FvwmRandomBack

to the fvwm configuration file.

B<FvwmRandomBack> needs to be executable and must reside in a directory which
is listed in C<ModulePath>.

=head1 OPTIONS

=over

=item *FvwmRandomBack: I<ImagePath> F<path>

The path where the images can be found.

=item *FvwmRandomBack: I<Magnify>

Magnify images which are smaller than the current viewport instead of tiling
them.

=item *FvwmRandomBack: I<RetainPixmap>

Causes FvwmBacker to retain and publish the Pixmap with which the background has
been set. For more information see L<FvwmTheme> and L<fvwm-root>.

=head1 AUTHOR

Guido Berhoerster <guido+guido.berhoerster.name@berhoerster.name>
