diff --git a/Makefile b/Makefile
index df8d668..9188181 100644
--- a/Makefile
+++ b/Makefile
@@ -4,6 +4,7 @@ BUILDOPTS=-ldflags="-s -w" -a -gcflags=all=-l -trimpath -buildvcs=false
BINARY=i3status-go
TEST1=battery-test
+TEST2=cmdrun-test
## Use calssic targets where first one is deafult target
all: clean build
@@ -16,15 +17,18 @@ battery-test:
rm -rf ${TEST1}
go build ${BUILDOPTS} -o ${TEST1} ./cmd/${TEST1}
+cmdrun-test:
+ rm -rf ${TEST1}
+ go build ${BUILDOPTS} -o ${TEST2} ./cmd/${TEST2}
+
## Remove binary with golang compiler' means
clean:
- rm -rf ${BINARY} ${TEST1}
-
+ rm -rf ${BINARY} ${TEST1} ${TEST2}
## Misc target, for development purposes. Updates vendored libs, brutal way.
upgrade:
$(RM) -r vendor
- go get -u -t -tool ./...
+ go get -u -t ./...
go mod tidy
go mod vendor
diff --git a/cmd/i3status-go/default-config.json b/cmd/i3status-go/default-config.json
index f180393..61d09f0 100644
--- a/cmd/i3status-go/default-config.json
+++ b/cmd/i3status-go/default-config.json
@@ -702,6 +702,78 @@
]
},
+// Prints stdout of command to panel.
+"cmdrun" : {
+ "enabled": false,
+
+ // Delay between two consecutive command batch runs.
+ "delay": 3,
+
+ // command' binary.
+ "cmd": "/bin/bash",
+
+ // Arguments for command binary.
+ "args": ["-c", "echo 42"],
+
+ // If omitted set to default color defined up here.
+ "color": "#3e78fd",
+
+ // If omitted set to default background color defined up here.
+ "background": "#000000",
+
+ // If omitted set to default font defined up here.
+ "font": "Noto Sans",
+
+ // ‘xx-small’, ‘x-small’, ‘small’, ‘medium’, ‘large’, ‘x-large’, ‘xx-large’, or one of relative sizes
+ // ‘smaller’ or ‘larger’. If omitted set to default font size defined up here.
+ "font_size": "medium",
+
+ // Re-define separator parameters for each command block here.
+ "separator": {
+ "left": {
+ // Set to false if omitted.
+ "enabled": true,
+
+ // If omitted set to default color defined for separator up here.
+ "color": "#3e78fd",
+
+ // If omitted set to default background color defined for separator up here.
+ "background": "#000000",
+
+ // If omitted set to default symbol defined for separator up here.
+ "symbol": "▶",
+
+ // If omitted set to default font defined for separator up here.
+ "font": "Noto Sans",
+
+ // ‘xx-small’, ‘x-small’, ‘small’, ‘medium’, ‘large’, ‘x-large’, ‘xx-large’, or one of relative sizes
+ // ‘smaller’ or ‘larger’. If omitted set to default font size defined for separator up here.
+ "font_size": "medium"
+ },
+
+ "right": {
+ // Set to false if omitted.
+ "enabled": true,
+
+ // If omitted set to default color defined for separator up here.
+ "color": "#3e78fd",
+
+ // If omitted set to default background color defined for separator up here.
+ "background": "#000000",
+
+ // If omitted set to default symbol defined for separator up here.
+ "symbol": "◀",
+
+ // If omitted set to default font defined for separator up here.
+ "font": "Noto Sans",
+
+ // ‘xx-small’, ‘x-small’, ‘small’, ‘medium’, ‘large’, ‘x-large’, ‘xx-large’, or one of relative sizes
+ // ‘smaller’ or ‘larger’. If omitted set to default font size defined for separator up here.
+ "font_size": "medium"
+ }
+ }
+}
+
// Whether to display Application Buttons.
"app_buttons": {
"enabled": true,
diff --git a/cmd/i3status-go/main.go b/cmd/i3status-go/main.go
index eb69c0f..aebc5f6 100644
--- a/cmd/i3status-go/main.go
+++ b/cmd/i3status-go/main.go
@@ -84,6 +84,14 @@ func main() {
Conf.Clock.FontSize,
)
+ Conf.Values.RunCommandOutput = fmt.Sprintf(
+ "?",
+ Conf.CmdRun.Color,
+ Conf.CmdRun.Background,
+ Conf.CmdRun.Font,
+ Conf.CmdRun.FontSize,
+ )
+
go Conf.Spawner()
go Conf.ParseStdin()
go Conf.CleanZombies()
@@ -143,6 +151,10 @@ func main() {
go Conf.UpdateVPNStatus()
}
+ if Conf.CmdRun.Enabled {
+ go Conf.RunCommand()
+ }
+
/*
I3bar documentation pretends that message protocol must be valid json. In practice, we only have to print valid
header, empty json array and (potentially infinite) json lines (line that is valid json by itself) that is
@@ -519,6 +531,50 @@ func main() {
j = append(j, b)
}
+ if Conf.CmdRun.Enabled {
+ var b lib.I3BarOutBlock
+
+ b.Name = `runcommandoutput`
+ b.Color = Conf.CmdRun.Color
+ b.Background = Conf.CmdRun.Background
+
+ if Conf.CmdRun.Separator.Left.Enabled {
+ b.FullText = fmt.Sprintf(
+ "%s",
+ Conf.CmdRun.Separator.Left.Color,
+ Conf.CmdRun.Separator.Left.Background,
+ Conf.CmdRun.Separator.Left.Font,
+ Conf.CmdRun.Separator.Left.FontSize,
+ Conf.CmdRun.Separator.Left.Symbol,
+ )
+ }
+
+ b.FullText += fmt.Sprintf(
+ "%s",
+ Conf.CmdRun.Color,
+ Conf.CmdRun.Background,
+ Conf.CmdRun.Font,
+ Conf.CmdRun.FontSize,
+ Conf.Values.RunCommandOutput,
+ )
+
+ if Conf.CmdRun.Separator.Right.Enabled {
+ b.FullText += fmt.Sprintf(
+ "%s",
+ Conf.CmdRun.Separator.Right.Color,
+ Conf.CmdRun.Separator.Right.Background,
+ Conf.CmdRun.Separator.Right.Font,
+ Conf.CmdRun.Separator.Right.FontSize,
+ Conf.CmdRun.Separator.Right.Symbol,
+ )
+ }
+
+ b.Markup = "pango"
+ b.Separator = false
+
+ j = append(j, b)
+ }
+
if Conf.Clock.Enabled {
var b lib.I3BarOutBlock
diff --git a/configs/i3status-go-example.json b/configs/i3status-go-example.json
index 297e1f1..15c7fc5 120000
--- a/configs/i3status-go-example.json
+++ b/configs/i3status-go-example.json
@@ -1 +1 @@
-/home/eleksir/git/i3status-go/cmd/i3status-go/default-config.json
\ No newline at end of file
+../cmd/i3status-go/default-config.json
\ No newline at end of file
diff --git a/internal/lib/config.go b/internal/lib/config.go
index 0ae0851..6146f14 100644
--- a/internal/lib/config.go
+++ b/internal/lib/config.go
@@ -59,16 +59,17 @@ type Separator struct {
type MyConfig struct {
Values struct {
- PrintOutput bool
- CPUTemperature int64
- BatteryString string
- ClockTime string
- IfStatus string
- VPNStatus string
- Memory Mem
- La string
- PA *p.Client
- SoundVolume string
+ PrintOutput bool
+ CPUTemperature int64
+ BatteryString string
+ ClockTime string
+ IfStatus string
+ VPNStatus string
+ Memory Mem
+ La string
+ PA *p.Client
+ SoundVolume string
+ RunCommandOutput string
}
Channels struct {
@@ -231,6 +232,18 @@ type MyConfig struct {
} `json:"tasks,omitempty"`
} `json:"cron,omitempty"`
+ CmdRun struct {
+ Enabled bool `json:"enabled,omitempty"`
+ Color string `json:"color,omitempty"`
+ Background string `json:"background,omitempty"`
+ Font string `json:"font,omitempty"`
+ FontSize string `json:"font_size,omitempty"`
+ Separator Separator `json:"separator,omitempty"`
+ Cmd string `json:"cmd,omitempty"`
+ Delay int `json:"delay,omitempty"`
+ Args []string `json:"args,omitempty"`
+ }
+
AppButtons struct {
Enabled bool `json:"enabled,omitempty"`
Separator Separator `json:"separator,omitempty"`
@@ -1446,6 +1459,121 @@ func ReadConf(defaultConfig []byte) (*MyConfig, error) {
}
}
+ // sampleConfig.CmdRun.Enabled will false if not set in config
+
+ if sampleConfig.CmdRun.Cmd == "" && sampleConfig.CmdRun.Enabled {
+ sampleConfig.CmdRun.Enabled = false
+ log.Print(
+ "Setting sampleConfig.CmdRun.Enabled to false because sampleConfig.CmdRun.Cmd is not set",
+ )
+ }
+
+ // sampleConfig.CmdRun.Args can be empty
+
+ if sampleConfig.CmdRun.Color == "" {
+ sampleConfig.CmdRun.Color = sampleConfig.Color
+ }
+
+ if sampleConfig.CmdRun.Background == "" {
+ sampleConfig.CmdRun.Background = sampleConfig.Background
+ }
+
+ if sampleConfig.CmdRun.Font == "" {
+ sampleConfig.CmdRun.Font = sampleConfig.Font
+ }
+
+ if sampleConfig.CmdRun.FontSize == "" {
+ sampleConfig.CmdRun.FontSize = sampleConfig.FontSize
+ }
+
+ if sampleConfig.CmdRun.Separator.Left.Color == "" {
+ sampleConfig.CmdRun.Separator.Left.Color = sampleConfig.Separator.Left.Color
+ }
+
+ if sampleConfig.CmdRun.Separator.Left.Background == "" {
+ sampleConfig.CmdRun.Separator.Left.Background = sampleConfig.Separator.Left.Background
+ }
+
+ if sampleConfig.CmdRun.Separator.Left.Symbol == "" {
+ sampleConfig.CmdRun.Separator.Left.Symbol = sampleConfig.Separator.Left.Symbol
+ }
+
+ if sampleConfig.CmdRun.Separator.Left.Font == "" {
+ sampleConfig.CmdRun.Separator.Left.Font = sampleConfig.Separator.Left.Font
+ }
+
+ if sampleConfig.CmdRun.Separator.Left.FontSize == "" {
+ sampleConfig.CmdRun.Separator.Left.FontSize = sampleConfig.Separator.Left.FontSize
+ } else {
+ matched, err := regexp.MatchString(
+ `^(xx-small|x-small|small|medium|large|x-large|xx-large|smaller|larger)$`,
+ sampleConfig.CmdRun.Separator.Left.FontSize,
+ )
+
+ if err != nil {
+ log.Printf(
+ "Unable to set sampleConfig.CmdRun.Separator.Left.FontSize: %s, fallback to %s",
+ err,
+ sampleConfig.Separator.Left.FontSize,
+ )
+
+ sampleConfig.CmdRun.Separator.Left.FontSize = sampleConfig.Separator.Left.FontSize
+ }
+
+ if !matched {
+ log.Printf(
+ "Unable to set sampleConfig.CmdRun.Separator.Left.FontSize, fallback to %s",
+ sampleConfig.Separator.Left.FontSize,
+ )
+
+ sampleConfig.CmdRun.Separator.Left.FontSize = sampleConfig.Separator.Left.FontSize
+ }
+ }
+
+ if sampleConfig.CmdRun.Separator.Right.Color == "" {
+ sampleConfig.CmdRun.Separator.Right.Color = sampleConfig.Separator.Right.Color
+ }
+
+ if sampleConfig.CmdRun.Separator.Right.Background == "" {
+ sampleConfig.CmdRun.Separator.Right.Background = sampleConfig.Separator.Right.Background
+ }
+
+ if sampleConfig.CmdRun.Separator.Right.Symbol == "" {
+ sampleConfig.CmdRun.Separator.Right.Symbol = sampleConfig.Separator.Right.Symbol
+ }
+
+ if sampleConfig.CmdRun.Separator.Right.Font == "" {
+ sampleConfig.CmdRun.Separator.Right.Font = sampleConfig.Separator.Right.Font
+ }
+
+ if sampleConfig.CmdRun.Separator.Right.FontSize == "" {
+ sampleConfig.CmdRun.Separator.Right.FontSize = sampleConfig.Separator.Right.FontSize
+ } else {
+ matched, err := regexp.MatchString(
+ `^(xx-small|x-small|small|medium|large|x-large|xx-large|smaller|larger)$`,
+ sampleConfig.CmdRun.Separator.Right.FontSize,
+ )
+
+ if err != nil {
+ log.Printf(
+ "Unable to set sampleConfig.CmdRun.Separator.Right.FontSize: %s, fallback to %s",
+ err,
+ sampleConfig.Separator.Right.FontSize,
+ )
+
+ sampleConfig.CmdRun.Separator.Right.FontSize = sampleConfig.Separator.Right.FontSize
+ }
+
+ if !matched {
+ log.Printf(
+ "Unable to set sampleConfig.CmdRun.Separator.Right.FontSize, fallback to %s",
+ sampleConfig.Separator.Right.FontSize,
+ )
+
+ sampleConfig.CmdRun.Separator.Right.FontSize = sampleConfig.Separator.Right.FontSize
+ }
+ }
+
// sampleConfig.AppButtons.Enabled will false if not set in config
if sampleConfig.AppButtons.Separator.Left.Color == "" {
diff --git a/internal/lib/spawn.go b/internal/lib/spawn.go
index 80534a2..ebff62d 100644
--- a/internal/lib/spawn.go
+++ b/internal/lib/spawn.go
@@ -1,9 +1,11 @@
package lib
import (
+ "bytes"
"log"
"os"
"os/exec"
+ "strings"
"syscall"
"time"
)
@@ -55,3 +57,41 @@ func (c *MyConfig) CleanZombies() {
time.Sleep(1 * time.Minute)
}
}
+
+// RunProcess runs given command and returns contens of command stdout. On error prints error on stderr and returns
+// empty syting.
+func RunProcess(command []string) string {
+ var (
+ cmd *exec.Cmd
+ stdout, stderr bytes.Buffer
+ )
+
+ switch len(command) {
+ case 1:
+ cmd = exec.Command(command[0], "")
+ default:
+ cmd = exec.Command(command[0], command[1:]...)
+ }
+
+ cmd.Dir = "/"
+ cmd.Stdout = &stdout
+ cmd.Stderr = &stderr
+
+ err := cmd.Run()
+
+ if err != nil {
+ if stdout.String() != "" {
+ log.Printf("Unable to run '%s': %s, %s", strings.Join(command, " "), err, stdout.String())
+ } else {
+ log.Printf("Unable to run '%s': %s", strings.Join(command, " "), err)
+ }
+
+ return ""
+ } else {
+ if stderr.String() != "" {
+ log.Printf("%s: %s", strings.Join(command, " "), stderr.String())
+ }
+ }
+
+ return strings.TrimRight(stdout.String(), "\n")
+}
diff --git a/internal/lib/vpn.go b/internal/lib/vpn.go
index bad81bf..9a41d04 100644
--- a/internal/lib/vpn.go
+++ b/internal/lib/vpn.go
@@ -70,7 +70,7 @@ func (c *MyConfig) UpdateVPNStatus() {
func (c *MyConfig) VPNTCPCheck() bool {
conn, err := net.DialTimeout(
"tcp",
- fmt.Sprintf("%s:%d", c.Vpn.TCPCheck.Host, c.Vpn.TCPCheck.Port),
+ fmt.Sprintf("%s:%d", c.Vpn.TCPCheck.Host, c.Vpn.TCPCheck.Port), //nolint: hostport
time.Second*time.Duration(c.Vpn.TCPCheck.Timeout),
)