hlctl/hlcl/herbstclient.go

191 lines
4.3 KiB
Go

// 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
}