2019-03-04 23:06:07 +00:00
|
|
|
package termboxScreen
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2019-03-06 13:39:23 +00:00
|
|
|
"fmt"
|
2019-03-04 23:06:07 +00:00
|
|
|
"os"
|
|
|
|
"runtime"
|
|
|
|
"syscall"
|
2019-03-05 03:42:22 +00:00
|
|
|
"time"
|
2019-03-04 23:06:07 +00:00
|
|
|
|
|
|
|
termbox "github.com/nsf/termbox-go"
|
|
|
|
)
|
|
|
|
|
2019-03-05 03:42:22 +00:00
|
|
|
const (
|
|
|
|
NoRefresh = 0
|
|
|
|
)
|
|
|
|
|
2019-03-04 23:06:07 +00:00
|
|
|
type Screen interface {
|
|
|
|
Id() int
|
|
|
|
Initialize(Bundle) error
|
|
|
|
HandleKeyEvent(termbox.Event) int
|
|
|
|
HandleNoneEvent(termbox.Event) int
|
|
|
|
DrawScreen()
|
|
|
|
ResizeScreen()
|
|
|
|
}
|
|
|
|
|
|
|
|
type Manager struct {
|
|
|
|
defaultFg termbox.Attribute
|
|
|
|
defaultBg termbox.Attribute
|
|
|
|
|
|
|
|
screens map[int]Screen
|
|
|
|
displayScreenId int
|
|
|
|
events chan termbox.Event
|
2019-03-05 03:42:22 +00:00
|
|
|
|
|
|
|
running bool
|
|
|
|
refreshRate time.Duration
|
2019-03-04 23:06:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewManager() *Manager {
|
|
|
|
m := Manager{
|
2019-03-05 03:42:22 +00:00
|
|
|
defaultFg: termbox.ColorWhite,
|
|
|
|
defaultBg: termbox.ColorBlack,
|
|
|
|
events: make(chan termbox.Event),
|
|
|
|
screens: make(map[int]Screen),
|
|
|
|
refreshRate: NoRefresh,
|
2019-03-04 23:06:07 +00:00
|
|
|
}
|
2019-03-06 13:39:23 +00:00
|
|
|
if err := termbox.Init(); err != nil {
|
|
|
|
fmt.Println("Error initializing termbox")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
termbox.SetOutputMode(termbox.Output256)
|
2019-03-04 23:06:07 +00:00
|
|
|
return &m
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) SetDefaultFg(c termbox.Attribute) { m.defaultFg = c }
|
|
|
|
func (m *Manager) SetDefaultBg(c termbox.Attribute) { m.defaultBg = c }
|
|
|
|
|
|
|
|
// AddScreen adds a screen to the screens map, they're indexed by the value
|
|
|
|
// returned from the screen's Id() function
|
|
|
|
func (m *Manager) AddScreen(s Screen) {
|
|
|
|
m.screens[s.Id()] = s
|
|
|
|
// If this is the only screen we've added, set it to active
|
|
|
|
if len(m.screens) == 1 {
|
|
|
|
m.SetDisplayScreen(s.Id())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-06 18:15:00 +00:00
|
|
|
// AddAndInitializeScreen adds a screen just like AddScreen, but then
|
|
|
|
// calls it's 'Initialize' function with a blank bundle
|
|
|
|
func (m *Manager) AddAndInitializeScreen(s Screen) error {
|
|
|
|
m.AddScreen(s)
|
|
|
|
return m.InitializeScreen(s.Id(), Bundle{})
|
|
|
|
}
|
|
|
|
|
2019-03-04 23:06:07 +00:00
|
|
|
func (m *Manager) GetScreens() map[int]Screen {
|
|
|
|
return m.screens
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) SetDisplayScreen(id int) error {
|
|
|
|
if id == m.displayScreenId {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
var ok bool
|
|
|
|
if _, ok = m.screens[id]; !ok {
|
|
|
|
return errors.New("Invalid Screen Id")
|
|
|
|
}
|
|
|
|
m.displayScreenId = id
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) InitializeScreen(id int, b Bundle) error {
|
|
|
|
var ok bool
|
|
|
|
if _, ok = m.screens[id]; !ok {
|
|
|
|
return errors.New("Invalid screen id")
|
|
|
|
}
|
|
|
|
return m.screens[id].Initialize(b)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) Loop() error {
|
|
|
|
if len(m.screens) == 0 {
|
|
|
|
return errors.New("Loop cannot run without screens")
|
|
|
|
}
|
2019-03-05 03:42:22 +00:00
|
|
|
m.running = true
|
|
|
|
go m.pollUserEvents()
|
2019-03-04 23:06:07 +00:00
|
|
|
// We always start display the first screen added
|
|
|
|
m.layoutAndDrawScreen()
|
|
|
|
for {
|
|
|
|
event := <-m.events
|
|
|
|
if event.Type == termbox.EventKey {
|
|
|
|
if event.Key == termbox.KeyCtrlC {
|
|
|
|
break
|
|
|
|
} else if event.Key == termbox.KeyCtrlZ {
|
|
|
|
if runtime.GOOS != "windows" {
|
|
|
|
process, _ := os.FindProcess(os.Getpid())
|
|
|
|
termbox.Close()
|
|
|
|
process.Signal(syscall.SIGSTOP)
|
|
|
|
termbox.Init()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
newScreenIndex := m.handleKeyEvent(event)
|
|
|
|
if err := m.SetDisplayScreen(newScreenIndex); err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
m.layoutAndDrawScreen()
|
|
|
|
}
|
|
|
|
} else if event.Type == termbox.EventNone {
|
|
|
|
// Type = EventNone is how we can trigger automatic events
|
|
|
|
newScreenIndex := m.handleNoneEvent(event)
|
|
|
|
if err := m.SetDisplayScreen(newScreenIndex); err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
m.layoutAndDrawScreen()
|
|
|
|
} else if event.Type == termbox.EventResize {
|
|
|
|
m.resizeScreen()
|
|
|
|
m.layoutAndDrawScreen()
|
|
|
|
}
|
|
|
|
}
|
2019-03-05 03:42:22 +00:00
|
|
|
m.running = false
|
2019-03-06 13:39:23 +00:00
|
|
|
close(m.events)
|
2019-03-04 23:06:07 +00:00
|
|
|
termbox.Close()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-03-05 03:42:22 +00:00
|
|
|
func (m *Manager) SendNoneEvent() {
|
|
|
|
m.SendEvent(termbox.Event{Type: termbox.EventNone})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) SendEvent(t termbox.Event) {
|
|
|
|
m.events <- t
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) pollUserEvents() {
|
|
|
|
for m.running {
|
|
|
|
m.SendEvent(termbox.PollEvent())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) SetRefreshRate(t time.Duration) {
|
|
|
|
m.refreshRate = t
|
|
|
|
go m.pollRefreshEvents()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) pollRefreshEvents() {
|
2019-03-06 13:39:23 +00:00
|
|
|
if m.refreshRate > time.Microsecond {
|
2019-03-05 03:42:22 +00:00
|
|
|
for m.running {
|
|
|
|
time.Sleep(m.refreshRate)
|
|
|
|
m.SendNoneEvent()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-04 23:06:07 +00:00
|
|
|
func (m *Manager) handleKeyEvent(event termbox.Event) int {
|
|
|
|
return m.screens[m.displayScreenId].HandleKeyEvent(event)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) handleNoneEvent(event termbox.Event) int {
|
|
|
|
return m.screens[m.displayScreenId].HandleNoneEvent(event)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) resizeScreen() {
|
|
|
|
m.screens[m.displayScreenId].ResizeScreen()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) drawBackground() {
|
|
|
|
termbox.Clear(0, m.defaultBg)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) layoutAndDrawScreen() {
|
|
|
|
m.drawBackground()
|
|
|
|
m.screens[m.displayScreenId].DrawScreen()
|
|
|
|
termbox.Flush()
|
|
|
|
}
|