split into multiple files

This commit is contained in:
Rasmus Moorats 2023-02-28 14:20:47 +02:00
parent a4077a3d8b
commit 86dc6b8f54
Signed by: xx
GPG key ID: FE14255A6AE7241C
9 changed files with 309 additions and 280 deletions

23
cache.go Normal file
View file

@ -0,0 +1,23 @@
package main
import "sync"
const (
// cache key definitions
CACHE_BT_CONNECTED = "bt_connected"
CACHE_BT_BATTERY = "bt_battery"
CACHE_MEDIA = "media"
CACHE_LAYOUT = "kbd_layout"
)
var CACHE = sync.Map{}
func getC[T comparable](c sync.Map, id string, fallback func() T) T {
val, ok := c.Load(id)
if !ok {
val = fallback()
c.Store(id, val)
}
return val.(T)
}

54
connections.go Normal file
View file

@ -0,0 +1,54 @@
package main
import (
"context"
"github.com/godbus/dbus/v5"
sway "github.com/joshuarubin/go-sway"
"github.com/noisetorch/pulseaudio"
)
var CONN Connections
type Connections struct {
dbusSession, dbusSystem *dbus.Conn
paClient *pulseaudio.Client
swayClient sway.Client
}
func init_connections() (Connections, error) {
ctx := context.Background()
dbusSession, err := dbus.ConnectSessionBus()
if err != nil {
return Connections{}, err
}
dbusSystem, err := dbus.ConnectSystemBus()
if err != nil {
return Connections{}, err
}
paClient, err := pulseaudio.NewClient()
if err != nil {
return Connections{}, err
}
swayClient, err := sway.New(ctx)
if err != nil {
return Connections{}, err
}
return Connections{
dbusSession: dbusSession,
dbusSystem: dbusSystem,
paClient: paClient,
swayClient: swayClient,
}, nil
}
func (c Connections) close_all() {
c.dbusSession.Close()
c.dbusSystem.Close()
c.paClient.Close()
}

7
consts.go Normal file
View file

@ -0,0 +1,7 @@
package main
const (
SEPARATOR = " | "
BATTERY = "BAT0"
HEADSET = "94:DB:56:6B:E3:8A"
)

6
go.mod
View file

