Show simple status indicator in the system tray
- organize a bit, add some new structs for doing various things
This commit is contained in:
parent
b832051bbe
commit
7b3203259e
84
icons.go
Normal file
84
icons.go
Normal 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
123
main.go
@ -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() {
|
||||||
|
Loading…
Reference in New Issue
Block a user