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
}

125
main.go
View File

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