2023-01-07 16:56:15 +00:00
|
|
|
// 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...)
|
2023-01-07 18:12:19 +00:00
|
|
|
log.Printf("Running command [%s] with args: %v", command, args)
|
2023-01-07 16:56:15 +00:00
|
|
|
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
|
|
|
|
}
|