Split up GUI code some and modularize it
This commit is contained in:
parent
a506546f1e
commit
1841b19211
|
@ -1,3 +1,3 @@
|
||||||
# ui
|
# gui
|
||||||
|
|
||||||
This is the main source code for the custom UI used by PiFrame. This is responsible for things like restart, WiFi config, ensuring the slide show is running and more.
|
This is the main source code for the custom UI used by PiFrame. This is responsible for things like restart, WiFi config, ensuring the slide show is running and more.
|
10
cmd/gui/gui.go
Normal file
10
cmd/gui/gui.go
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.kemonine.info/PiFrame/ui"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ui.Slideshow()
|
||||||
|
ui.ConfigGui()
|
||||||
|
}
|
3
ui/README.md
Normal file
3
ui/README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# ui
|
||||||
|
|
||||||
|
This is the main implementation for various aspects of the main PiFrame [G]UI
|
|
@ -1,422 +1,422 @@
|
||||||
package main
|
package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
"github.com/guillermo/go.procmeminfo"
|
"github.com/guillermo/go.procmeminfo"
|
||||||
"github.com/rivo/tview"
|
"github.com/rivo/tview"
|
||||||
|
|
||||||
"git.kemonine.info/PiFrame/wifi"
|
"git.kemonine.info/PiFrame/wifi"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CMD_SYSTEMCTL = "/usr/bin/systemctl"
|
CMD_SYSTEMCTL = "/usr/bin/systemctl"
|
||||||
CMD_FINDMNT = "/usr/bin/findmnt"
|
CMD_FINDMNT = "/usr/bin/findmnt"
|
||||||
CMD_VCGENCMD = "/opt/vc/bin/vcgencmd"
|
CMD_VCGENCMD = "/opt/vc/bin/vcgencmd"
|
||||||
FILE_CPU_TEMP = "/sys/class/thermal/thermal_zone0/temp"
|
FILE_CPU_TEMP = "/sys/class/thermal/thermal_zone0/temp"
|
||||||
ALBUM_ROOT_DIR = "/tank/pictures/"
|
ALBUM_ROOT_DIR = "/tank/pictures/"
|
||||||
SYNCTHING_FOLDER_SKIP = ".stfolder"
|
SYNCTHING_FOLDER_SKIP = ".stfolder"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PAGE_MAIN_UI = "PAGE_MAIN_UI"
|
PAGE_MAIN_UI = "PAGE_MAIN_UI"
|
||||||
PAGE_EXIT = "PAGE_EXIT"
|
PAGE_EXIT = "PAGE_EXIT"
|
||||||
PAGE_REBOOT = "PAGE_REBOOT"
|
PAGE_REBOOT = "PAGE_REBOOT"
|
||||||
PAGE_POWEROFF = "PAGE_POWEROFF"
|
PAGE_POWEROFF = "PAGE_POWEROFF"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func ConfigGui() {
|
||||||
// Memory info for status panel
|
// Memory info for status panel
|
||||||
meminfo := &procmeminfo.MemInfo{}
|
meminfo := &procmeminfo.MemInfo{}
|
||||||
err := meminfo.Update()
|
err := meminfo.Update()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error getting memory info : %s", err)
|
log.Printf("Error getting memory info : %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Network interfaces for status panel
|
// Network interfaces for status panel
|
||||||
ifaces, err := net.Interfaces()
|
ifaces, err := net.Interfaces()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error getting netork interfaces : %s", err)
|
log.Fatalf("Error getting netork interfaces : %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disk use
|
// Disk use
|
||||||
findmntOut, err := exec.Command(CMD_FINDMNT, "-n", "-l",
|
findmntOut, err := exec.Command(CMD_FINDMNT, "-n", "-l",
|
||||||
"-o", "TARGET,USE%",
|
"-o", "TARGET,USE%",
|
||||||
"-t", "ext4,exfat,vfat,btrfs,zfs,xfs").Output()
|
"-t", "ext4,exfat,vfat,btrfs,zfs,xfs").Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error getting disk use : %s", err)
|
log.Fatalf("Error getting disk use : %s", err)
|
||||||
}
|
}
|
||||||
filesystems := strings.Split(strings.Trim(string(findmntOut), "\n"), "\n")
|
filesystems := strings.Split(strings.Trim(string(findmntOut), "\n"), "\n")
|
||||||
|
|
||||||
// GPU Temp
|
// GPU Temp
|
||||||
vcgencmdOut, err := exec.Command(CMD_VCGENCMD, "measure_temp").Output()
|
vcgencmdOut, err := exec.Command(CMD_VCGENCMD, "measure_temp").Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error getting GPU temp : %s", err)
|
log.Fatalf("Error getting GPU temp : %s", err)
|
||||||
}
|
}
|
||||||
gpuTemp := strings.Split(strings.Trim(string(vcgencmdOut), "\n"), "=")[1]
|
gpuTemp := strings.Split(strings.Trim(string(vcgencmdOut), "\n"), "=")[1]
|
||||||
|
|
||||||
// CPU Temp
|
// CPU Temp
|
||||||
cpuTempFileContents, err := ioutil.ReadFile(FILE_CPU_TEMP)
|
cpuTempFileContents, err := ioutil.ReadFile(FILE_CPU_TEMP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error getting CPU temp : %s", err)
|
log.Fatalf("Error getting CPU temp : %s", err)
|
||||||
}
|
}
|
||||||
cpuTempStr := strings.Trim(string(cpuTempFileContents), "\n")
|
cpuTempStr := strings.Trim(string(cpuTempFileContents), "\n")
|
||||||
cpuTempInt, err := strconv.Atoi(cpuTempStr)
|
cpuTempInt, err := strconv.Atoi(cpuTempStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error processing CPU temp : %S", err)
|
log.Fatalf("Error processing CPU temp : %S", err)
|
||||||
}
|
}
|
||||||
cpuTemp := fmt.Sprintf("%.2f'C", float64(cpuTempInt)/1000.0)
|
cpuTemp := fmt.Sprintf("%.2f'C", float64(cpuTempInt)/1000.0)
|
||||||
|
|
||||||
// Get list of all folders that can be used as albums
|
// Get list of all folders that can be used as albums
|
||||||
var albums []string
|
var albums []string
|
||||||
err = filepath.Walk(ALBUM_ROOT_DIR, func(path string, fi os.FileInfo, err error) error {
|
err = filepath.Walk(ALBUM_ROOT_DIR, func(path string, fi os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if fi.IsDir() {
|
if fi.IsDir() {
|
||||||
if strings.Contains(path, SYNCTHING_FOLDER_SKIP) {
|
if strings.Contains(path, SYNCTHING_FOLDER_SKIP) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
albumName := strings.TrimPrefix(path, ALBUM_ROOT_DIR)
|
albumName := strings.TrimPrefix(path, ALBUM_ROOT_DIR)
|
||||||
if albumName == "" {
|
if albumName == "" {
|
||||||
albumName = "Main Folder"
|
albumName = "Main Folder"
|
||||||
}
|
}
|
||||||
albums = append(albums, albumName)
|
albums = append(albums, albumName)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error getting list of albums : %s", err)
|
log.Fatalf("Error getting list of albums : %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run config UI when slideshow stops
|
// Run config UI when slideshow stops
|
||||||
app := tview.NewApplication()
|
app := tview.NewApplication()
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
headerTitle := tview.NewTextView().
|
headerTitle := tview.NewTextView().
|
||||||
SetText("PiFrame").
|
SetText("PiFrame").
|
||||||
SetTextAlign(tview.AlignCenter).
|
SetTextAlign(tview.AlignCenter).
|
||||||
SetTextColor(tcell.ColorAqua)
|
SetTextColor(tcell.ColorAqua)
|
||||||
|
|
||||||
headerSubTitle := tview.NewTextView().
|
headerSubTitle := tview.NewTextView().
|
||||||
SetText("Management Utility").
|
SetText("Management Utility").
|
||||||
SetTextAlign(tview.AlignCenter).
|
SetTextAlign(tview.AlignCenter).
|
||||||
SetTextColor(tcell.ColorSilver)
|
SetTextColor(tcell.ColorSilver)
|
||||||
header := tview.NewFlex().
|
header := tview.NewFlex().
|
||||||
SetDirection(tview.FlexRow)
|
SetDirection(tview.FlexRow)
|
||||||
header.AddItem(headerTitle, 0, 1, false).
|
header.AddItem(headerTitle, 0, 1, false).
|
||||||
AddItem(headerSubTitle, 0, 1, false)
|
AddItem(headerSubTitle, 0, 1, false)
|
||||||
|
|
||||||
// Footer fields (Left Column)
|
// Footer fields (Left Column)
|
||||||
exitButton := tview.NewButton("Exit").
|
exitButton := tview.NewButton("Exit").
|
||||||
SetBackgroundColorActivated(tcell.ColorGray)
|
SetBackgroundColorActivated(tcell.ColorGray)
|
||||||
exitButton.SetLabelColor(tcell.ColorBlack).
|
exitButton.SetLabelColor(tcell.ColorBlack).
|
||||||
SetBorder(true).
|
SetBorder(true).
|
||||||
SetBorderColor(tcell.ColorBlack).
|
SetBorderColor(tcell.ColorBlack).
|
||||||
SetBackgroundColor(tcell.ColorGreen).
|
SetBackgroundColor(tcell.ColorGreen).
|
||||||
SetRect(0, 0, 22, 3)
|
SetRect(0, 0, 22, 3)
|
||||||
|
|
||||||
rebootButton := tview.NewButton("Reboot").
|
rebootButton := tview.NewButton("Reboot").
|
||||||
SetBackgroundColorActivated(tcell.ColorGray)
|
SetBackgroundColorActivated(tcell.ColorGray)
|
||||||
rebootButton.SetLabelColor(tcell.ColorBlack).
|
rebootButton.SetLabelColor(tcell.ColorBlack).
|
||||||
SetBorder(true).
|
SetBorder(true).
|
||||||
SetBorderColor(tcell.ColorBlack).
|
SetBorderColor(tcell.ColorBlack).
|
||||||
SetBackgroundColor(tcell.ColorYellow).
|
SetBackgroundColor(tcell.ColorYellow).
|
||||||
SetRect(0, 0, 22, 3)
|
SetRect(0, 0, 22, 3)
|
||||||
|
|
||||||
powerOffButton := tview.NewButton("Power Off").
|
powerOffButton := tview.NewButton("Power Off").
|
||||||
SetBackgroundColorActivated(tcell.ColorGray)
|
SetBackgroundColorActivated(tcell.ColorGray)
|
||||||
powerOffButton.SetLabelColor(tcell.ColorBlack).
|
powerOffButton.SetLabelColor(tcell.ColorBlack).
|
||||||
SetBorder(true).
|
SetBorder(true).
|
||||||
SetBorderColor(tcell.ColorBlack).
|
SetBorderColor(tcell.ColorBlack).
|
||||||
SetBackgroundColor(tcell.ColorRed).
|
SetBackgroundColor(tcell.ColorRed).
|
||||||
SetRect(0, 0, 22, 3)
|
SetRect(0, 0, 22, 3)
|
||||||
|
|
||||||
// Footer
|
// Footer
|
||||||
footer := tview.NewFlex()
|
footer := tview.NewFlex()
|
||||||
footer.AddItem(exitButton, 0, 1, false).
|
footer.AddItem(exitButton, 0, 1, false).
|
||||||
AddItem(rebootButton, 0, 1, false).
|
AddItem(rebootButton, 0, 1, false).
|
||||||
AddItem(powerOffButton, 0, 1, false)
|
AddItem(powerOffButton, 0, 1, false)
|
||||||
// Setup menu
|
// Setup menu
|
||||||
menu := tview.NewList()
|
menu := tview.NewList()
|
||||||
menu.SetBorder(true).
|
menu.SetBorder(true).
|
||||||
SetTitle("Menu").
|
SetTitle("Menu").
|
||||||
SetTitleColor(tcell.ColorAqua)
|
SetTitleColor(tcell.ColorAqua)
|
||||||
menu.AddItem("Select Albums", "", '1', nil)
|
menu.AddItem("Select Albums", "", '1', nil)
|
||||||
menu.AddItem("Configure WiFi", "", '2', nil)
|
menu.AddItem("Configure WiFi", "", '2', nil)
|
||||||
|
|
||||||
// Setup base var for main column so the menu setup is easier to manage
|
// Setup base var for main column so the menu setup is easier to manage
|
||||||
main := tview.NewFlex().
|
main := tview.NewFlex().
|
||||||
SetDirection(tview.FlexRow)
|
SetDirection(tview.FlexRow)
|
||||||
|
|
||||||
// Setup main panel (Center column)
|
// Setup main panel (Center column)
|
||||||
main.SetTitle("").
|
main.SetTitle("").
|
||||||
SetBorder(true).
|
SetBorder(true).
|
||||||
SetTitleColor(tcell.ColorAqua)
|
SetTitleColor(tcell.ColorAqua)
|
||||||
|
|
||||||
// WiFi Config Form
|
// WiFi Config Form
|
||||||
wifiConfigForm := tview.NewForm()
|
wifiConfigForm := tview.NewForm()
|
||||||
wifiConfigAccessPoint := ""
|
wifiConfigAccessPoint := ""
|
||||||
wifiConfigPassword := ""
|
wifiConfigPassword := ""
|
||||||
wifiConfigForm.AddInputField("Access Point", "", 0, nil, func(value string) {
|
wifiConfigForm.AddInputField("Access Point", "", 0, nil, func(value string) {
|
||||||
wifiConfigAccessPoint = value
|
wifiConfigAccessPoint = value
|
||||||
})
|
})
|
||||||
wifiConfigForm.AddPasswordField("Password", "", 0, '*', func(value string) {
|
wifiConfigForm.AddPasswordField("Password", "", 0, '*', func(value string) {
|
||||||
wifiConfigPassword = value
|
wifiConfigPassword = value
|
||||||
})
|
})
|
||||||
wifiConfigForm.AddButton("Apply", func() {
|
wifiConfigForm.AddButton("Apply", func() {
|
||||||
// Cleanup old wifi configs and apply new one
|
// Cleanup old wifi configs and apply new one
|
||||||
nmWifi := wifi.New(wifiConfigAccessPoint, wifiConfigPassword)
|
nmWifi := wifi.New(wifiConfigAccessPoint, wifiConfigPassword)
|
||||||
nmWifi.ApplyConfig()
|
nmWifi.ApplyConfig()
|
||||||
})
|
})
|
||||||
wifiConfigForm.AddButton("Cancel", func() {
|
wifiConfigForm.AddButton("Cancel", func() {
|
||||||
main.Clear()
|
main.Clear()
|
||||||
app.SetFocus(menu)
|
app.SetFocus(menu)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Select Albums Form
|
// Select Albums Form
|
||||||
selectAlbumsForm := tview.NewForm()
|
selectAlbumsForm := tview.NewForm()
|
||||||
for _, album := range albums {
|
for _, album := range albums {
|
||||||
selectAlbumsForm.AddCheckbox(album, true, nil)
|
selectAlbumsForm.AddCheckbox(album, true, nil)
|
||||||
}
|
}
|
||||||
selectAlbumsForm.AddButton("Apply", nil)
|
selectAlbumsForm.AddButton("Apply", nil)
|
||||||
selectAlbumsForm.AddButton("Cancel", func() {
|
selectAlbumsForm.AddButton("Cancel", func() {
|
||||||
main.Clear()
|
main.Clear()
|
||||||
app.SetFocus(menu)
|
app.SetFocus(menu)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Setup menu selection handler
|
// Setup menu selection handler
|
||||||
menu.SetSelectedFunc(func(index int, title string, desc string, shortcut rune) {
|
menu.SetSelectedFunc(func(index int, title string, desc string, shortcut rune) {
|
||||||
if title == "Select Albums" {
|
if title == "Select Albums" {
|
||||||
main.SetTitle("Select Albums")
|
main.SetTitle("Select Albums")
|
||||||
main.Clear()
|
main.Clear()
|
||||||
main.AddItem(selectAlbumsForm, 0, 1, true)
|
main.AddItem(selectAlbumsForm, 0, 1, true)
|
||||||
app.SetFocus(selectAlbumsForm)
|
app.SetFocus(selectAlbumsForm)
|
||||||
}
|
}
|
||||||
if title == "Configure WiFi" {
|
if title == "Configure WiFi" {
|
||||||
main.SetTitle("Configure WiFi")
|
main.SetTitle("Configure WiFi")
|
||||||
main.Clear()
|
main.Clear()
|
||||||
main.AddItem(wifiConfigForm, 0, 1, true)
|
main.AddItem(wifiConfigForm, 0, 1, true)
|
||||||
app.SetFocus(wifiConfigForm)
|
app.SetFocus(wifiConfigForm)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Side bar fields
|
// Side bar fields
|
||||||
sideBarCPUTempTitle := tview.NewTextView().
|
sideBarCPUTempTitle := tview.NewTextView().
|
||||||
SetText("CPU Temperature").
|
SetText("CPU Temperature").
|
||||||
SetTextColor(tcell.ColorYellow)
|
SetTextColor(tcell.ColorYellow)
|
||||||
sideBarCPUTemp := tview.NewTextView().
|
sideBarCPUTemp := tview.NewTextView().
|
||||||
SetText(fmt.Sprintf(" %s", cpuTemp))
|
SetText(fmt.Sprintf(" %s", cpuTemp))
|
||||||
sideBarGPUTempTitle := tview.NewTextView().
|
sideBarGPUTempTitle := tview.NewTextView().
|
||||||
SetText("GPU Temperature").
|
SetText("GPU Temperature").
|
||||||
SetTextColor(tcell.ColorYellow)
|
SetTextColor(tcell.ColorYellow)
|
||||||
sideBarGPUTemp := tview.NewTextView().
|
sideBarGPUTemp := tview.NewTextView().
|
||||||
SetText(fmt.Sprintf(" %s", gpuTemp))
|
SetText(fmt.Sprintf(" %s", gpuTemp))
|
||||||
sideBarMemoryTitle := tview.NewTextView().
|
sideBarMemoryTitle := tview.NewTextView().
|
||||||
SetText("Memory Use (Mb)").
|
SetText("Memory Use (Mb)").
|
||||||
SetTextColor(tcell.ColorYellow)
|
SetTextColor(tcell.ColorYellow)
|
||||||
divisor := math.Pow(1024.0, 2.0)
|
divisor := math.Pow(1024.0, 2.0)
|
||||||
sideBarMemoryStats := tview.NewTextView().
|
sideBarMemoryStats := tview.NewTextView().
|
||||||
SetText(fmt.Sprintf(" %.1f / %.1f",
|
SetText(fmt.Sprintf(" %.1f / %.1f",
|
||||||
float64(meminfo.Used())/divisor,
|
float64(meminfo.Used())/divisor,
|
||||||
float64(meminfo.Total())/divisor))
|
float64(meminfo.Total())/divisor))
|
||||||
sideBarSwapTitle := tview.NewTextView().
|
sideBarSwapTitle := tview.NewTextView().
|
||||||
SetText("Swap Use").
|
SetText("Swap Use").
|
||||||
SetTextColor(tcell.ColorYellow)
|
SetTextColor(tcell.ColorYellow)
|
||||||
sideBarSwapStats := tview.NewTextView().
|
sideBarSwapStats := tview.NewTextView().
|
||||||
SetText(fmt.Sprintf(" %d%%", meminfo.Swap()))
|
SetText(fmt.Sprintf(" %d%%", meminfo.Swap()))
|
||||||
sideBarFilesystemTitle := tview.NewTextView().
|
sideBarFilesystemTitle := tview.NewTextView().
|
||||||
SetText("Disk Use").
|
SetText("Disk Use").
|
||||||
SetTextColor(tcell.ColorYellow)
|
SetTextColor(tcell.ColorYellow)
|
||||||
var sideBarFilesystems []*tview.TextView
|
var sideBarFilesystems []*tview.TextView
|
||||||
for _, i := range filesystems {
|
for _, i := range filesystems {
|
||||||
filesystemAsTextView := tview.NewTextView().
|
filesystemAsTextView := tview.NewTextView().
|
||||||
SetText(fmt.Sprintf(" %s", i))
|
SetText(fmt.Sprintf(" %s", i))
|
||||||
sideBarFilesystems = append(sideBarFilesystems, filesystemAsTextView)
|
sideBarFilesystems = append(sideBarFilesystems, filesystemAsTextView)
|
||||||
}
|
}
|
||||||
sideBarIPAddressesTitle := tview.NewTextView().
|
sideBarIPAddressesTitle := tview.NewTextView().
|
||||||
SetText("IP Addresses").
|
SetText("IP Addresses").
|
||||||
SetTextColor(tcell.ColorYellow)
|
SetTextColor(tcell.ColorYellow)
|
||||||
var sideBarIPAddresses []*tview.TextView
|
var sideBarIPAddresses []*tview.TextView
|
||||||
for _, i := range ifaces {
|
for _, i := range ifaces {
|
||||||
addrs, err := i.Addrs()
|
addrs, err := i.Addrs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error getting interface addresses : %s", err)
|
log.Printf("Error getting interface addresses : %s", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, a := range addrs {
|
for _, a := range addrs {
|
||||||
ipAsTextView := tview.NewTextView().
|
ipAsTextView := tview.NewTextView().
|
||||||
SetText(fmt.Sprintf(" %v : %s", i.Name, a.String()))
|
SetText(fmt.Sprintf(" %v : %s", i.Name, a.String()))
|
||||||
sideBarIPAddresses = append(sideBarIPAddresses, ipAsTextView)
|
sideBarIPAddresses = append(sideBarIPAddresses, ipAsTextView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup side bar (Right column)
|
// Setup side bar (Right column)
|
||||||
sideBar := tview.NewFlex().
|
sideBar := tview.NewFlex().
|
||||||
SetDirection(tview.FlexRow)
|
SetDirection(tview.FlexRow)
|
||||||
sideBar.SetTitle("System Info").
|
sideBar.SetTitle("System Info").
|
||||||
SetBorder(true).
|
SetBorder(true).
|
||||||
SetTitleColor(tcell.ColorAqua)
|
SetTitleColor(tcell.ColorAqua)
|
||||||
sideBar.AddItem(sideBarCPUTempTitle, 1, 1, false)
|
sideBar.AddItem(sideBarCPUTempTitle, 1, 1, false)
|
||||||
sideBar.AddItem(sideBarCPUTemp, 1, 1, false)
|
sideBar.AddItem(sideBarCPUTemp, 1, 1, false)
|
||||||
sideBar.AddItem(sideBarGPUTempTitle, 1, 1, false)
|
sideBar.AddItem(sideBarGPUTempTitle, 1, 1, false)
|
||||||
sideBar.AddItem(sideBarGPUTemp, 1, 1, false)
|
sideBar.AddItem(sideBarGPUTemp, 1, 1, false)
|
||||||
sideBar.AddItem(sideBarMemoryTitle, 1, 1, false)
|
sideBar.AddItem(sideBarMemoryTitle, 1, 1, false)
|
||||||
sideBar.AddItem(sideBarMemoryStats, 1, 1, false)
|
sideBar.AddItem(sideBarMemoryStats, 1, 1, false)
|
||||||
sideBar.AddItem(sideBarSwapTitle, 1, 1, false)
|
sideBar.AddItem(sideBarSwapTitle, 1, 1, false)
|
||||||
sideBar.AddItem(sideBarSwapStats, 1, 1, false)
|
sideBar.AddItem(sideBarSwapStats, 1, 1, false)
|
||||||
sideBar.AddItem(sideBarFilesystemTitle, 1, 1, false)
|
sideBar.AddItem(sideBarFilesystemTitle, 1, 1, false)
|
||||||
for _, filesystemAsTextView := range sideBarFilesystems {
|
for _, filesystemAsTextView := range sideBarFilesystems {
|
||||||
sideBar.AddItem(filesystemAsTextView, 1, 1, false)
|
sideBar.AddItem(filesystemAsTextView, 1, 1, false)
|
||||||
}
|
}
|
||||||
sideBar.AddItem(sideBarIPAddressesTitle, 1, 1, false)
|
sideBar.AddItem(sideBarIPAddressesTitle, 1, 1, false)
|
||||||
for _, ipAsTextView := range sideBarIPAddresses {
|
for _, ipAsTextView := range sideBarIPAddresses {
|
||||||
sideBar.AddItem(ipAsTextView, 1, 1, false)
|
sideBar.AddItem(ipAsTextView, 1, 1, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pages
|
// Pages
|
||||||
pages := tview.NewPages()
|
pages := tview.NewPages()
|
||||||
|
|
||||||
// Main UI
|
// Main UI
|
||||||
mainUI := tview.NewGrid().
|
mainUI := tview.NewGrid().
|
||||||
SetRows(2, 0, 4).
|
SetRows(2, 0, 4).
|
||||||
SetColumns(25, 50, 50).
|
SetColumns(25, 50, 50).
|
||||||
SetBorders(true).
|
SetBorders(true).
|
||||||
AddItem(header, 0, 0, 1, 3, 0, 0, false).
|
AddItem(header, 0, 0, 1, 3, 0, 0, false).
|
||||||
AddItem(footer, 2, 0, 1, 3, 0, 0, false)
|
AddItem(footer, 2, 0, 1, 3, 0, 0, false)
|
||||||
|
|
||||||
mainUI.AddItem(menu, 1, 0, 1, 1, 0, 100, true).
|
mainUI.AddItem(menu, 1, 0, 1, 1, 0, 100, true).
|
||||||
AddItem(main, 1, 1, 1, 1, 0, 100, false).
|
AddItem(main, 1, 1, 1, 1, 0, 100, false).
|
||||||
AddItem(sideBar, 1, 2, 1, 1, 0, 100, false)
|
AddItem(sideBar, 1, 2, 1, 1, 0, 100, false)
|
||||||
|
|
||||||
pages.AddPage(PAGE_MAIN_UI, mainUI, true, true)
|
pages.AddPage(PAGE_MAIN_UI, mainUI, true, true)
|
||||||
|
|
||||||
// Button modals
|
// Button modals
|
||||||
exitModal := tview.NewModal().
|
exitModal := tview.NewModal().
|
||||||
SetText("Are you sure you want to [red]EXIT?").
|
SetText("Are you sure you want to [red]EXIT?").
|
||||||
AddButtons([]string{"Yes", "Cancel"}).
|
AddButtons([]string{"Yes", "Cancel"}).
|
||||||
SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
||||||
if buttonLabel == "Yes" {
|
if buttonLabel == "Yes" {
|
||||||
app.Stop()
|
app.Stop()
|
||||||
}
|
}
|
||||||
pages.SwitchToPage(PAGE_MAIN_UI)
|
pages.SwitchToPage(PAGE_MAIN_UI)
|
||||||
})
|
})
|
||||||
pages.AddPage(PAGE_EXIT, exitModal, true, false)
|
pages.AddPage(PAGE_EXIT, exitModal, true, false)
|
||||||
exitButton.SetSelectedFunc(func() {
|
exitButton.SetSelectedFunc(func() {
|
||||||
pages.ShowPage(PAGE_EXIT)
|
pages.ShowPage(PAGE_EXIT)
|
||||||
})
|
})
|
||||||
|
|
||||||
rebootModal := tview.NewModal().
|
rebootModal := tview.NewModal().
|
||||||
SetText("Are you sure you want to [red]REBOOT?").
|
SetText("Are you sure you want to [red]REBOOT?").
|
||||||
AddButtons([]string{"Yes", "Cancel"}).
|
AddButtons([]string{"Yes", "Cancel"}).
|
||||||
SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
||||||
if buttonLabel == "Yes" {
|
if buttonLabel == "Yes" {
|
||||||
err := exec.Command(CMD_SYSTEMCTL, "reboot").Run()
|
err := exec.Command(CMD_SYSTEMCTL, "reboot").Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Could not reboot : %s ", err)
|
log.Fatalf("Could not reboot : %s ", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pages.SwitchToPage(PAGE_MAIN_UI)
|
pages.SwitchToPage(PAGE_MAIN_UI)
|
||||||
})
|
})
|
||||||
pages.AddPage(PAGE_REBOOT, rebootModal, true, false)
|
pages.AddPage(PAGE_REBOOT, rebootModal, true, false)
|
||||||
rebootButton.SetSelectedFunc(func() {
|
rebootButton.SetSelectedFunc(func() {
|
||||||
pages.ShowPage(PAGE_REBOOT)
|
pages.ShowPage(PAGE_REBOOT)
|
||||||
})
|
})
|
||||||
|
|
||||||
powerOffModal := tview.NewModal().
|
powerOffModal := tview.NewModal().
|
||||||
SetText("Are you sure you want to [red]POWER [red]OFF?").
|
SetText("Are you sure you want to [red]POWER [red]OFF?").
|
||||||
AddButtons([]string{"Yes", "Cancel"}).
|
AddButtons([]string{"Yes", "Cancel"}).
|
||||||
SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
SetDoneFunc(func(buttonIndex int, buttonLabel string) {
|
||||||
if buttonLabel == "Yes" {
|
if buttonLabel == "Yes" {
|
||||||
err := exec.Command(CMD_SYSTEMCTL, "poweroff").Run()
|
err := exec.Command(CMD_SYSTEMCTL, "poweroff").Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Could not power off : %s ", err)
|
log.Fatalf("Could not power off : %s ", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pages.SwitchToPage(PAGE_MAIN_UI)
|
pages.SwitchToPage(PAGE_MAIN_UI)
|
||||||
})
|
})
|
||||||
pages.AddPage(PAGE_POWEROFF, powerOffModal, true, false)
|
pages.AddPage(PAGE_POWEROFF, powerOffModal, true, false)
|
||||||
powerOffButton.SetSelectedFunc(func() {
|
powerOffButton.SetSelectedFunc(func() {
|
||||||
pages.ShowPage(PAGE_POWEROFF)
|
pages.ShowPage(PAGE_POWEROFF)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Setup tracking of which are of the UI can/has focus
|
// Setup tracking of which are of the UI can/has focus
|
||||||
primitivesThatCanFocus := []tview.Primitive{menu, exitButton, rebootButton, powerOffButton}
|
primitivesThatCanFocus := []tview.Primitive{menu, exitButton, rebootButton, powerOffButton}
|
||||||
currentFocus := 0
|
currentFocus := 0
|
||||||
|
|
||||||
// Setup basic switching between main menu and buttons for the UI
|
// Setup basic switching between main menu and buttons for the UI
|
||||||
app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||||
// Don't process input if we aren't on an existing element that can focus
|
// Don't process input if we aren't on an existing element that can focus
|
||||||
canContinue := false
|
canContinue := false
|
||||||
focusedPrimitive := app.GetFocus()
|
focusedPrimitive := app.GetFocus()
|
||||||
|
|
||||||
// Override some of the default behavior so up/dn move between fields in forms
|
// Override some of the default behavior so up/dn move between fields in forms
|
||||||
// Per API GetFocusedItemIndex on a form will be -1 if a form item isn't currently focused
|
// Per API GetFocusedItemIndex on a form will be -1 if a form item isn't currently focused
|
||||||
// We use this as a bit of a cheat to figure out if we're inside of a form that needs better nav options for users (ie. tab doesn't exist on a remote)
|
// We use this as a bit of a cheat to figure out if we're inside of a form that needs better nav options for users (ie. tab doesn't exist on a remote)
|
||||||
wifiField, wifiButton := wifiConfigForm.GetFocusedItemIndex()
|
wifiField, wifiButton := wifiConfigForm.GetFocusedItemIndex()
|
||||||
albumField, albumButton := selectAlbumsForm.GetFocusedItemIndex()
|
albumField, albumButton := selectAlbumsForm.GetFocusedItemIndex()
|
||||||
if wifiField != -1 || wifiButton != -1 || albumField != -1 || albumButton != -1 {
|
if wifiField != -1 || wifiButton != -1 || albumField != -1 || albumButton != -1 {
|
||||||
switch event.Key() {
|
switch event.Key() {
|
||||||
case tcell.KeyUp:
|
case tcell.KeyUp:
|
||||||
return tcell.NewEventKey(tcell.KeyBacktab, 0, event.Modifiers())
|
return tcell.NewEventKey(tcell.KeyBacktab, 0, event.Modifiers())
|
||||||
case tcell.KeyDown:
|
case tcell.KeyDown:
|
||||||
return tcell.NewEventKey(tcell.KeyTab, 0, event.Modifiers())
|
return tcell.NewEventKey(tcell.KeyTab, 0, event.Modifiers())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Standard override for the screen primitives we are using (ie. don't take over control in modals)
|
// Standard override for the screen primitives we are using (ie. don't take over control in modals)
|
||||||
for _, primitive := range primitivesThatCanFocus {
|
for _, primitive := range primitivesThatCanFocus {
|
||||||
if primitive == focusedPrimitive {
|
if primitive == focusedPrimitive {
|
||||||
canContinue = true
|
canContinue = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bail if we shouldn't be affecting user input
|
// Bail if we shouldn't be affecting user input
|
||||||
if !canContinue {
|
if !canContinue {
|
||||||
return event
|
return event
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add various forms of nav that make sense for a TUI (common stuff folks are used to)
|
// Add various forms of nav that make sense for a TUI (common stuff folks are used to)
|
||||||
currentFocusChanged := false
|
currentFocusChanged := false
|
||||||
switch event.Key() {
|
switch event.Key() {
|
||||||
case tcell.KeyRight, tcell.KeyTab:
|
case tcell.KeyRight, tcell.KeyTab:
|
||||||
currentFocus += 1
|
currentFocus += 1
|
||||||
if currentFocus >= len(primitivesThatCanFocus) {
|
if currentFocus >= len(primitivesThatCanFocus) {
|
||||||
currentFocus = 0
|
currentFocus = 0
|
||||||
}
|
}
|
||||||
currentFocusChanged = true
|
currentFocusChanged = true
|
||||||
case tcell.KeyLeft, tcell.KeyBacktab:
|
case tcell.KeyLeft, tcell.KeyBacktab:
|
||||||
currentFocus -= 1
|
currentFocus -= 1
|
||||||
if currentFocus < 0 {
|
if currentFocus < 0 {
|
||||||
currentFocus = len(primitivesThatCanFocus) - 1
|
currentFocus = len(primitivesThatCanFocus) - 1
|
||||||
}
|
}
|
||||||
currentFocusChanged = true
|
currentFocusChanged = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the focus based on navigation
|
// Update the focus based on navigation
|
||||||
if currentFocusChanged {
|
if currentFocusChanged {
|
||||||
app.SetFocus(primitivesThatCanFocus[currentFocus])
|
app.SetFocus(primitivesThatCanFocus[currentFocus])
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass through event so the main UI can process stuff properly
|
// Pass through event so the main UI can process stuff properly
|
||||||
return event
|
return event
|
||||||
})
|
})
|
||||||
|
|
||||||
// Show UI and panic if there are any errors
|
// Show UI and panic if there are any errors
|
||||||
if err := app.SetRoot(pages, true).SetFocus(primitivesThatCanFocus[currentFocus]).EnableMouse(false).Run(); err != nil {
|
if err := app.SetRoot(pages, true).SetFocus(primitivesThatCanFocus[currentFocus]).EnableMouse(false).Run(); err != nil {
|
||||||
log.Fatalf("Failed to run UI : ", err)
|
log.Fatalf("Failed to run UI : ", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -100,13 +100,13 @@ func Slideshow() {
|
||||||
}(keyboardCtx)
|
}(keyboardCtx)
|
||||||
|
|
||||||
// Run fim
|
// Run fim
|
||||||
// if err := fim.Run(); err != nil {
|
if err := fim.Run(); err != nil {
|
||||||
// // Unwrap the error a bit so we can find out if a signal killed fim or something else
|
// Unwrap the error a bit so we can find out if a signal killed fim or something else
|
||||||
// // An exit code of -1 means the program didn't exit in time or was terminated by a signal (per the docs)
|
// An exit code of -1 means the program didn't exit in time or was terminated by a signal (per the docs)
|
||||||
// if exitError, ok := err.(*exec.ExitError); ok && exitError.ExitCode() != -1 {
|
if exitError, ok := err.(*exec.ExitError); ok && exitError.ExitCode() != -1 {
|
||||||
// log.Fatalf("Error running fim : %s", err)
|
log.Fatalf("Error running fim : %s", err)
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// Stop fim slideshow advancing go routine
|
// Stop fim slideshow advancing go routine
|
||||||
close(stop_ticker)
|
close(stop_ticker)
|
Loading…
Reference in a new issue