-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmutelight.go
157 lines (132 loc) · 3.2 KB
/
mutelight.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
package main
// Trivial source mute indicator using a Thingm Blink(1)
//
// TODO:
// pidfile
// accept source device index as a flag
import (
"errors"
"github.com/godbus/dbus"
"github.com/todbot/go-blink1"
"github.com/sqp/pulseaudio"
"log"
"os"
"syscall"
)
var quit = make(chan struct{})
var state_chan = make(chan muteState)
var sock_path = "/tmp/mutelight.sock"
// The command I bind to a key in xmonad to mute my mic is
// pactl set-source-mute 1 toggle
// Adjust this to the source you want to mute
var target_device = dbus.ObjectPath("/org/pulseaudio/core1/source1")
func PreparePipe(path string) {
pipeExists := false
fileInfo, err := os.Stat(path)
if err == nil {
if (fileInfo.Mode() & os.ModeNamedPipe) > 0 {
pipeExists = true
} else {
log.Printf("%d != %d\n", os.ModeNamedPipe, fileInfo.Mode())
panic(path + " exists, but it's not a named pipe (FIFO)")
}
}
// Try to create pipe if needed
if !pipeExists {
err := syscall.Mkfifo(path, 0666)
if err != nil {
panic(err.Error())
}
}
}
func WriteState(pipe string, muted bool) {
sock, err := os.OpenFile(pipe, os.O_WRONLY|syscall.O_NONBLOCK, 0600)
testFatal(err, "Unable to open status pipe")
defer sock.Close()
if muted {
sock.Write([]byte("\n"));
} else {
sock.Write([]byte("!!! NOT MUTED !!!\n"))
}
}
type muteState struct {
muted bool
}
func main() {
isLoaded, e := pulseaudio.ModuleIsLoaded()
testFatal(e, "test pulse dbus module is loaded")
if !isLoaded {
e = pulseaudio.LoadModule()
testFatal(e, "load pulse dbus module")
defer pulseaudio.UnloadModule()
}
pulse, e := pulseaudio.New()
testFatal(e, "connect to the pulse service")
defer pulse.Close()
PreparePipe(sock_path)
app := &AppPulse{}
pulse.Register(app)
defer pulse.Unregister(app)
muted, err := is_muted(pulse, target_device)
testFatal(err, "couldn't find target device")
device, err := blink1.OpenNextDevice()
defer device.Close()
if muted {
color := blink1.State{}
device.SetState(color)
} else {
color := blink1.State{
Red: 255,
}
device.SetState(color)
}
WriteState(sock_path, muted)
go func() {
if err != nil {
panic(err)
}
for {
select {
case state := <-state_chan:
if state.muted {
color := blink1.State{}
device.SetState(color)
WriteState(sock_path, state.muted)
} else {
color := blink1.State{
Red: 255,
}
device.SetState(color)
WriteState(sock_path, state.muted)
}
}
}
}()
go pulse.Listen()
defer pulse.StopListening()
<-quit
}
func is_muted(client *pulseaudio.Client, path dbus.ObjectPath) (bool, error) {
sources, e := client.Core().ListPath("Sources")
testFatal(e, "get list of sinks")
if len(sources) == 0 {
return false, errors.New("no sources!")
}
dev := client.Device(sources[1])
mute, _ := dev.Bool("Mute")
return mute, nil
}
type AppPulse struct{}
func (ap *AppPulse) DeviceMuteUpdated(path dbus.ObjectPath, state bool) {
if path == target_device {
state_msg := muteState{
muted: state,
}
state_chan <- state_msg
}
}
func testFatal(e error, msg string) {
if e != nil {
log.Fatalln(msg+":", e)
}
}