1package wallutils
2
3import (
4	"errors"
5	"fmt"
6	"os"
7	"strings"
8)
9
10// WM is an interface with the functions that needs to be implemented for adding support for setting the wallpaper for a new WM or DE
11type WM interface {
12	Name() string
13	ExecutablesExists() bool
14	Running() bool
15	SetWallpaper(string) error
16	SetVerbose(bool)
17	SetMode(string)
18}
19
20// Wallpaper represents an image file that is part of a wallpaper collection (in a directory with several resolutions of the same image, for example)
21type Wallpaper struct {
22	CollectionName   string // the name of the directory containing this wallpaper, if it's not "pixmaps", "images" or "contents". May use the parent of the parent.
23	Path             string // full path to the image filename
24	Width            uint   // width of the image
25	Height           uint   // height of the image
26	PartOfCollection bool   // likely to be part of a wallpaper collection
27}
28
29// All backends should support these modes, if possible: stretch, fill, scale, tile, center
30const defaultMode = "stretch"
31
32// SetWallpaperCustom will set the given image filename as the wallpaper,
33// regardless of which display server, window manager or desktop environment is in use.
34func SetWallpaperCustom(imageFilename, mode string, verbose bool) error {
35	if !exists(imageFilename) {
36		return fmt.Errorf("no such file: %s", imageFilename)
37	}
38	var lastErr error
39	// Loop through all available WM structs
40	for _, wm := range WMs {
41		if wm.Running() && wm.ExecutablesExists() {
42			if verbose {
43				fmt.Printf("Using the %s backend.\n", wm.Name())
44			}
45			wm.SetVerbose(verbose)
46			if mode != "" && mode != defaultMode {
47				wm.SetMode(mode)
48			}
49			if err := wm.SetWallpaper(imageFilename); err != nil {
50				lastErr = err
51				switch wm.Name() {
52				case "Weston":
53					// If the current windowmanager is Weston, no method is currently available
54					return err
55				default:
56					if verbose {
57						fmt.Fprintf(os.Stderr, "failed: %v\n", err)
58					}
59					// If the wallpaper mode is wrong, don't try the next backend, but return the error
60					if strings.Contains(err.Error(), "invalid desktop wallpaper mode") {
61						return err
62					}
63					// Try the next one
64					continue
65				}
66			} else {
67				return nil
68			}
69		}
70	}
71	if lastErr != nil {
72		return fmt.Errorf("found no working method for setting the desktop wallpaper:\n%v", lastErr)
73	}
74	return errors.New("found no working method for setting the desktop wallpaper")
75
76}
77
78// SetWallpaperVerbose will set the desktop wallpaper, for any supported
79// windowmanager. The fallback is to use `feh`. The wallpaper mode is "fill".
80func SetWallpaperVerbose(imageFilename string, verbose bool) error {
81	return SetWallpaperCustom(imageFilename, defaultMode, verbose)
82}
83
84// SetWallpaper will set the desktop wallpaper, for any supported
85// windowmanager. The fallback is to use `feh`. The wallpaper mode is "fill".
86func SetWallpaper(imageFilename string) error {
87	return SetWallpaperCustom(imageFilename, defaultMode, false)
88}
89
90// Res returns the wallpaper resolution as a Res struct
91func (wp *Wallpaper) Res() *Res {
92	return NewRes(wp.Width, wp.Height)
93}
94
95// String returns a string with information about the wallpaper:
96// - if it's part of a wallpaper collection or not
97// - width
98// - height
99// - collection name
100// - path
101func (wp *Wallpaper) String() string {
102	star := " "
103	if wp.PartOfCollection {
104		star = "*"
105	}
106	return fmt.Sprintf("(%s) %dx%d\t%16s\t%s", star, wp.Width, wp.Height, wp.CollectionName, wp.Path)
107}
108