Skip to content

Commit

Permalink
feat: add Hyprpaper backend
Browse files Browse the repository at this point in the history
Hyprpaper is designed primarily for Hyprland but works for all wlroots based compositors. It runs as a daemon and is controlled via a socket, which prevents flashes during the transition between images.

The Hyprpaper backend is placed first in the order of possible backends but will only be chosen if it is already running.

Only two modes are supported, cover (the default) and contain, so all other modes are mapped to one of the two.

Due to a bug in Hyprpaper, the latest release (v0.6.0) is not compatible because it does not support wildcard display selection. Until a new release is published, only builds directly from Git will work correctly.
  • Loading branch information
FossoresLP authored and xyproto committed Aug 6, 2024
1 parent d6b7784 commit 47c1ea1
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 0 deletions.
1 change: 1 addition & 0 deletions backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package wallutils
// WMs contains all available backends for changing the wallpaper
// Some backends may require cgo (sway + x11)
var WMs = []WM{
&Hyprpaper{},
&Sway{},
&Deepin{},
&Xfce4{},
Expand Down
1 change: 1 addition & 0 deletions backend_nocgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package wallutils
// WMs contains all available backends for changing the wallpaper
// Only backends that do not require cgo should be included here.
var WMs = []WM{
&Hyprpaper{},
//&Sway{},
&Deepin{},
&Xfce4{},
Expand Down
114 changes: 114 additions & 0 deletions hyprpaper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package wallutils

import (
"fmt"
"net"
"path"

"github.com/xyproto/env/v2"
)

// Hyprpaper compatible windowmanager
type Hyprpaper struct {
mode string
verbose bool
sock string
}

// Name returns the name of this window manager or desktop environment
func (sb *Hyprpaper) Name() string {
return "Hyprpaper"
}

// ExecutablesExists checks if executables associated with this backend exists in the PATH
func (sb *Hyprpaper) ExecutablesExists() bool {
return which("hyprpaper") != ""
}

// Running examines environment variables to try to figure out if this backend is currently running
func (sb *Hyprpaper) Running() bool {
inst := env.Str("HYPRLAND_INSTANCE_SIGNATURE")
sb.sock = path.Join("/tmp/hypr", inst, ".hyprpaper.sock")
return exists(sb.sock)
}

// SetMode will set the current way to display the wallpaper (stretched, tiled etc)
func (sb *Hyprpaper) SetMode(mode string) {
sb.mode = mode
}

// SetVerbose can be used for setting the verbose field to true or false.
// This will cause this backend to output information about what is is doing on stdout.
func (sb *Hyprpaper) SetVerbose(verbose bool) {
sb.verbose = verbose
}

// SetWallpaper sets the desktop wallpaper, given an image filename.
// The image must exist and be readable.
func (sb *Hyprpaper) SetWallpaper(imageFilename string) error {
if !exists(imageFilename) {
return fmt.Errorf("no such file: %s", imageFilename)
}

// Connect to the Hyprpaper socket
sock, err := net.DialUnix("unix", nil, &net.UnixAddr{Name: sb.sock, Net: "unix"})

if err != nil {
return err
}

defer sock.Close()

// Preload the image
if err = runHyprCmd(sock, "preload "+imageFilename); err != nil {
return err
}

// Set the wallpaper mode
mode := defaultMode
if sb.mode != "" {
mode = sb.mode
}

switch mode {
case "center", "tile", "fill", "stretch", "scale", "scaled", "zoom", "zoomed", "stretched":
mode = ""
case "fit":
mode = "contain:"
default:
// Invalid and unrecognized desktop wallpaper mode
return fmt.Errorf("invalid desktop wallpaper mode for swaybg: %s", mode)
}

// Set the wallpaper
if err = runHyprCmd(sock, "wallpaper ,"+mode+imageFilename); err != nil {
return err
}

// Unload unused images
return runHyprCmd(sock, "unload unused")
}

func runHyprCmd(sock *net.UnixConn, cmd string) error {
_, err := sock.Write([]byte(cmd))

if err != nil {
return err
}

buf := [32]byte{}

n, err := sock.Read(buf[:])

if err != nil {
return err
}

res := string(buf[:n])

if res != "ok" {
return fmt.Errorf("cmd %q failed: %s", cmd, res)
}

return nil
}

0 comments on commit 47c1ea1

Please sign in to comment.