Skip to content

Commit

Permalink
Refactor
Browse files Browse the repository at this point in the history
 -  Delete `mpd` directory.

 -  Allow updating multiple items from the same "data source", e.g. for
    displaying time in two different formats without having to read the
    time twice.

I'm still not satisfied with how the code looks, and I want to think of
better names for some types, but this works for now.
  • Loading branch information
c032 committed Feb 10, 2024
1 parent 4f56bac commit c08df54
Show file tree
Hide file tree
Showing 10 changed files with 292 additions and 264 deletions.
32 changes: 2 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,8 @@

My custom status for i3bar.

There's no configuration files, everything is decided at compile time.

I don't even use this anymore, but I also don't want to archive it just
yet.

## Compile-time requirements

* Go compiler. See the `go.mod' file to know which version I was using
last time I updated this repository.

## Runtime requirements

* A running instance of MPD. Take a look at the `cmd/dsts' package to
know which port number is expected.

## What is being displayed

* Current date and time in the same format as Skyrim.
* Song currently playing in MPD.

## How it works

Each `component' is a function receiving a channel as its only argument.
Each time a status is sent to that channel, a refresh is triggered.

The main program coordinates all those updates, and sends new messages
when necessary.

This allows components to each have their own `refresh rate', instead of
using a single global refresh rate for everything.
There's runtime no configuration files. The whole point of this, is to
have a single executable that just works.

## License

Expand Down
97 changes: 51 additions & 46 deletions cmd/dsts/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,78 +4,83 @@
package main

import (
"context"
"encoding/json"
"errors"
"os"
"sync"
"sync/atomic"

"github.com/c032/dsts"
"github.com/c032/dsts/mpd"
"github.com/c032/dsts/tamrieltime"
dststime "github.com/c032/dsts/time"
)

const (
// mpdAddr is the address where MPD is listening in.
mpdAddr = ":6600"
func main() {
ctx := context.Background()

// mpdStatusMaxWidth is the maximum width, in characters, allowed for the
// MPD status.
mpdStatusMaxWidth = 80
)
timeSource := &dststime.Source{}

sources := []dsts.Source{
timeSource,
}

func main() {
// Active providers, in the same order that they will be displayed in the
// i3bar.
//
// Providers with a lower index will be displayed somewhere to the left of
// providers with a higher index.
providers := []dsts.Provider{
dsts.Wrap(
mpd.Dial(mpdAddr),
mpdStatusMaxWidth,
),
tamrieltime.TamrielTime,
mutableStatusItems := []*atomic.Pointer[dsts.I3Status]{
&timeSource.StatusUnix,
&timeSource.StatusDateTime,
}

enc := json.NewEncoder(os.Stdout)

os.Stdout.Write([]byte(`{"version":1}[[]`))

// tick is only used to notify when the status line must be updated.
tick := make(chan struct{})

// mu prevents multiple goroutines from modifying `i3sts` at the same time.
mu := sync.RWMutex{}
for _, source := range sources {
_ = source.OnUpdate(func() {
tick <- struct{}{}
})
}

// i3sts is what will be serialized as JSON and printed to standard output.
i3sts := make([]dsts.I3Status, len(providers))
enc := json.NewEncoder(os.Stdout)

// Run each provider on their own goroutine, coordinate updates to the
// i3status line, and trigger a refresh when necessary.
for i, p := range providers {
ch := make(chan dsts.I3Status)
os.Stdout.Write([]byte(`{"version":1}[[]`))

// Run the provider, usually forever.
go p(ch)
// visibleStatusItems is what will be serialized as JSON and printed to
// standard output.
visibleStatusItems := make([]dsts.I3Status, len(mutableStatusItems))

// For each update from the provider, update the current status and
// trigger a refresh.
go func(i int, p dsts.Provider) {
for status := range ch {
mu.Lock()
i3sts[i] = status
mu.Unlock()
// Wait for a refresh to trigger, and update the status line.
for range tick {
for i, itemPtr := range mutableStatusItems {
if itemPtr == nil {
continue
}

tick <- struct{}{}
v := itemPtr.Load()
if v == nil {
continue
}
}(i, p)
}

// Wait for a refresh to trigger, and update the status line.
for range tick {
visibleStatusItems[i] = *v
}

os.Stdout.Write([]byte{','})

mu.RLock()
_ = enc.Encode(i3sts)
mu.RUnlock()
_ = enc.Encode(visibleStatusItems)
}

<-ctx.Done()

err := ctx.Err()
if err != nil {
if !errors.Is(err, context.Canceled) {
os.Exit(1)
}

cause := context.Cause(ctx)
if cause != nil && cause != err {
os.Exit(1)
}
}
}
36 changes: 36 additions & 0 deletions color.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package dsts

import (
"errors"
)

var ErrInvalidColor = errors.New("invalid color")

const (
DefaultStatusColor = "#999999"
DefaultStatusColorError = "#e20024"
)

func isNumber(c rune) bool {
return c >= '0' && c <= '9'
}

func IsValidColor(color string) bool {
if len(color) != len("#000") && len(color) != len("#000000") {
return false
}
if color[0] != '#' {
return false
}

for i, c := range color {
if i == 0 {
continue
}
if !isNumber(c) {
return false
}
}

return true
}
171 changes: 0 additions & 171 deletions mpd/mpd.go

This file was deleted.

8 changes: 0 additions & 8 deletions provider.go

This file was deleted.

Loading

0 comments on commit c08df54

Please sign in to comment.