diff options
| author | AlexanderCurl <alexc@alexc.hu> | 2026-04-18 17:46:06 +0100 |
|---|---|---|
| committer | AlexanderCurl <alexc@alexc.hu> | 2026-04-18 17:46:06 +0100 |
| commit | 70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69 (patch) | |
| tree | ab1123d4067c1b086dd6faa7ee4ea643236b565a /raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus | |
| parent | 5d94c0a7d44a2255b81815a52a7056a94a39842d (diff) | |
| download | RaveOS-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')
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"` +} |