/* wallpaper - Small application to set a desktop background on all displays.
 * Almost entirely stolen from lightdm.
 *
 * Copyright (C) 2011 Canonical Ltd.
 * Copyright (C) 2010-2011 Robert Ancell.
 * Author: Robert Ancell <robert.ancell@canonical.com>
 * 
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
 * license.
 */

#include <gdk/gdkx.h>
#include <cairo-xlib.h>
#include <sys/stat.h>
#include <gtk/gtk.h>

static cairo_surface_t *
create_root_surface (GdkScreen *screen)
{
    gint number, width, height;
    Display *display;
    Pixmap pixmap;
    cairo_surface_t *surface;

    number = gdk_screen_get_number (screen);
    width = gdk_screen_get_width (screen);
    height = gdk_screen_get_height (screen);

    /* Open a new connection so with Retain Permanent so the pixmap remains when the greeter quits */
    gdk_flush ();
    display = XOpenDisplay (gdk_display_get_name (gdk_screen_get_display (screen)));
    if (!display)
    {
        g_warning ("Failed to create root pixmap");
        return NULL;
    }
    XSetCloseDownMode (display, RetainPermanent);
    pixmap = XCreatePixmap (display, RootWindow (display, number), width, height, DefaultDepth (display, number));
    XCloseDisplay (display);

    /* Convert into a Cairo surface */
    surface = cairo_xlib_surface_create (GDK_SCREEN_XDISPLAY (screen),
                                         pixmap,
                                         GDK_VISUAL_XVISUAL (gdk_screen_get_system_visual (screen)),
                                         width, height);

    /* Use this pixmap for the background */
    XSetWindowBackgroundPixmap (GDK_SCREEN_XDISPLAY (screen),
                                RootWindow (GDK_SCREEN_XDISPLAY (screen), number),
                                cairo_xlib_surface_get_drawable (surface));


    return surface;
}

int main (int argc, char** argv) {
	GdkScreen *screen;
	int monitor;
	int i;
	GError *error = NULL;
	GdkPixbuf *background_pixbuf = NULL;
	GdkRectangle monitor_geometry;
	cairo_t *c;
	cairo_surface_t *surface;
	struct stat st;

	gtk_init (&argc, &argv);

	if (argc != 2 || stat(argv[1], &st) != 0) {
		g_error ("First parameter must be an existing background");
	}
	background_pixbuf = gdk_pixbuf_new_from_file (argv[1], &error);

	if (!background_pixbuf) {
		g_error ("Failed to load background: %s", error->message);
		return 1;
	}
	g_clear_error (&error);
	for (i = 0; i < gdk_display_get_n_screens (gdk_display_get_default ()); i++) {
		screen = gdk_display_get_screen (gdk_display_get_default (), i);
		surface = create_root_surface (screen);
		c = cairo_create (surface);
		for (monitor = 0; monitor < gdk_screen_get_n_monitors (screen); monitor++) {
			gdk_screen_get_monitor_geometry (screen, monitor, &monitor_geometry);
			int img_w = gdk_pixbuf_get_width(background_pixbuf);
			int img_h = gdk_pixbuf_get_height(background_pixbuf);
			float monitor_aspect_ratio = (float) monitor_geometry.width / monitor_geometry.height;
			float img_aspect_ratio = (float) img_w / img_h;
			GdkPixbuf *subpixbuf = NULL;

            /* Crop image to fit the aspect ratio of monitor */
			if (monitor_aspect_ratio > img_aspect_ratio) {
				/* cut areas in upper and bottom edge */
				int cut_h = img_h - img_w / monitor_aspect_ratio;
				subpixbuf = gdk_pixbuf_new_subpixbuf (background_pixbuf, 0, cut_h/2, img_w, img_h-cut_h);
			} else if (monitor_aspect_ratio < img_aspect_ratio) {
				/* cut areas in left and right edge */
				int cut_w = img_w - img_h * monitor_aspect_ratio;
				subpixbuf = gdk_pixbuf_new_subpixbuf (background_pixbuf, cut_w/2, 0, img_w-cut_w, img_h);
			}
			GdkPixbuf *pixbuf;
			if (subpixbuf != NULL) 
				pixbuf = gdk_pixbuf_scale_simple (subpixbuf, 
					monitor_geometry.width, monitor_geometry.height,
					GDK_INTERP_BILINEAR);
			else 
				pixbuf = gdk_pixbuf_scale_simple (background_pixbuf,
					monitor_geometry.width, monitor_geometry.height,
					GDK_INTERP_BILINEAR);
			gdk_cairo_set_source_pixbuf (c, pixbuf, monitor_geometry.x, monitor_geometry.y);
			g_object_unref (pixbuf);
			if (subpixbuf) 
				g_object_unref (subpixbuf);
			cairo_paint (c);
		}
		cairo_destroy (c);
		gdk_flush ();
		XClearWindow (GDK_SCREEN_XDISPLAY (screen), RootWindow (GDK_SCREEN_XDISPLAY (screen), i));
	}
	gtk_main ();
	return 0;
}
