如何用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
相关产品推荐
相关产品推荐

