4
4
package main
5
5
6
6
import (
7
+ "context"
7
8
"encoding/json"
9
+ "errors"
8
10
"os"
9
11
"sync"
10
12
11
13
"github.com/c032/dsts"
12
- "github.com/c032/dsts/mpd"
13
- "github.com/c032/dsts/tamrieltime"
14
+ timesource "github.com/c032/dsts/sources/time"
14
15
)
15
16
16
- const (
17
- // mpdAddr is the address where MPD is listening in.
18
- mpdAddr = ":6600"
17
+ func main () {
18
+ rootContext := context . Background ()
19
+ ctx , cancel := context . WithCancelCause ( rootContext )
19
20
20
- // mpdStatusMaxWidth is the maximum width, in characters, allowed for the
21
- // MPD status.
22
- mpdStatusMaxWidth = 80
23
- )
21
+ srcTime , err := timesource .New (ctx , nil )
22
+ if err != nil {
23
+ // Programmer error.
24
+ panic (err )
25
+ }
24
26
25
- func main () {
26
- // Active providers, in the same order that they will be displayed in the
27
- // i3bar.
27
+ sources := []dsts.Source {
28
+ srcTime ,
29
+ }
30
+
31
+ // Active status readers, in the same order that they will be displayed in
32
+ // the i3bar.
28
33
//
29
- // Providers with a lower index will be displayed somewhere to the left of
30
- // providers with a higher index.
31
- providers := []dsts.Provider {
32
- dsts .Wrap (
33
- mpd .Dial (mpdAddr ),
34
- mpdStatusMaxWidth ,
35
- ),
36
- tamrieltime .TamrielTime ,
34
+ // From left to right. Index `0` if leftmost.
35
+ statusReaders := []dsts.StatusReader {
36
+ srcTime .StatusUnix ,
37
+ srcTime .StatusDateTime ,
37
38
}
38
39
39
40
enc := json .NewEncoder (os .Stdout )
@@ -42,40 +43,72 @@ func main() {
42
43
43
44
// tick is only used to notify when the status line must be updated.
44
45
tick := make (chan struct {})
46
+ go func (ctx context.Context ) {
47
+ <- ctx .Done ()
45
48
46
- // mu prevents multiple goroutines from modifying `i3sts` at the same time.
47
- mu := sync.RWMutex {}
48
-
49
- // i3sts is what will be serialized as JSON and printed to standard output.
50
- i3sts := make ([]dsts.I3Status , len (providers ))
51
-
52
- // Run each provider on their own goroutine, coordinate updates to the
53
- // i3status line, and trigger a refresh when necessary.
54
- for i , p := range providers {
55
- ch := make (chan dsts.I3Status )
56
-
57
- // Run the provider, usually forever.
58
- go p (ch )
49
+ close (tick )
50
+ }(ctx )
59
51
60
- // For each update from the provider, update the current status and
61
- // trigger a refresh.
62
- go func (i int , p dsts.Provider ) {
63
- for status := range ch {
64
- mu .Lock ()
65
- i3sts [i ] = status
66
- mu .Unlock ()
52
+ // `mu` prevents multiple goroutines from modifying `i3sts` at the same
53
+ // time.
54
+ mu := sync.RWMutex {}
67
55
56
+ // `i3sts` is what will be serialized as JSON and printed to standard
57
+ // output.
58
+ i3sts := make ([]dsts.I3Status , len (statusReaders ))
59
+
60
+ // Trigger a refresh each time a source has an update.
61
+ for _ , s := range sources {
62
+ // `ch` receives a value when there's an update.
63
+ //
64
+ // The `Subscribe` method will close `ch`.
65
+ ch := s .Subscribe (ctx )
66
+
67
+ // Start listening for updates in background.
68
+ //
69
+ // This goroutine will exit when `ch` is closed.
70
+ go func (tick chan <- struct {}, ch <- chan struct {}) {
71
+ for range ch {
68
72
tick <- struct {}{}
69
73
}
70
- }(i , p )
74
+ }(tick , ch )
71
75
}
72
76
73
- // Wait for a refresh to trigger, and update the status line.
77
+ // Wait for a refresh to trigger. Then read from all status readers and
78
+ // update the status line.
74
79
for range tick {
80
+ if ctx .Err () != nil {
81
+ // The error will be handled outside the loop.
82
+ break
83
+ }
84
+
75
85
os .Stdout .Write ([]byte {',' })
76
86
77
- mu .RLock ()
78
- _ = enc .Encode (i3sts )
79
- mu .RUnlock ()
87
+ mu .Lock ()
88
+
89
+ for i , readStatus := range statusReaders {
90
+ i3sts [i ] = readStatus ()
91
+ }
92
+
93
+ var err error
94
+ err = enc .Encode (i3sts )
95
+
96
+ mu .Unlock ()
97
+
98
+ if err != nil {
99
+ cancel (err )
100
+ }
101
+ }
102
+
103
+ err = ctx .Err ()
104
+ if err != nil {
105
+ if ! errors .Is (err , context .Canceled ) {
106
+ os .Exit (1 )
107
+ }
108
+
109
+ cause := context .Cause (ctx )
110
+ if cause != nil && cause != err {
111
+ os .Exit (1 )
112
+ }
80
113
}
81
114
}
0 commit comments