@ -4,10 +4,12 @@ go 1.19
require github.com/godbus/dbus/v5 v5.1.0
require github.com/noisetorch/pulseaudio v0.0.0-20220603053345-9303200c3861
require (
github.com/joshuarubin/go-sway v1.2.0
github.com/noisetorch/pulseaudio v0.0.0-20220603053345-9303200c3861
)
require (
github.com/joshuarubin/go-sway v1.2.0 // indirect
github.com/joshuarubin/lifecycle v1.0.0 // indirect
go.uber.org/atomic v1.3.2 // indirect
go.uber.org/multierr v1.1.0 // indirect

3
go.sum
View file

@ -1,3 +1,4 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@ -7,8 +8,10 @@ github.com/joshuarubin/lifecycle v1.0.0 h1:N/lPEC8f+dBZ1Tn99vShqp36LwB+LI7XNAiNa
github.com/joshuarubin/lifecycle v1.0.0/go.mod h1:sRy++ATvR9Ee21tkRdFkQeywAWvDsue66V70K0Dnl54=
github.com/noisetorch/pulseaudio v0.0.0-20220603053345-9303200c3861 h1:Xng5X+MlNK7Y/Ede75B86wJgaFMFvuey1K4Suh9k2E4=
github.com/noisetorch/pulseaudio v0.0.0-20220603053345-9303200c3861/go.mod h1:/zosM8PSkhuVyfJ9c/qzBhPSm3k06m9U4y4SDfH0jeA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=

113
module_dbus.go Normal file
View file

@ -0,0 +1,113 @@
package main
import (
"fmt"
"strings"
"github.com/godbus/dbus/v5"
)
var HEADSET_PATH = dbus.ObjectPath("/org/bluez/hci0/dev_" + strings.ReplaceAll(HEADSET, ":", "_"))
func dbus_system_handler(feedback chan bool) {
// watches for BT headset connections
headsetObjectPath := "/org/bluez/hci0/dev_" + strings.ReplaceAll(HEADSET, ":", "_")
err := CONN.dbusSystem.AddMatchSignal(
dbus.WithMatchObjectPath(dbus.ObjectPath(headsetObjectPath)),
dbus.WithMatchInterface("org.freedesktop.DBus.Properties"),
)
if err != nil {
warn("Failed to add match signal", err)
return
}
sigs := make(chan *dbus.Signal, 1)
CONN.dbusSystem.Signal(sigs)
for range sigs {
CACHE.Delete(CACHE_BT_CONNECTED)
CACHE.Delete(CACHE_BT_BATTERY)
feedback <- true
}
}
func dbus_session_handler(feedback chan bool) {
// watches for media changes
err := CONN.dbusSession.AddMatchSignal(
dbus.WithMatchObjectPath("/org/mpris/MediaPlayer2"),
dbus.WithMatchInterface("org.freedesktop.DBus.Properties"),
)
if err != nil {
warn("Failed to add match signal", err)
return
}
sigs := make(chan *dbus.Signal, 1)
CONN.dbusSession.Signal(sigs)
for range sigs {
CACHE.Delete(CACHE_MEDIA)
feedback <- true
}
}
func get_bt_headset_connected() bool {
obj := CONN.dbusSystem.Object("org.bluez", HEADSET_PATH)
status, err := obj.GetProperty("org.bluez.Device1.Connected")
if err == nil {
if status.String() == "true" {
return true
}
}
return false
}
func get_bt_headset_battery() string {
obj := CONN.dbusSystem.Object("org.bluez", HEADSET_PATH)
status, err := obj.GetProperty("org.bluez.Battery1.Percentage")
if err == nil {
return fmt.Sprintf("%v", status.Value())
}
return ""
}
func get_playing_spotify_media() string {
var output strings.Builder
obj := CONN.dbusSession.Object("org.mpris.MediaPlayer2.spotify", "/org/mpris/MediaPlayer2")
metadata, err := obj.GetProperty("org.mpris.MediaPlayer2.Player.Metadata")
if err != nil {
return ""
}
status, err := obj.GetProperty("org.mpris.MediaPlayer2.Player.PlaybackStatus")
if err != nil {
return ""
}
if status.String() != "\"Playing\"" {
output.WriteString("‖ ")
}
metadataMap := metadata.Value().(map[string]dbus.Variant)
artists := make([]string, 1)
metadataMap["xesam:artist"].Store(&artists)
title := metadataMap["xesam:title"].String()
if title == "\"\"" {
return ""
}
output.WriteString(fmt.Sprintf(
"%s - %s",
strings.Join(artists, ", "),
title[1:len(title)-1],
))
return output.String()
}

42
module_pulseaudio.go Normal file
View file

@ -0,0 +1,42 @@
package main
import "math"
func pulseaudio_handler(feedback chan bool) {
vol, muted := get_default_sink_volume()
sigs, err := CONN.paClient.Updates()
if err != nil {
warn("Failed to create PulseAudio update listener!", err)
return
}
for range sigs {
nVol, nMuted := get_default_sink_volume()
if nVol != vol || nMuted != muted {
feedback <- true
vol = nVol
muted = nMuted
}
}
}
func get_default_sink_volume() (int, bool) {
si, err := CONN.paClient.ServerInfo()
if err != nil {
return 0, false
}
sinks, err := CONN.paClient.Sinks()
if err != nil {
return 0, false
}
for i := range sinks {
if sinks[i].Name == si.DefaultSink {
return int(math.Round(float64(sinks[i].Cvolume[0]) / float64(sinks[i].BaseVolume) * 100)), sinks[i].Muted
}
}
return 0, false
}

63
module_sway.go Normal file
View file

@ -0,0 +1,63 @@
package main
import (
"context"
sway "github.com/joshuarubin/go-sway"
)
type sway_handler struct {
sway.EventHandler
feedback chan bool
}
func (h sway_handler) Input(ctx context.Context, ev sway.InputEvent) {
if ev.Change != "xkb_layout" || ev.Input.Type != "keyboard" {
return
}
oldval, _ := CACHE.Load(CACHE_LAYOUT)
if oldval == *ev.Input.XKBActiveLayoutName {
return
}
CACHE.Store(CACHE_LAYOUT, *ev.Input.XKBActiveLayoutName)
h.feedback <- true
}
func sway_input_handler(feedback chan bool) {
ctx := context.Background()
var h sway.EventHandler = sway_handler{feedback: feedback}
sway.Subscribe(ctx, h, sway.EventTypeInput)
}
func get_current_layout() string {
ctx := context.Background()
inputs, err := CONN.swayClient.GetInputs(ctx)
if err != nil {
warn("Failed to get Sway inputs!", err)
}
for _, input := range inputs {
if input.Type != "keyboard" {
continue
}
return *input.XKBActiveLayoutName
}
return ""
}
func long_layout_to_short(layout string) string {
switch layout {
case "English (US)":
return "US"
case "Estonian":
return "ET"
}
return layout
}

View file

@ -1,94 +1,14 @@
package main
import (
"context"
"fmt"
"math"
"os"
"os/signal"
"strings"
"syscall"
"time"
"sync"
sway "github.com/joshuarubin/go-sway"
"github.com/godbus/dbus/v5"
"github.com/noisetorch/pulseaudio"
)
const (
SEPARATOR = " | "
BATTERY = "BAT0"
HEADSET = "94:DB:56:6B:E3:8A"
// cache key definitions
CACHE_BT_CONNECTED = "bt_connected"
CACHE_BT_BATTERY = "bt_battery"
CACHE_MEDIA = "media"
CACHE_LAYOUT = "kbd_layout"
)
var (
CONN Connections
CACHE = sync.Map{}
HEADSET_PATH = dbus.ObjectPath("/org/bluez/hci0/dev_" + strings.ReplaceAll(HEADSET, ":", "_"))
)
func getC[T comparable](c sync.Map, id string, fallback func() T) T {
val, ok := c.Load(id)
if !ok {
val = fallback()
c.Store(id, val)
}
return val.(T)
}
type Connections struct {
dbusSession, dbusSystem *dbus.Conn
paClient *pulseaudio.Client
swayClient sway.Client
}
func init_connections() (Connections, error) {
ctx := context.Background()
dbusSession, err := dbus.ConnectSessionBus()
if err != nil {
return Connections{}, err
}
dbusSystem, err := dbus.ConnectSystemBus()
if err != nil {
return Connections{}, err
}
paClient, err := pulseaudio.NewClient()
if err != nil {
return Connections{}, err
}
swayClient, err := sway.New(ctx)
if err != nil {
return Connections{}, err
}
return Connections{
dbusSession: dbusSession,
dbusSystem: dbusSystem,
paClient: paClient,
swayClient: swayClient,
}, nil
}
func (c Connections) close_all() {
c.dbusSession.Close()
c.dbusSystem.Close()
c.paClient.Close()
}
func main() {
update := make(chan bool, 1)
var err error // so assigning the global works
@ -185,66 +105,6 @@ func produce_output() string {
return output.String()
}
func get_bt_headset_connected() bool {
obj := CONN.dbusSystem.Object("org.bluez", HEADSET_PATH)
status, err := obj.GetProperty("org.bluez.Device1.Connected")
if err == nil {
if status.String() == "true" {
return true
}
}
return false
}
func get_bt_headset_battery() string {
obj := CONN.dbusSystem.Object("org.bluez", HEADSET_PATH)
status, err := obj.GetProperty("org.bluez.Battery1.Percentage")
if err == nil {
return fmt.Sprintf("%v", status.Value())
}
return ""
}
func get_playing_spotify_media() string {
var output strings.Builder
obj := CONN.dbusSession.Object("org.mpris.MediaPlayer2.spotify", "/org/mpris/MediaPlayer2")
metadata, err := obj.GetProperty("org.mpris.MediaPlayer2.Player.Metadata")
if err != nil {
return ""
}
status, err := obj.GetProperty("org.mpris.MediaPlayer2.Player.PlaybackStatus")
if err != nil {
return ""
}
if status.String() != "\"Playing\"" {
output.WriteString("‖ ")
}
metadataMap := metadata.Value().(map[string]dbus.Variant)
artists := make([]string, 1)
metadataMap["xesam:artist"].Store(&artists)
title := metadataMap["xesam:title"].String()
if title == "\"\"" {
return ""
}
output.WriteString(fmt.Sprintf(
"%s - %s",
strings.Join(artists, ", "),
title[1:len(title)-1],
))
return output.String()
}
func signal_handler(feedback chan bool) {
// SIGUSR1 can be sent to manually trigger a reprint
sigs := make(chan os.Signal, 1)
@ -269,141 +129,3 @@ func time_handler(feedback chan bool) {
feedback <- true
}
}
func dbus_system_handler(feedback chan bool) {
// watches for BT headset connections
headsetObjectPath := "/org/bluez/hci0/dev_" + strings.ReplaceAll(HEADSET, ":", "_")
err := CONN.dbusSystem.AddMatchSignal(
dbus.WithMatchObjectPath(dbus.ObjectPath(headsetObjectPath)),
dbus.WithMatchInterface("org.freedesktop.DBus.Properties"),
)
if err != nil {
warn("Failed to add match signal", err)
return
}
sigs := make(chan *dbus.Signal, 1)
CONN.dbusSystem.Signal(sigs)
for range sigs {
CACHE.Delete(CACHE_BT_CONNECTED)
CACHE.Delete(CACHE_BT_BATTERY)
feedback <- true
}
}
func dbus_session_handler(feedback chan bool) {
// watches for media changes
err := CONN.dbusSession.AddMatchSignal(
dbus.WithMatchObjectPath("/org/mpris/MediaPlayer2"),
dbus.WithMatchInterface("org.freedesktop.DBus.Properties"),
)
if err != nil {
warn("Failed to add match signal", err)
return
}
sigs := make(chan *dbus.Signal, 1)
CONN.dbusSession.Signal(sigs)
for range sigs {
CACHE.Delete(CACHE_MEDIA)
feedback <- true
}
}
func get_default_sink_volume() (int, bool) {
si, err := CONN.paClient.ServerInfo()
if err != nil {
return 0, false
}
sinks, err := CONN.paClient.Sinks()
if err != nil {
return 0, false
}
for i := range sinks {
if sinks[i].Name == si.DefaultSink {
return int(math.Round(float64(sinks[i].Cvolume[0]) / float64(sinks[i].BaseVolume) * 100)), sinks[i].Muted
}
}
return 0, false
}
func pulseaudio_handler(feedback chan bool) {
vol, muted := get_default_sink_volume()
sigs, err := CONN.paClient.Updates()
if err != nil {
warn("Failed to create PulseAudio update listener!", err)
return
}
for range sigs {
nVol, nMuted := get_default_sink_volume()
if nVol != vol || nMuted != muted {
feedback <- true
vol = nVol
muted = nMuted
}
}
}
func get_current_layout() string {
ctx := context.Background()
inputs, err := CONN.swayClient.GetInputs(ctx)
if err != nil {
warn("Failed to get Sway inputs!", err)
}
for _, input := range inputs {
if input.Type != "keyboard" {
continue
}
return *input.XKBActiveLayoutName
}
return ""
}
func long_layout_to_short(layout string) string {
switch layout {
case "English (US)":
return "US"
case "Estonian":
return "ET"
}
return layout
}
type sway_handler struct {
sway.EventHandler
feedback chan bool
}
func (h sway_handler) Input(ctx context.Context, ev sway.InputEvent) {
if ev.Change != "xkb_layout" || ev.Input.Type != "keyboard" {
return
}
oldval, _ := CACHE.Load(CACHE_LAYOUT)
if oldval == *ev.Input.XKBActiveLayoutName {
return
}
CACHE.Store(CACHE_LAYOUT, *ev.Input.XKBActiveLayoutName)
h.feedback <- true
}
func sway_input_handler(feedback chan bool) {
ctx := context.Background()
var h sway.EventHandler = sway_handler{feedback: feedback}
sway.Subscribe(ctx, h, sway.EventTypeInput)
}