You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Go为X11/Wayland多屏幕设置对应尺寸的背景图?

多屏幕分屏壁纸设置的Go实现方案

针对你遇到的多屏尺寸不同时feh无法分别设置壁纸的问题,下面分X11和Wayland环境给出具体的Go实现步骤:

1. 获取多屏幕的分辨率与设备信息

这是基础,需要拿到每个屏幕的独立尺寸和设备名称,不同环境的获取方式不同:

X11环境

调用xrandr解析输出获取屏幕信息,比直接操作X11协议更简洁:

import (
    "os/exec"
    "strings"
    "strconv"
)

// 获取X11所有屏幕的名称和尺寸
func getX11ScreenInfos() ([]struct{ Name string; Width, Height int }, error) {
    out, err := exec.Command("xrandr", "--query").Output()
    if err != nil {
        return nil, err
    }

    var screens []struct{ Name string; Width, Height int }
    lines := strings.Split(string(out), "\n")
    for _, line := range lines {
        if strings.Contains(line, " connected") {
            parts := strings.Fields(line)
            name := parts[0]
            resPart := parts[2]
            if strings.Contains(resPart, "x") {
                res := strings.Split(resPart, "x")
                width, _ := strconv.Atoi(res[0])
                height, _ := strconv.Atoi(res[1])
                screens = append(screens, struct{ Name string; Width, Height int }{name, width, height})
            }
        }
    }
    return screens, nil
}

Wayland环境

Wayland没有统一的屏幕查询API,需根据当前 compositor(如Hyprland、Sway)调用对应工具:

import (
    "os/exec"
    "encoding/json"
)

// Hyprland屏幕信息结构
type HyprlandMonitor struct {
    Name   string `json:"name"`
    Width  int    `json:"width"`
    Height int    `json:"height"`
}

func getHyprlandMonitors() ([]HyprlandMonitor, error) {
    out, err := exec.Command("hyprctl", "monitors", "-j").Output()
    if err != nil {
        return nil, err
    }
    var monitors []HyprlandMonitor
    _ = json.Unmarshal(out, &monitors)
    return monitors, nil
}

// Sway屏幕信息结构
type SwayOutput struct {
    Name string `json:"name"`
    Rect struct {
        Width  int `json:"width"`
        Height int `json:"height"`
    } `json:"rect"`
}

func getSwayOutputs() ([]SwayOutput, error) {
    out, err := exec.Command("swaymsg", "-t", "get_outputs", "-r").Output()
    if err != nil {
        return nil, err
    }
    var outputs []SwayOutput
    _ = json.Unmarshal(out, &outputs)
    return outputs, nil
}

2. 为每个屏幕生成适配尺寸的图像

用github.com/disintegration/imaging库裁剪/缩放原图到目标屏幕尺寸,保持图像比例:

import (
    "github.com/disintegration/imaging"
    "os"
    "fmt"
)

// 生成适配屏幕尺寸的临时壁纸文件
func generateAdaptedWallpaper(srcPath string, width, height int) (string, error) {
    srcImg, err := imaging.Open(srcPath)
    if err != nil {
        return "", err
    }

    // 按屏幕尺寸填充裁剪,避免拉伸变形
    adaptedImg := imaging.Fill(srcImg, width, height, imaging.Center, imaging.Lanczos)

    tmpPath := fmt.Sprintf("/tmp/wallpaper_%dx%d.png", width, height)
    if err := imaging.Save(adaptedImg, tmpPath); err != nil {
        return "", err
    }
    return tmpPath, nil
}

3. 分屏幕设置壁纸

X11环境:替换feh为xwallpaper

feh不支持单屏独立设置,改用xwallpaper(需提前安装),它支持指定输出设备:

import "os/exec"

func setX11ScreenWallpaper(screenName, imgPath string) error {
    return exec.Command("xwallpaper", "--output", screenName, "--stretch", imgPath).Run()
}

Wayland环境:调用对应compositor的命令

  • Hyprland:用hyprctl直接指定monitor设置壁纸
func setHyprlandWallpaper(monitorName, imgPath string) error {
    return exec.Command("hyprctl", "wallpaper", monitorName, imgPath).Run()
}
  • Sway:用swaymsg指定output设置
func setSwayWallpaper(outputName, imgPath string) error {
    return exec.Command("swaymsg", "output", outputName, "bg", imgPath, "fill").Run()
}

4. 整合完整流程

import (
    "os"
    "fmt"
)

func main() {
    srcWallpaper := "/path/to/your/wallpaper.jpg"
    sessionType := os.Getenv("XDG_SESSION_TYPE")

    switch sessionType {
    case "x11":
        screens, err := getX11ScreenInfos()
        if err != nil {
            fmt.Println("Failed to get X11 screens:", err)
            return
        }
        for _, scr := range screens {
            imgPath, err := generateAdaptedWallpaper(srcWallpaper, scr.Width, scr.Height)
            if err != nil {
                fmt.Printf("Failed to generate wallpaper for %s: %v\n", scr.Name, err)
                continue
            }
            defer os.Remove(imgPath) // 用完清理临时文件
            if err := setX11ScreenWallpaper(scr.Name, imgPath); err != nil {
                fmt.Printf("Failed to set wallpaper for %s: %v\n", scr.Name, err)
            }
        }
    case "wayland":
        // 判断当前Wayland compositor
        if _, exists := os.LookupEnv("HYPRLAND_INSTANCE_SOCKET"); exists {
            monitors, err := getHyprlandMonitors()
            if err != nil {
                fmt.Println("Failed to get Hyprland monitors:", err)
                return
            }
            for _, m := range monitors {
                imgPath, err := generateAdaptedWallpaper(srcWallpaper, m.Width, m.Height)
                if err != nil {
                    fmt.Printf("Failed to generate wallpaper for %s: %v\n", m.Name, err)
                    continue
                }
                defer os.Remove(imgPath)
                if err := setHyprlandWallpaper(m.Name, imgPath); err != nil {
                    fmt.Printf("Failed to set wallpaper for %s: %v\n", m.Name, err)
                }
            }
        } else if _, exists := os.LookupEnv("SWAYSOCK"); exists {
            outputs, err := getSwayOutputs()
            if err != nil {
                fmt.Println("Failed to get Sway outputs:", err)
                return
            }
            for _, o := range outputs {
                imgPath, err := generateAdaptedWallpaper(srcWallpaper, o.Rect.Width, o.Rect.Height)
                if err != nil {
                    fmt.Printf("Failed to generate wallpaper for %s: %v\n", o.Name, err)
                    continue
                }
                defer os.Remove(imgPath)
                if err := setSwayWallpaper(o.Name, imgPath); err != nil {
                    fmt.Printf("Failed to set wallpaper for %s: %v\n", o.Name, err)
                }
            }
        } else {
            fmt.Println("Unsupported Wayland compositor")
        }
    default:
        fmt.Println("Unsupported session type")
    }
}

关键注意事项

  • 临时文件务必在设置完成后删除,避免磁盘冗余
  • Wayland生态碎片化严重,需要针对不同compositor做兼容判断
  • X11下如果不想依赖xwallpaper,也可以直接通过X11协议设置根窗口背景,但实现复杂,不如xwallpaper便捷
  • 可以考虑将生成的壁纸缓存起来,避免重复处理相同尺寸的图像

内容的提问来源于stack exchange,提问作者dimus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 04:55:13