Show simple status indicator in the system tray

- organize a bit, add some new structs for doing various things
This commit is contained in:
Aaron Bieber 2022-11-10 07:04:51 -07:00
parent b832051bbe
commit 7b3203259e
No known key found for this signature in database
2 changed files with 164 additions and 45 deletions

84
icons.go Normal file
View File

@ -0,0 +1,84 @@
package main
import (
"bytes"
"fmt"
"image"
"image/color"
"image/png"
"log"
)
const width, height = 288, 288
func parseHexColor(s string) (*color.RGBA, error) {
c := &color.RGBA{
A: 0xff,
}
_, err := fmt.Sscanf(s, "#%02x%02x%02x", &c.R, &c.G, &c.B)
if err != nil {
return nil, err
}
return c, nil
}
func isEdge(x, y int) bool {
if x == 0 || x == width {
return true
}
if y == 0 || y == height {
return true
}
return false
}
type myIcon struct {
data *image.RGBA
}
func (m *myIcon) Name() string {
return "Hi.png"
}
func (m *myIcon) Content() []byte {
buf := new(bytes.Buffer)
_ = png.Encode(buf, m.data)
return buf.Bytes()
}
func buildImage(xin *xinStatus) *myIcon {
i := &myIcon{}
u2d, err := parseHexColor("#46d700")
off, err := parseHexColor("#c1c1c1")
if err != nil {
log.Println(err)
}
i.data = image.NewRGBA(image.Rect(0, 0, width, height))
border := &color.RGBA{
R: 0x00,
G: 0x00,
B: 0x00,
A: 0xff,
}
for y := 0; y < width; y++ {
for x := 0; x < height; x++ {
if isEdge(x, y) {
i.data.Set(x, y, border)
} else {
if xin.uptodate() {
i.data.Set(x, y, u2d)
} else {
i.data.Set(x, y, off)
}
}
}
}
return i
}

123
main.go
View File

