This repository has been archived by the owner on Jun 25, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 44
/
colors.go
174 lines (156 loc) · 4.56 KB
/
colors.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package colors provides helper functions to manage color and color schemes.
package colors // import "barista.run/colors"
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"image/color"
"io/ioutil"
"os"
"os/exec"
"strings"
"github.com/lucasb-eyer/go-colorful"
"github.com/spf13/afero"
)
// ColorfulColor extends image/color.Color with the ability
// to get a go-colorful.Color. This is simpler than using
// go-colorful.MakeColor because the backing implementation
// already has a colorful value.
type ColorfulColor interface {
color.Color
Colorful() colorful.Color
}
type colorfulColor struct {
colorful.Color
}
func (c *colorfulColor) Colorful() colorful.Color {
return c.Color
}
// Hex sanity-checks and constructs a color from a hex-string.
// Any string that can be parsed by colorful is acceptable.
func Hex(hex string) ColorfulColor {
c, err := colorful.Hex(hex)
if err != nil {
return nil
}
return &colorfulColor{c}
}
// Scheme gets a color from the user-defined color scheme.
// Some common names are 'good', 'bad', and 'degraded'.
func Scheme(name string) ColorfulColor {
return scheme[name]
}
// Set sets a named scheme color to the given value.
func Set(name string, color color.Color) {
if color == nil {
delete(scheme, name)
return
}
if c, ok := colorful.MakeColor(color); ok {
scheme[name] = &colorfulColor{c}
}
}
// scheme holds the mapping of "name" to colour values.
// Modules can use this to provide default colors for their output
// by using the commonly accepted names "good", "bad", and "degraded".
// Bar authors can also define arbitrary names, e.g. to load XResource based colours
// from i3 using the "LoadFromArgs" method.
var scheme = map[string]ColorfulColor{}
func splitAtLastEqual(s string) (string, string, bool) {
idx := strings.LastIndex(s, "=")
if idx < 0 {
return "", "", false
}
return s[:idx], s[idx+1:], true
}
// LoadFromArgs loads a color scheme from command-line arguments of the form name=value.
func LoadFromArgs(args []string) {
for _, arg := range args {
if name, value, ok := splitAtLastEqual(arg); ok {
if color := Hex(value); color != nil {
scheme[name] = color
}
}
}
}
// LoadFromMap sets the colour scheme from code.
func LoadFromMap(s map[string]string) {
for name, value := range s {
if color := Hex(value); color != nil {
scheme[name] = color
}
}
}
var fs = afero.NewOsFs()
// LoadFromConfig loads a color scheme from a i3status config file
// by reading all keys that start with 'color_'
func LoadFromConfig(filename string) error {
f, err := fs.Open(filename)
if err != nil {
return err
}
defer f.Close()
s := bufio.NewScanner(f)
s.Split(bufio.ScanLines)
for s.Scan() {
line := strings.TrimSpace(s.Text())
if !strings.HasPrefix(line, "color_") {
continue
}
name, value, ok := splitAtLastEqual(line)
if !ok {
continue
}
name = strings.TrimSpace(name[len("color_"):])
value = strings.TrimSpace(value)
if value[0] == '"' && value[len(value)-1] == '"' {
value = value[1 : len(value)-1]
} else if value[0] == '\'' && value[len(value)-1] == '\'' {
value = value[1 : len(value)-1]
}
if color := Hex(value); color != nil {
scheme[name] = color
}
}
return nil
}
type barConfig struct {
Colors map[string]string `json:"colors"`
}
var getBarConfig = func(barID string) []byte {
out, _ := exec.Command("i3-msg", "-t", "get_bar_config", barID).Output()
return out
}
// LoadBarConfig automatically loads colors from the current bar's
// configuration. It assumes that the parent process is the i3bar instance,
// and the bar_id command-line flag identifies the bar id.
func LoadBarConfig() {
i3barPid := os.Getppid()
i3barCmdline, _ := ioutil.ReadFile(
fmt.Sprintf("/proc/%d/cmdline", i3barPid))
barID := "bar0"
for _, arg := range bytes.Split(i3barCmdline, []byte{0}) {
arg := string(arg)
if strings.HasPrefix(arg, "--bar_id=") {
barID = strings.TrimPrefix(arg, "--bar_id=")
break
}
}
var parsed barConfig
json.Unmarshal(getBarConfig(barID), &parsed)
LoadFromMap(parsed.Colors)
}