func TestMode(reader io.Reader, writer io.Writer) {
	instanceInit = sync.Once{}
	construct()
	instance.Lock()
	defer instance.Unlock()
	instance.reader = reader
	instance.writer = writer
	instance.includeErrorsInOutput = true
}
TestMode creates a new instance of the bar for testing purposes,
and runs it on the provided streams instead of stdin/stdout.
package barista  
import (
	"encoding/json"
	"errors"
	"image/color"
	"io"
	"os"
	"os/exec"
	"os/signal"
	"strconv"
	"sync"
	"barista.run/bar"
	"barista.run/core"
	l "barista.run/logging"
	"barista.run/oauth"
	"barista.run/timing"
	"github.com/lucasb-eyer/go-colorful"
	"golang.org/x/sys/unix"
)
type i3Event struct {
	bar.Event
	Name string `json:"name"`
}
type i3Header struct {
	Version     int  `json:"version"`
	StopSignal  int  `json:"stop_signal,omitempty"`
	ContSignal  int  `json:"cont_signal,omitempty"`
	ClickEvents bool `json:"click_events"`
}
type i3Bar struct {
	sync.Mutex
	modules   []bar.Module
	moduleSet *core.ModuleSet
	clickHandlers map[string]func(bar.Event)
	errorHandler func(bar.ErrorEvent)
	update chan struct{}
	events chan i3Event
	reader io.Reader
	writer io.Writer
	encoder *json.Encoder
	started bool
	suppressSignals bool
	paused          bool
	refreshOnResume bool
	includeErrorsInOutput bool
	debugChan chan<- debugEvent
	debugMask int
}
type debugEventKind int
const (
	dEvtPaused debugEventKind = 1 << iota
	dEvtResumed
	dEvtModuleStopped
)
type debugEvent struct {
	kind debugEventKind
	data string
}
var instance *i3Bar
var instanceInit sync.Once
func construct() {
	instanceInit.Do(func() {
		instance = &i3Bar{
			update: make(chan struct{}, 1),
			events: make(chan i3Event),
			reader: os.Stdin,
			writer: os.Stdout,
			paused: true,
			errorHandler: DefaultErrorHandler,
		}
	})
}
func Add(module bar.Module) {
	construct()
	instance.Lock()
	defer instance.Unlock()
	if instance.started {
		panic("Cannot add modules after .Run()")
	}
	instance.modules = append(instance.modules, module)
}
func SuppressSignals(suppressSignals bool) {
	construct()
	instance.Lock()
	defer instance.Unlock()
	if instance.started {
		panic("Cannot change signal handling after .Run()")
	}
	instance.suppressSignals = suppressSignals
}
func SetErrorHandler(handler func(bar.ErrorEvent)) {
	construct()
	instance.Lock()
	defer instance.Unlock()
	instance.errorHandler = handler
}
func Run(modules ...bar.Module) error {
	oauth.InteractiveSetup()
	construct()
	b := instance
	var signalChan chan os.Signal
	if !b.suppressSignals {
		signalChan = make(chan os.Signal, 2)
		signal.Notify(signalChan, unix.SIGUSR1, unix.SIGUSR2)
	}
	b.modules = append(b.modules, modules...)
	b.moduleSet = core.NewModuleSet(b.modules)
	b.started = true
	l.Log("Bar started")
	go func(i <-chan int) {
		for range i {
			b.refresh()
		}
	}(b.moduleSet.Stream())
	errChan := make(chan error)
	go func(e chan<- error) {
		e <- b.readEvents()
	}(errChan)
	header := i3Header{
		Version:     1,
		ClickEvents: true,
	}
	if !b.suppressSignals {
		header.StopSignal = int(unix.SIGUSR1)
		header.ContSignal = int(unix.SIGUSR2)
	}
	b.encoder = json.NewEncoder(b.writer)
	if err := b.encoder.Encode(&header); err != nil {
		return err
	}
	if _, err := io.WriteString(b.writer, "["); err != nil {
		return err
	}
	b.resume()
	for {
		select {
		case <-b.update:
			if err := b.print(); err != nil {
				return err
			}
		case event := <-b.events:
			if onClick, ok := b.clickHandlers[event.Name]; ok {
				go onClick(event.Event)
			}
		case sig := <-signalChan:
			switch sig {
			case unix.SIGUSR1:
				b.pause()
			case unix.SIGUSR2:
				b.resume()
			}
		case err := <-errChan:
			return err
		}
	}
}
func DefaultErrorHandler(e bar.ErrorEvent) {
	exec.Command("i3-nagbar", "-m", e.Error.Error()).Run()
}
func colorString(c color.Color) string {
	cful, _ := colorful.MakeColor(c)
	return cful.Hex()
}
func i3map(s *bar.Segment) map[string]interface{} {
	i3map := make(map[string]interface{})
	txt, pango := s.Content()
	i3map["full_text"] = txt
	if shortText, ok := s.GetShortText(); ok {
		i3map["short_text"] = shortText
	}
	if color, ok := s.GetColor(); ok {
		i3map["color"] = colorString(color)
	}
	if background, ok := s.GetBackground(); ok {
		i3map["background"] = colorString(background)
	}
	if border, ok := s.GetBorder(); ok {
		i3map["border"] = colorString(border)
	}
	if minWidth, ok := s.GetMinWidth(); ok {
		i3map["min_width"] = minWidth
	}
	if align, ok := s.GetAlignment(); ok {
		i3map["align"] = align
	}
	if urgent, ok := s.IsUrgent(); ok {
		i3map["urgent"] = urgent
	}
	if separator, ok := s.HasSeparator(); ok {
		i3map["separator"] = separator
	}
	if padding, ok := s.GetPadding(); ok {
		i3map["separator_block_width"] = padding
	}
	if pango {
		i3map["markup"] = "pango"
	} else {
		i3map["markup"] = "none"
	}
	return i3map
}
func (b *i3Bar) print() error {
	b.clickHandlers = map[string]func(bar.Event){}
	output := make([]map[string]interface{}, 0)
	for _, segments := range b.moduleSet.LastOutputs() {
		for _, segment := range segments {
			out := i3map(segment)
			var clickHandler func(bar.Event)
			if err := segment.GetError(); err != nil {
				segment := segment
				clickHandler = func(e bar.Event) {
					if e.Button == bar.ButtonRight {
						b.errorHandler(bar.ErrorEvent{Error: err, Event: e})
					} else {
						segment.Click(e)
					}
				}
			} else if segment.HasClick() {
				clickHandler = segment.Click
			}
			if clickHandler != nil {
				name := strconv.Itoa(len(b.clickHandlers))
				out["name"] = name
				b.clickHandlers[name] = clickHandler
			}
			output = append(output, out)
		}
	}
	if err := b.encoder.Encode(output); err != nil {
		return err
	}
	_, err := io.WriteString(b.writer, ",\n")
	return err
}
func (b *i3Bar) readEvents() error {
	decoder := json.NewDecoder(b.reader)
	_, err := decoder.Token()
	if err != nil {
		return err
	}
	for decoder.More() {
		var event i3Event
		err = decoder.Decode(&event)
		if err != nil {
			return err
		}
		b.events <- event
	}
	return errors.New("stdin exhausted")
}
func (b *i3Bar) pause() {
	l.Log("Bar paused")
	b.Lock()
	defer b.Unlock()
	if b.paused {
		return
	}
	b.paused = true
	timing.Pause()
	b.emitDebugEvent(dEvtPaused, "")
}
func (b *i3Bar) resume() {
	l.Log("Bar resumed")
	b.Lock()
	defer b.Unlock()
	if !b.paused {
		return
	}
	b.paused = false
	timing.Resume()
	if b.refreshOnResume {
		b.refreshOnResume = false
		b.maybeUpdate()
	}
	b.emitDebugEvent(dEvtResumed, "")
}
func (b *i3Bar) refresh() {
	b.Lock()
	defer b.Unlock()
	if b.paused {
		l.Fine("Refresh on next resume")
		b.refreshOnResume = true
		return
	}
	b.maybeUpdate()
}
func (b *i3Bar) maybeUpdate() {
	select {
	case b.update <- struct{}{}:
	default:
	}
}
func (b *i3Bar) emitDebugEvent(kind debugEventKind, data string) {
	if b.debugMask&int(kind) != 0 {
		b.debugChan <- debugEvent{kind, data}
	}
}