4

I make a custom wallpaper setter for window managers in Go. Currently I use github.com/xyproto/wallutils project, which in turn depends on feh.

For one screen I can use its dimensions to create the image of exact size, and all works well, but if there are several screens with different dimensions connected to a computer, this approach does not work.

For the exact image I use feh's 'fill' option, which would work correctly only for one of the monitors.

What I want to do is to create images of correct dimensions for each screen and send them as background images to corresponding screens either in X11 or Wayland. How can I achieve this in Go?

dimus
  • 8,712
  • 10
  • 45
  • 56
  • You have a working code (as you claim) for a single monitor. What is the problem? You don't know how to detect if and what displays are available? – icza Dec 10 '22 at 17:15
  • 1
    I love how [ChatGPT](https://chat.openai.com/chat) confidently suggests the package "`xgraphics/window`" to get the `Screen`(s) with `screens, err := window.Screens()`. Except, as far as I can see, "`xgraphics/window`" does not exist! – VonC Dec 10 '22 at 21:28
  • @icza, I added an explanation in the question why current feh-based approach works only for one monitor. – dimus Dec 11 '22 at 04:28
  • Please share more details, like your attempts to resolve the problem – Nico Haase Dec 11 '22 at 09:10
  • @NicoHaase, currently, for a single display, I use feh binding from wallutils https://codeberg.org/dimus/muro/src/commit/e5876d787c0fb6f683548404c7c5a917deccae30/pkg/muro.go#L171 However this approach does not allow sending image individually to each display. – dimus Dec 12 '22 at 11:34
  • 2
    As usual, please add **all** clarification to your question by editing it – Nico Haase Dec 12 '22 at 11:39

2 Answers2

3

Requirements:

  • send different wallpapers to different monitors
  • call from Go
  • use feh underneath

muro and wallutils

wallutils specifies a WM interface that provides, among other things, the SetWallpaper method. There are corresponding implementations of this interface for a number of different window managers.

The Go package muro in turn uses wallutils. Based on the flag WithAnyWindowManager it will either use wallutils' SetWallpaperCustom method which selects a concrete SetWallpaper implementation based on the detected window manager or just directly call SetWallpaper of the feh variant.

wallutils and feh

The specific display mode depends on how it is called, but SetWallpaper in wallutils feh.go would basically call feh in your case as follows:

    feh --bg-fill <image file name>

Two notes:

  • here feh sets the wallpaper on all screens to the very same image
  • therefore it's not directly supported to have different images

Also, wallutils' readme explicitly states:

Setting a wallpaper per monitor

Setting a wallpaper per monitor is not supported, yet. Currently, a wallpaper is set for all monitors.

see https://github.com/xyproto/wallutils#setting-a-wallpaper-per-monitor

Possible solution

Since you can determine monitors and resolution, we focus on sending the predefined images in the correct order as background images under use of feh to the appropriate screens.

feh itself supports setting different wallpapers per monitor. You just call feh with the different images having different resolutions. The order is guaranteed to be the same as determined by a call to xrandr --listmonitors.

After determining the order and taking it as a given, the simplest possible GO program would look something like this (see also wallutil's utility function run):

package main

import (
    "os/exec"
)

func main() {
    args := []string{"--bg-fill", "1.png", "2.png"}
    cmd := exec.Command("feh", args...)
    if _, err := cmd.CombinedOutput(); err != nil {
        panic(err)
    }
}

(tested with FluxBox window manager)

Provided feh works with the appropriate window manager and there are the two prepared images in the go directory, this is the simplest case. Of course, one could also programmatically determine the screens and dynamically adjust the call of feh.

Since feh does not work in every environment, wallutils provides concrete implementations of its WM interface for a number of window manager environments (Cinnamon, Deepin, Gnome, Mate, Pekwm, Plasma, Sway, Weston, Xfce4). This is of course very cool. However, if you wanted to create an MR for wallutils, you would probably have to do so in all variants, at least those that support it.

Stephan Schlecht
  • 26,556
  • 1
  • 33
  • 47
  • 1
    It worked for me as well in Qtile wm, thank you! I am going to pass your answer to the developers of wallutils, in case if they will decide to add this functionality there too. – dimus Dec 17 '22 at 15:21
  • 1
    I'm the wallutils developer and I would love to add this functionality, I just haven't gotten around to it yet. Thanks for using wallutils. :) – Alexander Jan 15 '23 at 12:42
  • Thank you for providing such a great library. :-) – Stephan Schlecht Jan 16 '23 at 17:27
0

You might consider vbsw/xlib, which includes:

That is inspired from the C implementation "Obtaining List of all Xorg Displays".

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • Thank you for the link, @VonC, wallutils also provide information about number of displays and their dimensions as well https://github.com/xyproto/wallutils/blob/main/backend.go#L37, what is missing for me is an ability to send an image to displays' background according to the dimensions of each of them. – dimus Dec 12 '22 at 11:28