// package hlcl contains logic for interacting with the // herbstclient package hlcl import ( "bytes" "fmt" "io" "os/exec" "strconv" "strings" "sectorinf.com/emilis/hlctl/ctllog" "sectorinf.com/emilis/hlctl/hlcl/cmd" ) var log = ctllog.Logger{}.New("hlcl", ctllog.Purple) func runGeneric(command string, stdin string, args ...string) (string, error) { cmd := exec.Command(command, args...) // log.Printf("Running command [%s] with args: %v", command, args) var stdout, stderr = bytes.Buffer{}, bytes.Buffer{} cmd.Stderr = &stderr cmd.Stdout = &stdout if stdin != "" { pipe, err := cmd.StdinPipe() if err != nil { return "", fmt.Errorf("stdin pipe: %w", err) } if err := cmd.Start(); err != nil { return "", fmt.Errorf("start: %w", err) } io.WriteString(pipe, stdin+"\n") _, err = pipe.Write([]byte(stdin)) if err != nil { return "", fmt.Errorf("write stdin: %w", err) } pipe.Close() } else { if err := cmd.Start(); err != nil { return "", fmt.Errorf("start: %w", err) } } if err := cmd.Wait(); err != nil { status, ok := err.(*exec.ExitError) if !ok { return "", fmt.Errorf("%s exec error: %w", command, err) } exitCode := status.ExitCode() return "", fmt.Errorf( "%s error (%d): %s", command, exitCode, strings.TrimSpace(stderr.String()), ) } return strings.TrimSpace(stdout.String()), nil } func run(command cmd.Command, args ...string) (string, error) { args = append([]string{command.String()}, args...) return runGeneric("herbstclient", "", args...) } // query executes a command and pretty formats and error message // based on the command's Pretty(), and returns the output func query(c cmd.Command, args ...string) (string, error) { value, err := run(c, args...) if err != nil { return value, fmt.Errorf( "%s(%s): %w", c.Pretty(), strings.Join(args, ", "), err, ) } return value, nil } // execute: wrapper for [query], discarding the stdout result func execute(c cmd.Command, args ...string) error { _, err := query(c, args...) return err } func NewAttr(path string, value string, aType cmd.AttributeType) error { return execute(cmd.NewAttr, aType.String(), path, value) } func SetAttr(path string, value string) error { return execute(cmd.SetAttr, path, value) } func GetAttr(path string) (string, error) { return query(cmd.GetAttr, path) } func AttrType(path string) (string, error) { return query(cmd.AttrType, path) } func KeyUnbind() error { return execute(cmd.Keyunbind, "--all") } func MouseUnbind() error { return execute(cmd.Mouseunbind, "--all") } func ListMonitors() ([]Screen, error) { out, err := query(cmd.ListMonitors) if err != nil { return nil, err } outs := strings.Split(out, "\n") scrs := make([]Screen, len(outs)) for i, line := range outs { scrs[i] = Screen{}.FromString(line) } return scrs, nil } func bindCMD( bindCommand cmd.Command, keys []string, command cmd.Command, args ...string, ) error { bindKeys := strings.Join(keys, "-") args = append([]string{bindKeys, command.String()}, args...) return execute(bindCommand, args...) } func Keybind(keys []string, command cmd.Command, args ...string) error { return bindCMD(cmd.Keybind, keys, command, args...) } func Mousebind(keys []string, command cmd.Command, args ...string) error { return bindCMD(cmd.Mousebind, keys, command, args...) } func Set(name, value string) error { return execute(cmd.Set, name, value) } func UseIndex(index uint8) error { return execute(cmd.UseIndex, strconv.Itoa(int(index))) } func MergeTag(name string) error { return execute(cmd.MergeTag, name) } func MoveIndex(index uint8) error { return execute(cmd.MoveIndex, strconv.Itoa(int(index))) } func TextWidth(font, message string) (string, error) { return runGeneric("textwidth", "", font, message) } func Rule(name string, args ...string) error { return execute(cmd.Rule, append([]string{name}, args...)...) } func Unrule() error { return execute(cmd.Unrule, "-F") } func SilentSpawn(command string, args ...string) error { return execute(cmd.Spawn, append([]string{command}, args...)...) } func Lock() error { return execute(cmd.Lock) } func Unlock() error { return execute(cmd.Unlock) } func AddTag(name string) error { return execute(cmd.AddTag, name) } func Dzen2(message string, args ...string) error { _, err := runGeneric("dzen2", message, args...) return err }