summaryrefslogtreecommitdiff
path: root/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez
diff options
context:
space:
mode:
authorNippy <nippy@rp1.hu>2026-05-09 13:33:09 +0200
committerNippy <nippy@rp1.hu>2026-05-09 13:33:09 +0200
commita2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5 (patch)
tree1a8769217f84bfe9d6216fafaadaf8cdd876d457 /raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez
parent34b9c34f982b2596cfa9e368a2d7f74e9a17477c (diff)
downloadRaveOS-PKGBUILD-a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5.tar.gz
RaveOS-PKGBUILD-a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5.zip
raveos update
Diffstat (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez')
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/agent.go341
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/broker.go21
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/broker_test.go220
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/handlers.go237
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/handlers_test.go41
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/manager.go588
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/subscription_broker.go86
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/types.go78
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/types_test.go210
9 files changed, 0 insertions, 1822 deletions
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/agent.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/agent.go
deleted file mode 100644
index b630a19..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/agent.go
+++ /dev/null
@@ -1,341 +0,0 @@
-package bluez
-
-import (
- "context"
- "errors"
- "fmt"
- "strconv"
- "time"
-
- "github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/log"
- "github.com/godbus/dbus/v5"
-)
-
-const (
- bluezService = "org.bluez"
- agentManagerPath = "/org/bluez"
- agentManagerIface = "org.bluez.AgentManager1"
- agent1Iface = "org.bluez.Agent1"
- device1Iface = "org.bluez.Device1"
- agentPath = "/com/danklinux/bluez/agent"
- agentCapability = "KeyboardDisplay"
-)
-
-const introspectXML = `
-<node>
- <interface name="org.bluez.Agent1">
- <method name="Release"/>
- <method name="RequestPinCode">
- <arg direction="in" type="o" name="device"/>
- <arg direction="out" type="s" name="pincode"/>
- </method>
- <method name="RequestPasskey">
- <arg direction="in" type="o" name="device"/>
- <arg direction="out" type="u" name="passkey"/>
- </method>
- <method name="DisplayPinCode">
- <arg direction="in" type="o" name="device"/>
- <arg direction="in" type="s" name="pincode"/>
- </method>
- <method name="DisplayPasskey">
- <arg direction="in" type="o" name="device"/>
- <arg direction="in" type="u" name="passkey"/>
- <arg direction="in" type="q" name="entered"/>
- </method>
- <method name="RequestConfirmation">
- <arg direction="in" type="o" name="device"/>
- <arg direction="in" type="u" name="passkey"/>
- </method>
- <method name="RequestAuthorization">
- <arg direction="in" type="o" name="device"/>
- </method>
- <method name="AuthorizeService">
- <arg direction="in" type="o" name="device"/>
- <arg direction="in" type="s" name="uuid"/>
- </method>
- <method name="Cancel"/>
- </interface>
- <interface name="org.freedesktop.DBus.Introspectable">
- <method name="Introspect">
- <arg direction="out" type="s" name="data"/>
- </method>
- </interface>
-</node>`
-
-type BluezAgent struct {
- conn *dbus.Conn
- broker PromptBroker
-}
-
-func NewBluezAgent(broker PromptBroker) (*BluezAgent, error) {
- conn, err := dbus.ConnectSystemBus()
- if err != nil {
- return nil, fmt.Errorf("system bus connection failed: %w", err)
- }
-
- agent := &BluezAgent{
- conn: conn,
- broker: broker,
- }
-
- if err := conn.Export(agent, dbus.ObjectPath(agentPath), agent1Iface); err != nil {
- conn.Close()
- return nil, fmt.Errorf("agent export failed: %w", err)
- }
-
- if err := conn.Export(agent, dbus.ObjectPath(agentPath), "org.freedesktop.DBus.Introspectable"); err != nil {
- conn.Close()
- return nil, fmt.Errorf("introspection export failed: %w", err)
- }
-
- mgr := conn.Object(bluezService, dbus.ObjectPath(agentManagerPath))
- if err := mgr.Call(agentManagerIface+".RegisterAgent", 0, dbus.ObjectPath(agentPath), agentCapability).Err; err != nil {
- conn.Close()
- return nil, fmt.Errorf("agent registration failed: %w", err)
- }
-
- if err := mgr.Call(agentManagerIface+".RequestDefaultAgent", 0, dbus.ObjectPath(agentPath)).Err; err != nil {
- log.Debugf("[BluezAgent] not default agent: %v", err)
- }
-
- log.Infof("[BluezAgent] registered at %s with capability %s", agentPath, agentCapability)
- return agent, nil
-}
-
-func (a *BluezAgent) Close() {
- if a.conn == nil {
- return
- }
- mgr := a.conn.Object(bluezService, dbus.ObjectPath(agentManagerPath))
- mgr.Call(agentManagerIface+".UnregisterAgent", 0, dbus.ObjectPath(agentPath))
- a.conn.Close()
-}
-
-func (a *BluezAgent) Release() *dbus.Error {
- log.Infof("[BluezAgent] Release called")
- return nil
-}
-
-func (a *BluezAgent) RequestPinCode(device dbus.ObjectPath) (string, *dbus.Error) {
- log.Infof("[BluezAgent] RequestPinCode: device=%s", device)
-
- secrets, err := a.promptFor(device, "pin", []string{"pin"}, nil)
- if err != nil {
- log.Warnf("[BluezAgent] RequestPinCode failed: %v", err)
- return "", a.errorFrom(err)
- }
-
- pin := secrets["pin"]
- log.Infof("[BluezAgent] RequestPinCode returning PIN (len=%d)", len(pin))
- return pin, nil
-}
-
-func (a *BluezAgent) RequestPasskey(device dbus.ObjectPath) (uint32, *dbus.Error) {
- log.Infof("[BluezAgent] RequestPasskey: device=%s", device)
-
- secrets, err := a.promptFor(device, "passkey", []string{"passkey"}, nil)
- if err != nil {
- log.Warnf("[BluezAgent] RequestPasskey failed: %v", err)
- return 0, a.errorFrom(err)
- }
-
- passkey, err := strconv.ParseUint(secrets["passkey"], 10, 32)
- if err != nil {
- log.Warnf("[BluezAgent] invalid passkey format: %v", err)
- return 0, dbus.MakeFailedError(fmt.Errorf("invalid passkey: %w", err))
- }
-
- log.Infof("[BluezAgent] RequestPasskey returning: %d", passkey)
- return uint32(passkey), nil
-}
-
-func (a *BluezAgent) DisplayPinCode(device dbus.ObjectPath, pincode string) *dbus.Error {
- log.Infof("[BluezAgent] DisplayPinCode: device=%s, pin=%s", device, pincode)
-
- _, err := a.promptFor(device, "display-pin", []string{}, &pincode)
- if err != nil {
- log.Warnf("[BluezAgent] DisplayPinCode acknowledgment failed: %v", err)
- }
-
- return nil
-}
-
-func (a *BluezAgent) DisplayPasskey(device dbus.ObjectPath, passkey uint32, entered uint16) *dbus.Error {
- log.Infof("[BluezAgent] DisplayPasskey: device=%s, passkey=%06d, entered=%d", device, passkey, entered)
-
- if entered == 0 {
- passkeyStr := strconv.FormatUint(uint64(passkey), 10)
- _, err := a.promptFor(device, "display-passkey", []string{}, &passkeyStr)
- if err != nil {
- log.Warnf("[BluezAgent] DisplayPasskey acknowledgment failed: %v", err)
- }
- }
-
- return nil
-}
-
-func (a *BluezAgent) RequestConfirmation(device dbus.ObjectPath, passkey uint32) *dbus.Error {
- log.Infof("[BluezAgent] RequestConfirmation: device=%s, passkey=%06d", device, passkey)
-
- passkeyStr := strconv.FormatUint(uint64(passkey), 10)
- secrets, err := a.promptFor(device, "confirm", []string{"decision"}, &passkeyStr)
- if err != nil {
- log.Warnf("[BluezAgent] RequestConfirmation failed: %v", err)
- return a.errorFrom(err)
- }
-
- if secrets["decision"] != "yes" && secrets["decision"] != "accept" {
- log.Debugf("[BluezAgent] RequestConfirmation rejected by user")
- return dbus.NewError("org.bluez.Error.Rejected", nil)
- }
-
- log.Infof("[BluezAgent] RequestConfirmation accepted")
- return nil
-}
-
-func (a *BluezAgent) RequestAuthorization(device dbus.ObjectPath) *dbus.Error {
- log.Infof("[BluezAgent] RequestAuthorization: device=%s", device)
-
- secrets, err := a.promptFor(device, "authorize", []string{"decision"}, nil)
- if err != nil {
- log.Warnf("[BluezAgent] RequestAuthorization failed: %v", err)
- return a.errorFrom(err)
- }
-
- if secrets["decision"] != "yes" && secrets["decision"] != "accept" {
- log.Debugf("[BluezAgent] RequestAuthorization rejected by user")
- return dbus.NewError("org.bluez.Error.Rejected", nil)
- }
-
- log.Infof("[BluezAgent] RequestAuthorization accepted")
- return nil
-}
-
-func (a *BluezAgent) AuthorizeService(device dbus.ObjectPath, uuid string) *dbus.Error {
- log.Infof("[BluezAgent] AuthorizeService: device=%s, uuid=%s", device, uuid)
-
- secrets, err := a.promptFor(device, "authorize-service:"+uuid, []string{"decision"}, nil)
- if err != nil {
- log.Warnf("[BluezAgent] AuthorizeService failed: %v", err)
- return a.errorFrom(err)
- }
-
- if secrets["decision"] != "yes" && secrets["decision"] != "accept" {
- log.Debugf("[BluezAgent] AuthorizeService rejected by user")
- return dbus.NewError("org.bluez.Error.Rejected", nil)
- }
-
- log.Infof("[BluezAgent] AuthorizeService accepted")
- return nil
-}
-
-func (a *BluezAgent) Cancel() *dbus.Error {
- log.Infof("[BluezAgent] Cancel called")
- return nil
-}
-
-func (a *BluezAgent) Introspect() (string, *dbus.Error) {
- return introspectXML, nil
-}
-
-func (a *BluezAgent) promptFor(device dbus.ObjectPath, requestType string, fields []string, displayValue *string) (map[string]string, error) {
- if a.broker == nil {
- return nil, fmt.Errorf("broker not initialized")
- }
-
- deviceName, deviceAddr := a.getDeviceInfo(device)
- hints := []string{}
- if displayValue != nil {
- hints = append(hints, *displayValue)
- }
-
- ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
- defer cancel()
-
- var passkey *uint32
- if requestType == "confirm" || requestType == "display-passkey" {
- if displayValue != nil {
- if pk, err := strconv.ParseUint(*displayValue, 10, 32); err == nil {
- pk32 := uint32(pk)
- passkey = &pk32
- }
- }
- }
-
- token, err := a.broker.Ask(ctx, PromptRequest{
- DevicePath: string(device),
- DeviceName: deviceName,
- DeviceAddr: deviceAddr,
- RequestType: requestType,
- Fields: fields,
- Hints: hints,
- Passkey: passkey,
- })
- if err != nil {
- return nil, fmt.Errorf("prompt creation failed: %w", err)
- }
-
- log.Infof("[BluezAgent] waiting for user response (token=%s)", token)
- reply, err := a.broker.Wait(ctx, token)
- if err != nil {
- if errors.Is(err, errdefs.ErrSecretPromptTimeout) {
- return nil, err
- }
- if reply.Cancel || errors.Is(err, errdefs.ErrSecretPromptCancelled) {
- return nil, errdefs.ErrSecretPromptCancelled
- }
- return nil, err
- }
-
- if !reply.Accept && len(fields) > 0 {
- return nil, errdefs.ErrSecretPromptCancelled
- }
-
- return reply.Secrets, nil
-}
-
-func (a *BluezAgent) getDeviceInfo(device dbus.ObjectPath) (string, string) {
- obj := a.conn.Object(bluezService, device)
-
- var name, alias, addr string
-
- nameVar, err := obj.GetProperty(device1Iface + ".Name")
- if err == nil {
- if n, ok := nameVar.Value().(string); ok {
- name = n
- }
- }
-
- aliasVar, err := obj.GetProperty(device1Iface + ".Alias")
- if err == nil {
- if a, ok := aliasVar.Value().(string); ok {
- alias = a
- }
- }
-
- addrVar, err := obj.GetProperty(device1Iface + ".Address")
- if err == nil {
- if a, ok := addrVar.Value().(string); ok {
- addr = a
- }
- }
-
- if alias != "" {
- return alias, addr
- }
- if name != "" {
- return name, addr
- }
- return addr, addr
-}
-
-func (a *BluezAgent) errorFrom(err error) *dbus.Error {
- if errors.Is(err, errdefs.ErrSecretPromptTimeout) {
- return dbus.NewError("org.bluez.Error.Canceled", nil)
- }
- if errors.Is(err, errdefs.ErrSecretPromptCancelled) {
- return dbus.NewError("org.bluez.Error.Canceled", nil)
- }
- return dbus.MakeFailedError(err)
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/broker.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/broker.go
deleted file mode 100644
index f817707..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/broker.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package bluez
-
-import (
- "context"
- "crypto/rand"
- "encoding/hex"
-)
-
-type PromptBroker interface {
- Ask(ctx context.Context, req PromptRequest) (token string, err error)
- Wait(ctx context.Context, token string) (PromptReply, error)
- Resolve(token string, reply PromptReply) error
-}
-
-func generateToken() (string, error) {
- bytes := make([]byte, 16)
- if _, err := rand.Read(bytes); err != nil {
- return "", err
- }
- return hex.EncodeToString(bytes), nil
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/broker_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/broker_test.go
deleted file mode 100644
index 9eebe61..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/broker_test.go
+++ /dev/null
@@ -1,220 +0,0 @@
-package bluez
-
-import (
- "context"
- "testing"
- "time"
-)
-
-func TestSubscriptionBrokerAskWait(t *testing.T) {
- promptReceived := false
- broker := NewSubscriptionBroker(func(p PairingPrompt) {
- promptReceived = true
- if p.Token == "" {
- t.Error("expected token to be non-empty")
- }
- if p.DeviceName != "TestDevice" {
- t.Errorf("expected DeviceName=TestDevice, got %s", p.DeviceName)
- }
- })
-
- ctx := context.Background()
- req := PromptRequest{
- DevicePath: "/org/bluez/test",
- DeviceName: "TestDevice",
- DeviceAddr: "AA:BB:CC:DD:EE:FF",
- RequestType: "pin",
- Fields: []string{"pin"},
- }
-
- token, err := broker.Ask(ctx, req)
- if err != nil {
- t.Fatalf("Ask failed: %v", err)
- }
-
- if token == "" {
- t.Fatal("expected non-empty token")
- }
-
- if !promptReceived {
- t.Fatal("expected prompt broadcast to be called")
- }
-
- go func() {
- time.Sleep(50 * time.Millisecond)
- broker.Resolve(token, PromptReply{
- Secrets: map[string]string{"pin": "1234"},
- Accept: true,
- })
- }()
-
- reply, err := broker.Wait(ctx, token)
- if err != nil {
- t.Fatalf("Wait failed: %v", err)
- }
-
- if reply.Secrets["pin"] != "1234" {
- t.Errorf("expected pin=1234, got %s", reply.Secrets["pin"])
- }
-
- if !reply.Accept {
- t.Error("expected Accept=true")
- }
-}
-
-func TestSubscriptionBrokerTimeout(t *testing.T) {
- broker := NewSubscriptionBroker(nil)
-
- ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
- defer cancel()
-
- req := PromptRequest{
- DevicePath: "/org/bluez/test",
- DeviceName: "TestDevice",
- RequestType: "passkey",
- Fields: []string{"passkey"},
- }
-
- token, err := broker.Ask(ctx, req)
- if err != nil {
- t.Fatalf("Ask failed: %v", err)
- }
-
- _, err = broker.Wait(ctx, token)
- if err == nil {
- t.Fatal("expected timeout error")
- }
-}
-
-func TestSubscriptionBrokerCancel(t *testing.T) {
- broker := NewSubscriptionBroker(nil)
-
- ctx := context.Background()
- req := PromptRequest{
- DevicePath: "/org/bluez/test",
- DeviceName: "TestDevice",
- RequestType: "confirm",
- Fields: []string{"decision"},
- }
-
- token, err := broker.Ask(ctx, req)
- if err != nil {
- t.Fatalf("Ask failed: %v", err)
- }
-
- go func() {
- time.Sleep(50 * time.Millisecond)
- broker.Resolve(token, PromptReply{
- Cancel: true,
- })
- }()
-
- _, err = broker.Wait(ctx, token)
- if err == nil {
- t.Fatal("expected cancelled error")
- }
-}
-
-func TestSubscriptionBrokerUnknownToken(t *testing.T) {
- broker := NewSubscriptionBroker(nil)
-
- ctx := context.Background()
- _, err := broker.Wait(ctx, "invalid-token")
- if err == nil {
- t.Fatal("expected error for unknown token")
- }
-}
-
-func TestGenerateToken(t *testing.T) {
- token1, err := generateToken()
- if err != nil {
- t.Fatalf("generateToken failed: %v", err)
- }
-
- token2, err := generateToken()
- if err != nil {
- t.Fatalf("generateToken failed: %v", err)
- }
-
- if token1 == token2 {
- t.Error("expected unique tokens")
- }
-
- if len(token1) != 32 {
- t.Errorf("expected token length 32, got %d", len(token1))
- }
-}
-
-func TestSubscriptionBrokerResolveUnknownToken(t *testing.T) {
- broker := NewSubscriptionBroker(nil)
-
- err := broker.Resolve("unknown-token", PromptReply{
- Secrets: map[string]string{"test": "value"},
- })
- if err == nil {
- t.Fatal("expected error for unknown token")
- }
-}
-
-func TestSubscriptionBrokerMultipleRequests(t *testing.T) {
- broker := NewSubscriptionBroker(nil)
- ctx := context.Background()
-
- req1 := PromptRequest{
- DevicePath: "/org/bluez/test1",
- DeviceName: "Device1",
- RequestType: "pin",
- Fields: []string{"pin"},
- }
-
- req2 := PromptRequest{
- DevicePath: "/org/bluez/test2",
- DeviceName: "Device2",
- RequestType: "passkey",
- Fields: []string{"passkey"},
- }
-
- token1, err := broker.Ask(ctx, req1)
- if err != nil {
- t.Fatalf("Ask1 failed: %v", err)
- }
-
- token2, err := broker.Ask(ctx, req2)
- if err != nil {
- t.Fatalf("Ask2 failed: %v", err)
- }
-
- if token1 == token2 {
- t.Error("expected different tokens")
- }
-
- go func() {
- time.Sleep(50 * time.Millisecond)
- broker.Resolve(token1, PromptReply{
- Secrets: map[string]string{"pin": "1234"},
- Accept: true,
- })
- broker.Resolve(token2, PromptReply{
- Secrets: map[string]string{"passkey": "567890"},
- Accept: true,
- })
- }()
-
- reply1, err := broker.Wait(ctx, token1)
- if err != nil {
- t.Fatalf("Wait1 failed: %v", err)
- }
-
- reply2, err := broker.Wait(ctx, token2)
- if err != nil {
- t.Fatalf("Wait2 failed: %v", err)
- }
-
- if reply1.Secrets["pin"] != "1234" {
- t.Errorf("expected pin=1234, got %s", reply1.Secrets["pin"])
- }
-
- if reply2.Secrets["passkey"] != "567890" {
- t.Errorf("expected passkey=567890, got %s", reply2.Secrets["passkey"])
- }
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/handlers.go
deleted file mode 100644
index be5a1c4..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/handlers.go
+++ /dev/null
@@ -1,237 +0,0 @@
-package bluez
-
-import (
- "encoding/json"
- "fmt"
- "net"
-
- "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params"
-)
-
-type BluetoothEvent struct {
- Type string `json:"type"`
- Data BluetoothState `json:"data"`
-}
-
-func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
- switch req.Method {
- case "bluetooth.getState":
- handleGetState(conn, req, manager)
- case "bluetooth.startDiscovery":
- handleStartDiscovery(conn, req, manager)
- case "bluetooth.stopDiscovery":
- handleStopDiscovery(conn, req, manager)
- case "bluetooth.setPowered":
- handleSetPowered(conn, req, manager)
- case "bluetooth.pair":
- handlePairDevice(conn, req, manager)
- case "bluetooth.connect":
- handleConnectDevice(conn, req, manager)
- case "bluetooth.disconnect":
- handleDisconnectDevice(conn, req, manager)
- case "bluetooth.remove":
- handleRemoveDevice(conn, req, manager)
- case "bluetooth.trust":
- handleTrustDevice(conn, req, manager)
- case "bluetooth.untrust":
- handleUntrustDevice(conn, req, manager)
- case "bluetooth.subscribe":
- handleSubscribe(conn, req, manager)
- case "bluetooth.pairing.submit":
- handlePairingSubmit(conn, req, manager)
- case "bluetooth.pairing.cancel":
- handlePairingCancel(conn, req, manager)
- default:
- models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method))
- }
-}
-
-func handleGetState(conn net.Conn, req models.Request, manager *Manager) {
- models.Respond(conn, req.ID, manager.GetState())
-}
-
-func handleStartDiscovery(conn net.Conn, req models.Request, manager *Manager) {
- if err := manager.StartDiscovery(); err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
- models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "discovery started"})
-}
-
-func handleStopDiscovery(conn net.Conn, req models.Request, manager *Manager) {
- if err := manager.StopDiscovery(); err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
- models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "discovery stopped"})
-}
-
-func handleSetPowered(conn net.Conn, req models.Request, manager *Manager) {
- powered, err := params.Bool(req.Params, "powered")
- if err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
-
- if err := manager.SetPowered(powered); err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
-
- models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "powered state updated"})
-}
-
-func handlePairDevice(conn net.Conn, req models.Request, manager *Manager) {
- devicePath, err := params.String(req.Params, "device")
- if err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
-
- if err := manager.PairDevice(devicePath); err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
-
- models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "pairing initiated"})
-}
-
-func handleConnectDevice(conn net.Conn, req models.Request, manager *Manager) {
- devicePath, err := params.String(req.Params, "device")
- if err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
-
- if err := manager.ConnectDevice(devicePath); err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
-
- models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "connecting"})
-}
-
-func handleDisconnectDevice(conn net.Conn, req models.Request, manager *Manager) {
- devicePath, err := params.String(req.Params, "device")
- if err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
-
- if err := manager.DisconnectDevice(devicePath); err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
-
- models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "disconnected"})
-}
-
-func handleRemoveDevice(conn net.Conn, req models.Request, manager *Manager) {
- devicePath, err := params.String(req.Params, "device")
- if err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
-
- if err := manager.RemoveDevice(devicePath); err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
-
- models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "device removed"})
-}
-
-func handleTrustDevice(conn net.Conn, req models.Request, manager *Manager) {
- devicePath, err := params.String(req.Params, "device")
- if err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
-
- if err := manager.TrustDevice(devicePath, true); err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
-
- models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "device trusted"})
-}
-
-func handleUntrustDevice(conn net.Conn, req models.Request, manager *Manager) {
- devicePath, err := params.String(req.Params, "device")
- if err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
-
- if err := manager.TrustDevice(devicePath, false); err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
-
- models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "device untrusted"})
-}
-
-func handlePairingSubmit(conn net.Conn, req models.Request, manager *Manager) {
- token, err := params.String(req.Params, "token")
- if err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
-
- secrets := params.StringMapOpt(req.Params, "secrets")
- accept := params.BoolOpt(req.Params, "accept", false)
-
- if err := manager.SubmitPairing(token, secrets, accept); err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
-
- models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "pairing response submitted"})
-}
-
-func handlePairingCancel(conn net.Conn, req models.Request, manager *Manager) {
- token, err := params.String(req.Params, "token")
- if err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
-
- if err := manager.CancelPairing(token); err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
-
- models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "pairing cancelled"})
-}
-
-func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
- clientID := fmt.Sprintf("client-%p", conn)
- stateChan := manager.Subscribe(clientID)
- defer manager.Unsubscribe(clientID)
-
- initialState := manager.GetState()
- event := BluetoothEvent{
- Type: "state_changed",
- Data: initialState,
- }
-
- if err := json.NewEncoder(conn).Encode(models.Response[BluetoothEvent]{
- ID: req.ID,
- Result: &event,
- }); err != nil {
- return
- }
-
- for state := range stateChan {
- event := BluetoothEvent{
- Type: "state_changed",
- Data: state,
- }
- if err := json.NewEncoder(conn).Encode(models.Response[BluetoothEvent]{
- Result: &event,
- }); err != nil {
- return
- }
- }
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/handlers_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/handlers_test.go
deleted file mode 100644
index 1846eb0..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/handlers_test.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package bluez
-
-import (
- "context"
- "testing"
- "time"
-)
-
-func TestBrokerIntegration(t *testing.T) {
- broker := NewSubscriptionBroker(nil)
- ctx := context.Background()
-
- req := PromptRequest{
- DevicePath: "/org/bluez/test",
- DeviceName: "TestDevice",
- RequestType: "pin",
- Fields: []string{"pin"},
- }
-
- token, err := broker.Ask(ctx, req)
- if err != nil {
- t.Fatalf("Ask failed: %v", err)
- }
-
- go func() {
- time.Sleep(50 * time.Millisecond)
- broker.Resolve(token, PromptReply{
- Secrets: map[string]string{"pin": "1234"},
- Accept: true,
- })
- }()
-
- reply, err := broker.Wait(ctx, token)
- if err != nil {
- t.Fatalf("Wait failed: %v", err)
- }
-
- if reply.Secrets["pin"] != "1234" {
- t.Errorf("expected pin=1234, got %s", reply.Secrets["pin"])
- }
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/manager.go
deleted file mode 100644
index 4408d25..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/manager.go
+++ /dev/null
@@ -1,588 +0,0 @@
-package bluez
-
-import (
- "fmt"
- "strings"
- "sync"
- "time"
-
- "github.com/AvengeMedia/DankMaterialShell/core/internal/log"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/dbusutil"
- "github.com/godbus/dbus/v5"
-)
-
-const (
- adapter1Iface = "org.bluez.Adapter1"
- objectMgrIface = "org.freedesktop.DBus.ObjectManager"
- propertiesIface = "org.freedesktop.DBus.Properties"
-)
-
-func NewManager() (*Manager, error) {
- conn, err := dbus.ConnectSystemBus()
- if err != nil {
- return nil, fmt.Errorf("system bus connection failed: %w", err)
- }
-
- m := &Manager{
- state: &BluetoothState{
- Powered: false,
- Discovering: false,
- Devices: []Device{},
- PairedDevices: []Device{},
- ConnectedDevices: []Device{},
- },
- stateMutex: sync.RWMutex{},
-
- stopChan: make(chan struct{}),
- dbusConn: conn,
- signals: make(chan *dbus.Signal, 256),
- dirty: make(chan struct{}, 1),
- eventQueue: make(chan func(), 32),
- }
-
- broker := NewSubscriptionBroker(m.broadcastPairingPrompt)
- m.promptBroker = broker
-
- adapter, err := m.findAdapter()
- if err != nil {
- conn.Close()
- return nil, fmt.Errorf("no bluetooth adapter found: %w", err)
- }
- m.adapterPath = adapter
-
- if err := m.initialize(); err != nil {
- conn.Close()
- return nil, err
- }
-
- if err := m.startAgent(); err != nil {
- conn.Close()
- return nil, fmt.Errorf("agent start failed: %w", err)
- }
-
- if err := m.startSignalPump(); err != nil {
- m.Close()
- return nil, err
- }
-
- m.notifierWg.Add(1)
- go m.notifier()
-
- m.eventWg.Add(1)
- go m.eventWorker()
-
- return m, nil
-}
-
-func (m *Manager) findAdapter() (dbus.ObjectPath, error) {
- obj := m.dbusConn.Object(bluezService, dbus.ObjectPath("/"))
- var objects map[dbus.ObjectPath]map[string]map[string]dbus.Variant
-
- if err := obj.Call(objectMgrIface+".GetManagedObjects", 0).Store(&objects); err != nil {
- return "", err
- }
-
- for path, interfaces := range objects {
- if _, ok := interfaces[adapter1Iface]; ok {
- log.Infof("[BluezManager] found adapter: %s", path)
- return path, nil
- }
- }
-
- return "", fmt.Errorf("no adapter found")
-}
-
-func (m *Manager) initialize() error {
- if err := m.updateAdapterState(); err != nil {
- return err
- }
-
- if err := m.updateDevices(); err != nil {
- return err
- }
-
- return nil
-}
-
-func (m *Manager) updateAdapterState() error {
- obj := m.dbusConn.Object(bluezService, m.adapterPath)
-
- poweredVar, err := obj.GetProperty(adapter1Iface + ".Powered")
- if err != nil {
- return err
- }
-
- discoveringVar, err := obj.GetProperty(adapter1Iface + ".Discovering")
- if err != nil {
- return err
- }
-
- m.stateMutex.Lock()
- m.state.Powered = dbusutil.AsOr(poweredVar, false)
- m.state.Discovering = dbusutil.AsOr(discoveringVar, false)
- m.stateMutex.Unlock()
-
- return nil
-}
-
-func (m *Manager) updateDevices() error {
- obj := m.dbusConn.Object(bluezService, dbus.ObjectPath("/"))
- var objects map[dbus.ObjectPath]map[string]map[string]dbus.Variant
-
- if err := obj.Call(objectMgrIface+".GetManagedObjects", 0).Store(&objects); err != nil {
- return err
- }
-
- devices := []Device{}
- paired := []Device{}
- connected := []Device{}
-
- for path, interfaces := range objects {
- devProps, ok := interfaces[device1Iface]
- if !ok {
- continue
- }
-
- if !strings.HasPrefix(string(path), string(m.adapterPath)+"/") {
- continue
- }
-
- dev := m.deviceFromProps(string(path), devProps)
- devices = append(devices, dev)
-
- if dev.Paired {
- paired = append(paired, dev)
- }
- if dev.Connected {
- connected = append(connected, dev)
- }
- }
-
- m.stateMutex.Lock()
- m.state.Devices = devices
- m.state.PairedDevices = paired
- m.state.ConnectedDevices = connected
- m.stateMutex.Unlock()
-
- return nil
-}
-
-func (m *Manager) deviceFromProps(path string, props map[string]dbus.Variant) Device {
- return Device{
- Path: path,
- Address: dbusutil.GetOr(props, "Address", ""),
- Name: dbusutil.GetOr(props, "Name", ""),
- Alias: dbusutil.GetOr(props, "Alias", ""),
- Paired: dbusutil.GetOr(props, "Paired", false),
- Trusted: dbusutil.GetOr(props, "Trusted", false),
- Blocked: dbusutil.GetOr(props, "Blocked", false),
- Connected: dbusutil.GetOr(props, "Connected", false),
- Class: dbusutil.GetOr(props, "Class", uint32(0)),
- Icon: dbusutil.GetOr(props, "Icon", ""),
- RSSI: dbusutil.GetOr(props, "RSSI", int16(0)),
- LegacyPairing: dbusutil.GetOr(props, "LegacyPairing", false),
- }
-}
-
-func (m *Manager) startAgent() error {
- if m.promptBroker == nil {
- return fmt.Errorf("prompt broker not initialized")
- }
-
- agent, err := NewBluezAgent(m.promptBroker)
- if err != nil {
- return err
- }
-
- m.agent = agent
- return nil
-}
-
-func (m *Manager) startSignalPump() error {
- m.dbusConn.Signal(m.signals)
-
- if err := m.dbusConn.AddMatchSignal(
- dbus.WithMatchInterface(propertiesIface),
- dbus.WithMatchMember("PropertiesChanged"),
- ); err != nil {
- return err
- }
-
- if err := m.dbusConn.AddMatchSignal(
- dbus.WithMatchInterface(objectMgrIface),
- dbus.WithMatchMember("InterfacesAdded"),
- ); err != nil {
- return err
- }
-
- if err := m.dbusConn.AddMatchSignal(
- dbus.WithMatchInterface(objectMgrIface),
- dbus.WithMatchMember("InterfacesRemoved"),
- ); err != nil {
- return err
- }
-
- m.sigWG.Add(1)
- go func() {
- defer m.sigWG.Done()
- for {
- select {
- case <-m.stopChan:
- return
- case sig, ok := <-m.signals:
- if !ok {
- return
- }
- if sig == nil {
- continue
- }
- m.handleSignal(sig)
- }
- }
- }()
-
- return nil
-}
-
-func (m *Manager) handleSignal(sig *dbus.Signal) {
- switch sig.Name {
- case propertiesIface + ".PropertiesChanged":
- if len(sig.Body) < 2 {
- return
- }
-
- iface, ok := sig.Body[0].(string)
- if !ok {
- return
- }
-
- changed, ok := sig.Body[1].(map[string]dbus.Variant)
- if !ok {
- return
- }
-
- switch iface {
- case adapter1Iface:
- if strings.HasPrefix(string(sig.Path), string(m.adapterPath)) {
- m.handleAdapterPropertiesChanged(changed)
- }
- case device1Iface:
- m.handleDevicePropertiesChanged(sig.Path, changed)
- }
-
- case objectMgrIface + ".InterfacesAdded":
- m.notifySubscribers()
-
- case objectMgrIface + ".InterfacesRemoved":
- m.notifySubscribers()
- }
-}
-
-func (m *Manager) handleAdapterPropertiesChanged(changed map[string]dbus.Variant) {
- m.stateMutex.Lock()
- dirty := false
-
- if powered, ok := dbusutil.Get[bool](changed, "Powered"); ok {
- m.state.Powered = powered
- dirty = true
- }
- if discovering, ok := dbusutil.Get[bool](changed, "Discovering"); ok {
- m.state.Discovering = discovering
- dirty = true
- }
-
- m.stateMutex.Unlock()
-
- if dirty {
- m.notifySubscribers()
- }
-}
-
-func (m *Manager) handleDevicePropertiesChanged(path dbus.ObjectPath, changed map[string]dbus.Variant) {
- paired, hasPaired := dbusutil.Get[bool](changed, "Paired")
- _, hasConnected := changed["Connected"]
- _, hasTrusted := changed["Trusted"]
-
- if hasPaired {
- devicePath := string(path)
- if paired {
- _, wasPending := m.pendingPairings.LoadAndDelete(devicePath)
- if wasPending {
- select {
- case m.eventQueue <- func() {
- time.Sleep(300 * time.Millisecond)
- log.Infof("[Bluetooth] Auto-trusting newly paired device: %s", devicePath)
- if err := m.TrustDevice(devicePath, true); err != nil {
- log.Warnf("[Bluetooth] Auto-trust failed: %v", err)
- }
- log.Infof("[Bluetooth] Auto-connecting newly paired device: %s", devicePath)
- if err := m.ConnectDevice(devicePath); err != nil {
- log.Warnf("[Bluetooth] Auto-connect failed: %v", err)
- }
- }:
- default:
- }
- }
- } else {
- m.pendingPairings.Delete(devicePath)
- }
- }
-
- if hasPaired || hasConnected || hasTrusted {
- select {
- case m.eventQueue <- func() {
- time.Sleep(100 * time.Millisecond)
- m.updateDevices()
- m.notifySubscribers()
- }:
- default:
- }
- }
-}
-
-func (m *Manager) eventWorker() {
- defer m.eventWg.Done()
- for {
- select {
- case <-m.stopChan:
- return
- case event := <-m.eventQueue:
- event()
- }
- }
-}
-
-func (m *Manager) notifier() {
- defer m.notifierWg.Done()
- const minGap = 200 * time.Millisecond
- timer := time.NewTimer(minGap)
- timer.Stop()
- var pending bool
-
- for {
- select {
- case <-m.stopChan:
- timer.Stop()
- return
- case <-m.dirty:
- if pending {
- continue
- }
- pending = true
- timer.Reset(minGap)
- case <-timer.C:
- if !pending {
- continue
- }
- m.updateDevices()
-
- currentState := m.snapshotState()
-
- if m.lastNotifiedState != nil && !stateChanged(m.lastNotifiedState, &currentState) {
- pending = false
- continue
- }
-
- m.subscribers.Range(func(key string, ch chan BluetoothState) bool {
- select {
- case ch <- currentState:
- default:
- }
- return true
- })
-
- stateCopy := currentState
- m.lastNotifiedState = &stateCopy
- pending = false
- }
- }
-}
-
-func (m *Manager) notifySubscribers() {
- select {
- case m.dirty <- struct{}{}:
- default:
- }
-}
-
-func (m *Manager) GetState() BluetoothState {
- return m.snapshotState()
-}
-
-func (m *Manager) snapshotState() BluetoothState {
- m.stateMutex.RLock()
- defer m.stateMutex.RUnlock()
-
- s := *m.state
- s.Devices = append([]Device(nil), m.state.Devices...)
- s.PairedDevices = append([]Device(nil), m.state.PairedDevices...)
- s.ConnectedDevices = append([]Device(nil), m.state.ConnectedDevices...)
- return s
-}
-
-func (m *Manager) Subscribe(id string) chan BluetoothState {
- ch := make(chan BluetoothState, 64)
- m.subscribers.Store(id, ch)
- return ch
-}
-
-func (m *Manager) Unsubscribe(id string) {
- if ch, ok := m.subscribers.LoadAndDelete(id); ok {
- close(ch)
- }
-}
-
-func (m *Manager) SubscribePairing(id string) chan PairingPrompt {
- ch := make(chan PairingPrompt, 16)
- m.pairingSubscribers.Store(id, ch)
- return ch
-}
-
-func (m *Manager) UnsubscribePairing(id string) {
- if ch, ok := m.pairingSubscribers.LoadAndDelete(id); ok {
- close(ch)
- }
-}
-
-func (m *Manager) broadcastPairingPrompt(prompt PairingPrompt) {
- m.pairingSubscribers.Range(func(key string, ch chan PairingPrompt) bool {
- select {
- case ch <- prompt:
- default:
- }
- return true
- })
-}
-
-func (m *Manager) SubmitPairing(token string, secrets map[string]string, accept bool) error {
- if m.promptBroker == nil {
- return fmt.Errorf("prompt broker not initialized")
- }
-
- return m.promptBroker.Resolve(token, PromptReply{
- Secrets: secrets,
- Accept: accept,
- Cancel: false,
- })
-}
-
-func (m *Manager) CancelPairing(token string) error {
- if m.promptBroker == nil {
- return fmt.Errorf("prompt broker not initialized")
- }
-
- return m.promptBroker.Resolve(token, PromptReply{
- Cancel: true,
- })
-}
-
-func (m *Manager) StartDiscovery() error {
- obj := m.dbusConn.Object(bluezService, m.adapterPath)
- return obj.Call(adapter1Iface+".StartDiscovery", 0).Err
-}
-
-func (m *Manager) StopDiscovery() error {
- obj := m.dbusConn.Object(bluezService, m.adapterPath)
- return obj.Call(adapter1Iface+".StopDiscovery", 0).Err
-}
-
-func (m *Manager) SetPowered(powered bool) error {
- obj := m.dbusConn.Object(bluezService, m.adapterPath)
- return obj.Call(propertiesIface+".Set", 0, adapter1Iface, "Powered", dbus.MakeVariant(powered)).Err
-}
-
-func (m *Manager) PairDevice(devicePath string) error {
- m.pendingPairings.Store(devicePath, true)
-
- obj := m.dbusConn.Object(bluezService, dbus.ObjectPath(devicePath))
- err := obj.Call(device1Iface+".Pair", 0).Err
-
- if err != nil {
- m.pendingPairings.Delete(devicePath)
- }
-
- return err
-}
-
-func (m *Manager) ConnectDevice(devicePath string) error {
- obj := m.dbusConn.Object(bluezService, dbus.ObjectPath(devicePath))
- return obj.Call(device1Iface+".Connect", 0).Err
-}
-
-func (m *Manager) DisconnectDevice(devicePath string) error {
- obj := m.dbusConn.Object(bluezService, dbus.ObjectPath(devicePath))
- return obj.Call(device1Iface+".Disconnect", 0).Err
-}
-
-func (m *Manager) RemoveDevice(devicePath string) error {
- obj := m.dbusConn.Object(bluezService, m.adapterPath)
- return obj.Call(adapter1Iface+".RemoveDevice", 0, dbus.ObjectPath(devicePath)).Err
-}
-
-func (m *Manager) TrustDevice(devicePath string, trusted bool) error {
- obj := m.dbusConn.Object(bluezService, dbus.ObjectPath(devicePath))
- return obj.Call(propertiesIface+".Set", 0, device1Iface, "Trusted", dbus.MakeVariant(trusted)).Err
-}
-
-func (m *Manager) Close() {
- close(m.stopChan)
- m.notifierWg.Wait()
- m.eventWg.Wait()
-
- m.sigWG.Wait()
-
- if m.signals != nil {
- m.dbusConn.RemoveSignal(m.signals)
- close(m.signals)
- }
-
- if m.agent != nil {
- m.agent.Close()
- }
-
- m.subscribers.Range(func(key string, ch chan BluetoothState) bool {
- close(ch)
- m.subscribers.Delete(key)
- return true
- })
-
- m.pairingSubscribers.Range(func(key string, ch chan PairingPrompt) bool {
- close(ch)
- m.pairingSubscribers.Delete(key)
- return true
- })
-
- if m.dbusConn != nil {
- m.dbusConn.Close()
- }
-}
-
-func stateChanged(old, new *BluetoothState) bool {
- if old.Powered != new.Powered {
- return true
- }
- if old.Discovering != new.Discovering {
- return true
- }
- if len(old.Devices) != len(new.Devices) {
- return true
- }
- if len(old.PairedDevices) != len(new.PairedDevices) {
- return true
- }
- if len(old.ConnectedDevices) != len(new.ConnectedDevices) {
- return true
- }
- for i := range old.Devices {
- if old.Devices[i].Path != new.Devices[i].Path {
- return true
- }
- if old.Devices[i].Paired != new.Devices[i].Paired {
- return true
- }
- if old.Devices[i].Connected != new.Devices[i].Connected {
- return true
- }
- }
- return false
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/subscription_broker.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/subscription_broker.go
deleted file mode 100644
index ea04a19..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/subscription_broker.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package bluez
-
-import (
- "context"
- "fmt"
-
- "github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
-)
-
-type SubscriptionBroker struct {
- pending syncmap.Map[string, chan PromptReply]
- requests syncmap.Map[string, PromptRequest]
- broadcastPrompt func(PairingPrompt)
-}
-
-func NewSubscriptionBroker(broadcastPrompt func(PairingPrompt)) PromptBroker {
- return &SubscriptionBroker{
- broadcastPrompt: broadcastPrompt,
- }
-}
-
-func (b *SubscriptionBroker) Ask(ctx context.Context, req PromptRequest) (string, error) {
- token, err := generateToken()
- if err != nil {
- return "", err
- }
-
- replyChan := make(chan PromptReply, 1)
- b.pending.Store(token, replyChan)
- b.requests.Store(token, req)
-
- if b.broadcastPrompt != nil {
- prompt := PairingPrompt{
- Token: token,
- DevicePath: req.DevicePath,
- DeviceName: req.DeviceName,
- DeviceAddr: req.DeviceAddr,
- RequestType: req.RequestType,
- Fields: req.Fields,
- Hints: req.Hints,
- Passkey: req.Passkey,
- }
- b.broadcastPrompt(prompt)
- }
-
- return token, nil
-}
-
-func (b *SubscriptionBroker) Wait(ctx context.Context, token string) (PromptReply, error) {
- replyChan, exists := b.pending.Load(token)
- if !exists {
- return PromptReply{}, fmt.Errorf("unknown token: %s", token)
- }
-
- select {
- case <-ctx.Done():
- b.cleanup(token)
- return PromptReply{}, errdefs.ErrSecretPromptTimeout
- case reply := <-replyChan:
- b.cleanup(token)
- if reply.Cancel {
- return reply, errdefs.ErrSecretPromptCancelled
- }
- return reply, nil
- }
-}
-
-func (b *SubscriptionBroker) Resolve(token string, reply PromptReply) error {
- replyChan, exists := b.pending.Load(token)
- if !exists {
- return fmt.Errorf("unknown or expired token: %s", token)
- }
-
- select {
- case replyChan <- reply:
- return nil
- default:
- return fmt.Errorf("failed to deliver reply for token: %s", token)
- }
-}
-
-func (b *SubscriptionBroker) cleanup(token string) {
- b.pending.Delete(token)
- b.requests.Delete(token)
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/types.go
deleted file mode 100644
index 663acca..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/types.go
+++ /dev/null
@@ -1,78 +0,0 @@
-package bluez
-
-import (
- "sync"
-
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
- "github.com/godbus/dbus/v5"
-)
-
-type BluetoothState struct {
- Powered bool `json:"powered"`
- Discovering bool `json:"discovering"`
- Devices []Device `json:"devices"`
- PairedDevices []Device `json:"pairedDevices"`
- ConnectedDevices []Device `json:"connectedDevices"`
-}
-
-type Device struct {
- Path string `json:"path"`
- Address string `json:"address"`
- Name string `json:"name"`
- Alias string `json:"alias"`
- Paired bool `json:"paired"`
- Trusted bool `json:"trusted"`
- Blocked bool `json:"blocked"`
- Connected bool `json:"connected"`
- Class uint32 `json:"class"`
- Icon string `json:"icon"`
- RSSI int16 `json:"rssi"`
- LegacyPairing bool `json:"legacyPairing"`
-}
-
-type PromptRequest struct {
- DevicePath string `json:"devicePath"`
- DeviceName string `json:"deviceName"`
- DeviceAddr string `json:"deviceAddr"`
- RequestType string `json:"requestType"`
- Fields []string `json:"fields"`
- Hints []string `json:"hints"`
- Passkey *uint32 `json:"passkey,omitempty"`
-}
-
-type PromptReply struct {
- Secrets map[string]string `json:"secrets"`
- Accept bool `json:"accept"`
- Cancel bool `json:"cancel"`
-}
-
-type PairingPrompt struct {
- Token string `json:"token"`
- DevicePath string `json:"devicePath"`
- DeviceName string `json:"deviceName"`
- DeviceAddr string `json:"deviceAddr"`
- RequestType string `json:"requestType"`
- Fields []string `json:"fields"`
- Hints []string `json:"hints"`
- Passkey *uint32 `json:"passkey,omitempty"`
-}
-
-type Manager struct {
- state *BluetoothState
- stateMutex sync.RWMutex
- subscribers syncmap.Map[string, chan BluetoothState]
- stopChan chan struct{}
- dbusConn *dbus.Conn
- signals chan *dbus.Signal
- sigWG sync.WaitGroup
- agent *BluezAgent
- promptBroker PromptBroker
- pairingSubscribers syncmap.Map[string, chan PairingPrompt]
- dirty chan struct{}
- notifierWg sync.WaitGroup
- lastNotifiedState *BluetoothState
- adapterPath dbus.ObjectPath
- pendingPairings syncmap.Map[string, bool]
- eventQueue chan func()
- eventWg sync.WaitGroup
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/types_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/types_test.go
deleted file mode 100644
index ab5b857..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/types_test.go
+++ /dev/null
@@ -1,210 +0,0 @@
-package bluez
-
-import (
- "encoding/json"
- "testing"
-)
-
-func TestBluetoothStateJSON(t *testing.T) {
- state := BluetoothState{
- Powered: true,
- Discovering: false,
- Devices: []Device{
- {
- Path: "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF",
- Address: "AA:BB:CC:DD:EE:FF",
- Name: "TestDevice",
- Alias: "My Device",
- Paired: true,
- Trusted: false,
- Connected: true,
- Class: 0x240418,
- Icon: "audio-headset",
- RSSI: -50,
- },
- },
- PairedDevices: []Device{
- {
- Path: "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF",
- Address: "AA:BB:CC:DD:EE:FF",
- Paired: true,
- },
- },
- ConnectedDevices: []Device{
- {
- Path: "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF",
- Address: "AA:BB:CC:DD:EE:FF",
- Connected: true,
- },
- },
- }
-
- data, err := json.Marshal(state)
- if err != nil {
- t.Fatalf("failed to marshal state: %v", err)
- }
-
- var decoded BluetoothState
- if err := json.Unmarshal(data, &decoded); err != nil {
- t.Fatalf("failed to unmarshal state: %v", err)
- }
-
- if decoded.Powered != state.Powered {
- t.Errorf("expected Powered=%v, got %v", state.Powered, decoded.Powered)
- }
-
- if len(decoded.Devices) != 1 {
- t.Fatalf("expected 1 device, got %d", len(decoded.Devices))
- }
-
- if decoded.Devices[0].Address != "AA:BB:CC:DD:EE:FF" {
- t.Errorf("expected address AA:BB:CC:DD:EE:FF, got %s", decoded.Devices[0].Address)
- }
-}
-
-func TestDeviceJSON(t *testing.T) {
- device := Device{
- Path: "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF",
- Address: "AA:BB:CC:DD:EE:FF",
- Name: "TestDevice",
- Alias: "My Device",
- Paired: true,
- Trusted: true,
- Blocked: false,
- Connected: true,
- Class: 0x240418,
- Icon: "audio-headset",
- RSSI: -50,
- LegacyPairing: false,
- }
-
- data, err := json.Marshal(device)
- if err != nil {
- t.Fatalf("failed to marshal device: %v", err)
- }
-
- var decoded Device
- if err := json.Unmarshal(data, &decoded); err != nil {
- t.Fatalf("failed to unmarshal device: %v", err)
- }
-
- if decoded.Address != device.Address {
- t.Errorf("expected Address=%s, got %s", device.Address, decoded.Address)
- }
-
- if decoded.Name != device.Name {
- t.Errorf("expected Name=%s, got %s", device.Name, decoded.Name)
- }
-
- if decoded.Paired != device.Paired {
- t.Errorf("expected Paired=%v, got %v", device.Paired, decoded.Paired)
- }
-
- if decoded.RSSI != device.RSSI {
- t.Errorf("expected RSSI=%d, got %d", device.RSSI, decoded.RSSI)
- }
-}
-
-func TestPairingPromptJSON(t *testing.T) {
- passkey := uint32(123456)
- prompt := PairingPrompt{
- Token: "test-token",
- DevicePath: "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF",
- DeviceName: "TestDevice",
- DeviceAddr: "AA:BB:CC:DD:EE:FF",
- RequestType: "confirm",
- Fields: []string{"decision"},
- Hints: []string{},
- Passkey: &passkey,
- }
-
- data, err := json.Marshal(prompt)
- if err != nil {
- t.Fatalf("failed to marshal prompt: %v", err)
- }
-
- var decoded PairingPrompt
- if err := json.Unmarshal(data, &decoded); err != nil {
- t.Fatalf("failed to unmarshal prompt: %v", err)
- }
-
- if decoded.Token != prompt.Token {
- t.Errorf("expected Token=%s, got %s", prompt.Token, decoded.Token)
- }
-
- if decoded.DeviceName != prompt.DeviceName {
- t.Errorf("expected DeviceName=%s, got %s", prompt.DeviceName, decoded.DeviceName)
- }
-
- if decoded.Passkey == nil {
- t.Fatal("expected non-nil Passkey")
- }
-
- if *decoded.Passkey != *prompt.Passkey {
- t.Errorf("expected Passkey=%d, got %d", *prompt.Passkey, *decoded.Passkey)
- }
-}
-
-func TestPromptReplyJSON(t *testing.T) {
- reply := PromptReply{
- Secrets: map[string]string{
- "pin": "1234",
- "passkey": "567890",
- },
- Accept: true,
- Cancel: false,
- }
-
- data, err := json.Marshal(reply)
- if err != nil {
- t.Fatalf("failed to marshal reply: %v", err)
- }
-
- var decoded PromptReply
- if err := json.Unmarshal(data, &decoded); err != nil {
- t.Fatalf("failed to unmarshal reply: %v", err)
- }
-
- if decoded.Secrets["pin"] != reply.Secrets["pin"] {
- t.Errorf("expected pin=%s, got %s", reply.Secrets["pin"], decoded.Secrets["pin"])
- }
-
- if decoded.Accept != reply.Accept {
- t.Errorf("expected Accept=%v, got %v", reply.Accept, decoded.Accept)
- }
-}
-
-func TestPromptRequestJSON(t *testing.T) {
- passkey := uint32(123456)
- req := PromptRequest{
- DevicePath: "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF",
- DeviceName: "TestDevice",
- DeviceAddr: "AA:BB:CC:DD:EE:FF",
- RequestType: "confirm",
- Fields: []string{"decision"},
- Hints: []string{"hint1", "hint2"},
- Passkey: &passkey,
- }
-
- data, err := json.Marshal(req)
- if err != nil {
- t.Fatalf("failed to marshal request: %v", err)
- }
-
- var decoded PromptRequest
- if err := json.Unmarshal(data, &decoded); err != nil {
- t.Fatalf("failed to unmarshal request: %v", err)
- }
-
- if decoded.DevicePath != req.DevicePath {
- t.Errorf("expected DevicePath=%s, got %s", req.DevicePath, decoded.DevicePath)
- }
-
- if decoded.RequestType != req.RequestType {
- t.Errorf("expected RequestType=%s, got %s", req.RequestType, decoded.RequestType)
- }
-
- if len(decoded.Fields) != len(req.Fields) {
- t.Errorf("expected %d fields, got %d", len(req.Fields), len(decoded.Fields))
- }
-}