Add variable-period block refreshes
Signed-off-by: AKP <tom@tdpain.net>
This commit is contained in:
parent
5c7c87d911
commit
ec663d15d1
14 changed files with 140 additions and 64 deletions
|
@ -33,7 +33,7 @@ func main() {
|
|||
}
|
||||
|
||||
func run() error {
|
||||
b := i3bar.New(os.Stdout, os.Stdin, time.Second*2, syscall.SIGUSR1)
|
||||
b := i3bar.New(os.Stdout, os.Stdin, syscall.SIGUSR1)
|
||||
if err := b.Initialise(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -57,8 +57,8 @@ func run() error {
|
|||
providers.NewAudioPlayer(32),
|
||||
)
|
||||
|
||||
if err := b.Emit([]i3bar.BlockGenerator{
|
||||
providers.NewPlainText("cdmbar" + commitHash),
|
||||
if err := b.Emit([]*i3bar.Block{
|
||||
{FullText: "cdmbar" + commitHash},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -14,23 +14,25 @@ import (
|
|||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type I3bar struct {
|
||||
writer io.Writer
|
||||
reader io.Reader
|
||||
updateInterval time.Duration
|
||||
updateSignal syscall.Signal
|
||||
registeredGenerators []BlockGenerator
|
||||
registeredConsumers []ClickEventConsumer
|
||||
|
||||
hasInitialised bool
|
||||
hasSentFirstLine bool
|
||||
type generatorInfo struct {
|
||||
Provider BlockGenerator
|
||||
HasClickConsumer bool
|
||||
Last *Block
|
||||
}
|
||||
|
||||
func New(writer io.Writer, reader io.Reader, updateInterval time.Duration, updateSignal syscall.Signal) *I3bar {
|
||||
type I3bar struct {
|
||||
writer io.Writer
|
||||
reader io.Reader
|
||||
updateSignal syscall.Signal
|
||||
|
||||
generators []*generatorInfo
|
||||
tickNumber uint8
|
||||
}
|
||||
|
||||
func New(writer io.Writer, reader io.Reader, updateSignal syscall.Signal) *I3bar {
|
||||
return &I3bar{
|
||||
writer: writer,
|
||||
reader: reader,
|
||||
updateInterval: updateInterval,
|
||||
updateSignal: updateSignal,
|
||||
}
|
||||
}
|
||||
|
@ -44,11 +46,10 @@ func (b *I3bar) Initialise() error {
|
|||
return err
|
||||
}
|
||||
|
||||
if _, err := b.writer.Write(append(capabilities, '\n')); err != nil {
|
||||
if _, err := b.writer.Write(append(capabilities, []byte("\n[\n")...)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b.hasInitialised = true
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -59,42 +60,13 @@ var defaultColorSet = &ColorSet{
|
|||
Background: &Color{0x28, 0x28, 0x28},
|
||||
}
|
||||
|
||||
func (b *I3bar) Emit(generators []BlockGenerator) error {
|
||||
if !b.hasInitialised {
|
||||
if err := b.Initialise(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var blocks []*Block
|
||||
for _, generator := range generators {
|
||||
b, err := generator.Block(defaultColorSet)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("generator", fmt.Sprintf("%T", generator)).Send()
|
||||
b = &Block{
|
||||
FullText: "ERROR",
|
||||
TextColor: defaultColorSet.Bad,
|
||||
}
|
||||
}
|
||||
if b == nil {
|
||||
continue
|
||||
}
|
||||
blocks = append(blocks, b)
|
||||
}
|
||||
|
||||
func (b *I3bar) Emit(blocks []*Block) error {
|
||||
jsonData, err := json.Marshal(blocks)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !b.hasSentFirstLine {
|
||||
jsonData = append([]byte("[\n"), jsonData...)
|
||||
b.hasSentFirstLine = true
|
||||
} else {
|
||||
jsonData = append([]byte{','}, jsonData...)
|
||||
}
|
||||
|
||||
jsonData = append(jsonData, '\n')
|
||||
jsonData = append(jsonData, []byte(",\n")...)
|
||||
|
||||
if _, err := b.writer.Write(jsonData); err != nil {
|
||||
return err
|
||||
|
@ -107,24 +79,25 @@ func (b *I3bar) Emit(generators []BlockGenerator) error {
|
|||
// function should not be called after StartLoop is called.
|
||||
func (b *I3bar) RegisterBlockGenerator(bg ...BlockGenerator) {
|
||||
for _, bgx := range bg {
|
||||
b.registeredGenerators = append([]BlockGenerator{bgx}, b.registeredGenerators...)
|
||||
}
|
||||
_, hasClickConsumer := bgx.(ClickEventConsumer)
|
||||
|
||||
for _, generator := range bg {
|
||||
if g, ok := generator.(ClickEventConsumer); ok {
|
||||
b.registeredConsumers = append(b.registeredConsumers, g)
|
||||
}
|
||||
metadata := new(generatorInfo)
|
||||
metadata.Provider = bgx
|
||||
metadata.HasClickConsumer = hasClickConsumer
|
||||
|
||||
b.generators = append([]*generatorInfo{metadata}, b.generators...)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *I3bar) StartLoop() error {
|
||||
|
||||
// The ticker will start after the specified duration, not when we
|
||||
// instantiate it. Circumventing that here by calling Emit now.
|
||||
if err := b.Emit(b.registeredGenerators); err != nil {
|
||||
if err := b.tick(false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(b.updateInterval)
|
||||
ticker := time.NewTicker(time.Second)
|
||||
sigUpdate := make(chan os.Signal, 1)
|
||||
signal.Notify(sigUpdate, os.Signal(b.updateSignal))
|
||||
|
||||
|
@ -135,17 +108,58 @@ func (b *I3bar) StartLoop() error {
|
|||
for {
|
||||
select {
|
||||
case <-sigUpdate:
|
||||
if err := b.Emit(b.registeredGenerators); err != nil {
|
||||
log.Error().Err(err).Msg("could not emit registered generators")
|
||||
if err := b.tick(true); err != nil {
|
||||
log.Error().Err(err).Msg("could not tick")
|
||||
}
|
||||
case <-ticker.C:
|
||||
if err := b.Emit(b.registeredGenerators); err != nil {
|
||||
log.Error().Err(err).Msg("could not emit registered generators")
|
||||
if err := b.tick(false); err != nil {
|
||||
log.Error().Err(err).Msg("could not tick")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *I3bar) tick(override bool) error {
|
||||
var hasChanged bool
|
||||
for _, gen := range b.generators {
|
||||
if override || (gen.Provider.Frequency() == 0 && gen.Last == nil) || (gen.Provider.Frequency() != 0 && b.tickNumber%gen.Provider.Frequency() == 0) {
|
||||
block, err := gen.Provider.Block(defaultColorSet)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("generator", fmt.Sprintf("%T", gen.Provider)).Send()
|
||||
block = &Block{
|
||||
FullText: "ERROR",
|
||||
TextColor: defaultColorSet.Bad,
|
||||
}
|
||||
}
|
||||
if block == nil {
|
||||
block = &Block{
|
||||
FullText: "MISSING",
|
||||
TextColor: defaultColorSet.Warning,
|
||||
}
|
||||
}
|
||||
|
||||
if block != gen.Last {
|
||||
gen.Last = block
|
||||
hasChanged = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if hasChanged {
|
||||
var blocks []*Block
|
||||
for _, gen := range b.generators {
|
||||
blocks = append(blocks, gen.Last)
|
||||
}
|
||||
if err := b.Emit(blocks); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
b.tickNumber += 1
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *I3bar) consumerLoop(requestBarRefresh func()) {
|
||||
r := bufio.NewReader(b.reader)
|
||||
for {
|
||||
|
@ -166,10 +180,13 @@ func (b *I3bar) consumerLoop(requestBarRefresh func()) {
|
|||
continue // idk what this could be but it's not relevant so BYE!
|
||||
}
|
||||
|
||||
for _, consumer := range b.registeredConsumers {
|
||||
consumerName, consumerInstance := consumer.GetNameAndInstance()
|
||||
for _, consumer := range b.generators {
|
||||
if !consumer.HasClickConsumer {
|
||||
continue
|
||||
}
|
||||
consumerName, consumerInstance := consumer.Provider.GetNameAndInstance()
|
||||
if consumerName == event.Name && (consumerName == "" || consumerInstance == event.Instance) {
|
||||
if consumer.OnClick(event) {
|
||||
if consumer.Provider.(ClickEventConsumer).OnClick(event) {
|
||||
requestBarRefresh()
|
||||
}
|
||||
}
|
||||
|
@ -204,6 +221,9 @@ type ProvidesNameAndInstance interface {
|
|||
type BlockGenerator interface {
|
||||
ProvidesNameAndInstance
|
||||
Block(*ColorSet) (*Block, error)
|
||||
// Frequency should return the frequency at which the block is updated, as
|
||||
// "1 per n seconds".
|
||||
Frequency() uint8
|
||||
}
|
||||
|
||||
type ClickEvent struct {
|
||||
|
|
|
@ -38,6 +38,10 @@ func NewAudioPlayer(maxLabelLength int) *AudioPlayer {
|
|||
}
|
||||
}
|
||||
|
||||
func (g *AudioPlayer) Frequency() uint8 {
|
||||
return 1
|
||||
}
|
||||
|
||||
type playingAudioInfo struct {
|
||||
Track string
|
||||
Artist string
|
||||
|
|
|
@ -27,6 +27,7 @@ type Battery struct {
|
|||
|
||||
name string
|
||||
previousWasBackgroundWarning bool
|
||||
isAlert bool
|
||||
}
|
||||
|
||||
func NewBattery(deviceName string, fullThreshold, okThreshold, warningThreshold float32) i3bar.BlockGenerator {
|
||||
|
@ -35,10 +36,17 @@ func NewBattery(deviceName string, fullThreshold, okThreshold, warningThreshold
|
|||
FullThreshold: fullThreshold,
|
||||
OkThreshold: okThreshold,
|
||||
WarningThreshold: warningThreshold,
|
||||
name: "battery",
|
||||
name: "battery",
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Battery) Frequency() uint8 {
|
||||
if g.isAlert {
|
||||
return 1
|
||||
}
|
||||
return 5
|
||||
}
|
||||
|
||||
func (g *Battery) infoPath() string {
|
||||
return path.Join("/sys/class/power_supply", g.DeviceName)
|
||||
}
|
||||
|
@ -124,6 +132,8 @@ func (g *Battery) Block(colors *i3bar.ColorSet) (*i3bar.Block, error) {
|
|||
|
||||
if percentage < g.WarningThreshold && g.WarningThreshold != 0 {
|
||||
|
||||
g.isAlert = true
|
||||
|
||||
if g.previousWasBackgroundWarning || state == batteryStateCharging { // disable flashing when on charge
|
||||
block.TextColor = colors.Bad
|
||||
} else {
|
||||
|
@ -134,6 +144,8 @@ func (g *Battery) Block(colors *i3bar.ColorSet) (*i3bar.Block, error) {
|
|||
|
||||
} else if percentage < g.OkThreshold && g.OkThreshold != 0 {
|
||||
block.TextColor = colors.Warning
|
||||
} else {
|
||||
g.isAlert = false
|
||||
}
|
||||
|
||||
switch state {
|
||||
|
|
|
@ -30,6 +30,10 @@ func NewCPU(okThreshold, warningThreshold float32) i3bar.BlockGenerator {
|
|||
return m
|
||||
}
|
||||
|
||||
func (g *CPU) Frequency() uint8 {
|
||||
return 2
|
||||
}
|
||||
|
||||
func (g *CPU) doSample() error {
|
||||
contents, err := ioutil.ReadFile("/proc/stat")
|
||||
if err != nil {
|
||||
|
|
|
@ -17,6 +17,10 @@ func NewDateTime() i3bar.BlockGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
func (g *DateTime) Frequency() uint8 {
|
||||
return 1
|
||||
}
|
||||
|
||||
func (g *DateTime) Block(*i3bar.ColorSet) (*i3bar.Block, error) {
|
||||
cTime := time.Now().Local()
|
||||
|
||||
|
|
|
@ -27,6 +27,10 @@ func NewDisk(mountPath string, okThreshold, warningThreshold float32) i3bar.Bloc
|
|||
}
|
||||
}
|
||||
|
||||
func (g *Disk) Frequency() uint8 {
|
||||
return 5
|
||||
}
|
||||
|
||||
func (g *Disk) getAvailable() (float32, error) {
|
||||
cmdout, err := runCommand("df", "-x", "fuse.portal")
|
||||
if err != nil {
|
||||
|
|
|
@ -21,6 +21,10 @@ func NewIPAddress(adapter string) i3bar.BlockGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
func (g *IPAddress) Frequency() uint8 {
|
||||
return 5
|
||||
}
|
||||
|
||||
func (g *IPAddress) getAdapterIPAddress() (string, error) {
|
||||
// call ifconfig
|
||||
output, err := runCommand("ifconfig")
|
||||
|
|
|
@ -21,6 +21,10 @@ func NewLaunchProgram(text string, executable string) i3bar.BlockGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
func (g *LaunchProgram) Frequency() uint8 {
|
||||
return 255
|
||||
}
|
||||
|
||||
func (g *LaunchProgram) Block(*i3bar.ColorSet) (*i3bar.Block, error) {
|
||||
return &i3bar.Block{
|
||||
Name: g.name,
|
||||
|
|
|
@ -25,6 +25,10 @@ func NewMemory(okThreshold, warningThreshold float32) i3bar.BlockGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
func (g *Memory) Frequency() uint8 {
|
||||
return 2
|
||||
}
|
||||
|
||||
var (
|
||||
memoryTotalRegexp = regexp.MustCompile(`MemTotal: +(\d+) kB`)
|
||||
memoryAvailableRegexp = regexp.MustCompile(`MemAvailable: +(\d+) kB`)
|
||||
|
|
|
@ -15,6 +15,10 @@ func NewPlainText(text string) i3bar.BlockGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
func (g *PlainText) Frequency() uint8 {
|
||||
return 255
|
||||
}
|
||||
|
||||
func (g *PlainText) Block(*i3bar.ColorSet) (*i3bar.Block, error) {
|
||||
return &i3bar.Block{
|
||||
Name: g.name,
|
||||
|
|
|
@ -25,6 +25,10 @@ func NewPulseaudioVolume() i3bar.BlockGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
func (g *PulseaudioVolume) Frequency() uint8 {
|
||||
return 2
|
||||
}
|
||||
|
||||
func (g *PulseaudioVolume) getInfo() (string, error) {
|
||||
x, err := runCommand("pacmd", "info")
|
||||
return string(x), err
|
||||
|
|
|
@ -28,6 +28,10 @@ func NewTimer(useShortLabel bool) i3bar.BlockGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
func (g *Timer) Frequency() uint8 {
|
||||
return 1
|
||||
}
|
||||
|
||||
func (g *Timer) OnClick(event *i3bar.ClickEvent) bool {
|
||||
resetButtonPressed := event.Button == i3bar.RightMouseButton
|
||||
triggerButtonPressed := event.Button == i3bar.LeftMouseButton
|
||||
|
|
|
@ -26,6 +26,10 @@ func NewWiFi(adapter string, okThreshold float32) i3bar.BlockGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
func (g *WiFi) Frequency() uint8 {
|
||||
return 5
|
||||
}
|
||||
|
||||
var (
|
||||
// For use with iwconfig
|
||||
essidRegexp = regexp.MustCompile(`ESSID:(?:"(.+)"|off/any)`)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue