diff --git a/kittens/desktop_ui/main.go b/kittens/desktop_ui/main.go index f67a3c8d7..6bc7dad87 100644 --- a/kittens/desktop_ui/main.go +++ b/kittens/desktop_ui/main.go @@ -71,6 +71,37 @@ func EntryPoint(root *cli.Command) { return utils.IfElse(err == nil, 0, 1), err }, }) + st := parent.AddSubCommand(&cli.Command{ + Name: "set-setting", + ShortDescription: "Change an arbitrary setting", + Usage: " key [value]", + HelpText: "Set an arbitrary setting. If you want to set the color-scheme use the dedicated command for it. Use this command with care as it does no validation for the type of value. The syntax for specifying values is described at: :link:`the glib docs `. Leaving out the value or specifying an empty value, will delete the setting.", + Run: func(cmd *cli.Command, args []string) (rc int, err error) { + val := "" + if len(args) < 1 { + cmd.ShowHelp() + return 1, fmt.Errorf("must specify the key") + } + if len(args) > 1 { + val = args[1] + } + opts := SetOptions{} + if err = cmd.GetOptionValues(&opts); err == nil { + err = set_setting(args[0], val, &opts) + } + return utils.IfElse(err == nil, 0, 1), err + }, + }) + st.Add(cli.OptionSpec{ + Name: "--namespace -n", + Help: "The namespace in which to change the setting.", + Default: PORTAL_APPEARANCE_NAMESPACE, + }) + st.Add(cli.OptionSpec{ + Name: "--data-type", + Help: "The DBUS data type signature of the value. The default is to guess from the textual representation, see :link:`the glib docs ` for details.", + }) + ss := parent.AddSubCommand(&cli.Command{ Name: "show-settings", ShortDescription: "Print the current values of the desktop settings", diff --git a/kittens/desktop_ui/portal.go b/kittens/desktop_ui/portal.go index 2c0ea62f5..54307b9b4 100644 --- a/kittens/desktop_ui/portal.go +++ b/kittens/desktop_ui/portal.go @@ -171,6 +171,7 @@ func (self *Portal) Start() (err error) { } methods = MethodSpec{ "ChangeSetting": {{"namespace", "s", false}, {"key", "s", false}, {"value", "v", false}}, + "RemoveSetting": {{"namespace", "s", false}, {"key", "s", false}}, } props["version"].Value = uint32(1) if err = ExportInterface(self.bus, self, CHANGE_SETTINGS_INTERFACE, CHANGE_SETTINGS_OBJECT_PATH, methods, props, nil); err != nil { @@ -189,7 +190,10 @@ func ParseValueWithSignature(value, value_type_signature string) (v dbus.Variant } v, err = dbus.ParseVariant(value, s) if err != nil { - return dbus.Variant{}, fmt.Errorf("%s is not a valid value for signature: %s with error: %w", value, value_type_signature, err) + if value_type_signature == "" { + return dbus.Variant{}, fmt.Errorf("could not guess the data type of: %s with error: %w", value, err) + } + return dbus.Variant{}, fmt.Errorf("%s is not a valid value for signature: %#v with error: %w", value, value_type_signature, err) } return v, nil } @@ -414,6 +418,35 @@ Exec=kitten run-server // }}} +type SetOptions struct { + Namespace, DataType string +} + +func set_setting(key, value string, opts *SetOptions) (err error) { + conn, err := dbus.SessionBus() + if err != nil { + return fmt.Errorf("failed to connect to system bus with error: %w", err) + } + defer conn.Close() + method := "ChangeSetting" + var vals = []any{opts.Namespace, key} + if value == "" { + method = "RemoveSetting" + } else { + v, err := ParseValueWithSignature(value, opts.DataType) + if err != nil { + return err + } + vals = append(vals, v) + } + obj := conn.Object(PORTAL_BUS_NAME, dbus.ObjectPath(CHANGE_SETTINGS_OBJECT_PATH)) + call := obj.Call(CHANGE_SETTINGS_INTERFACE+"."+method, dbus.FlagNoAutoStart, vals...) + if err = call.Store(); err != nil { + return fmt.Errorf("failed to call %s with error: %w", method, err) + } + return +} + func set_color_scheme(which string) (err error) { conn, err := dbus.SessionBus() if err != nil { @@ -479,6 +512,20 @@ func (self *Portal) ChangeSetting(namespace, key string, value dbus.Variant) *db return nil } +func (self *Portal) RemoveSetting(namespace, key string) *dbus.Error { + self.lock.Lock() + defer self.lock.Unlock() + existed := false + if m := self.settings[namespace]; m != nil { + _, existed = m[key] + } + if !existed { + return nil + } + delete(self.settings[namespace], key) + return nil +} + func (self *Portal) Read(namespace, key string) (dbus.Variant, *dbus.Error) { self.lock.Lock() defer self.lock.Unlock()