@ -23,7 +23,13 @@ import (
"golang.org/x/crypto/ssh/knownhosts" "golang.org/x/crypto/ssh/knownhosts"
) )
var commitCache = make(map[string]string) var commitCache = make(map[string]commit)
type commit struct {
hash string
date time.Time
message string
}
type xinStatus struct { type xinStatus struct {
debug bool debug bool
@ -32,15 +38,14 @@ type xinStatus struct {
boundStrings []binding.ExternalString boundStrings []binding.ExternalString
boundBools []binding.ExternalBool boundBools []binding.ExternalBool
log *widget.TextGrid log *widget.TextGrid
repoCommitHash string repoCommit commit
repoCommitMsg string
config Config config Config
upgradeProgress *widget.ProgressBar upgradeProgress *widget.ProgressBar
} }
type Status struct { type Status struct {
card *widget.Card card *widget.Card
commitMessage string commit commit
client *ssh.Client client *ssh.Client
clientEstablished bool clientEstablished bool
@ -52,6 +57,44 @@ type Status struct {
Port int32 `json:"port"` Port int32 `json:"port"`
} }
type Config struct {
Statuses []*Status `json:"statuses"`
Repo string `json:"repo"`
PrivKeyPath string `json:"priv_key_path"`
}
func (c *commit) getInfo(repo string) error {
msgCmd := exec.Command("git", "log", "--format=%B", "-n", "1", c.hash)
msgCmd.Dir = repo
msg, err := msgCmd.Output()
if err != nil {
return err
}
c.message = trim(msg)
dateCmd := exec.Command("git", "log", "--format=%ci", c.hash)
dateCmd.Dir = repo
d, err := dateCmd.Output()
if err != nil {
return err
}
dateStr := trim(d)
date, err := time.Parse("2006-01-01 15:04:05 -0700", dateStr)
if err != nil {
return err
}
c.date = date
return nil
}
func NewCommit(c string) *commit {
return &commit{
hash: c,
}
}
func trim(b []byte) string { func trim(b []byte) string {
head := bytes.Split(b, []byte("\n")) head := bytes.Split(b, []byte("\n"))
return string(head[0]) return string(head[0])
@ -61,25 +104,26 @@ func (x *xinStatus) uptodate() bool {
return x.upgradeProgress.Value == float64(len(x.config.Statuses)) return x.upgradeProgress.Value == float64(len(x.config.Statuses))
} }
func (x *xinStatus) getCommitInfo(c string) string { func (x *xinStatus) getCommit(c string) (*commit, error) {
commit := &commit{
hash: c,
}
if c == "DIRTY" { if c == "DIRTY" {
return c return commit, nil
} }
if commitCache[c] != "" { if commit, ok := commitCache[c]; ok {
return commitCache[c] return &commit, nil
} } else {
commit := NewCommit(c)
msgCmd := exec.Command("git", "log", "--format=%B", "-n", "1", c) err := commit.getInfo(x.config.Repo)
msgCmd.Dir = x.config.Repo
msg, err := msgCmd.Output()
if err != nil { if err != nil {
x.Log(err.Error()) return nil, err
}
commitCache[c] = *commit
} }
strMsg := trim(msg) return commit, nil
commitCache[c] = strMsg
return strMsg
} }
func (x *xinStatus) updateRepoInfo() error { func (x *xinStatus) updateRepoInfo() error {
@ -90,13 +134,8 @@ func (x *xinStatus) updateRepoInfo() error {
return err return err
} }
x.repoCommitHash = trim(currentRev) commit, err := x.getCommit(trim(currentRev))
x.repoCommit = *commit
if commitCache[x.repoCommitHash] != "" {
x.repoCommitMsg = commitCache[x.repoCommitHash]
} else {
x.repoCommitMsg = x.getCommitInfo(x.repoCommitHash)
}
return nil return nil
} }
@ -167,7 +206,7 @@ func (x *xinStatus) updateHostInfo() error {
continue continue
} }
if s.ConfigurationRevision != x.repoCommitHash { if s.ConfigurationRevision != x.repoCommit.hash {
s.card.Subtitle = fmt.Sprintf("%.8s", s.ConfigurationRevision) s.card.Subtitle = fmt.Sprintf("%.8s", s.ConfigurationRevision)
upToDateCount = upToDateCount - 1 upToDateCount = upToDateCount - 1
} else { } else {
@ -175,7 +214,12 @@ func (x *xinStatus) updateHostInfo() error {
} }
s.card.Refresh() s.card.Refresh()
s.commitMessage = x.getCommitInfo(s.ConfigurationRevision) commit, err := x.getCommit(s.ConfigurationRevision)
if err != nil {
x.Log(err.Error())
continue
}
s.commit = *commit
} }
x.upgradeProgress.SetValue(float64(upToDateCount)) x.upgradeProgress.SetValue(float64(upToDateCount))
@ -196,12 +240,6 @@ func (x *xinStatus) Log(s string) {
*/ */
} }
type Config struct {
Statuses []*Status `json:"statuses"`
Repo string `json:"repo"`
PrivKeyPath string `json:"priv_key_path"`
}
func (c *Config) Load(file string) error { func (c *Config) Load(file string) error {
data, err := ioutil.ReadFile(file) data, err := ioutil.ReadFile(file)
if err != nil { if err != nil {
@ -265,7 +303,7 @@ func buildCards(stat *xinStatus) fyne.CanvasObject {
return stat.config.Statuses[i].Host < stat.config.Statuses[j].Host return stat.config.Statuses[i].Host < stat.config.Statuses[j].Host
}) })
for _, s := range stat.config.Statuses { for _, s := range stat.config.Statuses {
commitBStr := binding.BindString(&s.commitMessage) commitBStr := binding.BindString(&s.commit.message)
bsl := widget.NewLabelWithData(commitBStr) bsl := widget.NewLabelWithData(commitBStr)
restartBBool := binding.BindBool(&s.NeedsRestart) restartBBool := binding.BindBool(&s.NeedsRestart)
@ -295,8 +333,8 @@ func buildCards(stat *xinStatus) fyne.CanvasObject {
stat.upgradeProgress.Value, stat.upgradeProgress.Max) stat.upgradeProgress.Value, stat.upgradeProgress.Max)
} }
bsCommitMsg := binding.BindString(&stat.repoCommitMsg) bsCommitMsg := binding.BindString(&stat.repoCommit.message)
bsCommitHash := binding.BindString(&stat.repoCommitHash) bsCommitHash := binding.BindString(&stat.repoCommit.hash)
stat.boundStrings = append(stat.boundStrings, bsCommitMsg) stat.boundStrings = append(stat.boundStrings, bsCommitMsg)
stat.boundStrings = append(stat.boundStrings, bsCommitHash) stat.boundStrings = append(stat.boundStrings, bsCommitHash)
@ -335,11 +373,6 @@ func main() {
status.log.SetText(err.Error()) status.log.SetText(err.Error())
} }
err = status.updateHostInfo()
if err != nil {
status.log.SetText(err.Error())
}
go func() { go func() {
for { for {
err = status.updateRepoInfo() err = status.updateRepoInfo()
@ -368,16 +401,17 @@ func main() {
tabs.SetTabLocation(container.TabLocationLeading) tabs.SetTabLocation(container.TabLocationLeading)
if desk, ok := a.(desktop.App); ok { if desk, ok := a.(desktop.App); ok {
iconImg := buildImage(status)
m := fyne.NewMenu("xintray", m := fyne.NewMenu("xintray",
fyne.NewMenuItem("Show", func() { fyne.NewMenuItem("Show", func() {
w.Show() w.Show()
})) }))
desk.SetSystemTrayMenu(m) desk.SetSystemTrayMenu(m)
desk.SetSystemTrayIcon(iconImg)
go func() { go func() {
if status.uptodate() { for {
desk.SetSystemTrayIcon(theme.CheckButtonCheckedIcon()) desk.SetSystemTrayIcon(buildImage(status))
} else { time.Sleep(3 * time.Second)
desk.SetSystemTrayIcon(theme.CheckButtonIcon())
} }
}() }()
} }
@ -386,6 +420,7 @@ func main() {
w.SetContent(container.NewAppTabs( w.SetContent(container.NewAppTabs(
container.NewTabItem("Hosts", tabs), container.NewTabItem("Hosts", tabs),
container.NewTabItem("Config", container.NewMax(widget.NewCard("Config", "", nil))),
container.NewTabItem("Logs", container.NewMax(status.log)), container.NewTabItem("Logs", container.NewMax(status.log)),
)) ))
w.SetCloseIntercept(func() { w.SetCloseIntercept(func() {