summaryrefslogtreecommitdiff
path: root/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus
diff options
context:
space:
mode:
authorAlexanderCurl <alexc@alexc.hu>2026-04-18 17:46:06 +0100
committerAlexanderCurl <alexc@alexc.hu>2026-04-18 17:46:06 +0100
commit70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69 (patch)
treeab1123d4067c1b086dd6faa7ee4ea643236b565a /raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus
parent5d94c0a7d44a2255b81815a52a7056a94a39842d (diff)
downloadRaveOS-PKGBUILD-70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69.tar.gz
RaveOS-PKGBUILD-70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69.zip
Replaced file structures for themes in the correct format
Diffstat (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus')
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus/handlers.go237
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus/manager.go392
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus/types.go52
3 files changed, 681 insertions, 0 deletions
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus/handlers.go
new file mode 100644
index 0000000..e625d79
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus/handlers.go
@@ -0,0 +1,237 @@
+package dbus
+
+import (
+ "fmt"
+ "net"
+
+ "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
+ "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params"
+)
+
+type objectParams struct {
+ bus string
+ dest string
+ path string
+ iface string
+}
+
+func extractObjectParams(p map[string]any, requirePath bool) (objectParams, error) {
+ bus, err := params.String(p, "bus")
+ if err != nil {
+ return objectParams{}, err
+ }
+ dest, err := params.String(p, "dest")
+ if err != nil {
+ return objectParams{}, err
+ }
+
+ var path string
+ if requirePath {
+ path, err = params.String(p, "path")
+ if err != nil {
+ return objectParams{}, err
+ }
+ } else {
+ path = params.StringOpt(p, "path", "/")
+ }
+
+ iface, err := params.String(p, "interface")
+ if err != nil {
+ return objectParams{}, err
+ }
+
+ return objectParams{bus: bus, dest: dest, path: path, iface: iface}, nil
+}
+
+func HandleRequest(conn net.Conn, req models.Request, m *Manager, clientID string) {
+ switch req.Method {
+ case "dbus.call":
+ handleCall(conn, req, m)
+ case "dbus.getProperty":
+ handleGetProperty(conn, req, m)
+ case "dbus.setProperty":
+ handleSetProperty(conn, req, m)
+ case "dbus.getAllProperties":
+ handleGetAllProperties(conn, req, m)
+ case "dbus.introspect":
+ handleIntrospect(conn, req, m)
+ case "dbus.listNames":
+ handleListNames(conn, req, m)
+ case "dbus.subscribe":
+ handleSubscribe(conn, req, m, clientID)
+ case "dbus.unsubscribe":
+ handleUnsubscribe(conn, req, m)
+ default:
+ models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method))
+ }
+}
+
+func handleCall(conn net.Conn, req models.Request, m *Manager) {
+ op, err := extractObjectParams(req.Params, true)
+ if err != nil {
+ models.RespondError(conn, req.ID, err.Error())
+ return
+ }
+
+ method, err := params.String(req.Params, "method")
+ if err != nil {
+ models.RespondError(conn, req.ID, err.Error())
+ return
+ }
+
+ var args []any
+ if argsRaw, ok := params.Any(req.Params, "args"); ok {
+ if argsSlice, ok := argsRaw.([]any); ok {
+ args = argsSlice
+ }
+ }
+
+ result, err := m.Call(op.bus, op.dest, op.path, op.iface, method, args)
+ if err != nil {
+ models.RespondError(conn, req.ID, err.Error())
+ return
+ }
+
+ models.Respond(conn, req.ID, result)
+}
+
+func handleGetProperty(conn net.Conn, req models.Request, m *Manager) {
+ op, err := extractObjectParams(req.Params, true)
+ if err != nil {
+ models.RespondError(conn, req.ID, err.Error())
+ return
+ }
+
+ property, err := params.String(req.Params, "property")
+ if err != nil {
+ models.RespondError(conn, req.ID, err.Error())
+ return
+ }
+
+ result, err := m.GetProperty(op.bus, op.dest, op.path, op.iface, property)
+ if err != nil {
+ models.RespondError(conn, req.ID, err.Error())
+ return
+ }
+
+ models.Respond(conn, req.ID, result)
+}
+
+func handleSetProperty(conn net.Conn, req models.Request, m *Manager) {
+ op, err := extractObjectParams(req.Params, true)
+ if err != nil {
+ models.RespondError(conn, req.ID, err.Error())
+ return
+ }
+
+ property, err := params.String(req.Params, "property")
+ if err != nil {
+ models.RespondError(conn, req.ID, err.Error())
+ return
+ }
+
+ value, ok := params.Any(req.Params, "value")
+ if !ok {
+ models.RespondError(conn, req.ID, "missing 'value' parameter")
+ return
+ }
+
+ if err := m.SetProperty(op.bus, op.dest, op.path, op.iface, property, value); err != nil {
+ models.RespondError(conn, req.ID, err.Error())
+ return
+ }
+
+ models.Respond(conn, req.ID, models.SuccessResult{Success: true})
+}
+
+func handleGetAllProperties(conn net.Conn, req models.Request, m *Manager) {
+ op, err := extractObjectParams(req.Params, true)
+ if err != nil {
+ models.RespondError(conn, req.ID, err.Error())
+ return
+ }
+
+ result, err := m.GetAllProperties(op.bus, op.dest, op.path, op.iface)
+ if err != nil {
+ models.RespondError(conn, req.ID, err.Error())
+ return
+ }
+
+ models.Respond(conn, req.ID, result)
+}
+
+func handleIntrospect(conn net.Conn, req models.Request, m *Manager) {
+ bus, err := params.String(req.Params, "bus")
+ if err != nil {
+ models.RespondError(conn, req.ID, err.Error())
+ return
+ }
+
+ dest, err := params.String(req.Params, "dest")
+ if err != nil {
+ models.RespondError(conn, req.ID, err.Error())
+ return
+ }
+
+ path := params.StringOpt(req.Params, "path", "/")
+
+ result, err := m.Introspect(bus, dest, path)
+ if err != nil {
+ models.RespondError(conn, req.ID, err.Error())
+ return
+ }
+
+ models.Respond(conn, req.ID, result)
+}
+
+func handleListNames(conn net.Conn, req models.Request, m *Manager) {
+ bus, err := params.String(req.Params, "bus")
+ if err != nil {
+ models.RespondError(conn, req.ID, err.Error())
+ return
+ }
+
+ result, err := m.ListNames(bus)
+ if err != nil {
+ models.RespondError(conn, req.ID, err.Error())
+ return
+ }
+
+ models.Respond(conn, req.ID, result)
+}
+
+func handleSubscribe(conn net.Conn, req models.Request, m *Manager, clientID string) {
+ bus, err := params.String(req.Params, "bus")
+ if err != nil {
+ models.RespondError(conn, req.ID, err.Error())
+ return
+ }
+
+ sender := params.StringOpt(req.Params, "sender", "")
+ path := params.StringOpt(req.Params, "path", "")
+ iface := params.StringOpt(req.Params, "interface", "")
+ member := params.StringOpt(req.Params, "member", "")
+
+ result, err := m.Subscribe(clientID, bus, sender, path, iface, member)
+ if err != nil {
+ models.RespondError(conn, req.ID, err.Error())
+ return
+ }
+
+ models.Respond(conn, req.ID, result)
+}
+
+func handleUnsubscribe(conn net.Conn, req models.Request, m *Manager) {
+ subID, err := params.String(req.Params, "subscriptionId")
+ if err != nil {
+ models.RespondError(conn, req.ID, err.Error())
+ return
+ }
+
+ if err := m.Unsubscribe(subID); err != nil {
+ models.RespondError(conn, req.ID, err.Error())
+ return
+ }
+
+ models.Respond(conn, req.ID, models.SuccessResult{Success: true})
+}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus/manager.go
new file mode 100644
index 0000000..86da6bd
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus/manager.go
@@ -0,0 +1,392 @@
+package dbus
+
+import (
+ "crypto/rand"
+ "encoding/hex"
+ "fmt"
+ "strings"
+
+ "github.com/AvengeMedia/DankMaterialShell/core/internal/log"
+ "github.com/AvengeMedia/DankMaterialShell/core/pkg/dbusutil"
+ "github.com/godbus/dbus/v5"
+)
+
+func NewManager() (*Manager, error) {
+ systemConn, err := dbus.ConnectSystemBus()
+ if err != nil {
+ return nil, fmt.Errorf("failed to connect to system bus: %w", err)
+ }
+
+ sessionConn, err := dbus.ConnectSessionBus()
+ if err != nil {
+ systemConn.Close()
+ return nil, fmt.Errorf("failed to connect to session bus: %w", err)
+ }
+
+ m := &Manager{
+ systemConn: systemConn,
+ sessionConn: sessionConn,
+ }
+
+ go m.processSystemSignals()
+ go m.processSessionSignals()
+
+ return m, nil
+}
+
+func (m *Manager) getConn(bus string) (*dbus.Conn, error) {
+ switch bus {
+ case "system":
+ if m.systemConn == nil {
+ return nil, fmt.Errorf("system bus not connected")
+ }
+ return m.systemConn, nil
+ case "session":
+ if m.sessionConn == nil {
+ return nil, fmt.Errorf("session bus not connected")
+ }
+ return m.sessionConn, nil
+ default:
+ return nil, fmt.Errorf("invalid bus: %s (must be 'system' or 'session')", bus)
+ }
+}
+
+func (m *Manager) Call(bus, dest, path, iface, method string, args []any) (*CallResult, error) {
+ conn, err := m.getConn(bus)
+ if err != nil {
+ return nil, err
+ }
+
+ obj := conn.Object(dest, dbus.ObjectPath(path))
+ fullMethod := iface + "." + method
+
+ convertedArgs := convertArgs(args)
+ call := obj.Call(fullMethod, 0, convertedArgs...)
+ if call.Err != nil {
+ return nil, fmt.Errorf("dbus call failed: %w", call.Err)
+ }
+
+ return &CallResult{Values: dbusutil.NormalizeSlice(call.Body)}, nil
+}
+
+func convertArgs(args []any) []any {
+ result := make([]any, len(args))
+ for i, arg := range args {
+ result[i] = convertArg(arg)
+ }
+ return result
+}
+
+func convertArg(arg any) any {
+ switch v := arg.(type) {
+ case float64:
+ if v == float64(uint32(v)) && v >= 0 && v <= float64(^uint32(0)) {
+ return uint32(v)
+ }
+ if v == float64(int32(v)) {
+ return int32(v)
+ }
+ return v
+ case []any:
+ return convertArgs(v)
+ case map[string]any:
+ result := make(map[string]any)
+ for k, val := range v {
+ result[k] = convertArg(val)
+ }
+ return result
+ default:
+ return arg
+ }
+}
+
+func (m *Manager) GetProperty(bus, dest, path, iface, property string) (*PropertyResult, error) {
+ conn, err := m.getConn(bus)
+ if err != nil {
+ return nil, err
+ }
+
+ obj := conn.Object(dest, dbus.ObjectPath(path))
+
+ var variant dbus.Variant
+ err = obj.Call("org.freedesktop.DBus.Properties.Get", 0, iface, property).Store(&variant)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get property: %w", err)
+ }
+
+ return &PropertyResult{Value: dbusutil.Normalize(variant.Value())}, nil
+}
+
+func (m *Manager) SetProperty(bus, dest, path, iface, property string, value any) error {
+ conn, err := m.getConn(bus)
+ if err != nil {
+ return err
+ }
+
+ obj := conn.Object(dest, dbus.ObjectPath(path))
+
+ call := obj.Call("org.freedesktop.DBus.Properties.Set", 0, iface, property, dbus.MakeVariant(value))
+ if call.Err != nil {
+ return fmt.Errorf("failed to set property: %w", call.Err)
+ }
+
+ return nil
+}
+
+func (m *Manager) GetAllProperties(bus, dest, path, iface string) (map[string]any, error) {
+ conn, err := m.getConn(bus)
+ if err != nil {
+ return nil, err
+ }
+
+ obj := conn.Object(dest, dbus.ObjectPath(path))
+
+ var props map[string]dbus.Variant
+ err = obj.Call("org.freedesktop.DBus.Properties.GetAll", 0, iface).Store(&props)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get properties: %w", err)
+ }
+
+ result := make(map[string]any)
+ for k, v := range props {
+ result[k] = dbusutil.Normalize(v.Value())
+ }
+
+ return result, nil
+}
+
+func (m *Manager) Introspect(bus, dest, path string) (*IntrospectResult, error) {
+ conn, err := m.getConn(bus)
+ if err != nil {
+ return nil, err
+ }
+
+ obj := conn.Object(dest, dbus.ObjectPath(path))
+
+ var xml string
+ err = obj.Call("org.freedesktop.DBus.Introspectable.Introspect", 0).Store(&xml)
+ if err != nil {
+ return nil, fmt.Errorf("failed to introspect: %w", err)
+ }
+
+ return &IntrospectResult{XML: xml}, nil
+}
+
+func (m *Manager) ListNames(bus string) (*ListNamesResult, error) {
+ conn, err := m.getConn(bus)
+ if err != nil {
+ return nil, err
+ }
+
+ var names []string
+ err = conn.BusObject().Call("org.freedesktop.DBus.ListNames", 0).Store(&names)
+ if err != nil {
+ return nil, fmt.Errorf("failed to list names: %w", err)
+ }
+
+ return &ListNamesResult{Names: names}, nil
+}
+
+func (m *Manager) Subscribe(clientID, bus, sender, path, iface, member string) (*SubscribeResult, error) {
+ conn, err := m.getConn(bus)
+ if err != nil {
+ return nil, err
+ }
+
+ subID := generateSubscriptionID()
+
+ parts := []string{"type='signal'"}
+ if sender != "" {
+ parts = append(parts, fmt.Sprintf("sender='%s'", sender))
+ }
+ if path != "" {
+ parts = append(parts, fmt.Sprintf("path='%s'", path))
+ }
+ if iface != "" {
+ parts = append(parts, fmt.Sprintf("interface='%s'", iface))
+ }
+ if member != "" {
+ parts = append(parts, fmt.Sprintf("member='%s'", member))
+ }
+ matchRule := strings.Join(parts, ",")
+
+ call := conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, matchRule)
+ if call.Err != nil {
+ return nil, fmt.Errorf("failed to add match rule: %w", call.Err)
+ }
+
+ sub := &signalSubscription{
+ Bus: bus,
+ Sender: sender,
+ Path: path,
+ Interface: iface,
+ Member: member,
+ ClientID: clientID,
+ }
+ m.subscriptions.Store(subID, sub)
+
+ log.Debugf("dbus: subscribed %s to %s", subID, matchRule)
+
+ return &SubscribeResult{SubscriptionID: subID}, nil
+}
+
+func (m *Manager) Unsubscribe(subID string) error {
+ sub, ok := m.subscriptions.LoadAndDelete(subID)
+ if !ok {
+ return fmt.Errorf("subscription not found: %s", subID)
+ }
+
+ conn, err := m.getConn(sub.Bus)
+ if err != nil {
+ return err
+ }
+
+ parts := []string{"type='signal'"}
+ if sub.Sender != "" {
+ parts = append(parts, fmt.Sprintf("sender='%s'", sub.Sender))
+ }
+ if sub.Path != "" {
+ parts = append(parts, fmt.Sprintf("path='%s'", sub.Path))
+ }
+ if sub.Interface != "" {
+ parts = append(parts, fmt.Sprintf("interface='%s'", sub.Interface))
+ }
+ if sub.Member != "" {
+ parts = append(parts, fmt.Sprintf("member='%s'", sub.Member))
+ }
+ matchRule := strings.Join(parts, ",")
+
+ call := conn.BusObject().Call("org.freedesktop.DBus.RemoveMatch", 0, matchRule)
+ if call.Err != nil {
+ log.Warnf("dbus: failed to remove match rule: %v", call.Err)
+ }
+
+ log.Debugf("dbus: unsubscribed %s", subID)
+
+ return nil
+}
+
+func (m *Manager) UnsubscribeClient(clientID string) {
+ var toDelete []string
+ m.subscriptions.Range(func(subID string, sub *signalSubscription) bool {
+ if sub.ClientID == clientID {
+ toDelete = append(toDelete, subID)
+ }
+ return true
+ })
+
+ for _, subID := range toDelete {
+ _ = m.Unsubscribe(subID)
+ }
+}
+
+func (m *Manager) SubscribeSignals(clientID string) chan SignalEvent {
+ ch := make(chan SignalEvent, 64)
+ existing, loaded := m.signalSubscribers.LoadOrStore(clientID, ch)
+ if loaded {
+ return existing
+ }
+ return ch
+}
+
+func (m *Manager) UnsubscribeSignals(clientID string) {
+ if ch, ok := m.signalSubscribers.LoadAndDelete(clientID); ok {
+ close(ch)
+ }
+ m.UnsubscribeClient(clientID)
+}
+
+func (m *Manager) processSystemSignals() {
+ if m.systemConn == nil {
+ return
+ }
+ ch := make(chan *dbus.Signal, 256)
+ m.systemConn.Signal(ch)
+
+ for sig := range ch {
+ m.dispatchSignal("system", sig)
+ }
+}
+
+func (m *Manager) processSessionSignals() {
+ if m.sessionConn == nil {
+ return
+ }
+ ch := make(chan *dbus.Signal, 256)
+ m.sessionConn.Signal(ch)
+
+ for sig := range ch {
+ m.dispatchSignal("session", sig)
+ }
+}
+
+func (m *Manager) dispatchSignal(bus string, sig *dbus.Signal) {
+ path := string(sig.Path)
+ iface := ""
+ member := sig.Name
+
+ if idx := strings.LastIndex(sig.Name, "."); idx != -1 {
+ iface = sig.Name[:idx]
+ member = sig.Name[idx+1:]
+ }
+
+ m.subscriptions.Range(func(subID string, sub *signalSubscription) bool {
+ if sub.Bus != bus {
+ return true
+ }
+ if sub.Path != "" && sub.Path != path && !strings.HasPrefix(path, sub.Path) {
+ return true
+ }
+ if sub.Interface != "" && sub.Interface != iface {
+ return true
+ }
+ if sub.Member != "" && sub.Member != member {
+ return true
+ }
+
+ event := SignalEvent{
+ SubscriptionID: subID,
+ Sender: sig.Sender,
+ Path: path,
+ Interface: iface,
+ Member: member,
+ Body: dbusutil.NormalizeSlice(sig.Body),
+ }
+
+ ch, ok := m.signalSubscribers.Load(sub.ClientID)
+ if !ok {
+ return true
+ }
+
+ select {
+ case ch <- event:
+ default:
+ log.Warnf("dbus: channel full for %s, dropping signal", subID)
+ }
+
+ return true
+ })
+}
+
+func (m *Manager) Close() {
+ m.signalSubscribers.Range(func(clientID string, ch chan SignalEvent) bool {
+ close(ch)
+ m.signalSubscribers.Delete(clientID)
+ return true
+ })
+
+ if m.systemConn != nil {
+ m.systemConn.Close()
+ }
+ if m.sessionConn != nil {
+ m.sessionConn.Close()
+ }
+}
+
+func generateSubscriptionID() string {
+ b := make([]byte, 8)
+ if _, err := rand.Read(b); err != nil {
+ log.Warnf("dbus: failed to generate random subscription ID: %v", err)
+ }
+ return hex.EncodeToString(b)
+}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus/types.go
new file mode 100644
index 0000000..55c45bf
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus/types.go
@@ -0,0 +1,52 @@
+package dbus
+
+import (
+ "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+ "github.com/godbus/dbus/v5"
+)
+
+type Manager struct {
+ systemConn *dbus.Conn
+ sessionConn *dbus.Conn
+
+ subscriptions syncmap.Map[string, *signalSubscription]
+ signalSubscribers syncmap.Map[string, chan SignalEvent]
+}
+
+type signalSubscription struct {
+ Bus string
+ Sender string
+ Path string
+ Interface string
+ Member string
+ ClientID string
+}
+
+type SignalEvent struct {
+ SubscriptionID string `json:"subscriptionId"`
+ Sender string `json:"sender"`
+ Path string `json:"path"`
+ Interface string `json:"interface"`
+ Member string `json:"member"`
+ Body []any `json:"body"`
+}
+
+type CallResult struct {
+ Values []any `json:"values"`
+}
+
+type PropertyResult struct {
+ Value any `json:"value"`
+}
+
+type IntrospectResult struct {
+ XML string `json:"xml"`
+}
+
+type ListNamesResult struct {
+ Names []string `json:"names"`
+}
+
+type SubscribeResult struct {
+ SubscriptionID string `json:"subscriptionId"`
+}